在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 }