Skip to content
KitploitKITPLOIT
工具博客
提交
工具博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
CLR-Unhook — 现代安全产品(CrowdStrike、Bitdefender、SentinelOne等)会钩住clr.dll中的nLoadImage函数,以拦截和扫描内存中的.NET程序集加载。该工具会解除对该函数的钩子。 | Kitploit
工具/GitHubGitHub/hwbp/clr-unhook
防御工具漏洞利用后渗透利用渗透测试红队Payload 开发
GitHubhwbp/clr-unhook

CLR-Unhook

现代安全产品(CrowdStrike、Bitdefender、SentinelOne等)会钩住clr.dll中的nLoadImage函数,以拦截和扫描内存中的.NET程序集加载。该工具会解除对该函数的钩子。

查看仓库
217228个月前Kitploit 审核通过

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

CLR Unhooking Tool

  • 注意:要使此操作达到干净 CLR 的效果,您需要手动将 DLL 从磁盘映射到内存。不能使用 LoadLibraryA/W,因为防病毒解决方案会检测到 DLL 加载事件并可能立即挂钩。如果您想要此行为,可以在 GitHub 上查找现有的手动映射器并集成到您的代码库中。我在此不包含此类映射器,因为 AV 供应商通常不喜欢那样。

一个原生 C++ 工具,通过恢复原始的 nLoadImage 函数实现,绕过 .NET 公共语言运行时中的 EDR/AV 挂钩。

快速描述

此工具从 CLR 的 nLoadImage 函数中移除安全产品挂钩——该函数是处理所有内存中 .NET 程序集加载的关键原生入口点。通过从磁盘读取干净的 clr.dll 并覆盖内存中被挂钩的函数字节,它恢复了原始的 CLR 行为,使得 Assembly.Load(byte[]) 可以在没有 EDR 检查或扫描的情况下执行。

这个工具有什么作用?

现代安全产品(如 BitDefender、CrowdStrike、SentinelOne 等)挂钩 clr.dll 中的 nLoadImage 函数,以拦截和扫描内存中的 .NET 程序集加载。此工具通过以下方式解除该函数的挂钩:

  1. 从磁盘读取干净的 clr.dll
  2. 找到原始的 nLoadImage 字节
  3. 在内存中覆盖被挂钩的版本

解除挂钩后,Assembly.Load(byte[]) 无需 EDR 检查即可执行。

理解 nLoadImage

nLoadImage 是 .NET 运行时中处理所有内存中程序集加载的关键原生函数。它在托管代码中被声明为 InternalCall,意味着它没有 C# 实现——而是直接桥接到原生 CLR 代码。

调用链:

root@kitploit:~
托管代码 (C#)
    ↓
Assembly.Load(byte[])
    ↓
RuntimeAssembly.nLoadImage(...) [InternalCall - 无托管体]
    ↓
clr.dll!AssemblyNative::LoadImage (原生 C++ 实现)
    ↓
程序集加载到 AppDomain

为什么它很关键:

几乎所有内存中的程序集加载都通过 nLoadImage。Assembly.Load(byte[]) 方法及其重载(包括加载符号字节)都在底层调用 nLoadImage。当您调用 Assembly.Load(byte[]) 时,mscorlib.dll 中的托管代码通过 RuntimeAssembly.nLoadImage() 传递您的字节数组,该方法标记为 [MethodImpl(MethodImplOptions.InternalCall)]——这意味着其 C# 体为空,执行立即跳转到原生 CLR 代码。

即使是动态代码生成场景——运行时发出程序集的序列化框架、XML 序列化生成器以及像 Cobalt Strike 的 execute-assembly 这样的红队工具——都汇聚到这一个函数。

原生实现:

mscorlib.dll 中的 nLoadImage InternalCall 存根指向 clr.dll 内的原生 C++ 函数 AssemblyNative::LoadImage。此函数:

  • 从字节数组中解析 PE 头
  • 验证元数据和 IL 代码
  • 为程序集分配内存
  • 在 AppDomain 中注册程序集
  • 触发加载后事件(ETW、.NET 4.8+ 中的 AMSI 扫描)
  • 处理混合模式程序集(原生 + 托管)
  • 执行强名称验证

在 .NET Framework 4.8+ 中,每次 nLoadImage 调用都会自动将程序集字节传递给 Windows Defender 的 AMSI(AmsiScanBuffer)进行扫描,这使其成为安全产品的关键阻塞点。

函数签名 (.NET Framework 4.7+):

root@kitploit:~
[MethodImpl(MethodImplOptions.InternalCall)]
static internal extern Assembly nLoadImage(
    byte[] rawAssembly,              // PE 字节
    byte[] rawSymbolStore,           // 可选的 PDB 字节
    Evidence evidence,               // CAS 证据(已废弃)
    ref StackCrawlMark stackMark,    // 安全堆栈标记
    bool fIntrospection,             // 仅反射标志
    bool fSkipIntegrityCheck,        // 跳过完整性验证
    SecurityContextSource securityContextSource  // 安全上下文
);

当您调用 Assembly.Load(byte[]) 时,它会使用这些典型参数调用 nLoadImage:

root@kitploit:~
StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
return RuntimeAssembly.nLoadImage(
    rawAssembly,                            // 您的字节数组
    null,                                   // rawSymbolStore
    null,                                   // evidence
    ref stackMark,                          // LookForMyCaller
    false,                                  // fIntrospection
    SecurityContextSource.CurrentAssembly   // securityContextSource
);

fIntrospection 参数控制程序集是加载用于执行(false)还是仅反射检查(true)。Assembly.ReflectionOnlyLoad(byte[]) 方法以 fIntrospection=true 调用 nLoadImage,允许在无需代码执行的情况下检查元数据。

为什么 EDR 要挂钩它:

由于 nLoadImage 是所有内存中程序集加载的单一入口点,EDR 产品在 clr.dll 的原生层面挂钩它。这使得它们能够:

  • 在加载前检查每个程序集
  • 扫描字节数组中的恶意模式
  • 在 .NET 处理程序集之前就阻止执行
  • 绕过 AMSI/ETW 规避技术(因为挂钩位于这些层之下)

传统的绕过方法(AMSI 修补、ETW 禁用)不会影响 CLR 级别的挂钩,因为它们运行在堆栈中更高的层次。挂钩发生在 CLR 内部,甚至在 AMSI 被调用之前。

使用方法

本地进程(当前进程)

root@kitploit:~
CLRUnhook.exe

解除当前进程中 CLR 的挂钩。注意: 只有在 CLR 已加载的情况下才有效(即从 .NET 应用程序运行,或手动加载 CLR 后)。

远程进程(目标另一个进程)

root@kitploit:~
CLRUnhook.exe powershell.exe

CLRUnhook.exe 1234

解除远程进程中 CLR 的挂钩。

示例输出

成功的远程解除挂钩

root@kitploit:~
=== 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...

挂钩链

root@kitploit:~
托管代码 (C#)
    ↓
Assembly.Load(byte[])
    ↓
RuntimeAssembly.nLoadImage(...) [InternalCall]
    ↓
clr.dll!AssemblyNative::LoadImage
    ↓
[EDR 挂钩] ← 我们绕过这里
    ↓
原始 CLR 代码

解除挂钩过程

  1. 定位被挂钩的函数 - 查找已加载的 clr.dll 中的 nLoadImage(当前已被挂钩)
  2. 加载干净副本 - 从 C:\Windows\Microsoft.NET\Framework64\v4.0.30319\ 读取原始 clr.dll
  3. 提取干净字节 - 获取原始函数的前 30 个字节(.NET 是 JIT 编译,我们不希望出问题)
  4. 覆盖挂钩 - 用干净字节修补被挂钩的版本

函数发现

使用模式扫描定位 nLoadImage:

  1. 在模块内存中搜索字符串 "nLoadImage"
  2. 找到指向该字符串的指针
  3. 定位字符串指针相邻的函数指针
  4. 验证地址是否在模块范围内

致谢

技术研究:

  • Matthew Graeber (@mattifestation) - 逆向工程 InternalCall 方法和 CLR 内部机制

实现:

  • HWBP - 通过内存恢复实现 CLR 解除挂钩
  • @Evilbytecode - 在解除挂钩方面帮助了我,.NET 的 JIT 特性给我带来了一些问题。

免责声明

仅供教育和授权的安全研究使用。

未经授权使用此工具绕过安全控制可能违反计算机欺诈法律(如 CFAA 等类似法规)。仅可在您拥有所有权或获得明确书面许可的系统上使用。

参考资料

  • Reverse Engineering InternalCall Methods - Matthew Graeber
  • Microsoft .NET Reference Source
  • CLR Assembly Loading Pipeline Documentation
下载工具