Windows Hook的小研究

今天在看罗云彬的Windows环境下32位汇编语言程序设计时,在408页看到有句话“……所以远程钩子的钩子函数必须位于一个的动态链接库中,而且必须是共享数据段的动态链接库”,前面那段没有问题,我们来研究下后面那一部分。

他书上在后面的那键盘例子里面也许真需要这样的技术,但,这不是一般情况,这里共享数据段只是为了跨进程共享数据来用。

我们先来看看那例子,有几个有趣的地方。

/////////////////////////////////////////////////////////////////////

Main.asm
 1 ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
 2 ; Sample code for < Win32ASM Programming 2nd Edition>
 3 ; by 罗云彬, http://asm.yeah.net
 4 ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
 5 ; Main.asm
 6 ; 键盘钩子演示程序的主程序,调用 dll 装载键盘钩子
 7 ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
 8 ; 使用 nmake 或下列命令进行编译和链接:
 9 ; ml /c /coff Main.asm
10 ; rc Main.rc
11 ; Link  /subsystem:windows Main.obj Main.res
12 ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
13         .386
14         .model flat, stdcall
15         option casemap :none
16 ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
17 ; Include 文件定义
18 ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
19 include        windows.inc
20 include        user32.inc
21 includelib    user32.lib
22 include        kernel32.inc
23 includelib    kernel32.lib
24 include        Hookdll.inc
25 includelib    Hookdll.lib
26 ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
27 ; Equ 等值定义
28 ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
29 ICO_MAIN    equ    1000
30 DLG_MAIN    equ    1000
31 IDC_TEXT    equ    1001
32 WM_HOOK        equ    WM_USER + 100h
33 ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
34 ; 代码段
35 ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
36 
37         .code
38 ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
39 _ProcDlgMain    proc    uses ebx edi esi hWnd,wMsg,wParam,lParam
40         local    @dwTemp
41 
42         mov    eax,wMsg
43 ;********************************************************************
44         .if    eax ==    WM_CLOSE
45             invoke    UninstallHook
46             invoke    EndDialog,hWnd,NULL
47 ;********************************************************************
48         .elseif    eax ==    WM_INITDIALOG
49             invoke    InstallHook,hWnd,WM_HOOK
50             .if    ! eax
51                 invoke    EndDialog,hWnd,NULL
52             .endif
53 ;********************************************************************
54         .elseif    eax ==    WM_HOOK
55             mov    eax,wParam
56             .if    al == 0dh
57                 mov    eax,0a0dh
58             .endif
59             mov    @dwTemp,eax
60             invoke    SendDlgItemMessage,hWnd,IDC_TEXT,EM_REPLACESEL,0,addr @dwTemp
61         .else
62             mov    eax,FALSE
63             ret
64         .endif
65         mov    eax,TRUE
66         ret
67 
68 _ProcDlgMain    endp
69 ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
70 start:
71         invoke    GetModuleHandle,NULL
72         invoke    DialogBoxParam,eax,DLG_MAIN,NULL,offset _ProcDlgMain,NULL
73         invoke    ExitProcess,NULL
74 ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
75         end    start

/////////////////////////////////////////////////////////////////////

Hookdll.asm
 1 ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
 2 ; Sample code for < Win32ASM Programming 2nd Edition>
 3 ; by 罗云彬, http://asm.yeah.net
 4 ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
 5 ; Hookdll.asm
 6 ; 键盘钩子使用的 dll 程序
 7 ; 用来方置钩子过程
 8 ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
 9 ; 使用 nmake 或下列命令进行编译和链接:
10 ; ml /c /coff Hookdll.asm
11 ; Link  /subsystem:windows /section:.bss,S /Def:Hookdll.def /Dll Hookdll.obj
12 ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
13         .386
14         .model flat, stdcall
15         option casemap :none
16 ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
17 ; Include 文件定义
18 ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
19 include        windows.inc
20 include        user32.inc
21 includelib    user32.lib
22 include        kernel32.inc
23 includelib    kernel32.lib
24 ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
25         .data
26 hInstance    dd    ?
27 
28         .data?
29 hWnd        dd    ?
30 hHook        dd    ?
31 dwMessage    dd    ?
32 szAscii        db    4 dup (?)
33 ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
34         .code
35 ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
36 ; dll 的入口函数
37 ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
38 DllEntry    proc    _hInstance,_dwReason,_dwReserved
39 
40         push    _hInstance
41         pop    hInstance
42         mov    eax,TRUE
43         ret
44 
45 DllEntry    Endp
46 ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
47 ; 键盘钩子回调函数
48 ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
49 HookProc    proc    _dwCode,_wParam,_lParam
50         local    @szKeyState[256]:byte
51 
52         invoke    CallNextHookEx,hHook,_dwCode,_wParam,_lParam
53         invoke    GetKeyboardState,addr @szKeyState
54         invoke    GetKeyState,VK_SHIFT
55         mov    @szKeyState + VK_SHIFT,al
56         mov    ecx,_lParam
57         shr    ecx,16
58         invoke    ToAscii,_wParam,ecx,addr @szKeyState,addr szAscii,0
59         mov    byte ptr szAscii [eax],0
60         invoke    SendMessage,hWnd,dwMessage,dword ptr szAscii,NULL
61         xor    eax,eax
62         ret
63 
64 HookProc    endp
65 ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
66 ; 安装钩子
67 ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
68 InstallHook    proc    _hWnd,_dwMessage
69 
70         push    _hWnd
71         pop    hWnd
72         push    _dwMessage
73         pop    dwMessage
74         invoke    SetWindowsHookEx,WH_KEYBOARD,addr HookProc,hInstance,NULL
75         mov    hHook,eax
76         ret
77 
78 InstallHook    endp
79 ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
80 ; 卸载钩子
81 ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
82 UninstallHook    proc
83 
84         invoke    UnhookWindowsHookEx,hHook
85         ret
86 
87 UninstallHook    endp
88 ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
89         End    DllEntry

/////////////////////////////////////////////////////////////////////

MakeFile
 1 NAME = Main
 2 DLL = Hookdll
 3 
 4 ML_FLAG = /c /coff
 5 LINK_FLAG = /subsystem:windows
 6 DLL_LINK_FLAG = /subsystem:windows /section:.bss,S
 7 
 8 $(DLL).dll $(NAME).exe:
 9 
10 $(DLL).dll: $(DLL).obj $(DLL).def
11     Link  $(DLL_LINK_FLAG) /Def:$(DLL).def /Dll $(DLL).obj
12 $(NAME).exe: $(NAME).obj $(NAME).res
13     Link  $(LINK_FLAG) $(NAME).obj $(NAME).res
14 
15 .asm.obj:
16     ml $(ML_FLAG) $<
17 .rc.res:
18     rc $<
19 
20 clean:
21     del *.obj
22     del *.res
23     del *.exp
24     del *.lib

 

注意一下,在makefile里面,将bss区段设置成为了共享区段, /section:.bss,S  未初始化的数据段(.data?)默认放在这里

 

///////////////////////////////

初初我看到他这源码的时候给我的感觉就是怎么这么乱。乱用,滥用。刚开始时我还一度以为这不能正常工作,后来发现是我误解彬哥的意图了,即使这不是个好的例子,但它还是能正常运行的。


重点在Hookdll.asm里面,他下的钩子是全局,最后一个线程ID如果是0,就是全局钩子。

1 invoke    SetWindowsHookEx,WH_KEYBOARD,addr HookProc,hInstance,NULL

然后,他在回调函数里面调用SendMessage来将它自己定义的一个消息回传到hwnd主窗口,

1 invoke    SendMessage,hWnd,dwMessage,dword ptr szAscii,NULL

不过由于hWnd位于共享数据段内,所以当打开多个这个程序时,暂且叫做A1,A2,SendMessage发送的数据其实是发到之后打开的那个程序A2回调函数里面。虽然在SendMessage里面它的LParam里面的参数是地址,但由于这地址位于共享数据段里面,所以该程序能正常获取数据,但A1就不能再获取数据了。

好,现在又引发了个有趣的问题了,当按一下键,那窗口显示两个字符(开了两个那个程序),这是因为hook了两次,回调函数调用了CallNextHookEx,分别调用了两个Hook回调函数,相当于在hook链表里面加入了两个,由于操作的变量都存放在共享数据段里面,所以,一切的结果都反映在最后打开的那个程序里。

图示为先后打开两个程序.

 


 

这里我们看到了一个知识点,SetWindowsHookEx不是像其他的内核对象一样简单地增加计数,而是真真切切地把Hook函数增加到Hook链表里面,即使那Hook是同一个。调用一次SetWindowsHookEx就增加一个,这我后来测试也成立。


我们来修改一下,让他只hook自己的线程,然后在MakeFile里面把共享数据段给取消掉,验证下是否仍然可以正常运行 

将InstallHook函数里面的SetWIndowsHookEx换成下面两条函数

1 invoke    GetentThreadId
2 invoke    SetWindowsHookEx,WH_KEYBOARD,addr HookProc,hInstance,eax

结果发现,生成的那个程序,暂且叫做A吧,成功执行了,不过也跟预料的情况一样,只记录当前进程的按键,但当打开多个程序时,每个进程都可以记录,却记录的不重复。

跟预料中的一样,“必须是共享数据段的动态链接库”这是不正确的,也许作者只是笔误,他想描述的是它后面的例子应该把数据段放到共享区段里面,检查的时候却不料将其放到前面跟例子不搭边的内容上去了。

//////////////////////////////////////////////////////////////////////////////////////


再来看个有趣的问题,跟原先的一样,不修改源代码,只将共享区段给去掉。

当开了两个该程序时,暂且称为A1,A2,按下一个键时,会发现,窗口反馈出两个相同的键。

这又是为什么呢,取消了共享数据段了啦,理应每个HookDll里的变量都是位于不同的进程空间里的,不受影响才对的。

仔细想想整个流程...

呵呵,聪明的你想到了?

来,我来说说我的看法吧。

因为我们执行的是相同的程序,程序的变量szAscii等放的地址空间是完全一样的,而下的Hook是全局的,所以在每个程序里面其实都有发送自定义的WH_HOOK消息(只是其他的程序没有处理而已),Hook链表里面有两个回调函数都用SendMessage发送了信息,所以累计发送了两次,接收到的是两个字符。

是这样吗?

没错?

唔,感觉好像是对的。

但是,别忘了,我们是取消了共享数据段的。

还是把SendMessage贴上来看得仔细点吧,

invoke    SendMessage,hWnd,dwMessage,dword ptr szAscii,NULL

hWnd是个窗口句柄,这是两个窗口喔,hWnd由消息循环获得,两个窗口,理应是不同的值吧?

有点怀疑是不是hWnd指向的结果是同一个,经过调试,发现打开两个该程序,传递的hWnd的值竟然是一样的。

而当我改了下HookDll.asm后,重新生成了个,现在有两个不同的dll了,测试的结果却是hWnd是不一样的,一个为NULL,另外一个为真实的值。

实际上,只要dll是不一样,即使代码没改(不如生成的时间不一样),传递的hWnd的值都是不一样的,这样很符合我们的思维。

posted @ 2013-04-13 13:37  ┌LiHoo┐  阅读(425)  评论(2编辑  收藏  举报