pjf获得SwapContext地址方法的解析
在用hook SwapContext法来检查隐藏进程时,首要条件是获得SwapContext的地址。Pjf在博文 ([1]) 中提出了使用线程KernelStack中保存的地址来获得SwapContext地址的方法。本文主要来解析此法。注意本文中正在运行的、即将被换出的线程我们称为旧线程;即将换进的线程称为新线程。
SwapContext在KiSwapContext中调用。下面先来看看XPSP2下KiSwapContext的汇编代码:
nt!KiSwapContext: 8086dbbc 83ec10 sub esp,10h 8086dbbf 895c240c mov dword ptr [esp+0Ch],ebx 8086dbc3 89742408 mov dword ptr [esp+8],esi 8086dbc7 897c2404 mov dword ptr [esp+4],edi 8086dbcb 892c24 mov dword ptr [esp],ebp 8086dbce 648b1d1c000000 mov ebx,dword ptr fs:[1Ch] <--ebx = kpcr 8086dbd5 8bf1 mov esi,ecx <--esi=新线程的ETHREAD. 8086dbd7 8bbb24010000 mov edi,dword ptr [ebx+124h] <--edi=旧线程ETHREAD 8086dbdd 89b324010000 mov dword ptr [ebx+124h],esi <--当前线程表示已经变了. 8086dbe3 8a4f58 mov cl,byte ptr [edi+58h]
8086dbe6 e8f5000000 call nt!SwapContext (8086dce0)
8086dbeb 8b2c24 mov ebp,dword ptr [esp] 8086dbee 8b7c2404 mov edi,dword ptr [esp+4] 8086dbf2 8b742408 mov esi,dword ptr [esp+8] 8086dbf6 8b5c240c mov ebx,dword ptr [esp+0Ch] 8086dbfa 83c410 add esp,10h 8086dbfd c3 ret
|
再来看看XPSP2下SwapContext部分汇编代码:
//ESI为新线程的ETHREAD,EDI为旧线程的ETHREAD。 nt!SwapContext: 8086dce0 0ac9 or cl,cl 8086dce2 26c6462d02 mov byte ptr es:[esi+2Dh],2 8086dce7 9c pushfd //<---------旧线程的ESP-4 8086dce8 8d8b40050000 lea ecx,[ebx+540h] 8086dcee e8ddafffff call nt!KeAcquireQueuedSpinLockAtDpcLevel (80868cd0) 8086dcf3 8d8b38050000 lea ecx,[ebx+538h] 8086dcf9 e8feafffff call nt!KeReleaseQueuedSpinLockFromDpcLevel (80868cfc) 8086dcfe 8b0b mov ecx,dword ptr [ebx] 8086dd00 83bb9409000000 cmp dword ptr [ebx+994h],0 8086dd07 51 push ecx//<---------旧线程的ESP-4 8086dd08 0f8583010000 jne nt!ScPatchFxe+0x3c (8086de91) 8086dd0e 833d6cc5888000 cmp dword ptr [nt!PPerfGlobalGroupMask (8088c56c)],0 8086dd15 0f854d010000 jne nt!ScPatchFxe+0x13 (8086de68) 8086dd1b 0f20c5 mov ebp,cr0 8086dd1e 8bd5 mov edx,ebp 8086dd20 807f3100 cmp byte ptr [edi+31h],0 8086dd24 0f8419010000 je nt!SwapContext+0x163 (8086de43) 8086dd2a 8a4e2c mov cl,byte ptr [esi+2Ch] 8086dd2d 884b50 mov byte ptr [ebx+50h],cl 8086dd30 fa cli 8086dd31 896728 mov dword ptr [edi+28h],esp //旧线程的ESP保存在KernelStack中
8086dd34 8b4618 mov eax,dword ptr [esi+18h] 8086dd37 8b4e1c mov ecx,dword ptr [esi+1Ch] 8086dd3a 2d10020000 sub eax,210h 8086dd3f 894b08 mov dword ptr [ebx+8],ecx 8086dd42 894304 mov dword ptr [ebx+4],eax 8086dd45 33c9 xor ecx,ecx 8086dd47 8a4e31 mov cl,byte ptr [esi+31h] 8086dd4a 83e2f1 and edx,0FFFFFFF1h 8086dd4d 0bca or ecx,edx 8086dd4f 0b880c020000 or ecx,dword ptr [eax+20Ch] 8086dd55 3be9 cmp ebp,ecx 8086dd57 0f85de000000 jne nt!SwapContext+0x15b (8086de3b) 8086dd5d 8d4900 lea ecx,[ecx] 8086dd60 f740e400000200 test dword ptr [eax-1Ch],20000h 8086dd67 7503 jne nt!SwapContext+0x8c (8086dd6c) 8086dd69 83e810 sub eax,10h 8086dd6c 8b4b40 mov ecx,dword ptr [ebx+40h] 8086dd6f 894104 mov dword ptr [ecx+4],eax 8086dd72 8b6628 mov esp,dword ptr [esi+28h] //此时esp已是新线程的上次切换时的值。 8086dd75 8b4620 mov eax,dword ptr [esi+20h] 8086dd78 894318 mov dword ptr [ebx+18h],eax 8086dd7b fb sti 8086dd7c 8b4744 mov eax,dword ptr [edi+44h] 8086dd7f 3b4644 cmp eax,dword ptr [esi+44h] 8086dd82 c6475000 mov byte ptr [edi+50h],0 8086dd86 7440 je nt!SwapContext+0xe8 (8086ddc8) 8086dd88 8b7e44 mov edi,dword ptr [esi+44h] //注意:edi改为新线程的EPROCESS 8086dd8b 8b4b48 mov ecx,dword ptr [ebx+48h] 8086dd8e 314834 xor dword ptr [eax+34h],ecx 8086dd91 314f34 xor dword ptr [edi+34h],ecx 8086dd94 66f74720ffff test word ptr [edi+20h],0FFFFh 8086dd9a 7571 jne nt!SwapContext+0x12d (8086de0d) 8086dd9c 33c0 xor eax,eax 8086dd9e 0f00d0 lldt ax 8086dda1 8d8b40050000 lea ecx,[ebx+540h] 8086dda7 e850afffff call nt!KeReleaseQueuedSpinLockFromDpcLevel (80868cfc) 8086ddac 33c0 xor eax,eax 8086ddae 8ee8 mov gs,ax 8086ddb0 8b4718 mov eax,dword ptr [edi+18h] 8086ddb3 8b6b40 mov ebp,dword ptr [ebx+40h] 8086ddb6 8b4f30 mov ecx,dword ptr [edi+30h] 8086ddb9 89451c mov dword ptr [ebp+1Ch],eax 8086ddbc 0f22d8 mov cr3,eax 8086ddbf 66894d66 mov word ptr [ebp+66h],cx 8086ddc3 eb0e jmp nt!SwapContext+0xf3 (8086ddd3) 8086ddc5 8d4900 lea ecx,[ecx] 8086ddc8 8d8b40050000 lea ecx,[ebx+540h] 8086ddce e829afffff call nt!KeReleaseQueuedSpinLockFromDpcLevel (80868cfc) 8086ddd3 8b4318 mov eax,dword ptr [ebx+18h] 8086ddd6 8b4b3c mov ecx,dword ptr [ebx+3Ch] 8086ddd9 6689413a mov word ptr [ecx+3Ah],ax 8086dddd c1e810 shr eax,10h 8086dde0 88413c mov byte ptr [ecx+3Ch],al 8086dde3 88613f mov byte ptr [ecx+3Fh],ah 8086dde6 ff464c inc dword ptr [esi+4Ch] 8086dde9 ff831c060000 inc dword ptr [ebx+61Ch] 8086ddef 59 pop ecx//<---------本线程ESP+4 8086ddf0 890b mov dword ptr [ebx],ecx 8086ddf2 807e4900 cmp byte ptr [esi+49h],0 8086ddf6 7504 jne nt!SwapContext+0x11c (8086ddfc) 8086ddf8 9d popfd //<---------本线程ESP+4 8086ddf9 33c0 xor eax,eax 8086ddfb c3 ret |
我们主要来分析保存在线程KernelStack中的ESP堆栈的状态:
KernelStack在ETHREAD的0x28处:
nt!_ETHREAD +0x000 Tcb : _KTHREAD +0x000 Header : _DISPATCHER_HEADER +0x000 Type : UChar +0x001 Absolute : UChar +0x002 Size : UChar +0x003 Inserted : UChar +0x004 SignalState : Int4B +0x008 WaitListHead : _LIST_ENTRY +0x000 Flink : Ptr32 +0x004 Blink : Ptr32 +0x010 MutantListHead : _LIST_ENTRY +0x000 Flink : Ptr32 +0x004 Blink : Ptr32 +0x018 InitialStack : Ptr32 +0x01c StackLimit : Ptr32 +0x020 Teb : Ptr32 +0x024 TlsArray : Ptr32 +0x028 KernelStack : Ptr32 ß----此处 +0x02c DebugActive : UChar ………… |
因为当前线程已经不是这个结构了,SwapContext在8086dd88已经替换了ESP了。所以我们需要找一个非当前线程的PETHREAD,于是
*(PULONG)((PETHREAD->KernelStack) + 8) 就是SwapContext的返回地址了。
Pfj就是HOOK的这个地址。但是需要注意的是此处ESI还是新线程的ETHREAD;但是EDI已经不是旧线程的ETHREAD了,它已在SwapContext中被修改成新线程的EPROCESS(见SwapContext的8086dd88行)或其他值了。
所以若是你若HOOK此处,则只能使用ESI值作为新线程的ETHREAD,EDI是不能用了。网文《check hidden process(hook SwapContext)》([2])就是在此处出了点问题。
其实找到SwapContext地址也不难,可以使用KiSwapContext的Call SwapContext指令(8086dbe6 e8f5000000 call nt!SwapContext)。SwapContext地址为:
pSwapContext = *(PULONG)((PETHREAD->KernelStack) + 8); pSwapContext = pSwapContext + *(PULONG)((ULONG)pSwapContext – 4);
|
HOOK此处的地址,ESI就是新线程的ETHREAD;EDI就是旧线程的ETHREAD了。
参考文献:
[1]. http://pjf.blogcn.com/diary,109827361.shtml
[2]. http://www.antiprote ct.com/printer_friendly_posts.asp?TID=4