实验环境是Win7 X86系统。
曾经在这篇文章中常见的几种DLL注入技术说过,通过修改注册表的内容可以实现AppInit_DLLs注入。那么本文的实验是通过CmRegisterCallback来实现对注册表修改的监控以此来阻止修改。并通过对CmRegisterCallback的逆向分析来实现对监控函数的删除。
在Windows系统中,可以通过使用CmRegisterCallback来设置注册表监控的回调函数,该函数在文档中的定义如下
PEX_CALLBACK_FUNCTION回调函数在文档中的定义如下,该函数的返回值为STATUS_SUCCESS之外的错误码的时候,则表示系统会拒绝操作相应的注册表。
其中的REG_NOTIFY_CLASS定义如下
其中的几个比较常用的类型,它们的意义,以及对应的Argument2的结构体的内容如下
PREG_CREATE_KEY_INFORMATION结构体定义如下
关键的两个成员
CompleteName
PREG_DELETE_KEY_INFORMATION的结构体定义如下
成员Object指向要删除的注册表项的注册表项对象的指针。
PREG_DELETE_VALUE_KEY_INFORMATION结构体定义如下
PREG_SET_VALUE_KEY_INFORMATION结构体定义如下
根据上面的内容可以知道,回调函数中是可以获取要操作的注册表键的对象的,所以可以使用ObQueryNameString函数来获得键操作的键的名称,该函数在文档中的定义如下
OBJECT_NAME_INFORMATION的定义如下
只有一个UNICODE_STRING的Name参数,保存了返回的名称。
想要实现对监控函数的删除,可以使用CmUnRegisterCallback函数来实现,该函数在文档中的定义如下。它只有一个参数,就是前面设置监控函数时候指定的Cookie的地址
根据上面的内容就可以实现对注册表的监控来拒绝AppInit_DLLs的注入,具体实现代码如下
接下来通过IDA分析CmRegisterCallback来看看是如何保存注册表的回调函数。IDA中对CmRegisterCallback的反汇编如下
这个函数做了两件事情,将Init_Data的赋值赋给eax,将需要的参数入栈以后调用CmCRegisterCallback函数。经过分析以后发现,这个Init_Data是用来赋值一些数据,不过在这里用处不大,入栈的1也是。
继续看CmCRegisterCallback的反汇编代码
可以看到,系统分配0x30大小的内存来保存相应的内容,根据以上的分析可以得出下面的结论
最开始的8个字节保存的是一个LIST_ENTRY类型的双向链表
偏移0x10保存的是COOKIE
偏移0x1C保存的Context
偏移0x18保存的是回调函数的地址
所以可以想到注册表监控的回调函数是用LIST_ENTRY双向链表来一个个连起来的。而真正将分配的这块内存加入链表的函数则是SetRegisterCallback函数,以下是这个函数的反汇编分析
在这个ListBegin就是链表头的地址,里面保存的就是第一个链表的地址。首先会将它保存的内容取出判断保存的是否就是ListBegin的首地址,如果是的话就说明到了链表的尾部,接下来就会跳转到loc_69F3DE来把结构加进链表。如果不是的话,它会执行以下的循环,其中的一部分是
这个循环就是不断的取链表中的下一个数据直到链表尾。
找到以后,程序就会在loc_69F3DE处把数据加到链表里面
根据上面分析可以知道,注册表的监控回调函数被以链表的形式保存到了内存中,其中这个链表头是ListBegin。接下来只要找到这个链表头并且遍历这个链表,取出COOKIE就可以实现回调函数的删除。
而想要找到这个链表头,首先需要在CmRegisterCallback中使用0xE8找到CmcRegisterCallback
随后需要在CmCRegisterCallback中使用0x8BC6E8找到SetRegisterCallback
最终才在SetRegisterCallback中使用0xBB找到这个链表头
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
最后于 2021-11-30 09:59
被1900编辑
,原因: