以PspExitThread为例介绍如何寻找未导出函数的函数地址
ntoskrnl.exe导出了很多内核例程供驱动开发人员方便的使用,它也隐藏了很多很强大的历程,防止驱动开发人员过于方便的用它们。比如PspExitThread,这个函数没什么特别的,但配合APC使用它就能杀死绝大多数的进程,包括一些杀毒软件在内。gussing.cnblogs.com
那么如何才能找到这些未导出的函数并使用它们呢?答案就是反汇编,借助强大的windbg,加上一点点汇编基础,操作系统在你面前就会变的很透明。还是以PspExitThread为例子,我们已经知道导出函数PsTerminateSystemThread会调用PspTerminateThreadByPointer()函数,PspTerminateThreadByPointer进而会调用PspExitThread,所谓的函数调用就是 call 指令加上一个函数地址,所以挖出PspExitThread的地址完全是有可能的。gussing.cnblogs.com
打开windbg的本地内核调试,把该加载的pdb都加载了,然后反汇编PsTerminateSystemThread:gussing.cnblogs.com
lkd> uf nt!PsTerminateSystemThread nt!PsTerminateSystemThread: 805d2c36 8bff mov edi,edi 805d2c38 55 push ebp 805d2c39 8bec mov ebp,esp 805d2c3b 64a124010000 mov eax,dword ptr fs:[00000124h] 805d2c41 f6804802000010 test byte ptr [eax+248h],10h 805d2c48 7507 jne nt!PsTerminateSystemThread+0x1b (805d2c51) nt!PsTerminateSystemThread+0x14: 805d2c4a b80d0000c0 mov eax,0C000000Dh 805d2c4f eb09 jmp nt!PsTerminateSystemThread+0x24 (805d2c5a) nt!PsTerminateSystemThread+0x1b: 805d2c51 ff7508 push dword ptr [ebp+8] 805d2c54 50 push eax 805d2c55 e828fcffff call nt!PspTerminateThreadByPointer (805d2882) nt!PsTerminateSystemThread+0x24: 805d2c5a 5d pop ebp 805d2c5b c20400 ret 4
地址805d2c55处的call指令可以解析出PspTerminateThreadByPointer的地址。那么该call指令的地址又是如何得到的呢?看上面那行push dword ptr [ebp+8],也就是“ff7508”,这是PspTerminateThreadByPointer函数调用的“特征码”,从PsTerminateSystemThread函数开始到PspTerminateThreadByPointer函数调用为止,该特征码都只出现一次,所以搜索到ff7508串就能很方便的定位到PspTerminateThreadByPointer。gussing.cnblogs.com
再看PspTerminateThreadByPointer函数(805d2882)的反汇编:gussing.cnblogs.com
lkd> uf 805d2882 nt!PspTerminateThreadByPointer: 805d2882 8bff mov edi,edi 805d2884 55 push ebp 805d2885 8bec mov ebp,esp 805d2887 83ec0c sub esp,0Ch 805d288a 834df8ff or dword ptr [ebp-8],0FFFFFFFFh 805d288e 56 push esi 805d288f 57 push edi 805d2890 8b7d08 mov edi,dword ptr [ebp+8] 805d2893 8db748020000 lea esi,[edi+248h] 805d2899 f60640 test byte ptr [esi],40h 805d289c c745f4c0bdf0ff mov dword ptr [ebp-0Ch],0FFF0BDC0h 805d28a3 7417 je nt!PspTerminateThreadByPointer+0x3a (805d28bc) nt!PspTerminateThreadByPointer+0x23: 805d28a5 8b8720020000 mov eax,dword ptr [edi+220h] 805d28ab 0574010000 add eax,174h 805d28b0 50 push eax 805d28b1 57 push edi 805d28b2 6852285d80 push offset nt!PspExitNormalApc+0x54 (805d2852) 805d28b7 e894f1ffff call nt!PspCatchCriticalBreak (805d1a50) nt!PspTerminateThreadByPointer+0x3a: 805d28bc 64a124010000 mov eax,dword ptr fs:[00000124h] 805d28c2 3bf8 cmp edi,eax 805d28c4 750e jne nt!PspTerminateThreadByPointer+0x52 (805d28d4) nt!PspTerminateThreadByPointer+0x44: 805d28c6 33c0 xor eax,eax 805d28c8 40 inc eax 805d28c9 f00906 lock or dword ptr [esi],eax 805d28cc ff750c push dword ptr [ebp+0Ch] 805d28cf e8bef7ffff call nt!PspExitThread (805d2092)
。。。
如之前所述,call PspExitThread之前有一串特征码ff750c,从PspTerminateThreadByPointer函数开始到PspExitThread函数调用间只出现一次,搜索到串ff750c就能很方便的找到PspExitThreadgussing.cnblogs.com
最后提供一个寻找PspExitThread的例子,部分代码来自网上,(忘了谁写的了,谁提醒下我马上改):
NTSTATUS PspTerminateThreadByPointer(PETHREAD Thread, NTSTATUS status); typedef VOID (*MyPspExitThread)(NTSTATUS status); ULONG GetPspTerminateThreadByPointer() { char * PsTerminateSystemThreadAddr; int iLen; ULONG dwAddr; ULONG NtTerminateThreadAddr; char * pAddr; gussing.cnblogs.com PsTerminateSystemThreadAddr= (char *)PsTerminateSystemThread; __asm { __emit 0x90; __emit 0x90; } for (iLen=0;iLen<50;iLen++) { if (*PsTerminateSystemThreadAddr == (char)0xff && *(PsTerminateSystemThreadAddr+1) == (char)0x75 && *(PsTerminateSystemThreadAddr+2) == (char)0x08 ) { __asm { __emit 0x90; __emit 0x90; } PsTerminateSystemThreadAddr += 5; dwAddr = *(ULONG *)PsTerminateSystemThreadAddr + (ULONG)PsTerminateSystemThreadAddr +4; DbgPrint("[ring0] PspTerminateThreadByPointer:: 0x%x ",dwAddr); return dwAddr; //break; } PsTerminateSystemThreadAddr++; } return FALSE; } gussing.cnblogs.com PVOID GetPspExitThread() { #ifdef XP_SP3 char* sPtr; ULONG end = (ULONG)GetPspTerminateThreadByPointer() + 0x60; sPtr = (char*)GetPspTerminateThreadByPointer(); while ( (ULONG)sPtr < end) //0x60 = ±??úé? PspTerminateThreadByPointer ·ê??3ì?è { //DbgPrint("[ring0] addr: 0x%x, val: 0x%x", sPtr, *(WORD*)sPtr); if ( *(WORD*)sPtr == 3189 ) {gussing.cnblogs.com return (PVOID)(*(ULONG*)(sPtr + 3) + sPtr + 7); } sPtr ++; }gussing.cnblogs.com return NULL; #else return NULL; #endif }