【转】消息钩子注册浅析
标 题: 【原创】消息钩子注册浅析
作 者: RootSuLe
时 间: 2011-06-18,23:10:34
链 接: http://bbs.pediy.com/showthread.php?t=135702
windows消息钩子很古老,但目前仍有很多地方需要用到,简单跟踪了一下函数执行流程,函数用法如下(MSDN):
函数功能:该函数将一个应用程序定义的挂钩处理过程安装到挂钩链中去,您可以通过安装挂钩处理过程来对系统的某些类型事件进行监控,这些事件与某个特定的线程或系统中的所有事件相关.
函数原形:
HHOOK SetWindowsHookEx(int idHook, HOOKPROC lpfn, HINSTANCE hMod, DWORD dwThreadId);
参数:
idHook:指示欲被安装的挂钩处理过程之类型,此参数可以是以下值之一:
WH_CALLWNDPROC(4): 安装一个挂钩处理过程,在系统将消息发送至目标窗口处理过程之前,对该消息进行监视,详情参见CallWndProc挂钩处理过程.
WH_CALLWNDPROCRET(12) :安装一个挂钩处理过程,它对已被目标窗口处理过程处理过了的消息进行监视,详情参见 CallWndRetProc 挂钩处理过程.
WH_CBT(5) :安装一个挂钩处理过程,接受对CBT应用程序有用的消息 ,详情参见 CBTProc 挂钩处理过程.
WH_DEBUG(9):安装一个挂钩处理过程以便对其他挂钩处理过程进行调试, 详情参见DebugProc挂钩处理过程.
WH_FOREGROUNDIDLE(11):安装一个挂钩处理过程,该挂钩处理过程当应用程序的前台线程即将进入空闲状态时被调用,它有助于在空闲时间内执行低优先级的任务.
WH_GETMESSAGE(3):安装一个挂钩处理过程对寄送至消息队列的消息进行监视,详情参见 GetMsgProc 挂钩处理过程.
WH_JOURNALPLAYBACK(1):安装一个挂钩处理过程,对此前由WH_JOURNALRECORD 挂钩处理过程纪录的消息进行寄送.详情参见 JournalPlaybackProc挂钩处理过程.
WH_JOURNALRECORD(0):安装一个挂钩处理过程,对寄送至系统消息队列的输入消息进行纪录.详情参见JournalRecordProc挂钩处理过程.
WH_KEYBOARD(2):安装一个挂钩处理过程对击键消息进行监视. 详情参见KeyboardProc挂钩处理过程.
WH_KEYBOARD_LL(13):此挂钩只能在Windows NT中被安装,用来对底层的键盘输入事件进行监视.详情参见LowLevelKeyboardProc挂钩处理过程.
WH_MOUSE(7):安装一个挂钩处理过程,对鼠标消息进行监视. 详情参见 MouseProc挂钩处理过程.
WH_MOUSE_LL(14):此挂钩只能在Windows NT中被安装,用来对底层的鼠标输入事件进行监视.详情参见LowLevelMouseProc挂钩处理过程.
WH_MSGFILTER(-1):安装一个挂钩处理过程, 以监视由对话框、消息框、菜单条、或滚动条中的输入事件引发的消息.详情参见MessageProc挂钩处理过程.
WH_SHELL(10):安装一个挂钩处理过程以接受对外壳应用程序有用的通知, 详情参见 ShellProc挂钩处理过程.
WH_SYSMSGFILTER(6):安装一个挂钩处理过程,以监视由对话框、消息框、菜单条、或滚动条中的输入事件引发的消息.这个挂钩处理过程对系统中所有应用程序的这类消息都进行监视.详情参见 SysMsgProc挂钩处理过程.
lpfn:指向相应的挂钩处理过程.若参数dwThreadId为0或者指示了一个其他进程创建的线程之标识符,则参数lpfn必须指向一个动态链接中的挂钩处理过程.否则,参数lpfn可以指向一个与当前进程相关的代码中定义的挂钩处理过程.
hMod:指示了一个动态链接的句柄,该动态连接库包含了参数lpfn 所指向的挂钩处理过程.若参数dwThreadId指示的线程由当前进程创建,并且相应的挂钩处理过程定义于当前进程相关的代码中,则参数hMod必须被设置为NULL(0).
dwThreadId:指示了一个线程标识符,挂钩处理过程与线程相关.若此参数值为0,则该挂钩处理过程与所有现存的线程相关.
返回值:若此函数执行成功,则返回值就是该挂钩处理过程的句柄;若此函数执行失败,则返回值为NULL(0).若想获得更多错误信息,请调用GetLasError函数.
备注:若参数hMod为NULL,而参数dwThreadld为0或者指示了一个其他进程创建的线程标识符,则会产生错误.
对函数CallNextHookEx进行调用以下链接下一个挂钩处理过程是可选的,但也是被推荐的否则,其他安装了此挂钩的应用程序将无法获得此挂钩通知,从而可能导致错误的行为.除非您确实希望防止其他应用程序看到此挂钩通知,您应当调用函数CallNextHookEx.
在终止一个应用程序之前,必须调用函数UnhookWindowsHookEx以释放与此挂钩相关的系统资源.
挂钩的作用域依赖与挂钩的类型.一些挂钩只能被设置成系统作用域,其他挂钩(如下所示)还可以被设置为某一特定线程的作用域:
WH_CALLWNDPROC 线程或系统
WH_CALLWNDPROCRET 线程或系统
WH_CBT 线程或系统
WH_DEBUG 线程或系统
WH_FOREGROUNDIDLE 线程或系统
WH_GETMESSAGE 线程或系统
WH_JOURNALPLAYBACK 系统
WH_JOURNALRECORD 系统
WH_KEYBOARD 线程或系统
WH_KEYBOARD_LL 线程或系统
WH_MOUSE 线程或系统
WH_MOUSE_LL 线程或系统
WH_MSGFILTER 线程或系统
WH_SHELL 线程或系统
WH_SYSMSGFILTER 系统
对于一个特定的挂钩类型,现成的挂钩先被调用,然后才是系统挂钩被调用.
系统挂钩作为共享资源,安装一次就对所用应用程序产生影响.所有的系统挂钩函数必须在库中.系统挂钩应当被限制用于一些特殊用途的应用程序或者用来作为应用程序调试的辅助工具.不再需要挂钩的库应当将相应的挂钩处理过程删除掉.
HHOOK SetWindowsHookEx(int idHook, HOOKPROC lpfn, HINSTANCE hMod, DWORD dwThreadId ); .text:77D31211 ; HHOOK __stdcall SetWindowsHookExA(int idHook, HOOKPROC lpfn, HINSTANCE hmod, DWORD dwThreadId) .text:77D31211 public _SetWindowsHookExA@16 .text:77D31211 _SetWindowsHookExA@16 proc near ; DATA XREF: .text:off_77D13928.o .text:77D31211 .text:77D31211 idHook = dword ptr 8 .text:77D31211 lpfn = dword ptr 0Ch .text:77D31211 hModule = dword ptr 10h .text:77D31211 dwThreadId = dword ptr 14h .text:77D31211 .text:77D31211 mov edi, edi .text:77D31213 push ebp .text:77D31214 mov ebp, esp .text:77D31216 push 2 ;DWORD dwFlags .text:77D31218 push [ebp+dwThreadId] .text:77D3121B push [ebp+hModule] .text:77D3121E push [ebp+lpfn] .text:77D31221 push [ebp+idHook] .text:77D31224 call _SetWindowsHookExAW@20 ; SetWindowsHookExAW(x,x,x,x,x) .text:77D31229 pop ebp .text:77D3122A retn 10h .text:77D3122A _SetWindowsHookExA@16 endp HHOOK __stdcall SetWindowsHookExAW(int idHook, HOOKPROC lpfn, HINSTANCE hmod, DWORD dwThreadId,DWORD dwFlags .text:77D28157 ; int __stdcall SetWindowsHookExAW(int idHook, int lpfn, HMODULE hModule, int dwThreadId, DWORD dwFlags .text:77D28157 _SetWindowsHookExAW@20 proc near ; CODE XREF: SetWindowsHookExW(x,x,x,x)+13.p .text:77D28157 ; SetWindowsHookExA(x,x,x,x)+13.p .text:77D28157 .text:77D28157 Filename = word ptr -20Ch .text:77D28157 var_4 = dword ptr -4 .text:77D28157 idHook = dword ptr 8 .text:77D28157 lpfn = dword ptr 0Ch .text:77D28157 hModule = dword ptr 10h .text:77D28157 dwThreadId = dword ptr 14h .text:77D28157 Mod = dword ptr 18h .text:77D28157 .text:77D28157 mov edi, edi .text:77D28159 push ebp .text:77D2815A mov ebp, esp .text:77D2815C sub esp, 20Ch ; 为局部变量分配内存空间0x20Ch = 524字节,此为双字节数组,共261个元素,所以最大元素下标是260[MAX_PATH] .text:77D28162 mov eax, ___security_cookie .text:77D28167 push esi ; 保存esi .text:77D28168 mov esi, [ebp+hModule] .text:77D2816B test esi, esi .text:77D2816D push edi .text:77D2816E mov edi, [ebp+lpfn] .text:77D28171 mov [ebp+var_4], eax .text:77D28174 jz short loc_77D2818D ; hmod为NULL .text:77D28176 push 104h ; nSize .text:77D2817B lea eax, [ebp+Filename] .text:77D28181 push eax ; lpFilename .text:77D28182 push esi ; hModule .text:77D28183 call ds:__imp__GetModuleFileNameW@12 ; GetModuleFileNameW(x,x,x) .text:77D28189 test eax, eax .text:77D2818B jz short loc_77D281AC ; 获取模块句柄失败 .text:77D2818D .text:77D2818D loc_77D2818D: ; CODE XREF: SetWindowsHookExAW(x,x,x,x,x)+1D.j .text:77D2818D push [ebp+dwFlags] .text:77D28190 mov eax, esi ;esi也拥有模块句柄 .text:77D28192 push edi ;lpfn入栈 .text:77D28193 push [ebp+idHook] .text:77D28196 neg eax ;取反操作,如果eax为NULL的话,CF位为0,否则CF置1 .text:77D28198 push [ebp+dwThreadId] .text:77D2819B sbb eax, eax ;若CF为0,eax = 0,否则eax为-1 .text:77D2819D lea ecx, [ebp+Filename] .text:77D281A3 and eax, ecx ;sbb后,若eax为0,则结果为0,否则为ecx .text:77D281A5 push eax ;判断结果 .text:77D281A6 push esi ;esi拥有模块句柄 .text:77D281A7 call __SetWindowsHookEx@24 ; _SetWindowsHookEx(x,x,x,x,x,x) .text:77D281AC .text:77D281AC loc_77D281AC: ; CODE XREF: SetWindowsHookExAW(x,x,x,x,x)+34.j .text:77D281AC mov ecx, [ebp+var_4] .text:77D281AF pop edi .text:77D281B0 pop esi .text:77D281B1 call @__security_check_cookie@4 ; __security_check_cookie(x) .text:77D281B6 leave .text:77D281B7 retn 14h .text:77D281B7 _SetWindowsHookExAW@20 endp
伪代码:SetWindowsHookExAW
HHOOK __stdcall SetWindowsHookExAW(int idHook, HOOKPROC lpfn, HINSTANCE hmod, DWORD dwThreadId,DWORD dwFlags) { WCHAR Filename[MAX_PATH]; if (hmod != NULL) { //获取文件名 if (!GetModuleFileNameW(hmod,Filename,MAX_PATH)) { //该参数虽然存在,但经验证是伪造的,返回NULL return NULL; } } return _SetWindowsHookEx(hmod,(hmod == NULL) ? NULL:Filename,dwThreadId,idHook,lpfn,dwFlags); }
HHOOK __stdcall _SetWindowsHookEx(HMODULE hmod,LPCWSTR Filename,DWORD dwThreadId,int idHook,HOOKPROC lpfn,DWORD dwFlags);
.text:77D281BF ; int __stdcall _SetWindowsHookEx(HMODULE hmod, LPCWSTR Filename, DWORD dwThreadId, int idHook, HOOKPROC lpfn, DWORD dwFlags) .text:77D281BF __SetWindowsHookEx@24 proc near ; CODE XREF: SetWindowsHookExAW(x,x,x,x,x)+50.p .text:77D281BF .text:77D281BF usFileName = byte ptr -10h .text:77D281BF usModuleName = dword ptr -8 .text:77D281BF var_4 = dword ptr -4 .text:77D281BF hmod = dword ptr 8 .text:77D281BF Filename = dword ptr 0Ch .text:77D281BF dwThreadId = dword ptr 10h .text:77D281BF idHook = dword ptr 14h .text:77D281BF lpfn = dword ptr 18h .text:77D281BF Flags = dword ptr 1Ch .text:77D281BF .text:77D281BF mov edi, edi .text:77D281C1 push ebp .text:77D281C2 mov ebp, esp .text:77D281C4 sub esp, 10h ; 分配16字节大小堆栈空间 .text:77D281C7 push [ebp+Filename] ; Filename入栈 .text:77D281CA and [ebp+var_4], 0 ; .text:77D281CE lea eax, [ebp+usFileName] .text:77D281D1 push eax .text:77D281D2 mov [ebp+usModuleName], eax .text:77D281D5 call ds:__imp__RtlInitUnicodeString@8 ; RtlInitUnicodeString(x,x) .text:77D281DB push [ebp+dwFlags] .text:77D281DE push [ebp+lpfn] .text:77D281E1 push [ebp+idHook] .text:77D281E4 push [ebp+dwThreadId] .text:77D281E7 push [ebp+usModuleName] .text:77D281EA push [ebp+hmod] .text:77D281ED call _NtUserSetWindowsHookEx@24 ; NtUserSetWindowsHookEx(x,x,x,x,x,x) .text:77D281F2 leave .text:77D281F3 retn 18h .text:77D281F3 __SetWindowsHookEx@24 endp
伪代码:
HHOOK __stdcall _SetWindowsHookEx(HMODULE hmod,LPCWSTR Filename,DWORD dwThreadId,int idHook,HOOKPROC lpfn,DWORD dwFlags) { UNICODE_STRING usFileName; //初始化UNICODE字符串结构 RtlInitUnicodeString(&usFileName,Filename); return NtUserSetWindowsHookEx(hmod,&usFileName,dwThreadId,idHook,lpfn,dwFlags); } .text:77D281FB ; __stdcall NtUserSetWindowsHookEx(x, x, x, x, x, x) .text:77D281FB _NtUserSetWindowsHookEx@24 proc near ; CODE XREF: _SetWindowsHookEx(x,x,x,x,x,x)+2E.p .text:77D281FB mov eax, 1225h .text:77D28200 mov edx, 7FFE0300h .text:77D28205 call dword ptr [edx] .text:77D28207 retn 18h .text:77D28207 _NtUserSetWindowsHookEx@24 endp
////////////////////////////////内核下
HHOOK NTAPI NtUserSetWindowsHookEx( HINSTANCE Mod, PUNICODE_STRING ModuleName, DWORD ThreadId, int HookId, HOOKPROC HookProc, DWORD dwFlags); .text:BF85EA67 ; START OF FUNCTION CHUNK FOR _NtUserSetWindowsHookEx@24 .text:BF85EA67 .text:BF85EA67 loc_BF85EA67: ; CODE XREF: NtUserSetWindowsHookEx(x,x,x,x,x,x)+1C.j .text:BF85EA67 push 57h ; 错误码:87 ,参数不正确 .text:BF85EA69 call _UserSetLastError@4 ; UserSetLastError(x) .text:BF85EA6E jmp short loc_BF85EAAE ; 准备退出 .text:BF85EA6E ; END OF FUNCTION CHUNK FOR _NtUserSetWindowsHookEx@24 .text:BF85EA75 ; int __stdcall NtUserSetWindowsHookEx(HINSTANCE Mod, PUNICODE_STRING ModuleName, DWORD ThreadId, int HookId, HOOKPROC HookProc, BOOL Ansi) .text:BF85EA75 _NtUserSetWindowsHookEx@24 proc near ; DATA XREF: .data:BF99E094.o .text:BF85EA75 .text:BF85EA75 Mod = dword ptr 8 .text:BF85EA75 ModuleName = dword ptr 0Ch .text:BF85EA75 ThreadId = dword ptr 10h .text:BF85EA75 HookId = dword ptr 14h .text:BF85EA75 HookProc = dword ptr 18h .text:BF85EA75 Ansi = dword ptr 1Ch .text:BF85EA75 .text:BF85EA75 ; FUNCTION CHUNK AT .text:BF85EA67 SIZE 00000009 BYTES .text:BF85EA75 .text:BF85EA75 mov edi, edi .text:BF85EA77 push ebp .text:BF85EA78 mov ebp, esp .text:BF85EA7A push esi .text:BF85EA7B call _EnterCrit@0 ; EnterCrit() .text:BF85EA80 xor esi, esi .text:BF85EA82 cmp [ebp+ThreadId], esi .text:BF85EA85 jz short loc_BF85EABA ; 进程ID为0,说明是系统全局消息钩子 .text:BF85EA87 push [ebp+ThreadId] .text:BF85EA8A call _PtiFromThreadId@4 ; 通过线程ID获取PTHREADINFO指针 .text:BF85EA8F cmp eax, esi .text:BF85EA91 jz short loc_BF85EA67 ; 没有获取 .text:BF85EA93 .text:BF85EA93 loc_BF85EA93: ; CODE XREF: NtUserSetWindowsHookEx(x,x,x,x,x,x)+47.j .text:BF85EA93 push [ebp+dwFlags] .text:BF85EA96 push [ebp+HookProc] .text:BF85EA99 push [ebp+HookId] .text:BF85EA9C push eax .text:BF85EA9D push [ebp+ModuleName] .text:BF85EAA0 push [ebp+Mod] .text:BF85EAA3 call _zzzSetWindowsHookEx@24 ; zzzSetWindowsHookEx(x,x,x,x,x,x) .text:BF85EAA8 cmp eax, esi ; 判断结果为NULL .text:BF85EAAA jz short loc_BF85EAAE ; 失败则跳转 .text:BF85EAAC mov esi, [eax] ; 暂存结果 .text:BF85EAAE .text:BF85EAAE loc_BF85EAAE: ; CODE XREF: NtUserSetWindowsHookEx(x,x,x,x,x,x)-7.j .text:BF85EAAE ; NtUserSetWindowsHookEx(x,x,x,x,x,x)+35.j .text:BF85EAAE call _LeaveCrit@0 ; LeaveCrit() .text:BF85EAB3 mov eax, esi .text:BF85EAB5 pop esi .text:BF85EAB6 pop ebp .text:BF85EAB7 retn 18h .text:BF85EABA ; --------------------------------------------------------------------------- .text:BF85EABA .text:BF85EABA loc_BF85EABA: ; CODE XREF: NtUserSetWindowsHookEx(x,x,x,x,x,x)+10.j .text:BF85EABA xor eax, eax .text:BF85EABC jmp short loc_BF85EA93 .text:BF85EABC _NtUserSetWindowsHookEx@24 endp
HHOOK __stdcall NtUserSetWindowsHookEx(HINSTANCE Mod, PUNICODE_STRING ModuleName, DWORD ThreadId, int HookId, HOOKPROC HookProc, DWORD dwFlags) { HHOOK hRet; PTHREADINFO pti; HHOOK TempHandle; EnterCrit(); hRet = NULL; if ( ThreadId ) { pti = PtiFromThreadId(ThreadId); if ( !pti ) { //设置用户模式错误码:87 ,参数不正确 UserSetLastError(87); } } else { pti = NULL; } //zzzSetWindowsHookEx返回的是PHOOK类型,强制转换成HHOOK TempHandle = (HHOOK)zzzSetWindowsHookEx(Mod, ModuleName, pti, HookId, HookProc, dwFlags); if ( TempHandle ) { hRet = TempHandle; } LeaveCrit(); return hRet; }
.text:BF85EABE ; START OF FUNCTION CHUNK FOR _zzzSetWindowsHookEx@24 .text:BF85EABE .text:BF85EABE loc_BF85EABE: ; CODE XREF: zzzSetWindowsHookEx(x,x,x,x,x,x)+21.j .text:BF85EABE push 593h ; 错误码:1427,无效的挂接程序。 .text:BF85EAC3 jmp loc_BF85ED2A .text:BF85EAC8 ; --------------------------------------------------------------------------- .text:BF85EAC8 .text:BF85EAC8 loc_BF85EAC8: ; CODE XREF: zzzSetWindowsHookEx(x,x,x,x,x,x)+3F.j .text:BF85EAC8 push 595h ; 错误码:1429,此挂接程序只可整体设置。 .text:BF85EACD jmp loc_BF85ED2A .text:BF85EAD2 ; --------------------------------------------------------------------------- .text:BF85EAD2 .text:BF85EAD2 loc_BF85EAD2: ; CODE XREF: zzzSetWindowsHookEx(x,x,x,x,x,x)+6B.j .text:BF85EAD2 push 20h ; 错误码:32,另一个程序正在使用此文件,进程无法访问。 .text:BF85EAD4 jmp loc_BF85EC31 .text:BF85EAD9 ; --------------------------------------------------------------------------- .text:BF85EAD9 .text:BF85EAD9 loc_BF85EAD9: ; CODE XREF: zzzSetWindowsHookEx(x,x,x,x,x,x)+64.j .text:BF85EAD9 push 10h ; 错误码:16,无法删除目录。 .text:BF85EADB jmp loc_BF85EC31 .text:BF85EAE0 ; --------------------------------------------------------------------------- .text:BF85EAE0 .text:BF85EAE0 loc_BF85EAE0: ; CODE XREF: zzzSetWindowsHookEx(x,x,x,x,x,x)+8C.j .text:BF85EAE0 mov eax, [edi+3Ch] .text:BF85EAE3 mov eax, [eax+10h] .text:BF85EAE6 test byte ptr [eax+10h], 4 ; ptiCurrent->rpdesk->rpwinstaParent->dwWSF_Flags .text:BF85EAEA jz loc_BF85EC50 .text:BF85EAF0 push 5B3h ; 错误码:1459,该操作需要交互式窗口工作站。 .text:BF85EAF5 jmp loc_BF85ED2A .text:BF85EAFA ; --------------------------------------------------------------------------- .text:BF85EAFA .text:BF85EAFA loc_BF85EAFA: ; CODE XREF: zzzSetWindowsHookEx(x,x,x,x,x,x)+C1.j .text:BF85EAFA push 7Eh ; 错误码:126,找不到指定的模块。 .text:BF85EAFC call _UserSetLastError@4 ; UserSetLastError(x) .text:BF85EB01 push esi .text:BF85EB02 call _HMFreeObject@4 ; HMFreeObject(x) .text:BF85EB07 jmp loc_BF85ED2F ; 返回NULL .text:BF85EB0C ; --------------------------------------------------------------------------- .text:BF85EB0C .text:BF85EB0C loc_BF85EB0C: ; CODE XREF: zzzSetWindowsHookEx(x,x,x,x,x,x)+100.j .text:BF85EB0C push dword ptr [ecx] .text:BF85EB0E call ds:__imp__KeAttachProcess@4 ; KeAttachProcess(x) .text:BF85EB14 mov eax, [ebp+pti] .text:BF85EB17 mov [ebp+HookId], 1 .text:BF85EB1E jmp loc_BF85ECC8 ; 判断是否HOOK成功 .text:BF85EB23 ; --------------------------------------------------------------------------- .text:BF85EB23 .text:BF85EB23 loc_BF85EB23: ; CODE XREF: zzzSetWindowsHookEx(x,x,x,x,x,x)+11A.j .text:BF85EB23 call ds:__imp__KeDetachProcess@0 ; 解除绑定进程 .text:BF85EB29 mov eax, [ebp+pti] .text:BF85EB2C jmp loc_BF85ECDE ; 记录哪个线程被HOOK了 .text:BF85EB31 ; --------------------------------------------------------------------------- .text:BF85EB31 .text:BF85EB31 loc_BF85EB31: ; CODE XREF: zzzSetWindowsHookEx(x,x,x,x,x,x)+D9.j .text:BF85EB31 mov eax, [edi+40h] .text:BF85EB34 or dword ptr [esi+20h], 1 .text:BF85EB38 lea ecx, [ebx+1] .text:BF85EB3B lea eax, [eax+ebx*4+14h] .text:BF85EB3F shl edx, cl .text:BF85EB41 mov [ebp+ModuleName], eax .text:BF85EB44 mov eax, [edi+40h] .text:BF85EB47 or [eax+0Ch], edx .text:BF85EB4A and dword ptr [esi+28h], 0 .text:BF85EB4E jmp loc_BF85ECE1 .text:BF85EB53 ; --------------------------------------------------------------------------- .text:BF85EB53 .text:BF85EB53 loc_BF85EB53: ; CODE XREF: zzzSetWindowsHookEx(x,x,x,x,x,x)+11.j .text:BF85EB53 ; zzzSetWindowsHookEx(x,x,x,x,x,x)+16.j .text:BF85EB53 push 592h ; 错误码:1426,无效的挂接程序类型。 .text:BF85EB58 jmp loc_BF85ED2A .text:BF85EB5D ; --------------------------------------------------------------------------- .text:BF85EB5D .text:BF85EB5D loc_BF85EB5D: ; CODE XREF: zzzSetWindowsHookEx(x,x,x,x,x,x)+14F.j .text:BF85EB5D test ds:abHookFlags[ebx], 10h .text:BF85EB64 jz loc_BF85ED13 ; 无效的钩子类型,返回 .text:BF85EB6A or byte ptr [edi+4Ah], 4 .text:BF85EB6E push 0Eh ; Priority .text:BF85EB70 push dword ptr [edi] ; Thread .text:BF85EB72 call ds:__imp__KeSetPriorityThread@8 ; KeSetPriorityThread(x,x) .text:BF85EB78 test ds:abHookFlags[ebx], 4 ; HKF_JOURNAL .text:BF85EB7F jz loc_BF85ED13 .text:BF85EB85 mov eax, [edi+28h] .text:BF85EB88 mov [ebp+var_C], eax .text:BF85EB8B lea eax, [ebp+var_C] .text:BF85EB8E mov [edi+28h], eax .text:BF85EB91 mov [ebp+var_8], esi .text:BF85EB94 inc dword ptr [esi+4] .text:BF85EB97 call _zzzSetFMouseMoved@0 ; zzzSetFMouseMoved() .text:BF85EB9C call _ThreadUnlock1@0 ; ThreadUnlock1() .text:BF85EBA1 cmp ebx, 1 ; 是否是WH_JOURNALPLAYBACK .text:BF85EBA4 mov esi, eax .text:BF85EBA6 jnz loc_BF85ED13 .text:BF85EBAC mov eax, [edi+2Ch] .text:BF85EBAF mov _gppiInputProvider, eax .text:BF85EBB4 jmp loc_BF85ED13 .text:BF85EBB4 ; END OF FUNCTION CHUNK FOR _zzzSetWindowsHookEx@24 .text:BF85EBB4 ; --------------------------------------------------------------------------- .text:BF85EBB9 db 5 dup(90h) .text:BF85EBBE .text:BF85EBBE ; =============== S U B R O U T I N E ======================================= .text:BF85EBBE .text:BF85EBBE ; Attributes: bp-based frame .text:BF85EBBE .text:BF85EBBE ; HHOOK __stdcall zzzSetWindowsHookEx(HINSTANCE Mod, PUNICODE_STRING ModuleName, PTHREADINFO pti, int HookId, HOOKPROC HookProc, DWORD dwFlags) .text:BF85EBBE _zzzSetWindowsHookEx@24 proc near ; CODE XREF: NtUserSetWindowsHookEx(x,x,x,x,x,x)+2E.p .text:BF85EBBE ; zzzSetWindowsHookAW(x,x,x)+1A.p .text:BF85EBBE .text:BF85EBBE var_C = dword ptr -0Ch .text:BF85EBBE var_8 = dword ptr -8 .text:BF85EBBE Mod = dword ptr 8 .text:BF85EBBE ModuleName = dword ptr 0Ch .text:BF85EBBE pti = dword ptr 10h .text:BF85EBBE HookId = dword ptr 14h .text:BF85EBBE HookProc = dword ptr 18h .text:BF85EBBE Ansi = dword ptr 1Ch .text:BF85EBBE arg_18 = dword ptr 20h .text:BF85EBBE .text:BF85EBBE ; FUNCTION CHUNK AT .text:BF85EABE SIZE 000000FB BYTES .text:BF85EBBE .text:BF85EBBE mov edi, edi .text:BF85EBC0 push ebp .text:BF85EBC1 mov ebp, esp .text:BF85EBC3 sub esp, 0Ch ; 开辟新空间 .text:BF85EBC6 push ebx .text:BF85EBC7 mov ebx, [ebp+HookId] .text:BF85EBCA cmp ebx, 0FFFFFFFFh .text:BF85EBCD push esi .text:BF85EBCE push edi .text:BF85EBCF jl short loc_BF85EB53 ; 钩子类型小于0 .text:BF85EBD1 cmp ebx, 0Eh ; 钩子类型是否大于15,MSDN共定义了15种钩子类型 .text:BF85EBD4 jg loc_BF85EB53 ; 大于15了,也跳转 .text:BF85EBDA xor edx, edx .text:BF85EBDC cmp [ebp+HookProc], edx ; HookProc是否存在 .text:BF85EBDF jz loc_BF85EABE ; 不存在,跳 .text:BF85EBE5 mov esi, [ebp+pti] .text:BF85EBE8 cmp esi, edx ; 验证PTHREADINFO .text:BF85EBEA mov edi, _gptiCurrent ; 使用本线程的THREADINFO .text:BF85EBF0 jz loc_BF85ED1C .text:BF85EBF6 test ds:abHookFlags[ebx], 2 ; 判断该消息钩子是否键盘钩子 .text:BF85EBFD jz loc_BF85EAC8 .text:BF85EC03 mov eax, [esi+3Ch] ; THREADINFO+3C是rpdesk .text:BF85EC06 cmp eax, [edi+3Ch] ; 判断是否属于同一个桌面 .text:BF85EC09 jnz loc_BF85EDCE ; 不在同一个桌面无法设置钩子 .text:BF85EC0F mov eax, [edi+2Ch] ; 2c地方是tagPROCESSINFO *ppi .text:BF85EC12 mov ecx, [esi+2Ch] .text:BF85EC15 cmp eax, ecx .text:BF85EC17 jnz loc_BF85ED6F ; 应用程序必须使用DLL来钩 .text:BF85EC1D .text:BF85EC1D loc_BF85EC1D: ; CODE XREF: zzzSetWindowsHookEx(x,x,x,x,x,x)+161.j .text:BF85EC1D ; zzzSetWindowsHookEx(x,x,x,x,x,x)+1DC.j ... .text:BF85EC1D mov eax, ebx .text:BF85EC1F sub eax, 0 .text:BF85EC22 jz loc_BF85EAD9 ; 钩子类型如果为WH_JOURNALRECORD .text:BF85EC28 dec eax ; 钩子类型数值减1,如果eax=0,则将eax置-1 .text:BF85EC29 jz loc_BF85EAD2 .text:BF85EC2F push 8 .text:BF85EC31 .text:BF85EC31 loc_BF85EC31: ; CODE XREF: zzzSetWindowsHookEx(x,x,x,x,x,x)-EA.j .text:BF85EC31 ; zzzSetWindowsHookEx(x,x,x,x,x,x)-E3.j .text:BF85EC31 pop esi .text:BF85EC32 push esi ; DesiredAccess .text:BF85EC33 push dword ptr [edi+0E8h] ; GrantedAccess .text:BF85EC39 call ds:__imp__RtlAreAllAccessesGranted@8 ; RtlAreAllAccessesGranted(x,x) .text:BF85EC3F test al, al .text:BF85EC41 jz loc_BF85EDB7 ; 取当前进程EPROCESS .text:BF85EC41 ; .text:BF85EC47 .text:BF85EC47 loc_BF85EC47: ; CODE XREF: zzzSetWindowsHookEx(x,x,x,x,x,x)+20A.j .text:BF85EC47 cmp esi, 8 .text:BF85EC4A jnz loc_BF85EAE0 ; DESKTOP_HOOKCONTROL = 0x8L .text:BF85EC50 .text:BF85EC50 loc_BF85EC50: ; CODE XREF: zzzSetWindowsHookEx(x,x,x,x,x,x)-D4.j .text:BF85EC50 push 30h ; sizeof(HOOK) .text:BF85EC52 push 5 ; 枚举类型TYPE_HOOK .text:BF85EC54 push dword ptr [edi+3Ch] ; pti->rpdesk .text:BF85EC57 push edi ; pti .text:BF85EC58 call _HMAllocObject@16 ; 分配新的钩子结构 .text:BF85EC5D mov esi, eax .text:BF85EC5F test esi, esi .text:BF85EC61 jz loc_BF85ED2F ; 分配失败 .text:BF85EC67 or dword ptr [esi+24h], 0FFFFFFFFh ; -1 .text:BF85EC6B cmp [ebp+Mod], 0 .text:BF85EC6F jz short loc_BF85EC8F ; 如果模块句柄为NULL .text:BF85EC71 push [ebp+ModuleName] .text:BF85EC74 call _GetHmodTableIndex@4 ; GetHmodTableIndex(x) .text:BF85EC79 cmp eax, 0FFFFFFFFh ; 如果没有找到,则返回-1 .text:BF85EC7C mov [esi+24h], eax .text:BF85EC7F jz loc_BF85EAFA ; 错误码:126,找不到指定的模块。 .text:BF85EC85 test eax, eax .text:BF85EC87 jl short loc_BF85EC8F ; 小于0则跳转 .text:BF85EC89 push eax ; phkNew->ihmod .text:BF85EC8A call _AddHmodDependency@4 ; AddHmodDependency(x) .text:BF85EC8F .text:BF85EC8F loc_BF85EC8F: ; CODE XREF: zzzSetWindowsHookEx(x,x,x,x,x,x)+B1.j .text:BF85EC8F ; zzzSetWindowsHookEx(x,x,x,x,x,x)+C9.j .text:BF85EC8F mov eax, [ebp+pti] .text:BF85EC92 xor edx, edx .text:BF85EC94 inc edx .text:BF85EC95 test eax, eax .text:BF85EC97 jz loc_BF85EB31 ; 给定线程的PTHREADINFO为空 .text:BF85EC9D lea ecx, [eax+ebx*4+0F8h] ; ptiThread.aphkStart[id] .text:BF85ECA4 mov [ebp+ModuleName], ecx .text:BF85ECA7 lea ecx, [ebx+1] .text:BF85ECAA shl edx, cl .text:BF85ECAC or [eax+98h], edx .text:BF85ECB2 cmp dword ptr [eax+44h], 0 .text:BF85ECB6 jz short loc_BF85ECDE ; pti+0x44是pClientInfo .text:BF85ECB8 mov ecx, [eax+2Ch] ; ppi .text:BF85ECBB cmp ecx, [edi+2Ch] .text:BF85ECBE jnz loc_BF85EB0C ; ppi不相等,就是说要HOOK的是别的进程 .text:BF85ECC4 and [ebp+HookId], 0 .text:BF85ECC8 .text:BF85ECC8 loc_BF85ECC8: ; CODE XREF: zzzSetWindowsHookEx(x,x,x,x,x,x)-A0.j .text:BF85ECC8 cmp [ebp+HookId], 0 ; 判断是否HOOK成功 .text:BF85ECCC mov ecx, [eax+44h] ; ptiThread->pClientInfo .text:BF85ECCF mov edx, [eax+98h] ; ptiThread->fsHooks; .text:BF85ECD5 mov [ecx+24h], edx ; ecx+24h = pClientInfo->fsHooks .text:BF85ECD8 jnz loc_BF85EB23 ; 已经HOOK了,则解除进程绑定 .text:BF85ECDE .text:BF85ECDE loc_BF85ECDE: ; CODE XREF: zzzSetWindowsHookEx(x,x,x,x,x,x)-92.j .text:BF85ECDE ; zzzSetWindowsHookEx(x,x,x,x,x,x)+F8.j .text:BF85ECDE mov [esi+28h], eax ; 记录哪个线程被HOOK了 .text:BF85ECE1 .text:BF85ECE1 loc_BF85ECE1: ; CODE XREF: zzzSetWindowsHookEx(x,x,x,x,x,x)-70.j .text:BF85ECE1 mov eax, [ebp+dwFlags] .text:BF85ECE4 and eax, 2 .text:BF85ECE7 or [esi+20h], eax .text:BF85ECEA mov eax, [ebp+HookProc] .text:BF85ECED sub eax, [ebp+Mod] .text:BF85ECF0 mov [esi+18h], ebx .text:BF85ECF3 mov [esi+1Ch], eax .text:BF85ECF6 mov eax, [ebp+ModuleName] .text:BF85ECF9 mov ecx, [eax] .text:BF85ECFB mov [esi+14h], ecx .text:BF85ECFE mov [eax], esi .text:BF85ED00 test ds:abHookFlags[ebx], 4 .text:BF85ED07 jnz short loc_BF85ED33 .text:BF85ED09 .text:BF85ED09 loc_BF85ED09: ; CODE XREF: zzzSetWindowsHookEx(x,x,x,x,x,x)+1AD.j .text:BF85ED09 test byte ptr [esi+20h], 1 .text:BF85ED0D jnz loc_BF85EB5D .text:BF85ED13 .text:BF85ED13 loc_BF85ED13: ; CODE XREF: zzzSetWindowsHookEx(x,x,x,x,x,x)-5A.j .text:BF85ED13 ; zzzSetWindowsHookEx(x,x,x,x,x,x)-3F.j ... .text:BF85ED13 mov eax, esi .text:BF85ED15 .text:BF85ED15 loc_BF85ED15: ; CODE XREF: zzzSetWindowsHookEx(x,x,x,x,x,x)+173.j .text:BF85ED15 pop edi .text:BF85ED16 pop esi .text:BF85ED17 pop ebx .text:BF85ED18 leave .text:BF85ED19 retn 18h .text:BF85ED1C ; --------------------------------------------------------------------------- .text:BF85ED1C .text:BF85ED1C loc_BF85ED1C: ; CODE XREF: zzzSetWindowsHookEx(x,x,x,x,x,x)+32.j .text:BF85ED1C cmp [ebp+Mod], edx .text:BF85ED1F jnz loc_BF85EC1D .text:BF85ED25 .text:BF85ED25 loc_BF85ED25: ; CODE XREF: zzzSetWindowsHookEx(x,x,x,x,x,x)+1B4.j .text:BF85ED25 push 594h ; 错误码:1428,没有模块句柄无法设置非本机的挂接。 .text:BF85ED2A .text:BF85ED2A loc_BF85ED2A: ; CODE XREF: zzzSetWindowsHookEx(x,x,x,x,x,x)-FB.j .text:BF85ED2A ; zzzSetWindowsHookEx(x,x,x,x,x,x)-F1.j ... .text:BF85ED2A call _UserSetLastError@4 ; UserSetLastError(x) .text:BF85ED2F .text:BF85ED2F loc_BF85ED2F: ; CODE XREF: zzzSetWindowsHookEx(x,x,x,x,x,x)-B7.j .text:BF85ED2F ; zzzSetWindowsHookEx(x,x,x,x,x,x)+A3.j ... .text:BF85ED2F xor eax, eax ; 返回NULL .text:BF85ED31 jmp short loc_BF85ED15 .text:BF85ED33 ; --------------------------------------------------------------------------- .text:BF85ED33 .text:BF85ED33 loc_BF85ED33: ; CODE XREF: zzzSetWindowsHookEx(x,x,x,x,x,x)+149.j .text:BF85ED33 mov eax, [edi+28h] .text:BF85ED36 mov [ebp+var_C], eax .text:BF85ED39 lea eax, [ebp+var_C] .text:BF85ED3C mov [edi+28h], eax .text:BF85ED3F push 1 .text:BF85ED41 mov [ebp+var_8], esi .text:BF85ED44 inc dword ptr [esi+4] .text:BF85ED47 push edi .text:BF85ED48 call _zzzJournalAttach@8 ; zzzJournalAttach(x,x) .text:BF85ED4D test eax, eax .text:BF85ED4F jnz short loc_BF85ED62 .text:BF85ED51 call _ThreadUnlock1@0 ; ThreadUnlock1() .text:BF85ED56 test eax, eax .text:BF85ED58 jz short loc_BF85ED2F ; 返回NULL .text:BF85ED5A push esi .text:BF85ED5B call _zzzUnhookWindowsHookEx@4 ; zzzUnhookWindowsHookEx(x) .text:BF85ED60 jmp short loc_BF85ED2F ; 返回NULL .text:BF85ED62 ; --------------------------------------------------------------------------- .text:BF85ED62 .text:BF85ED62 loc_BF85ED62: ; CODE XREF: zzzSetWindowsHookEx(x,x,x,x,x,x)+191.j .text:BF85ED62 call _ThreadUnlock1@0 ; ThreadUnlock1() .text:BF85ED67 mov esi, eax .text:BF85ED69 test esi, esi .text:BF85ED6B jnz short loc_BF85ED09 .text:BF85ED6D jmp short loc_BF85ED2F ; 返回NULL .text:BF85ED6F ; --------------------------------------------------------------------------- .text:BF85ED6F .text:BF85ED6F loc_BF85ED6F: ; CODE XREF: zzzSetWindowsHookEx(x,x,x,x,x,x)+59.j .text:BF85ED6F cmp [ebp+Mod], edx .text:BF85ED72 jz short loc_BF85ED25 ; 模块句柄为NULL .text:BF85ED74 mov edx, [ecx+160h] ; PROCESSINFO+160h存储进程luidSession .text:BF85ED7A cmp edx, [eax+160h] .text:BF85ED80 jnz short loc_BF85ED90 ; ptiThread->TIF_flags & TIF_ALLOWOTHERACCOUNTHOOK .text:BF85ED82 mov ecx, [ecx+164h] ; PROCESSINFO+164h存储进程HighPart .text:BF85ED88 cmp ecx, [eax+164h] .text:BF85ED8E jz short loc_BF85ED96 .text:BF85ED90 .text:BF85ED90 loc_BF85ED90: ; CODE XREF: zzzSetWindowsHookEx(x,x,x,x,x,x)+1C2.j .text:BF85ED90 test byte ptr [esi+4Ah], 40h ; ptiThread->TIF_flags & TIF_ALLOWOTHERACCOUNTHOOK .text:BF85ED94 jz short loc_BF85EDCE ; 错误码:5,拒绝访问 .text:BF85ED96 .text:BF85ED96 loc_BF85ED96: ; CODE XREF: zzzSetWindowsHookEx(x,x,x,x,x,x)+1D0.j .text:BF85ED96 test byte ptr [esi+48h], 0Ch .text:BF85ED9A jz loc_BF85EC1D .text:BF85EDA0 test ds:abHookFlags[ebx], 10h .text:BF85EDA7 jnz loc_BF85EC1D ; 钩子类型验证通过 .text:BF85EDAD push 5B2h ; 错误码:1458,不允许使用的挂钩类型。 .text:BF85EDB2 jmp loc_BF85ED2A .text:BF85EDB7 ; --------------------------------------------------------------------------- .text:BF85EDB7 .text:BF85EDB7 loc_BF85EDB7: ; CODE XREF: zzzSetWindowsHookEx(x,x,x,x,x,x)+83.j .text:BF85EDB7 call ds:__imp__PsGetCurrentProcess@0 ; 取当前进程EPROCESS .text:BF85EDB7 ; .text:BF85EDBD cmp eax, _gpepCSRSS ; 比较是否是CSRSS.exe的进程 .text:BF85EDC3 jnz short loc_BF85EDCE ; 错误码:5,拒绝访问 .text:BF85EDC5 cmp ebx, 0FFFFFFFFh .text:BF85EDC8 jz loc_BF85EC47 .text:BF85EDCE .text:BF85EDCE loc_BF85EDCE: ; CODE XREF: zzzSetWindowsHookEx(x,x,x,x,x,x)+4B.j .text:BF85EDCE ; zzzSetWindowsHookEx(x,x,x,x,x,x)+1D6.j ... .text:BF85EDCE push 5 ; 错误码:5,拒绝访问 .text:BF85EDD0 jmp loc_BF85ED2A .text:BF85EDD0 _zzzSetWindowsHookEx@24 endp
//zzzSetWindowsHookEx允许在制定的线程中或者系统全局范围内设置钩子,该函数返回一个HOOK对象的句柄如果成功,如果有错误发生,则返回NULL
PHOOK zzzSetWindowsHookEx( HANDLE hmod, //模块句柄 PUNICODE_STRING pstrLib, //库名字 PTHREADINFO ptiThread, //线程信息 int id, //钩子ID(类型) PROC pfnFilterProc, //钩子回调地址 DWORD dwFlags) //版本标志 { ACCESS_MASK amDesired; PHOOK phkNew; TL tlphkNew; PHOOK *pphkStart; PTHREADINFO ptiCurrent; //检查钩子类型是否允许,MSDN共定义16种钩子 if ((nFilterType < WH_MIN) || (nFilterType > WH_MAX)) { //设置错误码,1426,无效的挂接类型 UserSetLastError(1426); return NULL; } //检验提供的钩子函数回调 if (pfnFilterProc == NULL) { //设置错误码,1427,无效的挂接过程 UserSetLastError(1427); return NULL; } //获取当前调用者线程的THREADINFO结构指针 ptiCurrent = PtiCurrent(); if (ptiThread == NULL) { //如果应用程序没有使用DLL就用全局HOOK if (hmod == NULL) { //设置错误码,1428,没有模块句柄无法设置非本机的挂接 UserSetLastError(1428); return NULL; } } else { //如果应用程序想设置本地HOOK,但使用的只能全局的钩子类型 if (!(abHookFlags[nFilterType + 1] & HKF_TASK)) { //设置错误码,1429,此程序只可整体设置 UserSetLastError(1428); return NULL; } //不能HOOK不同在同一桌面的线程 if (ptiThread->rpdesk != ptiCurrent->rpdesk) { //设置错误码,5,拒绝访问 UserSetLastError(5); return NULL; } if (ptiCurrent->ppi != ptiThread->ppi) { //如果程序想HOOK别的进程,但没有使用DLL if (hmod == NULL) { //设置错误码,1428,没有模块句柄无法设置非本机的挂接 UserSetLastError(1428); return NULL; } //如果要HOOK别的用户没有权限 if ((!RtlEqualLuid(&ptiThread->ppi->luidSession, &ptiCurrent->ppi->luidSession)) && !(ptiThread->TIF_flags & TIF_ALLOWOTHERACCOUNTHOOK)) { //设置错误码,5,拒绝访问 UserSetLastError(5); return NULL; } if ((ptiThread->TIF_flags & (TIF_CSRSSTHREAD | TIF_SYSTEMTHREAD)) && !(abHookFlags[nFilterType + 1] & HKF_INTERSENDABLE)) { //不能HOOK控制台程序以及系统线程 //设置错误码,1458,不允许使用的挂钩类型 UserSetLastError(1458); return NULL; } } } //权限检查都通过了 switch( id ) { //WH_JOURNALRECORD = HKF_SYSTEM | HKF_JOURNAL | HKF_INTERSENDABLE case WH_JOURNALRECORD: amDesired = DESKTOP_JOURNALRECORD; break; case WH_JOURNALPLAYBACK: amDesired = DESKTOP_JOURNALPLAYBACK; break; default: amDesired = DESKTOP_HOOKCONTROL; break; } /* //权限是否足够 BOOLEAN NTAPI RtlAreAllAccessesGranted(ACCESS_MASK GrantedAccess, ACCESS_MASK DesiredAccess) { PAGED_CODE_RTL(); return ((GrantedAccess & DesiredAccess) == DesiredAccess); } */ if (!RtlAreAllAccessesGranted(ptiCurrent->amdesk, amDesired)) { //设置错误码,5,拒绝访问 UserSetLastError(5); return NULL; } if (amDesired != DESKTOP_HOOKCONTROL && (ptiCurrent->rpdesk->rpwinstaParent->dwWSF_Flags & WSF_NOIO)) { //错误码:1459,该操作需要交互式窗口工作站。 UserSetLastError(5); return NULL; } //为新的钩子对象分配空间 phkNew = (PHOOK)HMAllocObject(ptiCurrent, ptiCurrent->rpdesk,TYPE_HOOK, sizeof(HOOK)); //若分配失败,则直接返回NULL if (phkNew == NULL) { return NULL; } //如果一个DLL是本钩子所必须的,那么像系统注册该DLL,注册后,才能保证被加载到别的进程中 phkNew->ihmod = -1; if (hmod != NULL) { #if defined(WX86) phkNew->flags |= (dwFlags & HF_WX86KNOWNDLL); #endif phkNew->ihmod = GetHmodTableIndex(pstrLib); if (phkNew->ihmod == -1) { //错误码:126,系统找不到指定的模块 UserSetLastError(126); //释放掉刚分配的HOOK对象 HMFreeObject((PVOID)phkNew); return NULL; } //为模块曾加一个属性,说明这个模块被HOOK了多少次 if (phkNew->ihmod >= 0) { AddHmodDependency(phkNew->ihmod); } } //找到钩子链表,如果是全局钩子,则设置HF_GLOBAL标志 if (ptiThread != NULL) { //获取本线程传入钩子类型开始地址 pphkStart = &ptiThread->aphkStart[id]; //设置WHF_*标志在THREADINFO中,就知道被HOOK过了 ptiThread->fsHooks |= WHF_FROM_WH(id); if (ptiThread->pClientInfo) { BOOL fAttached; //判断是否是要钩其他进程 if (ptiThread->ppi != ptiCurrent->ppi) { //附加到别的进程,这样,就可以访问别的进程的内存 KeAttachProcess(&ptiThread->ppi->Process->Pcb); fAttached = TRUE; } else { fAttached = FALSE; } //是指该线程被HOOK,当被HOOK进程接受到windows消息后,会先判断是否存在HOOK ptiThread->pClientInfo->fsHooks = ptiThread->fsHooks; if (fAttached) { KeDetachProcess(); } } //记录哪个线程被HOOK了 phkNew->ptiHooked = ptiThread; } //线程信息为NULL,说明要HOOK当前系统所有线程 else { //从当前线程中找到桌面对象,只要钩住桌面,就钩住了所有线程 pphkStart = &ptiCurrent->pDeskInfo->aphkStart[id]; //设置全局HOOK标志 phkNew->flags |= HF_GLOBAL; ptiCurrent->pDeskInfo->fsHooks |= WHF_FROM_WH(id); phkNew->ptiHooked = NULL; } //钩子类型版本,ANSI或者是UNICODE phkNew->flags |= (dwFlags & HF_ANSI); phkNew->iHook = id; //模块在不同的进程中回被加载到不同的地址空间,出于这个原因,当设置钩子时,需要计算出偏移,跟PE文件VA类似 phkNew->offPfn = ((ULONG_PTR)pfnFilterProc) - ((ULONG_PTR)hmod); //把这个钩子添加的钩子链表的后面 phkNew->phkNext = *pphkStart; //重置钩子链表 *pphkStart = phkNew; //如果是日志钩子,需要对同步进行处理 if (abHookFlags[id] & HKF_JOURNAL) { ThreadLockAlwaysWithPti(ptiCurrent, phkNew, &tlphkNew); if (!zzzJournalAttach(ptiCurrent, TRUE)) { if (ThreadUnlock(&tlphkNew) != NULL) { zzzUnhookWindowsHookEx(phkNew); } return NULL; } if ((phkNew = ThreadUnlock(&tlphkNew)) == NULL) { return NULL; } } //不允许一个进程,设置了全局挂钩,工程运行在后台的权限,提升优先级和标记所以不能重置 if ((phkNew->flags & HF_GLOBAL) && (abHookFlags[id] & HKF_INTERSENDABLE)) { ptiCurrent->TIF_flags |= TIF_GLOBALHOOKER; KeSetPriorityThread(&ptiCurrent->pEThread->Tcb, LOW_REALTIME_PRIORITY-2); if (abHookFlags[id] & HKF_JOURNAL) { ThreadLockAlwaysWithPti(ptiCurrent, phkNew, &tlphkNew); //如果要改变HOOK,移动鼠标,这样第一个时间总是鼠标移动时间,可以确保光标被正确设置 zzzSetFMouseMoved(); phkNew = ThreadUnlock(&tlphkNew); //若要设置一个回放钩子,这个进程提供输入,这使它有SetForegroundWindow的机会 if (id == WH_JOURNALPLAYBACK) { gppiInputProvider = ptiCurrent->ppi; } } else { UserAssert(id != WH_JOURNALPLAYBACK); } } else { UserAssert(!(abHookFlags[id] & HKF_JOURNAL)); UserAssert(id != WH_JOURNALPLAYBACK); } //返回钩子指针,以便CallNextHookEx调用 DbgValidateHooks(phkNew, phkNew->iHook); return phkNew; }
至此,假定已经注册了一个消息钩子,简单查一下钩子如何被调用,由于windows是基于消息事件驱动的,在windows向应用程序发送消息之前会先检查是否有钩子,如果有消息钩子存在,则会调用钩子函数,如果钩子函数在一个动态链接库中,且该链接库尚未映射到应用程序进程空间,则系统用LoadLibrary加载该DLL到址空间中,然后调用钩子函数
LRESULT SendMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam );
SendMessage主要调用:
LRESULT SendMessageWorker(PWND pwnd, UINT message, WPARAM wParam, LPARAM lParam, BOOL fAnsi);
/*参看win2k代码
SendMessageWorker取得CLIENTINFO信息,并检查fsHooks域,如果存在HOOK,return CsSendMessage(hwnd, message, wParam, lParam, 0L,
FNID_SENDMESSAGE, fAnsi);
#define CsSendMessage(hwnd, msg, wParam, lParam, xParam, pfn, bAnsi) \ (((msg) >= WM_USER) ? \ NtUserMessageCall(hwnd, msg, wParam, lParam, xParam, pfn, bAnsi) : \ gapfnScSendMessage[MessageTable[msg].iFunction](hwnd, msg, wParam, lParam, xParam, pfn, bAnsi))
MessageTable是一个全局的表,存在系统消息的数组,其中存在消息钩子入口地址
*/
R3下枚举系统中的消息钩子,网上已有例子.
//可能用到的结构体:
CONST BYTE abHookFlags[CWINHOOKS] = { HKF_SYSTEM | HKF_TASK | HKF_NZRET , // WH_MSGFILTER (-1) HKF_SYSTEM | HKF_JOURNAL | HKF_INTERSENDABLE , // WH_JOURNALRECORD 0 HKF_SYSTEM | HKF_JOURNAL | HKF_INTERSENDABLE , // WH_JOURNALPLAYBACK 1 HKF_SYSTEM | HKF_TASK | HKF_NZRET | HKF_INTERSENDABLE , // WH_KEYBOARD 2 HKF_SYSTEM | HKF_TASK , // WH_GETMESSAGE 3 HKF_SYSTEM | HKF_TASK , // WH_CALLWNDPROC 4 HKF_SYSTEM | HKF_TASK , // WH_CBT 5 HKF_SYSTEM , // WH_SYSMSGFILTER 6 HKF_SYSTEM | HKF_TASK | HKF_INTERSENDABLE , // WH_MOUSE 7 HKF_SYSTEM | HKF_TASK , // WH_HARDWARE 8 HKF_SYSTEM | HKF_TASK , // WH_DEBUG 9 HKF_SYSTEM | HKF_TASK , // WH_SHELL 10 HKF_SYSTEM | HKF_TASK , // WH_FOREGROUNDIDLE 11 HKF_SYSTEM | HKF_TASK , // WH_CALLWNDPROCRET 12 HKF_SYSTEM | HKF_LOWLEVEL | HKF_INTERSENDABLE , // WH_KEYBOARD_LL 13 HKF_SYSTEM | HKF_LOWLEVEL | HKF_INTERSENDABLE // WH_MOUSE_LL 14 #ifdef REDIRECTION ,HKF_SYSTEM | HKF_LOWLEVEL | HKF_INTERSENDABLE // WH_HITTEST 15 #endif }; typedef struct tagHOOK { THRDESKHEAD head; struct tagHOOK *phkNext; int iHook; // 钩子类型 DWORD offPfn; UINT flags; // 钩子标志 int ihmod; PTHREADINFO ptiHooked; // 被HOOK的线程 PDESKTOP rpdesk; // 全局钩子 #ifdef HOOKBATCH DWORD cEventMessages; DWORD iCurrentEvent; DWORD CacheTimeOut; PEVENTMSG aEventCache; #endif } HOOK, *PHOOK; typedef struct _W32PROCESS { PEPROCESS peProcess; DWORD RefCount; ULONG W32PF_flags; PKEVENT InputIdleEvent; DWORD StartCursorHideTime; struct _W32PROCESS * NextStart; PVOID pDCAttrList; PVOID pBrushAttrList; DWORD W32Pid; LONG GDIHandleCount; LONG UserHandleCount; PEX_PUSH_LOCK GDIPushLock; /* Locking Process during access to structure. */ RTL_AVL_TABLE GDIEngUserMemAllocTable; /* Process AVL Table. */ LIST_ENTRY GDIDcAttrFreeList; LIST_ENTRY GDIBrushAttrFreeList; } W32PROCESS, *PW32PROCESS; typedef struct tagTHREADINFO { W32THREAD; LIST_ENTRY PtiLink; // 当前桌面中其他线程的链表 PTL ptl; // Listhead for thread lock list PTL ptlOb; // Listhead for kernel object thread lock list PTL ptlPool; // Listhead for temp pool usage int cEnterCount; struct tagPROCESSINFO *ppi; // 线程所属进程的PROCESSINFO结构指针 struct tagQ *pq; // 键盘鼠标输入队列 PKL spklActive; // active keyboard layout for this thread MLIST mlPost; // 已投递消息列表 USHORT fsChangeBitsRemoved;// Bits removed during PeekMessage USHORT cDeskClient; // Ref count for CSRSS desktop PCLIENTTHREADINFO pcti; // Info that must be visible from client CLIENTTHREADINFO cti; // Use this when no desktop is available HANDLE hEventQueueClient; PKEVENT pEventQueueServer; PKEVENT *apEvent; // Wait array for xxxPollAndWaitForSingleObject PDESKTOP rpdesk; HDESK hdesk; // 桌面句柄 ACCESS_MASK amdesk; // 允许桌面权限 PDESKTOPINFO pDeskInfo; // Desktop info visible to client PCLIENTINFO pClientInfo; // Client info stored in TEB DWORD TIF_flags; // TIF_ flags go here. PUNICODE_STRING pstrAppName; // 程序名 struct tagSMS *psmsSent; // Most recent SMS this thread has sent struct tagSMS *psmsCurrent; // Received SMS this thread is currently processing struct tagSMS *psmsReceiveList; // SMSs to be processed LONG timeLast; // Time, position, and ID of last message POINT ptLast; DWORD idLast; int cQuit; int exitCode; int cPaintsReady; UINT cTimersReady; PMENUSTATE pMenuState; union { PTDB ptdb; // Win16Task Schedule data for WOW thread PWINDOWSTATION pwinsta; // Window station for SYSTEM thread PDESKTOP pdeskClient; // Desktop for CSRSS thread }; PSVR_INSTANCE_INFO psiiList; // thread DDEML instance list DWORD dwExpWinVer; DWORD dwCompatFlags; // The Win 3.1 Compat flags UINT cWindows; // Number of windows owned by this thread UINT cVisWindows; // Number of visible windows on this thread struct tagQ *pqAttach; // calculation variabled used in // AttachThreadInput() int iCursorLevel; // keep track of each thread's level DWORD fsReserveKeys; // Keys that must be sent to the active // active console window. struct tagTHREADINFO *ptiSibling; // pointer to sibling thread info PMOVESIZEDATA pmsd; DWORD fsHooks; // WHF_ Flags for which hooks are installed PHOOK asphkStart[CWINHOOKS]; // 本线程已注册的钩子 PHOOK sphkCurrent; // Hook this thread is currently processing PSBTRACK pSBTrack; #ifdef FE_IME PWND spwndDefaultIme; // Default IME Window for this thread PIMC spDefaultImc; // Default input context for this thread HKL hklPrev; // Previous active keyboard layout #endif } THREADINFO; typedef struct _LUID { DWORD LowPart; LONG HighPart; } LUID, *PLUID; typedef struct _PROCESSINFO { W32PROCESS; PTHREADINFO ptiList; PTHREADINFO ptiMainThread; struct _DESKTOP* rpdeskStartup; PCLS pclsPrivateList; PCLS pclsPublicList; INT cThreads; DWORD dwhmodLibLoadedMask; HANDLE ahmodLibLoaded[CLIBS]; struct _WINSTATION_OBJECT *prpwinsta; HWINSTA hwinsta; ACCESS_MASK amwinsta; DWORD dwHotkey; HMONITOR hMonitor; LUID luidSession; USERSTARTUPINFO usi; DWORD dwLayout; DWORD dwRegisteredClasses; LIST_ENTRY MenuListHead; FAST_MUTEX PrivateFontListLock; LIST_ENTRY PrivateFontListHead; FAST_MUTEX DriverObjListLock; LIST_ENTRY DriverObjListHead; struct _KBL* KeyboardLayout; // THREADINFO only W32HEAP_USER_MAPPING HeapMappings; } PROCESSINFO; typedef struct _CLIENTINFO { ULONG_PTR CI_flags; ULONG_PTR cSpins; DWORD dwExpWinVer; DWORD dwCompatFlags; DWORD dwCompatFlags2; DWORD dwTIFlags; // ThreadInfo TIF_Xxx flags for User space. PDESKTOPINFO pDeskInfo; ULONG_PTR ulClientDelta; PHOOK phkCurrent; ULONG fsHooks; CALLBACKWND CallbackWnd; DWORD dwHookCurrent; INT cInDDEMLCallback; PCLIENTTHREADINFO pClientThreadInfo; ULONG_PTR dwHookData; DWORD dwKeyCache; BYTE afKeyState[8]; DWORD dwAsyncKeyCache; BYTE afAsyncKeyState[8]; BYTE afAsyncKeyStateRecentDow[8]; HKL hKL; USHORT CodePage; UCHAR achDbcsCF[2]; MSG msgDbcsCB; LPDWORD lpdwRegisteredClasses; ULONG Win32ClientInfo3[27]; PPROCESSINFO ppi; } CLIENTINFO, *PCLIENTINFO;
线程为窗口维护一个THREADINFO结构,每个线程有一个gptiCurrent的全局变量.可从当前线程中,枚举出系统所有消息钩子,若要枚举其他单个线程的消息钩子,关键是要获得该线程的THREADINFO结构指针,通过未公开函数win32k.sys中PtiFromThreadId(HANDLE ThreadId)得到,该函数内部使用NTSTATUS LockThreadByClientId(HANDLE ThreadId,PETHREAD pEThread)得到线程ETHREAD,然后使用PTHREADINFO PtiFromThread(pEThread);得到THREADINFO指针,这样即可通过THREADINFO.asphkStart[CWINHOOKS]遍历该线程中的消息钩子.
#define PtiFromThread(pEThread) ((PTHREADINFO)((pEThread)->Tcb.Win32Thread)) CWINHOOKS = WH_MAX - WH_MIN + 1 #define WH_MIN (-1) #if(WINVER >= 0x0400) #if (_WIN32_WINNT >= 0x0400) #define WH_MAX 14 #else #define WH_MAX 12 #endif // (_WIN32_WINNT >= 0x0400) #else #define WH_MAX 11 #endif ForExample: VOID DumpThreadMsgHooks(HANDLE ThreadId) { PHOOK Currenthk; PTHREADINFO pti,Temppti; int HookCount = 0; pti = PtiFromThreadId(ThreadId); Temppti = pti; while(Temppti->PtiLink.FLink != pti) { for(;HookCount < CWINHOOKS;HookCount++) { Currenthk = pti->asphkStart[HookCount]; if(Currenthk) { KdPrint(("Hook id is %d\n",Currenthk->iHook)); } } Temppti = Temppti->PtiLink.FLink; } }
只能分析这么点了,不足或错误之处,还请各位指正