首先,注意一个比较特别的现象。原始版本在某些Windows上会崩溃,而崩溃后的Runtime Error对话框的OK按钮居然也是特效。说明什么?一定是钩子。
用IDA反汇编正常的KG2,注意到和KG1的不同点,可以发现多了一个WM_SHOWWINDOW。其处理程序如下:
CODE:00408482 loc_408482:
CODE:00408482 push 0
CODE:00408484 call sub_406984
CODE:00408489 jmp loc_4088E0
可以看到,这是个stdcall/pascal类型的过程。至于到底是哪种,无关紧要。这里的参数只有一个,所以不必担心顺序。
CODE:00406984 sub_406984 proc near
CODE:00406984
CODE:00406984 arg_0 = dword ptr 8
CODE:00406984
CODE:00406984 push ebp
CODE:00406985 mov ebp, esp
//这里是典型的pascal/stdcall调用的开头。但是因为只有1个参数,无法肯定是哪个类型。
//值得注意的是,这是个VC的obj。所以不能因为是VC编译的obj就一概使用cdecl。
CODE:00406987 push ebx
CODE:00406988 mov ebx, [ebp+arg_0]
CODE:0040698B xor eax, eax
CODE:0040698D test ebx, ebx
CODE:0040698F jnz short loc_4069CC
CODE:00406991 cmp ds:hhk, 0
CODE:00406998 jnz short loc_4069D2
CODE:0040699A push 1 ; lParam
CODE:0040699C push offset fn ; lpfn
CODE:004069A1 call GetCurrentThreadId_0
CODE:004069A6 push eax ; dwThreadId
CODE:004069A7 call EnumThreadWindows
CODE:004069AC call GetCurrentThreadId_0
CODE:004069B1 push eax ; dwThreadId
CODE:004069B2 push 0 ; hmod
CODE:004069B4 push offset sub_406D4A ; lpfn
CODE:004069B9 push 4 ; idHook
CODE:004069BB call SetWindowsHookExA
CODE:004069C0 mov ds:hhk, eax
CODE:004069C5 mov eax, ds:hhk
CODE:004069CA jmp short loc_4069D2
CODE:004069CC
CODE:004069CC loc_4069CC:
CODE:004069CC push ebx
CODE:004069CD call sub_406A1F
CODE:004069D2
CODE:004069D2 loc_4069D2:
CODE:004069D2 pop ebx
CODE:004069D3 pop ebp
CODE:004069D4 retn 4
CODE:004069D4 sub_406984 endp
这个初始化过程(实际是个函数)的流程还是非常清楚的。但是这个参数到底是什么?从上面来看,如果参数为0,那么就枚举当前线程的窗口。看来应该是某窗口的句柄。通过查看sub_406A1F,发现00406A53处有对GetWindowLong的调用,而且传过去的ebx作为首个参数。这样,可以判断它是个窗口句柄。这里给出该过程的Pascal形式
CurrentHook又是什么呢?从上面看,应该是起类似于标志的作用。初始值为0,Hook过以后就是代码:
Var
CurrentHook:HHOOK;
Function Init(Window:HWND):HHOOK;
Begin
Result:=0;
If Window=0 Then
Begin
If CurrentHook=0 Then
Begin
EnumThreadWindows(GetCurrentThreadId,,1);
CurrentHook:=SetWindowsHookEx(WH_CALLWNDPROC,sub_406D4A,0,GetCurrentThreadId);
Result:=CurrentHook;
End;
End
Else
Begin
sub_406A1F(Window);
End;
End;
Hook函数的句柄。所以修改一下定义
???? 首先看看sub_406D4A,这是一个CallWndProc函数。MSDN中有关定义如下:代码:
Const
CurrentHook:HHOOK=0;
???? 再看看函数体:代码:
LRESULT CALLBACK CallWndProc(
int nCode,
WPARAM wParam,
LPARAM lParam
);
seg000:00406D4A ; LRESULT __stdcall sub_406D4A(int,WPARAM,LPARAM)
seg000:00406D4A sub_406D4A proc near ; DATA XREF: sub_406984+30
seg000:00406D4A
seg000:00406D4A arg_0 = dword ptr 8
seg000:00406D4A wParam = dword ptr 0Ch
seg000:00406D4A arg_8 = dword ptr 10h
seg000:00406D4A
seg000:00406D4A push ebp
seg000:00406D4B mov ebp, esp
seg000:00406D4D push ebx
seg000:00406D4E push esi
seg000:00406D4F mov ebx, [ebp+arg_8]
//ebx:=LPARAM
//注意这里是一个地址,指向一个CWPSTRUCT结构
seg000:00406D52 mov esi, [ebp+arg_0]
//esi:=nCode
seg000:00406D55 cmp dword ptr [ebx+8], 1
//判断CWPSTRUCT.Message
//由于BN_PAINT=WM_CREATE=1,所以
//具体1是哪个message,需要根据进一步的分析来判断
seg000:00406D59 jnz short loc_406D67
seg000:00406D5B test esi, esi
seg000:00406D5D jl short loc_406D67
seg000:00406D5F push dword ptr [ebx+0Ch]
//CWPSTRUCT.hwnd
seg000:00406D62 call sub_406A1F
seg000:00406D67
seg000:00406D67 loc_406D67: ; CODE XREF: sub_406D4A+F
seg000:00406D67 ; sub_406D4A+13
seg000:00406D67 push ebx ; lParam
seg000:00406D68 push [ebp+wParam] ; wParam
seg000:00406D6B push esi ; nCode
seg000:00406D6C push ds:hhk ; hhk
seg000:00406D72 call CallNextHookEx
seg000:00406D77 pop esi
seg000:00406D78 pop ebx
seg000:00406D79 pop ebp
seg000:00406D7A retn 0Ch
seg000:00406D7A sub_406D4A endp
其中的CWPSTRUCT结构:
???? 这个函数也不复杂:代码:
typedef struct {
LPARAM lParam;
WPARAM wParam;
UINT message;
HWND hwnd;
} CWPSTRUCT, *PCWPSTRUCT;
代码:
Procedure CallBackWndProc(nCode:Integer;wParam:Cardinal;var lParam:CWPSTRUCT);stdcall;
Begin
If (lParam.message=1) AND (nCode<0) Then
Begin
sub_406A1F(lParam.hwnd);
End;
CallNextHookEx(CurrentHook,nCode,wParam,lParam);
End;