Sprachen: English | 简体中文 | 繁體中文 | 日本語 | 한국어 | Français | Deutsch | Español | Italiano | Русский | العربية
Dieses Dokument erklärt, wie der DynCode-Compiler auf neue Zielplattformen erweitert wird. Aktuell unterstützt: arm64 / x86_64 auf macOS / Linux / Android / Windows (8 Triples), jeweils mit unabhängigen User / Kernel Kontexten (16 Varianten insgesamt). Das Hinzufügen einer neuen Plattform erfordert typischerweise einige hundert Codezeilen.
Alle Passes sind zielunabhängig. Plattformunterschiede konzentrieren sich auf zwei Stellen:
TargetDesc.cppsdescribeTriple()Tabelleneinträge- Drei Extraktoren (Mach-O / ELF / COFF) Architektur-Switches
Neue Plattform hinzufügen = eine Zeile in (1) + ein Case in (2).
Den entsprechenden OS-Zweig in describeTriple() hinzufügen:
if (TT.isOSFreeBSD()) {
D.OS = DynCodeOS::FreeBSD;
D.Format = ObjectFormat::ELF;
D.TextSectionName = ".text";
if (D.Arch == DynCodeArch::X86_64) {
D.Syscall = SyscallABI::FreeBSDSyscall;
D.AsmTemplate = "syscall";
D.SyscallNumberReg = "rax";
D.SyscallRetReg = "rax";
D.ArgRegs = kX86_64FreeBSDArgRegs;
D.NumArgRegs = 6;
D.DriverInjectFlags = kX86_64UnixInjectFlags;
}
return D;
}Pflichtfelder (alle in TargetDesc.h definiert):
| Feld | Zweck | Bei Fehlen |
|---|---|---|
OS / Arch / Format |
Dispatch-Schlüssel | describeTriple gibt Unknown zurück → Treiber lehnt früh ab |
TextSectionName |
Extraktor findet Eingangssektion | Extraktor findet .text nicht → Ablehnung |
Syscall |
SyscallStubPass Ersetzungsentscheidung | None → SyscallStubPass No-Op |
AsmTemplate / SyscallNumberReg / SyscallRetReg / ArgRegs |
SyscallStubPass Inline-Asm-Erzeugung | Eines leer → SyscallStubPass No-Op |
TCBReadAsm / TCBReadConstraint |
WinPEBImportPass TEB-Lese-Inline-Asm | Leer → PEB-Walk erzeugt leeres InlineAsm (Windows: erforderlich) |
DriverInjectFlags |
Plattformspezifische Flags im DynCode-Modus | null → keine Injektion |
- Enum-Wert zu
SyscallABIinTargetDesc.hhinzufügen kXxxTableinSyscallTables.cpphinzufügen- Case in
lookupSyscalls Switch hinzufügen SyscallStubPassbleibt unverändert — InlineAsm-Vorlagen/Einschränkungen kommen vonTargetDesc
Windows hat kein stabiles Syscall-ABI; Benutzeraufrufe an WriteFile / CreateThread / VirtualAlloc gehen durch WinPEBImportPass. Die Whitelist ist eine Multi-DLL-Tabelle:
- Definiert in
Tables/Win32Apis.def - Jede Zeile:
NEVERC_WIN32_API(ApiName, "dll.dll") - Der Resolver unterstützt bereits beliebige DLLs über
__neverc_win_resolve(dll_hash, api_hash)
Neue API hinzufügen (z.B. DeviceIoControl):
- Eine Zeile zu
Win32Apis.defhinzufügen - Deklaration im DynCode-Zweig von
lib/Headers/windows.hspiegeln - Keine Pass-Änderungen nötig
Neuen DLL-Bucket hinzufügen (z.B. winhttp.dll):
- Einfach Zeilen mit dem neuen DLL-Namen in
Win32Apis.defhinzufügen
Drei Dinge zu behandeln:
- Reloc-Typen identifizieren → Bytes patchen oder ablehnen
- Verbotene Datensektionsnamen-Liste aktualisieren (neues OS hat möglicherweise eigene Sektionen)
- Eingang-bei-Offset-0 Reloc-Zielbereichsvalidierung aktualisieren
Für ein völlig neues Objektformat (z.B. WASM-Module):
ObjectFormatEnum-Wert hinzufügen- Case in
DynCodeExtractor.cpps Dispatch-Switch hinzufügen <Format>Extractor.cppschreiben (Struktur vonELFExtractor.cppfolgen)
- Referenz
tests/neverc/dyncode/loader_linux.cundloader_windows.c - Typischerweise:
mmap(RWX) → memcpy → icache flush → call
- Cross-Compile-Check in
tests/neverc/DynCodeCrossTargetTests.cpphinzufügen - Wenn CI auf der Plattform ausführen kann, Loader-Roundtrip-Test hinzufügen
- Endianness: NeverC unterstützt nur Little-Endian (LE), deckt alle Mainstream-Ziele ab.
- ABI-Unterschiede: Win64 (rcx/rdx/r8/r9) vs System V AMD64 (rdi/rsi/rdx/rcx/r8/r9) haben komplett verschiedene Argumentregister. Dies wird in der NeverC-Frontend-Schicht behandelt; die DynCode-Pipeline muss sich nicht darum kümmern.
- Syscall-Nummern: Auf Linux pro Architektur unterschiedlich, Android gleich Linux, Darwin hat eigene BSD-Nummern, Windows hat keine stabilen Nummern (daher PEB-Walk). Im Tabelle per (OS, arch) indiziert.
- Cache-Kohärenz: ARM braucht expliziten i-Cache-Flush; x86 nicht. macOS arm64 JIT braucht auch
pthread_jit_write_protect_np; Linux arm64 nutzt__builtin___clear_cache; Windows nutztFlushInstructionCache(auf x86 No-Op). - SELinux / W^X: Android wird durch SELinux
execmemeingeschränkt; nicht-gejailbreaktes iOS lehntmmap(RWX)komplett ab,MAP_JIT+ Code-Signierung erforderlich.
| Ziel | Geschätzter Aufwand | Abhängigkeiten |
|---|---|---|
iOS arm64 (Jailbreak / MAP_JIT) |
1 Tag | Mach-O Extraktor wiederverwenden, Loader modifizieren |
| FreeBSD / OpenBSD x86_64 | Halber Tag | ELF Extraktor wiederverwenden + neue Syscall-Tabelle |
| RISC-V64 Linux | 2 Tage | RISC-V TargetDesc + neue AllBlr-Variante + RISC-V Reloc-Patching nötig |
Die DynCode-Pipeline exponiert 11 Interposes über Pipeline.h::ObfuscationInterposes für Drittanbieter-Obfuskationsbibliotheken:
PipelineStartEP:
RunBeforePrep → [ZeroReloc Prep] → RunAfterPrep →
[IndirectBr → MemIntrin → CompilerRt → SyscallStub →
WinPEBImport → KernelImport → Data2Text phase 1] →
RunBeforeInlining
OptimizerLastEP:
RunAfterInlining → [Data2Text phase 2 → ZeroReloc Stackify] →
RunAfterStackify → [AllBlrPass] → RunAfterFinalIR
MIR: RunBeforePreEmit → [MIRPrepPass] → RunAfterPreEmit →
[LLVM addPreEmitPass/addPreEmitPass2] → RunAfterFinalMIR
Byte-Stream: RunPostExtract → [finalize Kette] → RunPostFinalize
Verwendung auf IR-Ebene:
neverc::dyncode::ObfuscationInterposes H;
H.RunAfterInlining = [](llvm::ModulePassManager &MPM,
const neverc::dyncode::DynCodeOptions &Opts) {
MPM.addPass(MyCFFPass(Opts.ObfuscateSpec));
};
// Registrierung über die Plugin-API: NEVERC_INTERPOSE_SC_*-Interposes (siehe plugin-api-Doku)Verwendung auf MIR-Ebene:
H.RunAfterPreEmit = [](llvm::TargetPassConfig &TPC,
const neverc::dyncode::DynCodeOptions &Opts) {
TPC.addExternalPass(new MyInstructionSubstitutionPass(Opts.MirObfuscateSpec));
};Eingebautes MIR-Patching ist ebenfalls tabellengesteuert: Tables/MIRRewritePatterns.def erfasst Muster-Diagnosenamen, Arch-Filter und Helfernamen; Tables/MIRRewriteOpcodes.def erfasst Backend-Opcode-Namen. Beim Hinzufügen neuer dyncode-freundlicher Backend-Formen sollten Tabelleneinträge und schmale Helfer bevorzugt werden, statt zielspezifische Verzweigungen im Pass-Body zu verstreuen.