
I moderni prodotti di sicurezza (CrowdStrike, Bitdefender, SentinelOne, ecc.) eseguono l'hook della funzione nLoadImage all'interno di clr.dll per intercettare e scansionare i caricamenti di assembly .NET in memoria. Questo strumento rimuove l'hook di quella funzione.
Un'utilità C++ nativa che bypassa gli hook EDR/AV nel Common Language Runtime .NET ripristinando l'implementazione originale della funzione nLoadImage.
Questo strumento rimuove gli hook dei prodotti di sicurezza dalla funzione nLoadImage del CLR - il punto di ingresso nativo critico che gestisce tutto il caricamento degli assembly .NET in memoria. Leggendo il clr.dll pulito dal disco e sovrascrivendo i byte della funzione hookata in memoria, ripristina il comportamento originale del CLR, permettendo a Assembly.Load(byte[]) di essere eseguito senza ispezione o scansione EDR.
I moderni prodotti di sicurezza (BitDefender, CrowdStrike, SentinelOne, ecc.) hookano la funzione all'interno di per intercettare e scansionare i caricamenti degli assembly .NET in memoria. Questo strumento rimuove l'hook di quella funzione:
nLoadImageclr.dllclr.dll pulito dal disconLoadImageDopo l'unhooking, Assembly.Load(byte[]) viene eseguito senza ispezione EDR.
nLoadImage è la funzione nativa critica che gestisce tutti i caricamenti degli assembly in memoria nel runtime .NET. È dichiarata come InternalCall nel codice gestito, il che significa che non ha un'implementazione in C# - invece, è un ponte diretto verso il codice nativo del CLR.
La Catena di Chiamate:
Managed Code (C#)
↓
Assembly.Load(byte[])
↓
RuntimeAssembly.nLoadImage(...) [InternalCall - no managed body]
↓
clr.dll!AssemblyNative::LoadImage (Native C++ implementation)
↓
Assembly loaded into AppDomain
Perché è Critico:
Quasi ogni caricamento di assembly in memoria passa attraverso nLoadImage. Il metodo Assembly.Load(byte[]) e i suoi overload (incluso il caricamento con byte di simboli) invocano tutti nLoadImage sotto il cofano. Quando chiami Assembly.Load(byte[]), il codice gestito in mscorlib.dll passa il tuo array di byte attraverso RuntimeAssembly.nLoadImage(), che è marcata con [MethodImpl(MethodImplOptions.InternalCall)] - ciò significa che il suo corpo è vuoto in C# e l'esecuzione salta immediatamente al codice nativo del CLR.
Anche scenari di generazione dinamica del codice - framework di serializzazione che emettono assembly in fase di esecuzione, generazione di serializzatori XML e strumenti red team come execute-assembly di Cobalt Strike - confluiscono tutti attraverso questa singola funzione.
Implementazione Nativa:
Lo stub InternalCall nLoadImage in mscorlib.dll punta alla funzione C++ nativa AssemblyNative::LoadImage all'interno di clr.dll. Questa funzione:
In .NET Framework 4.8+, ogni chiamata nLoadImage passa automaticamente i byte dell'assembly all'AMSI di Windows Defender (AmsiScanBuffer) per la scansione prima dell'esecuzione, rendendolo un punto di strozzatura critico per i prodotti di sicurezza.
Firma della Funzione (.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
);
Quando chiami Assembly.Load(byte[]), invoca nLoadImage con questi parametri tipici:
StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
return RuntimeAssembly.nLoadImage(
rawAssembly, // Your byte array
null, // rawSymbolStore
null, // evidence
ref stackMark, // LookForMyCaller
false, // fIntrospection
SecurityContextSource.CurrentAssembly // securityContextSource
);
Il parametro fIntrospection controlla se l'assembly viene caricato per l'esecuzione (false) o solo per ispezione tramite reflection (true). Il metodo Assembly.ReflectionOnlyLoad(byte[]) chiama nLoadImage con fIntrospection=true, permettendo l'esame dei metadati senza esecuzione del codice.
Perché EDR lo Hooka:
Poiché nLoadImage è il singolo punto di ingresso per tutti i caricamenti di assembly in memoria, i prodotti EDR lo hookano a livello nativo in clr.dll. Ciò consente loro di:
I bypass tradizionali (patching di AMSI, disabilitazione di ETW) non influenzano gli hook a livello CLR perché operano a un livello più alto nello stack. L'hook avviene all'interno del CLR stesso, prima ancora che AMSI venga invocato.
CLRUnhook.exe
Rimuove l'hook del CLR nel processo corrente. Nota: Funziona solo se il CLR è già caricato (cioè, in esecuzione da un'applicazione .NET o dopo aver caricato manualmente il CLR).
CLRUnhook.exe powershell.exe
CLRUnhook.exe 1234
Rimuove l'hook del CLR in un processo remoto.
=== 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 nel clr.dll caricato (attualmente hookato)clr.dll originale da C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Usa la scansione di pattern per localizzare nLoadImage:
Ricerca della Tecnica:
Implementazione:
SOLO PER RICERCA SULLA SICUREZZA AUTORIZZATA E A SCOPO EDUCATIVO.
L'uso non autorizzato di questo strumento per bypassare i controlli di sicurezza potrebbe violare le leggi sulla frode informatica (CFAA, leggi equivalenti). Utilizzare solo su sistemi di propria proprietà o per i quali si ha esplicita autorizzazione scritta per il test.