在XP中通过注册表回调 CmRegisterCallback 注册注册表通知函数,在走 XP 的 RegNtPreCreateKey 分支的时候,会出现问题,

使用过的人可能会知道,这个问题就是,注册表路径拿不全,这怎么办,

路径拿不全,过滤就没法做啊,

很愁,

而这种情况下,如果又不想使用SSDT HOOK,那么就更愁了,怎么办,怎么解决,

 

我想了一个看似好办法的馊主意,但是可用,

就是,爬调用栈,

既然是XP下,那么内联汇编很容易,拿到EBP之后,直接就可以一层层爬调用栈了,

除非中间被人截断,否则可以爬出完整的调用栈,然后,就是爬到头,找到调用源,然后开干,

 

事先声明,因为这本身就是微软没有提供的一个不可能完成的任务,

所以为了完成它,所选择道路可能也不靠谱,这个很正常,

 

好了,这里说准备工作,

准备工作是,需要首先得到所有进程的PEB,然后拿到所有进程的模块链,枚举出advapi32模块,

然后得到模块基址和偏移,得到内部所需要用到的函数RVA,然后根据模块的BASE和RVA就能算出函数的具体位置,

然后就是开始无尽地爬调用栈,

趴到什么地方是个头呢,一边爬,一边计算,算 (char *)(*(DWORD*)(ebp + 4) - 5) ,这个东西的第0个元素是否是一个call,

然后后续的4个字节组成的地址是否是我们要找的函数的地址,如果是的话,

那么,好办了,这里就是入口点,

然后根据我们要找的函数,直接用ebp 来算参数就好了,参数1 是 +8 ,参数2是 +C,

到这里就简单多了,轻车熟路了。

 

不过,这个方案里面,还有几个地方需要想一下,

首先内核是在读应用层地址,所以需要prob 一下是否可读,然后验证一下是否有效,最重要的还要加一个异常处理在外面,保证不会崩掉,

如果有能力的,其实可以每读一次之前做个MDL,这样的话就不会有内存访问的问题。

 

OK,结束。

 

其实第二个方法和第一个方法思路相同,只不过点不同,路上有N 多函数可以找参数,

如果可以Mm出内核函数地址,爬内核函数的参数的话,很有可能风险更低一些。

 

 

简单的代码实现就是这样的

 

得到EBP之后,开始一步一步地往上爬,最后爬到一个指定的返回地址,则认为找到了函数,

找到函数之后,抓一下函数的参数,就可以了,

第一个参数是HKEY,第二个参数是一个字符串,就直接拿到了,

因为,这里是 ADVAPI32!RegCreateKeyW

 1                 ULONG old_EBP = 0;
 2 
 3                 __asm mov old_EBP, ebp;            //    当前函数栈帧
 4 
 5                 for (;;)
 6                 {
 7                     old_EBP = *(ULONG*)old_EBP;
 8 
 9                     if (((*(ULONG*)(old_EBP + 4)) == 0))
10                     {
11                         DbgPrint("Have no find func \n");
12                         break;
13                     }
14                     if (((*(ULONG*)(old_EBP + 4)) == 0x77dcba74))
15                     {
16                         old_EBP = *(ULONG*)old_EBP;
17                         DbgPrint("Have find func : 0x%08X , %S\n" , (*(ULONG*)(old_EBP + 8)), (*(ULONG*)(old_EBP + 0xC)));
18                         break;
19                     }
20                 }