实验Windows对象初始化器 - 参见PG合规性免责声明* - 逆向工程
概述
在本文中,我想介绍一种有趣的方法,通过替代手段(实验性地)执行类似于Windows对象回调启用的功能。众所周知,Windows系统上的反恶意软件、反作弊和通用监控工具经常使用这些回调。然而,它们的可用性仅限于具有签名模块的各方,并且这些回调带有一些风险,主要是如果未充分验证,这些回调很容易被篡改。我将展示一个利用这种未记录方法的简单示例。我们将探讨所提出的方法如何在时间使用约束下或PG禁用时实现可比较的结果。我不会花太多时间讨论Windows对象的高级细节 - 我强烈推荐《Windows内部原理》或《Windows内核编程》以获取更多详细信息。我们将按任意顺序涵盖对象构造、各种类型、通知例程和用例,特别是在反恶意软件和反作弊软件中,然后检查一些问题,并详细说明替代进程通知和反调试方法的实现。
免责声明
此实现在Windows 11 23H2(OS构建22631.3085)上进行了测试。如果它们利用相同的机制,这些方法可能适用于早期版本的Windows,除了那些有PatchGuard哈希处理的版本,如本文所述。未来部署的Windows 11可能会更改这些机制及其组织或保护。 后来的Windows版本显示,PatchGuard在5分钟到6小时内的任何地方都会抛出错误。PsProcessType和IoDriverObjectType两者都明显放置在PG上下文中,以及ObpTypeObjectType。ObpObjectTypes列表也使用SHA256进行哈希处理并放置在PG上下文中。在处理任何对象类型时,请注意潜在的崩溃。所有结构都由PG保护。但是,_OBJECT_TYPE.CallbackList条目不受保护,可以在运行时取消链接/重组以插入或删除回调。修改各种对象类型(如PsProcessType)的回调列表可以实现类似的效果。 ‡ 109是Windows的CRITICAL_STRUCTURE_CORRUPTION错误检查(BSOD)代码的简写引用。
构建块
Windows内核中的对象是操作系统操作和记账的基础。我假设对Windows对象有轻度熟悉,但如果您需要复习,一些示例包括进程、线程、文件、互斥体、信号量、IoRing等。它们都由各自的组件在操作系统初始化期间构造,并由对象管理器管理(例程以Ob为前缀在ntoskrnl中)。我们将在以下小节中坚持使用一个熟悉的对象:进程。
进程创建和通知
Windows中的进程通知回调是系统监控和安全的基石。这些回调主要由反恶意软件和反作弊系统使用,提供关于进程创建和终止事件的实时通知。它们将初始化适当的结构,然后调用PsSetCreateProcessNotifyRoutine来注册回调。对于不熟悉的人来说,安全产品为什么利用这种机制可能很明显,但它支持广泛的操作,从一般日志记录到基于回调中提供的信息的首机会验证或进程终止。 当软件注册此通知例程时,它将被附加到内核中标记为PspCreateProcessNotifyRoutine的回调列表中。每当通过API(如NtCreateUserProcess或NtCreateProcess)创建进程时,结果将始终包括枚举此列表并随后执行任何添加的回调。从调用到通知的一般流程如下:
|
|
如果我们查看PspCallProcessNotifyRoutines的内部,我们会看到枚举和执行每个添加的回调。
攻击者防止这种首机会访问进程创建的几种方法已被记录。此博客上的一篇较早文章解决了一种潜在方法,从看到上述内容的下一步逻辑是定位感兴趣的回调条目并将其从PspCreateProcessNotifyRoutine列表中移除。有一篇文章详细介绍了这种方法。要点是,反恶意软件/反作弊/通用安全产品通常依赖这些回调,并可能假设它们未被篡改;然而,如前所述 - 通过滥用硬件和/或安全供应商推出的无数WHQL签名驱动程序,攻击这些机制的可靠性和可用性有些琐碎。 现在,让我们考虑不太合法的观点。在几年前,您可以使用未签名的驱动程序注册对象回调和进程通知回调(即,使用那些允许无限制访问系统资源的WHQL签名驱动程序之一来映射您自己的驱动程序)。一种方法是对DriverObject->DriverSection执行一些技巧,如这里所述。然而,如今,当Windows未处于测试签名模式或没有签名模块时,尝试注册对象通知时,您将遇到STATUS_ACCESS_DENIED结果。此方法绕过了修改驱动程序节属性、签名驱动程序或在测试签名模式下运行以获得与传统对象回调相同功能的需要。
函数指针重绑定
好了,不再有令人瞌睡的解释。让我们直接深入了解如何通过完全避免对象回调列表来实现进程通知回调。我将呈现一张图片;我相信您会立即看到这是如何工作的。如果没有,别担心……当第一个概念验证呈现时,它会变得清晰。准备好了吗?
啊……不错。 在PspInitPhase0函数中应用适当的类型到变量后,指向几种方法的指针脱颖而出。很好,那么如何找到这些的调用?我很高兴您没有问,让我展示给您。我凑合了一个IDA Python脚本来查找从起点N深度处函数的引用。对于在目标模块中 pinpointing opportunities 非常棒(是的,我本可以在PspProcessOpen上设置断点,但我对调用图中的所有间接调用感到好奇)。 让我们看看从数千个转储结果中的一些结果:
|
|
[2]和[9]项立即引起了兴趣,因为我不熟悉这些例程中执行的间接调用。在进一步检查地址0x14064B733后……
让我们稍微符号化一下。
谁需要打开WinDbg当你有DFS?我们确实需要……如果我们要彻底并验证这被命中。如果我们回顾初始图像,我们会看到PsProcessType的ObTypeInit.OpenProcedure指向PspProcessOpen。我将在WinDbg中设置断点以确认我的假设:bp nt!PspProcessOpen “kb;g”。结果很多,但一个确认了:
|
|
这是进程创建的一个命中,是我证明浪费时间搞这个所需要的。好了,那么我们现在如何利用这个?嗯,让我们列出一些我们知道的事情。
- 对象类型在内核初始化时创建。
- 每个对象类型都有一个与之关联的名称。
- 对象类型对象存储在其各自索引的ObTypeIndexTable中。
- 初始图像中的过程存储在_OBJECT_TYPE结构的TypeInfo字段中,该结构是ObTypeIndexTable中每个条目的类型。
- PG检查结构,但要么您假设PG被禁用,要么这将只在非常短的时间内保留。
- ObGetObjectType可以通过MmGetSystemRoutineAddress获取。
- Zydis存在。
- lock xchg go brrr。
- ???
- 利润。
知道以上内容,我们可以检测这些函数以实现我们的目标。首先,这里是一些您如果想要复制的话会想要的结构定义:
|
|