Hook 神威:绕过 EDR 内存保护

admin 2025年6月3日00:01:47评论22 views字数 4774阅读15分54秒阅读模式

Hook 神威:绕过 EDR 内存保护

引言

在最近一次内部渗透测试中,我遇到了一款 EDR 产品(这里不方便透露具体名称)。

这款产品严重阻碍了我访问 lsass 内存的能力,导致我无法使用我们自定义版本的 Mimikatz 来转储明文凭据。

Hook 神威:绕过 EDR 内存保护

误入歧途

作为一名前恶意软件开发者,我清楚地知道,驱动层可以通过多种方式实现检测与拦截的目的。

我最先想到的是 ObRegisterCallback,这在许多杀毒产品中是非常常见的做法。

微软之所以实现这个回调机制,是因为早期大量杀软使用了极具争议性的 WinAPI Hook 技术,这些 Hook 的行为与恶意 Rootkit 如出一辙。

不过,在 MSDN 页面底部你会注意到一行文字:“仅在 Windows Vista SP1 与 Windows Server 2008 及以上版本可用。”

为了补充背景,我当时所用的环境是 Windows Server 2003,也就是说,系统根本不支持这种拦截机制。

在花了数小时做一些黑魔法(如对 csrss.exe 动手脚、尝试通过它继承 lsass.exe 的句柄)之后,我终于成功以 PROCESS_ALL_ACCESS 权限获取了对 lsass.exe 的句柄。这个技巧是通过滥用 csrss 来生成一个子进程,并让子进程继承现有的 lsass 句柄实现的。

Hook 神威:绕过 EDR 内存保护

⚠️ 注:这台机器并未安装任何 EDR,本次操作仅是一个概念验证(PoC)。

然而,在我以为 “大功告成”、准备庆祝成功绕过某款 EDR 的时候,现实却给了我迎头一击:EDR 成功拦截了向 csrss 注入 shellcode 的行为,以及通过 RtlCreateUserThread 创建线程的操作。

但奇怪的是,尽管无法成功利用子进程继承句柄的方式启动线程,代码依然神奇地获得了 PROCESS_ALL_ACCESS 权限的 lsass.exe 句柄

 啥?!

等等,让我试试看,不整那些花里胡哨的操作,直接用这行代码打开 lsass.exe 的句柄:

HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, lsasspid);

结果你猜怎么着?

我居然成功拿到了对 lsass.exe 的完全控制权限句柄。

EDR 完全没有反应,一点响动都没有。

这时我才意识到,我一开始的思路就错了:EDR 根本不在乎你是否拿到了句柄,它关注的是你  拿到句柄之后做了什么 。

回到正轨

既然我们已经知道,拿到 lsass.exe 的完全控制句柄根本不需要什么花里胡哨的技巧,那么我们现在可以继续前进,寻找下一个问题的根源。

当我立刻使用该句柄调用 MiniDumpWriteDump() 时,调用却惨烈失败。

Hook 神威:绕过 EDR 内存保护

让我们更深入地剖析这个警告信息:“Violation: LsassRead”

我又没读取什么,搞什么呢?我只是想转储(dump)这个进程啊。

但我也知道,要对远程进程进行转储,MiniDumpWriteDump() 内部肯定会调用某些 WinAPI,比如 ReadProcessMemory(简称 RPM)。

我们来看下 ReactOS 上的 MiniDumpWriteDump 源码。

Hook 神威:绕过 EDR 内存保护

ps. 多次调用 ReadProcessMemory

正如你所见,函数(2)dump_exception_info() 以及许多其他函数,都依赖(3)ReadProcessMemory 来执行操作。

这些函数最终由(1)MiniDumpWriteDump 调用,而这很可能就是问题的根源。

这时候经验就派上用场了。你必须理解 Windows 系统内部机制,以及 WinAPI 的调用过程。以 ReadProcessMemory 为例 —— 它的工作流程大致如下:

ReadProcessMemory 只是一个 包装函数(wrapper),主要做一些合理性检查,比如空指针检查之类的,仅此而已。

但它接着会调用 NtReadVirtualMemory,这个函数会在执行 syscall 指令之前设置好寄存器。

syscall 是 CPU 进入内核态的指令,之后会调用另一个同名函数NtReadVirtualMemory,这个函数才是实际执行读取内存操作的逻辑。

Hook 神威:绕过 EDR 内存保护

有了这些知识,我们接下来需要识别 EDR 是如何检测并阻止对 RPM/NtReadVirtualMemory 的调用的

答案其实很简单 - “Hooking”(钩子注入)

在这里简要说一下:Hooking 允许你在某个函数的执行流程中插入自己的代码,从而获取它的参数和返回值。我可以百分之百确定该 EDR 是通过我曾提到过的一种或多种 Hook 技术来实现这个拦截的。

不过,读者也应当了解,大多数(如果不是全部)EDR 产品都会运行一个服务,** 该服务由内核模式下运行的驱动程序提供支持 **。

既然 EDR 有内核权限,那么它理应可以在 RPM 的调用栈中的任意层级进行 Hook。

但问题来了:

如果任何驱动程序都能轻松 Hook 任何层级的函数,那就会在 Windows 环境中打开一个巨大的安全漏洞

因此,微软提出了解决方案,即内核补丁保护机制Kernel Patch Protection,简称 KPP,也叫 PatchGuard)。

KPP 会在几乎所有层级扫描内核内存,一旦检测到修改,就会触发蓝屏(BSOD)。这包括 ntoskrnl(Windows 内核模块)中实现 WinAPI 的那部分逻辑。因此,我们可以确信 EDR ** 不会 ** 也 ** 无法 ** 在调用栈的内核层级(如 ntoskrnl)设置 Hook。

这样,我们就可以合理推断,EDR 的 Hook 必然是在用户态的 ReadProcessMemory 和 NtReadVirtualMemory 函数上实现的。

Hook 的实现

要查看某个函数在我们应用程序内存中的地址,其实很简单,就像下面这样用 %p 格式的 printf 打印函数地址:

Hook 神威:绕过 EDR 内存保护

然而,与 ReadProcessMemory (RPM) 不同,NtReadVirtualMemory 并不是 ntdll.dll 中导出的函数,因此你无法像正常函数一样直接引用它。你必须指定该函数的签名,并在项目中链接 ntdll.lib才能调用。

Hook 神威:绕过 EDR 内存保护

一切准备就绪后,运行看看吧!

Hook 神威:绕过 EDR 内存保护

这将为我们提供 RPM 和 NtReadVirtualMemory 的地址。

接下来,我将使用我最喜欢的逆向分析工具 —— Cheat Engine,来读取内存并分析它们的结构。

Hook 神威:绕过 EDR 内存保护

ReadProcessMemory

Hook 神威:绕过 EDR 内存保护

NtReadVirtualMemory

ReadProcessMemory

对于 ReadProcessMemory 函数,看起来一切正常。

它执行了一些栈和寄存器的设置操作,然后调用了 KernelBase 中的 ReadProcessMemory(这个是另一个话题,暂不展开)。

最终,它会进入 ntdll.dll 中的 NtReadVirtualMemory。

但是,当你查看 NtReadVirtualMemory 时,如果你了解最基础的 detour hook 技术,你会一眼看出这段代码不正常。

函数的前 5 个字节已被修改,而后面的内容仍然保持原样。

你可以通过对比其它类似的 Nt* 函数来判断:所有这些函数的结构通常是这样的:

0x4C, 0x8B, 0xD1        ; mov r10, rcx    ; Windows x64 syscall 调用标准0xB8, 0x3C, 0x00, 0x00, 0x00 ; mov eax, 0x3C ; 系统调用号 (syscall ID)0x0F, 0x05              ; syscall         ; 执行系统调用0xC3                    ; ret             ; 返回

唯一的区别就是 syscall ID 不同(每个 Nt* API 对应一个特定的 syscall ID)。

但是,在 NtReadVirtualMemory 中,第一条指令竟然是一个 JMP 跳转到内存中其他地方的指令

我们来跟踪一下这个跳转地址。

Hook 神威:绕过 EDR 内存保护

CyMemDef64.dll

哦,跟进跳转后,我们发现此时已经不再处于 ntdll.dll 的模块中,而是进入了 CyMemDef64.dll 模块。啊哈,现在一切都明白了。

这个 EDR 在 NtReadVirtualMemory 原始函数位置上放置了一个跳转指令(JMP) 将执行流程重定向到它自家的模块中(CyMemDef64.dll)。该模块对函数调用参数进行检查,用以检测是否存在恶意行为。

如果这些检查失败,Nt* 函数就会直接返回一个错误码,** 完全不会进入内核态,也不会执行真正的系统调用逻辑 **。

绕过方式(The Bypass)

现在我们已经非常清楚 EDR 是如何检测并拦截我们的 WINAPI 调用了。那么,我们该如何绕过它?

有两种解决方案:

修补被修补的内容(Re-Patch the Patch)

我们知道 NtReadVirtualMemory 函数原本应该是什么样子,因此我们可以很容易地将那条跳转(jmp)指令覆盖回正确的原始指令。

这样一来,我们的调用将不会再被 CyMemDef64.dll 拦截,而是会直接进入内核态(kernel)执行,在这个层面 EDR 无法进行控制。

ntdll 的 IAT Hook(Import Address Table Hook)

我们也可以构造一个新的函数,与前面 “修补方式”中提到的类似,但这一次我们 不去覆盖被 hook 的原函数,而是在其他位置重建该函数。

随后,我们遍历 ntdll.dll 的导入地址表(IAT),将指向 NtReadVirtualMemory 的函数指针替换为我们新建的 fixed_NtReadVirtualMemory

这种方式的优点在于:

即便 EDR 检查它设置的 inline hook,它也会发现原函数表面上没有任何修改 ;只是该函数从未真正被调用,因为 ntdll 的 IAT 被重定向到我们自己的函数地址了。

最终结果(The Result)

我选择了第一种方式(修补被 hook 的函数)。这种方法简单直接,也能让我更快写完这篇文章🙂。

当然,实现第二种方法也很容易,我计划在接下来的几天内尝试那种方式。

Hook 神威:绕过 EDR 内存保护

AndrewSpecial.exe was never caught Hook 神威:绕过 EDR 内存保护

顺便介绍一下这个 PoC 的代号:AndrewSpecial

结语(Conclusion)

当前的绕过方法对这个特定的 EDR 产品有效,但——要对类似的 EDR 实现通用绕过其实也很容易。因为它们在 hook 的能力上有天然限制(感谢 Windows 的 KPP(Kernel Patch Protection) 机制)。

我是不是忘了提?这个方法在 64 位系统下可完美运行(所有 Windows 版本都支持),而32 位系统目前尚未测试

源码已开放,详见 HERE:

https://github.com/hoangprod/AndrewSpecial/tree/master

再次感谢你的阅读!如果我有任何错误或遗漏,欢迎指出!

sources:https://medium.com/@fsx30/bypass-edrs-memory-protection-introduction-to-hooking-2efb21acffd6

技术点评

分析上述代码原理,可知

通过恢复被 EDR 在 ntdll.dll 中 Hook 的 NtReadVirtualMemory 原始 syscall stub,直接重写其入口为合法的 syscall 指令,从而绕过用户态的检测与拦截。

通过动态判断系统版本获取正确的 syscall ID,并构造 shellcode 写回对应函数地址,使后续调用如 MiniDumpWriteDump 能够顺利读取敏感进程(如 lsass)内存而不触发拦截,属于典型的用户态绕过技术,兼具隐蔽性与兼容性,适用于当前多数基于用户态钩子的 EDR 绕过场景。

其中比较有趣的代码是作者考虑周到,程序覆盖解决了 x64 下不同版本 window 环境下 syscall 调用号的差异问题!

同时KPP的这一机制奠定EDR防护在Hook上存在用户态对抗的可能性。

Hook 神威:绕过 EDR 内存保护

原文始发于微信公众号(一个不正经的黑客):Hook 神威:绕过 EDR 内存保护

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年6月3日00:01:47
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Hook 神威:绕过 EDR 内存保护https://cn-sec.com/archives/4124747.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息