保护软件一般都直接用新的函数指针替换CallBack表中__ClientLoadLibrary对应的位置
在Windows系统中,提供了几种方式从R0调用位于R3的函数,其中一种方式是KeUserModeCallBack,此函数流程如下:
nt!KeUserModeCallback->nt!KiCallUserMode->nt!KiServiceExit->ntdll!KiUserCallbackDispatcher->回调函数-> int2B->nt!KiCallbackReturn-> nt!KeUserModeCallback(调用后)这是一个 ring0->ring3->ring0的过程,在堆栈准备完毕后,借用KiServiceExit的力量回到了ring3,它的着陆点是 KiUserCallbackDispatcher,然后KiUserCallbackDispatcher从PEB中取出KernelCallbackTable的基址,再以ApiIndex作为索引在这个表中查找对应的回调函数并调用,调用完之后再int2B触发 nt!KiCallbackReturn再次进入内核,修正堆栈后跳回KeUserModeCallback,完成调用。
系统所有的SetWindowsHookEx都是利用KeUserModeCallBack完成的。所以可以通过挂钩KeUserModeCallback用来过滤对钩子的调用。
对于Ring3的应用程序,fs:[0]的地址指向的是TEB结构,这个结构的开头是一个NT_TIB结构,NT_TIB结构的0x18偏移处是一个Self指针,指向这个结构自身,也就是指向TEB结构的开头。
TEB结构的0x30偏移是一个指向PEB的指针。PEB又是一个结构,这个结构的0x2偏移处是一个UChar,名叫BeingDebugged,当进程被调试时,此值为1,未被调试时此值为0
因此以下代码逐行执行后的结果:
mov eax,dword ptr fs:[18h];eax=TEB的指针
mov eax,dword ptr [eax+30h];eax=PEB的指针
movzx eax,byte ptr [eax+2h];eax=PEB.BeingDebugged(byte扩展为dword)
TEB和PEB结构的详细内容可以在windbg内核调试状态下使用dt _TEB、dt _PEB命令来察看。
0:000> x user32!*__ClientLoadLibr* 77d28023 USER32!__ClientLoadLibrary = <no type information>
可以看出TEB结构的0x30偏移是一个指向PEB的指针
0:001> dt _TEB @$teb testDemo!_TEB +0x000 NtTib : _NT_TIB +0x01c EnvironmentPointer : (null) +0x020 ClientId : _CLIENT_ID +0x028 ActiveRpcHandle : (null) +0x02c ThreadLocalStoragePointer : (null) +0x030 ProcessEnvironmentBlock : 0x7ffdd000 _PEB
而KernelCallbackTable表在PEB的ox2c处:
0:001> dt _PEB @$peb testDemo!_PEB +0x000 InheritedAddressSpace : 0 '' +0x001 ReadImageFileExecOptions : 0 '' +0x002 BeingDebugged : 0x1 '' +0x003 SpareBool : 0 '' +0x004 Mutant : 0xffffffff +0x008 ImageBaseAddress : 0x00400000 +0x00c Ldr : 0x00251ea0 _PEB_LDR_DATA +0x010 ProcessParameters : 0x00020000 _RTL_USER_PROCESS_PARAMETERS +0x014 SubSystemData : (null) +0x018 ProcessHeap : 0x00150000 +0x01c FastPebLock : 0x7c99d600 _RTL_CRITICAL_SECTION +0x020 SparePtr1 : 0x7c921000 +0x024 SparePtr2 : 0x7c9210e0 +0x028 EnvironmentUpdateCount : 1 +0x02c KernelCallbackTable : 0x77d12970
不同版本的window系统KernelCallbackTable函数列表位置不同:以下是XPSP3的:
0:001> r @$peb $peb=7ffdd000 0:001> dds 0x77d12970 77d12970 77d27f3c USER32!__fnCOPYDATA 77d12974 77d587b3 USER32!__fnCOPYGLOBALDATA 77d12978 77d28ec8 USER32!__fnDWORD 77d1297c 77d2b149 USER32!__fnNCDESTROY 77d12980 77d5876c USER32!__fnDWORDOPTINLPMSG 77d12984 77d5896d USER32!__fnINOUTDRAG 77d12988 77d3b84d USER32!__fnGETTEXTLENGTHS 77d1298c 77d58c42 USER32!__fnINCNTOUTSTRING 77d12990 77d285c1 USER32!__fnINCNTOUTSTRINGNULL 77d12994 77d58b0f USER32!__fnINLPCOMPAREITEMSTRUCT 77d12998 77d2ce26 USER32!__fnINLPCREATESTRUCT 77d1299c 77d58b4d USER32!__fnINLPDELETEITEMSTRUCT 77d129a0 77d4feec USER32!__fnINLPDRAWITEMSTRUCT 77d129a4 77d58b8b USER32!__fnINLPHELPINFOSTRUCT 77d129a8 77d58b8b USER32!__fnINLPHELPINFOSTRUCT 77d129ac 77d589ad USER32!__fnINLPMDICREATESTRUCT 77d129b0 77d4f65c USER32!__fnINOUTLPMEASUREITEMSTRUCT 77d129b4 77d2be16 USER32!__fnINLPWINDOWPOS 77d129b8 77d2d063 USER32!__fnINOUTLPPOINT5 77d129bc 77d2bd0d USER32!__fnINOUTLPSCROLLINFO 77d129c0 77d3e285 USER32!__fnINOUTLPRECT 77d129c4 77d2bf4c USER32!__fnINOUTNCCALCSIZE 77d129c8 77d2bd0d USER32!__fnINOUTLPSCROLLINFO 77d129cc 77d589ff USER32!__fnINPAINTCLIPBRD 77d129d0 77d58a66 USER32!__fnINSIZECLIPBRD 77d129d4 77d30d41 USER32!__fnINDESTROYCLIPBRD 77d129d8 77d2aca1 USER32!__fnINSTRINGNULL 77d129dc 77d2aca1 USER32!__fnINSTRINGNULL 77d129e0 77d1e68c USER32!__fnINDEVICECHANGE 77d129e4 77d58cd7 USER32!__fnINOUTNEXTMENU 77d129e8 77d593f5 USER32!__fnLOGONNOTIFY 77d129ec 77d58728 USER32!__fnOUTDWORDDWORD 0:001> dds 77d129ec 77d129ec 77d58728 USER32!__fnOUTDWORDDWORD 77d129f0 77d58728 USER32!__fnOUTDWORDDWORD 77d129f4 77d586e5 USER32!__fnOUTDWORDINDWORD 77d129f8 77d58acc USER32!__fnOUTLPRECT 77d129fc 77d285c1 USER32!__fnINCNTOUTSTRINGNULL 77d12a00 77d58b8b USER32!__fnINLPHELPINFOSTRUCT 77d12a04 77d285c1 USER32!__fnINCNTOUTSTRINGNULL 77d12a08 77d5882b USER32!__fnSENTDDEMSG 77d12a0c 77d2c2f5 USER32!__fnINOUTSTYLECHANGE 77d12a10 77d30214 USER32!__fnHkINDWORD 77d12a14 77d4f92a USER32!__fnHkINLPCBTACTIVATESTRUCT 77d12a18 77d4f86c USER32!__fnHkINLPCBTCREATESTRUCT 77d12a1c 77d58dce USER32!__fnHkINLPDEBUGHOOKSTRUCT 77d12a20 77d316a3 USER32!__fnHkINLPMOUSEHOOKSTRUCTEX 77d12a24 77d58d54 USER32!__fnHkINLPKBDLLHOOKSTRUCT 77d12a28 77d58d91 USER32!__fnHkINLPMSLLHOOKSTRUCT 77d12a2c 77d278ab USER32!__fnHkINLPMSG 77d12a30 77d58d17 USER32!__fnHkINLPRECT 77d12a34 77d4f065 USER32!__fnHkOPTINLPEVENTMSG 77d12a38 77d58eb9 USER32!__ClientCopyDDEIn1 77d12a3c 77d58efb USER32!__ClientCopyDDEIn2 77d12a40 77d58f5e USER32!__ClientCopyDDEOut1 77d12a44 77d58f2d USER32!__ClientCopyDDEOut2 77d12a48 77d2eb09 USER32!__ClientCopyImage 77d12a4c 77d58f92 USER32!__ClientEventCallback 77d12a50 77d319f6 USER32!__ClientFindMnemChar 77d12a54 77d228f3 USER32!__ClientFontSweep 77d12a58 77d58e4c USER32!__ClientFreeDDEHandle 77d12a5c 77d282ff USER32!__ClientFreeLibrary 77d12a60 77d1f4b2 USER32!__ClientGetCharsetInfo 77d12a64 77d58e83 USER32!__ClientGetDDEFlags 77d12a68 77d58fdc USER32!__ClientGetDDEHookData 0:001> dds 77d12a68 77d12a68 77d58fdc USER32!__ClientGetDDEHookData 77d12a6c 77d4f9f5 USER32!__ClientGetListboxString 77d12a70 77d1ec46 USER32!__ClientGetMessageMPH 77d12a74 77d216eb USER32!__ClientLoadImage 77d12a78 77d28023 USER32!__ClientLoadLibrary 77d12a7c 77d2ec03 USER32!__ClientLoadMenu 77d12a80 77d1ee0d USER32!__ClientLoadLocalT1Fonts 77d12a84 77d209e4 USER32!__ClientLoadRemoteT1Fonts 77d12a88 77d5907b USER32!__ClientPSMTextOut 77d12a8c 77d590d1 USER32!__ClientLpkDrawTextEx 77d12a90 77d59135 USER32!__ClientExtTextOutW 77d12a94 77d5919a USER32!__ClientGetTextExtentPointW 77d12a98 77d59019 USER32!__ClientCharToWchar 77d12a9c 77d1ed14 USER32!__ClientAddFontResourceW 77d12aa0 77d1a13e USER32!__ClientThreadSetup 77d12aa4 77d59253 USER32!__ClientDeliverUserApc 77d12aa8 77d591f1 USER32!__ClientNoMemoryPopup 77d12aac 77d2a740 USER32!__ClientMonitorEnumProc 77d12ab0 77d5944a USER32!__ClientCallWinEventProc 77d12ab4 77d58e15 USER32!__ClientWaitMessageExMPH 77d12ab8 77d2cf8e USER32!__ClientWOWGetProcModule 77d12abc 77d5948d USER32!__ClientWOWTask16SchedNotify 77d12ac0 77d59266 USER32!__ClientImmLoadLayout 77d12ac4 77d592c2 USER32!__ClientImmProcessKey 77d12ac8 77d59302 USER32!__fnIMECONTROL 77d12acc 77d58896 USER32!__fnINWPARAMDBCSCHAR 77d12ad0 77d3b84d USER32!__fnGETTEXTLENGTHS 77d12ad4 77d58bdc USER32!__fnINLPKDRAWSWITCHWND 77d12ad8 77d21805 USER32!__ClientLoadStringW 77d12adc 77d65827 USER32!__ClientLoadOLE 77d12ae0 77d65704 USER32!__ClientRegisterDragDrop 77d12ae4 77d65743 USER32!__ClientRevokeDragDrop 0:001> dds 77d12ae4 77d12ae4 77d65743 USER32!__ClientRevokeDragDrop 77d12ae8 77d593b2 USER32!__fnINOUTMENUGETOBJECT 77d12aec 77d303f7 USER32!__ClientPrinterThunk 77d12af0 77d594cc USER32!__fnOUTLPCOMBOBOXINFO 77d12af4 77d5950c USER32!__fnOUTLPSCROLLBARINFO
我们可以找到__ClientLoadLibrary函数对应的位置:
77d12a78 77d28023 USER32!__ClientLoadLibrary
相对于KernelCallbackTable的起始位置77d12970偏移为0x108,保护软件一般是直接把这一行的77d28023换成自己的函数地址,然后判断传入参数,合法就在自己函数中跳回USER32!__ClientLoadLibrary
摘了一篇文章快照:(自己加点理解)
Anti SetWindowsHookEx DLL injection made possible Read on.
After spending some time reversing the user32 internals, I discovered this undocumented function. This function is responsible to load the SetWindowsHookEx() registered DLL into your process. This blog will only focus on usermode, where the actual DLL loading takes place.
user32.__ClientLoadLibrary(lpHook)
This function takes only 1 argument, a pointer to an undocumented structure allocated in process stack. It holds the path of the DLL, pointer to notification function and some yet to be known data.
typedef struct { DWORD unknow_0; // 0x00 DWORD unknow_4; // 0x04 DWORD nCount; // 0x08 DWORD unknow_c; // 0x0C DWORD offCbKPtrs; // 0x10 DWORD bFixed; // 0x14 UNICODE_STRING lpDllPath; // 0x18 DWORD lpfnNotiy; // 0x20 }USERHOOK;
At the beginning of function, it checks for _USERHOOK.nCount and _USERHOOK.bFixed value. Then it calls toFixupCallbackPointers.
user32.FixupCallbackPointers(lpHook)
It takes only 1 argument, the same argument passed to __ClientLoadLibrary. This function “fix up” the pointers in a pretty interesting way. First it locate the address of callback pointers.
lpCbkPtrs = lpHook + offCbkPtrs
Then it loops through the a list of pointers and fix it up by resolving the offset to actual address.
newaddress = lpHook + offset
After fixing up all the pointers, we return to __ClientLoadLibrary and then it calls to InitUserApiHook.
一般都是自己写FixupCallbackPointers:(其实后dumpbin一看,就会发现这个函数不是导出函数,所以没法直接调用)