
Moderne Sicherheitsprodukte (CrowdStrike, Bitdefender, SentinelOne usw.) haken die nLoadImage-Funktion in clr.dll ein, um das Laden von .NET-Assemblys im Speicher abzufangen und zu scannen. Dieses Tool entfernt den Hook dieser Funktion.
Ein natives C++-Dienstprogramm, das EDR/AV-Hooks in der .NET Common Language Runtime umgeht, indem es die ursprüngliche Implementierung der nLoadImage-Funktion wiederherstellt.
Dieses Tool entfernt Hooks von Sicherheitsprodukten aus der nLoadImage-Funktion der CLR – dem kritischen nativen Einstiegspunkt, der das gesamte In-Memory-Laden von .NET-Assemblies abwickelt. Indem es die saubere clr.dll von der Festplatte liest und die gehookten Funktionsbytes im Speicher überschreibt, stellt es das ursprüngliche CLR-Verhalten wieder her und ermöglicht es Assembly.Load(byte[]), ohne EDR-Überprüfung oder -Scanning ausgeführt zu werden.
Moderne Sicherheitsprodukte (BitDefender, CrowdStrike, SentinelOne usw.) hooken die nLoadImage-Funktion in clr.dll, um In-Memory-.NET-Assembly-Ladevorgänge abzufangen und zu scannen. Dieses Tool entfernt den Hook dieser Funktion, indem es:
clr.dll von der Festplatte liestnLoadImage-Bytes findetNach dem Unhooking wird Assembly.Load(byte[]) ohne EDR-Überprüfung ausgeführt.
nLoadImage ist die kritische native Funktion, die das gesamte In-Memory-Assembly-Laden in der .NET-Laufzeit abwickelt. Sie ist in verwaltetem Code als InternalCall deklariert, was bedeutet, dass sie keine C#-Implementierung hat – stattdessen ist sie eine direkte Brücke zum nativen CLR-Code.
Die Aufrufkette:
Managed Code (C#)
↓
Assembly.Load(byte[])
↓
RuntimeAssembly.nLoadImage(...) [InternalCall - no managed body]
↓
clr.dll!AssemblyNative::LoadImage (Native C++ implementation)
↓
Assembly loaded into AppDomain
Warum es kritisch ist:
Nahezu jeder In-Memory-Assembly-Ladevorgang läuft über nLoadImage. Die Methode Assembly.Load(byte[]) und ihre Überladungen (einschließlich des Ladens mit Symbolbytes) rufen intern alle nLoadImage auf. Wenn du Assembly.Load(byte[]) aufrufst, übergibt der verwaltete Code in mscorlib.dll dein Byte-Array über RuntimeAssembly.nLoadImage(), das mit [MethodImpl(MethodImplOptions.InternalCall)] markiert ist – das bedeutet, dass sein Rumpf in C# leer ist und die Ausführung sofort zum nativen CLR-Code springt.
Selbst Szenarien mit dynamischer Codegenerierung – Serialisierungsframeworks, die zur Laufzeit Assemblies erzeugen, XML-Serializer-Generierung und Red-Team-Tools wie Cobalt Strikes execute-assembly – laufen alle über diese einzelne Funktion.
Native Implementierung:
Der nLoadImage-InternalCall-Stub in mscorlib.dll zeigt auf die native C++-Funktion AssemblyNative::LoadImage in clr.dll. Diese Funktion:
In .NET Framework 4.8+ übergibt jeder nLoadImage-Aufruf die Assembly-Bytes automatisch an die AMSI von Windows Defender (AmsiScanBuffer) zum Scannen vor der Ausführung, was es zu einem kritischen Engpass für Sicherheitsprodukte macht.
Funktionssignatur (.NET Framework 4.7+):
[MethodImpl(MethodImplOptions.InternalCall)]
static internal extern Assembly nLoadImage(
byte[] rawAssembly, // PE bytes
byte[] rawSymbolStore, // Optional PDB bytes
Evidence evidence, // CAS evidence (obsolete)
ref StackCrawlMark stackMark, // Security stack marker
bool fIntrospection, // Reflection-only flag
bool fSkipIntegrityCheck, // Skip integrity validation
SecurityContextSource securityContextSource // Security context
);
Wenn du Assembly.Load(byte[]) aufrufst, wird nLoadImage mit diesen typischen Parametern aufgerufen:
StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
return RuntimeAssembly.nLoadImage(
rawAssembly, // Your byte array
null, // rawSymbolStore
null, // evidence
ref stackMark, // LookForMyCaller
false, // fIntrospection
SecurityContextSource.CurrentAssembly // securityContextSource
);
Der Parameter fIntrospection steuert, ob die Assembly zur Ausführung geladen wird (false) oder nur zur Reflexionsprüfung (true). Die Methode Assembly.ReflectionOnlyLoad(byte[]) ruft nLoadImage mit fIntrospection=true auf, was die Untersuchung von Metadaten ohne Codeausführung ermöglicht.
Warum EDR es hookt:
Da nLoadImage der einzige Einstiegspunkt für alle In-Memory-Assembly-Ladevorgänge ist, hooken EDR-Produkte es auf nativer Ebene in clr.dll. Dadurch können sie:
Traditionelle Bypasses (AMSI-Patching, ETW-Deaktivierung) beeinflussen Hooks auf CLR-Ebene nicht, da sie auf einer höheren Ebene im Stack agieren. Der Hook erfolgt innerhalb der CLR selbst, bevor AMSI überhaupt aufgerufen wird.
CLRUnhook.exe
Entfernt den Hook der CLR im aktuellen Prozess. Hinweis: Dies funktioniert nur, wenn die CLR bereits geladen ist (d. h., wenn du eine .NET-Anwendung ausführst oder die CLR manuell geladen hast).
CLRUnhook.exe powershell.exe
CLRUnhook.exe 1234
Entfernt den Hook der CLR in einem Remote-Prozess.
=== CLR Unhooking Tool ===
[*] Mode -> Remote Process Unhooking
[*] Target -> PID 21436
[+] Found PID -> 21436
[*] Unhooking CLR->nLoadImage in remote process...
[DEBUG] Remote mode enabled
[DEBUG] Found clr.dll at 0x00007FFD38CB0000
[DEBUG] CLR path -> C:\Windows\Microsoft.NET\Framework64\v4.0.30319\clr.dll
[DEBUG] CLR module size -> 10108928 bytes
[DEBUG] Read 10108928 bytes from remote process
[DEBUG] Searching for 'nLoadImage' in module (size: 10108928)
[DEBUG] Remote base address: 0x00007FFD38CB0000
[DEBUG] Scanning for string 'nLoadImage' (11 bytes)...
[DEBUG] Found string at RVA 0x7c12b8
[DEBUG] Searching for remote pointer: 0x7ffd394712b8
[DEBUG] Found pointer at offset 0x7a4340
[DEBUG] Valid function pointer found at RVA 0x5e4f30
[DEBUG] Found nLoadImage at RVA 0x00000000005E4F30
[DEBUG] Hooked function address -> 0x00007FFD39294F30
[DEBUG] Clean function at offset 0x00000000005E4F30 in disk file
[DEBUG] Reading hooked bytes before patch...
[DEBUG] First 16 bytes BEFORE unhook:
4C 8B DC 49 89 5B 08 49 89 73 10 4D 89 4B 20 57
[DEBUG] Clean bytes from disk:
8B 4B 78 E8 88 A9 EA FF C6 44 24 28 00 80 3D A4
[DEBUG] Wrote 30 bytes successfully
[DEBUG] First 16 bytes AFTER unhook:
8B 4B 78 E8 88 A9 EA FF C6 44 24 28 00 80 3D A4
[DEBUG] VERIFICATION SUCCESS: Patched bytes match clean bytes!
[+] SUCCESS -> CLR nLoadImage unhooked in remote process!
[+] EDR/AV hooks bypassed
[*] Press Enter to exit...
Managed Code (C#)
↓
Assembly.Load(byte[])
↓
RuntimeAssembly.nLoadImage(...) [InternalCall]
↓
clr.dll!AssemblyNative::LoadImage
↓
[EDR HOOK] ← We bypass this
↓
Original CLR Code
nLoadImage in der geladenen clr.dll (aktuell gehooked)clr.dll aus C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Verwendet Muster-Scanning, um nLoadImage zu lokalisieren:
Technik-Recherche:
Implementierung:
NUR FÜR BILDUNGSZWECKE UND AUTORISIERTE SICHERHEITSFORSCHUNG.
Die unbefugte Verwendung dieses Tools zur Umgehung von Sicherheitskontrollen kann gegen Gesetze zum Computerbetrug verstoßen (CFAA, entsprechende Gesetze). Verwende es nur auf Systemen, die dir gehören oder für die du eine ausdrückliche schriftliche Genehmigung zum Testen hast.