
Produtos de segurança modernos (CrowdStrike, Bitdefender, SentinelOne, etc.) fazem hook da função nLoadImage dentro de clr.dll para interceptar e verificar carregamentos de assemblies .NET em memória. Esta ferramenta remove o hook dessa função.
Um utilitário nativo em C++ que contorna hooks de EDR/AV no Common Language Runtime do .NET restaurando a implementação original da função nLoadImage.
Esta ferramenta remove os hooks de produtos de segurança da função nLoadImage do CLR - o ponto de entrada nativo crítico que lida com todo o carregamento de assemblies .NET em memória. Ao ler o clr.dll limpo do disco e sobrescrever os bytes da função com hook na memória, ela restaura o comportamento original do CLR, permitindo que Assembly.Load(byte[]) seja executado sem inspeção ou varredura de EDR.
Produtos de segurança modernos (BitDefender, CrowdStrike, SentinelOne, etc.) aplicam hook na função dentro de para interceptar e verificar carregamentos de assemblies .NET em memória. Esta ferramenta remove o hook dessa função ao:
nLoadImageclr.dllclr.dll limpo do disconLoadImageApós a remoção do hook, Assembly.Load(byte[]) é executado sem inspeção de EDR.
nLoadImage é a função nativa crítica que lida com todo o carregamento de assemblies em memória no runtime .NET. Ela é declarada como um InternalCall no código gerenciado, o que significa que não possui implementação em C# - em vez disso, é uma ponte direta para o código nativo do CLR.
A Cadeia de Chamadas:
Managed Code (C#)
↓
Assembly.Load(byte[])
↓
RuntimeAssembly.nLoadImage(...) [InternalCall - no managed body]
↓
clr.dll!AssemblyNative::LoadImage (Native C++ implementation)
↓
Assembly loaded into AppDomain
Por Que É Crítico:
Quase todo carregamento de assembly em memória passa por nLoadImage. O método Assembly.Load(byte[]) e suas sobrecargas (incluindo o carregamento com bytes de símbolo) invocam nLoadImage internamente. Quando você chama Assembly.Load(byte[]), o código gerenciado em mscorlib.dll passa seu array de bytes através de RuntimeAssembly.nLoadImage(), que é marcado com [MethodImpl(MethodImplOptions.InternalCall)] - ou seja, seu corpo é vazio em C# e a execução salta imediatamente para o código nativo do CLR.
Até mesmo cenários de geração dinâmica de código - frameworks de serialização que emitem assemblies em tempo de execução, geração de serializadores XML e ferramentas de red team como execute-assembly do Cobalt Strike - todos passam por essa única função.
Implementação Nativa:
O stub de InternalCall de nLoadImage em mscorlib.dll aponta para a função nativa em C++ AssemblyNative::LoadImage dentro de clr.dll. Esta função:
No .NET Framework 4.8+, toda chamada a nLoadImage passa automaticamente os bytes do assembly para o AMSI do Windows Defender (AmsiScanBuffer) para verificação antes da execução, tornando-o um gargalo crítico para produtos de segurança.
Assinatura da Função (.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 você chama Assembly.Load(byte[]), ele invoca nLoadImage com estes parâmetros típicos:
StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
return RuntimeAssembly.nLoadImage(
rawAssembly, // Your byte array
null, // rawSymbolStore
null, // evidence
ref stackMark, // LookForMyCaller
false, // fIntrospection
SecurityContextSource.CurrentAssembly // securityContextSource
);
O parâmetro fIntrospection controla se o assembly é carregado para execução (false) ou apenas para inspeção via reflexão (true). O método Assembly.ReflectionOnlyLoad(byte[]) chama nLoadImage com fIntrospection=true, permitindo o exame de metadados sem execução de código.
Por Que o EDR Aplica Hook Nele:
Como nLoadImage é o ponto de entrada único para todos os carregamentos de assemblies em memória, os produtos de EDR aplicam hook nele no nível nativo em clr.dll. Isso permite que eles:
Bypasses tradicionais (patch de AMSI, desativação de ETW) não afetam os hooks no nível do CLR porque operam em um nível mais alto na pilha. O hook acontece dentro do próprio CLR, antes mesmo de o AMSI ser invocado.
CLRUnhook.exe
Remove o hook do CLR no processo atual. Nota: Isso só funciona se o CLR já estiver carregado (ou seja, executando a partir de um aplicativo .NET ou após carregar o CLR manualmente).
CLRUnhook.exe powershell.exe
CLRUnhook.exe 1234
Remove o hook do CLR em um 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 no clr.dll carregado (atualmente com hook)clr.dll original de C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Usa varredura de padrões para localizar nLoadImage:
nLoadImage na memória do móduloPesquisa da Técnica:
Implementação:
APENAS PARA FINS EDUCACIONAIS E PESQUISA DE SEGURANÇA AUTORIZADA.
O uso não autorizado desta ferramenta para contornar controles de segurança pode violar leis de fraude computacional (CFAA, estatutos equivalentes). Use apenas em sistemas que você possui ou para os quais tenha permissão explícita por escrito para testar.