
Современные средства безопасности (CrowdStrike, Bitdefender, SentinelOne и т. д.) хукают функцию nLoadImage внутри clr.dll, чтобы перехватывать и сканировать загрузку .NET-сборок в памяти. Этот инструмент снимает хук с этой функции.
Нативная утилита на C++, которая обходит хуки EDR/AV в среде .NET Common Language Runtime, восстанавливая исходную реализацию функции nLoadImage.
Этот инструмент удаляет хуки продуктов безопасности из функции nLoadImage в CLR — критически важной нативной точки входа, которая обрабатывает все загрузки .NET-сборок в память. Читая чистый clr.dll с диска и перезаписывая в памяти байты захваченной функции, он восстанавливает исходное поведение CLR, позволяя Assembly.Load(byte[]) выполняться без проверки или сканирования со стороны EDR.
Современные продукты безопасности (BitDefender, CrowdStrike, SentinelOne и т.д.) хукают функцию nLoadImage внутри clr.dll, чтобы перехватывать и сканировать загрузку .NET-сборок в память. Этот инструмент снимает хук с этой функции:
clr.dll с дискаnLoadImageПосле снятия хука Assembly.Load(byte[]) выполняется без проверки со стороны EDR.
nLoadImage — это критически важная нативная функция, которая обрабатывает все загрузки сборок в память в .NET runtime. Она объявлена как InternalCall в управляемом коде, то есть у неё нет реализации на C# — вместо этого она является прямым мостом к нативному коду CLR.
Цепочка вызова:
Managed Code (C#)
↓
Assembly.Load(byte[])
↓
RuntimeAssembly.nLoadImage(...) [InternalCall - no managed body]
↓
clr.dll!AssemblyNative::LoadImage (Native C++ implementation)
↓
Assembly loaded into AppDomain
Почему это критично:
Почти каждая загрузка сборки в память проходит через nLoadImage. Метод Assembly.Load(byte[]) и его перегрузки (включая загрузку с байтами символов) внутри вызывают nLoadImage. Когда вы вызываете Assembly.Load(byte[]), управляемый код в mscorlib.dll передаёт ваш массив байтов через RuntimeAssembly.nLoadImage(), который помечен атрибутом [MethodImpl(MethodImplOptions.InternalCall)] — то есть его тело пусто в C#, и выполнение немедленно переходит к нативному коду CLR.
Даже сценарии динамической генерации кода — фреймворки сериализации, генерирующие сборки во время выполнения, генерация XML-сериализаторов и red team-инструменты, такие как execute-assembly от Cobalt Strike, — все проходят через эту единственную функцию.
Нативная реализация:
Заглушка InternalCall nLoadImage в mscorlib.dll указывает на нативную функцию C++ AssemblyNative::LoadImage внутри clr.dll. Эта функция:
В .NET Framework 4.8+ каждый вызов nLoadImage автоматически передаёт байты сборки в AMSI Защитника Windows (AmsiScanBuffer) для сканирования перед выполнением, что делает эту функцию критической точкой для продуктов безопасности.
Сигнатура функции (.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
);
Когда вы вызываете Assembly.Load(byte[]), nLoadImage вызывается с этими типичными параметрами:
StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
return RuntimeAssembly.nLoadImage(
rawAssembly, // Your byte array
null, // rawSymbolStore
null, // evidence
ref stackMark, // LookForMyCaller
false, // fIntrospection
SecurityContextSource.CurrentAssembly // securityContextSource
);
Параметр fIntrospection управляет тем, загружается ли сборка для выполнения (false) или только для проверки через рефлексию (true). Метод Assembly.ReflectionOnlyLoad(byte[]) вызывает nLoadImage с fIntrospection=true, что позволяет изучать метаданные без выполнения кода.
Почему EDR хукает её:
Поскольку nLoadImage — это единственная точка входа для всех загрузок сборок в память, продукты EDR хукают её на нативном уровне в clr.dll. Это позволяет им:
Традиционные обходы (патчинг AMSI, отключение ETW) не влияют на хуки уровня CLR, поскольку они работают на более высоком уровне стека. Хук происходит внутри самого CLR, ещё до вызова AMSI.
CLRUnhook.exe
Снимает хуки с CLR в текущем процессе. Примечание: это работает только если CLR уже загружена (т.е. вы запущены из .NET-приложения или после ручной загрузки CLR).
CLRUnhook.exe powershell.exe
CLRUnhook.exe 1234
Снимает хуки с CLR в удалённом процессе.
=== 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 в загруженном clr.dll (в данный момент захвачена хуком)clr.dll из C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Использует паттерн-сканирование для поиска nLoadImage:
Исследование техники:
Реализация:
ТОЛЬКО ДЛЯ ОБРАЗОВАТЕЛЬНЫХ И АВТОРИЗОВАННЫХ ИССЛЕДОВАНИЙ БЕЗОПАСНОСТИ.
Несанкционированное использование этого инструмента для обхода средств защиты может нарушать законы о компьютерных преступлениях (CFAA и аналогичные законы). Используйте только на системах, которыми вы владеете, или при наличии явного письменного разрешения на тестирование.