Chúng ta sẽ học về Windows hooks trong tut này. Windows hooks rất giàu năng lực. Với chúng, bạn có thể thọc sâu vào bên trong các processes khác và đôi khi biến đổi các hành vi của chúng.
Download the example [tut24.zip]
Lý thuyết:
Windows hooks có thể được xem xét như một trong các đặc trưng đầy năng lực nhất của Windows. Với chúng, bạn có thể bẫy các events (biến cố) sẽ xảy ra, hoặc trong process của chính bạn hoặc trong các processes khác. Bằng cách "hooking" (móc vào), bạn nói cho Windows biết một hàm function, đó là hàm filter function (hàm lọc) cũng được gọi là hook procedure (thủ tục hook), hàm này sẽ được gọi mọi lúc khi một event (biến cố) mà bạn quan tâm xảy ra. Chúng có 2 lọai: local hooks và remote hooks.
- Local hooks bẫy các biến cố events mà nó sẽ xảy ra trong process của chính bạn.
- Remote hooks bẫy các biến cố events mà nó sẽ xảy ra trong các process(es) khác. Có 2 lọai remote hooks
- thread-specific (tuyến cụ thể) bẫy các biến cố events mà nó sẽ xảy ra trong một thread cụ thể trong process khác. Ngắn gọn là, bạn muốn quan sát các events trong một thread cụ thể trong một process cụ thể.
- system-wide (tòan hệ thống) bẫy các biến cố events đã dự định cho tất cả các thread trong tất cả các processes trong hệ thống.
Khi bạn cài đặt hooks, hảy nhớ rằng chúng tác dụng đến họat động của hệ thống. System-wide hooks (các hook tòan hệ thống) là khét tiếng nhất. Khi ALL liên quan đến các events được gởi qua filter function (hàm lọc) của bạn, system của bạn có thể chậm lại một cách đáng quan tâm. Vì vậy nếu bạn dùng system-wide hook, bạn sẽ sử dụng nó một cách thận trọng và unhook nó ngay khi bạn ko cần nó. Cũng vậy, bạn có một sự rủi ro cao làm phá vỡ (crashing) các tiến trình processes khác, khi bạn có thể can thiệp vào các processes khác và nếu vài thứ bị sai trong filter function của bạn, nó có thể lôi kéo các processes khác trở nên bị lãng quên chung với nó. Hảy nhớ rằng: năng suất máy sẽ bị ảnh hưởng..
Bạn phải hiểu các công việc hook (móc vào) như thế nào trước khi bạn sử dụng nó một cách hiệu quả. Khi bạn cài đặt một hook, Windows cài đặt một cấu trúc data trong memory, chứa các thông tin về hook, và thêm nó vào một danh sách liên kết các hooks tồn tại sẳn. Một hook mới sẽ được thêm vào trước các hook cũ. Khi một biến cố xảy ra, nếu bạn install một local hook, hàm filter function trong process của bạn được gọi vì vậy nó “trực tiếp “ hơn. Nhưng nếu nó là một remote hook, system phải tiêm vào một code cho hook procedure trong vùng địa chỉ (address space(s) ) của các process(es) khác. Và hệ thống có thể chỉ thực hiện được nếu hàm function cư trú trong một DLL. Vì vậy , nếu bạn muốn dùng một remote hook, hook procedure của bạn phải cư trú trong một DLL. Có 2 ngọai lệ đối với quy tắc này: journal record hooks (các hook thu lại thường nhật) và journal playback hooks (các hook phát lại thường nhật). Hook procedures cho hai hooks này phải cư trú trong thread cài đặt của các hooks. Lý do tại sao nó phải là như vậy chính là: cả hai hooks đề cập sự chặn lớp thấp (low-level interception )của hardware input events (các biến cố nhập liệu phần cứng). Input events (biến cố nhập liệu) phải được recorded/playbacked (thu lại/phát lại) theo thứ tự mà chúng xuất hiện. Nếu code của hai hooks này ở trong một DLL, input events (các biến cố nhập liệu) có thể xuất hiện rãi rác trong vài threads nào đó và nó ko thể biết thứ tự của chúng. Hướng giải quyết: hook procedure của hai hooks này chỉ phải ở trong một thread đơn , có nghĩa là thread mà nó install các hooks này.
Có 14 lọai hooks:
- WH_CALLWNDPROC called when SendMessage is called
- WH_CALLWNDPROCRET called when SendMessage returns
- WH_GETMESSAGE called when GetMessage or PeekMessage is called
- WH_KEYBOARD called when GetMessage or PeekMessage retrieves(tìm lại) WM_KEYUP or WM_KEYDOWN from the message queue(hàng)
- WH_MOUSE called when GetMessage or PeekMessage retrieves a mouse message from the message queue(hàng)
- WH_HARDWARE called when GetMessage or PeekMessage (peek : nhìn trộm) retrieves some hardware message that is not related (liên quan,liên hệ) to keyboard or mouse.
- WH_MSGFILTER called when a dialog box, menu or scrollbar is about to process a message. This hook is local. It's specifically (đặc trưng, riêng biệt) for those objects which have their own internal (bên trong) message loops.
- WH_SYSMSGFILTER same as WH_MSGFILTER but system-wide (wide: mở rộng)
- WH_JOURNALRECORD called when Windows retrieves (lấy lại, gọi ra) message from the hardware input queue (hàng)
- WH_JOURNALPLAYBACK called when an event is requested (yêu cầu) from the system's hardware input (nhập liệu) queue.
- WH_SHELL called when something interesting (quan tâm) about the shell occurs such as when the task (tác vụ) bar needs to redraw its button.
- WH_CBT used specifically (đặc trưng) for computer-based training (huấn luyện)(CBT).
- WH_FOREGROUNDIDLE used internally(bên trong) by Windows. Little (ít) use for general (thông thường) applications
- WH_DEBUG used to debug the hooking procedure
Bây giờ chúng ta cần biết một chút về lý thuyết, Chúng ta có thể thực hiện như thế nào để install/uninstall các hooks.
Để install một hook, bạn gọi hàm SetWindowsHookEx theo cú pháp sau đây:
SetWindowsHookEx proto HookType:DWORD, pHookProc:DWORD, hInstance:DWORD, ThreadID:DWORD
- HookType là một trong những giá trị đã liệt kê bên trên, ví dụ: WH_MOUSE, WH_KEYBOARD
- pHookProc là address của hook procedure (thủ tục hook) mà chúng sẽ được gọi để xử lý các thông điệp messages cho hook đã chỉ định. Nếu hook này là một remote hook, nó phải cư trú trong một DLL. Nếu khác, nó phải ở trong process của bạn.
- hInstance là instance handle của DLL mà hook procedure cư trú trong đó. Nếu hook là một local hook, giá trị này phải là NULL
- ThreadID là chỉ mục ID của thread bạn muốn cài đặt hook để theo dõi (spy on). Tham số này là một tham số xác định hook là local hay là remote. Nếu tham số này là NULL, Windows sẽ hiểu hook như là một system-wide remote hook mà nó ảnh hưởng đến tất cả các threads trong system. Nếu bạn chỉ định thread ID của một thread trong process của riêng bạn, hook này là một local hook. Nếu bạn chỉ định thread ID từ process khác, hook là một thread-specific remote hook. Có 2 ngoại lệ đối với rule này: WH_JOURNALRECORD và WH_JOURNALPLAYBACK luôn luôn là local system-wide hooks mà chúng ko yêu cầu nằm trong một DLL. Và WH_SYSMSGFILTER luôn là một system-wide remote hook. Thực chất nó chính là WH_MSGFILTER hook với ThreadID==0.
Nếu call thành công, nó sẽ trả về một hook handle trong eax. Nếu ko thành công, trả về giá trị NULL. Bạn phải save hook handle cho việc unhooking sau này.
Bạn có thể uninstall một hook bằng cách gọi hàm UnhookWindowsHookEx mà nó chỉ chấp nhận một tham số, đó là handle của hook mà bạn muốn uninstall. Nếu gọi thành công, nó trả về một giá trị non-zero trong eax. Nếu khác, nó returns giá trị NULL.
Bây giờ bạn biết như thế nào để install/uninstall hooks, chúng ta có thể nghiên cứu thủ tục hook procedure.
Hook procedure sẽ được gọi bất cứ khi nào một event (biến cố) xảy ra mà event đó được kết hợp với lọai hook mà bạn đã install .Cho ví dụ, nếu bạn install WH_MOUSE hook, khi một mouse event xảy ra, hook procedure của bạn sẽ được gọi . Bất chấp lọai hook mà bạn đã cài đặt installed, hook procedure luôn có prototype như sau:
HookProc proto nCode:DWORD, wParam:DWORD, lParam:DWORD
- nCode chỉ ra hook code.
- wParam và lParam chứa thông tin thêm về biến cố event
HookProc thực tế là một cái tên dùng để lưu giữ tên của hàm. Bạn có thể đặt tên nó bất kỳ gì mà bạn thích miễn là nó có prototype như trên. Giá trị thể hiện của nCode, wParam và lParam được dựa trên lọai hook mà bạn install. Cũng như giá trị trả về từ hook procedure. Cho ví dụ:
WH_CALLWNDPROC
- nCode có thể chỉ là HC_ACTION có nghĩa là có một message được gởi từ window
- wParam chứa message đang gởi đến, nếu nó ko là zero
- lParam trỏ đến một cấu trúc CWPSTRUCT structure
- return value: ko được sử dụng, return zero
WH_MOUSE
- nCode có thể là HC_ACTION hay HC_NOREMOVE
- wParam chứa mouse message
- lParam trỏ đến một cấu trúc MOUSEHOOKSTRUCT structure
- return value: zero nếu message đã được tiến hành. 1 nếu message được lọai bỏ.
Lời cuối cùng muốn nói với bạn là: bạn phải tra cứu win32 api reference để biết thêm chi tiết về ý nghĩa của các tham số và giá trị trả về của hook mà bạn muốn install.
Bây giờ bạn đã nắm bắt được một ít về hook procedure. Hảy nhớ rằng các hooks được trói lại (liên kết lại) trong một danh sách liên kết (linked list ) và hook được install gần nhất nằm tại đầu của danh sách liên kết hook. Khi một event xảy ra, Windows sẽ chỉ gọi hook đầu tiên trong chuổi mắt xích liên kết đó . Hook procedure của bạn sẽ chịu trách nhiệm gọi hook kế tiếp trong chuổi mắt xích (chain : chuổi mắt xích). Bạn có thể ko cần phải gọi hook kế tiếp nhưng bạn phải hiểu kỹ những gì bạn đang làm khi ko gọi hook kế tiếp. Phần lớn trong thực hành để đạt kế quả tốt là gọi procedure kế tiếp , do vậy các hooks khác có thể có tham gia đóng góp tại lúc biến cố event xảy ra. Bạn có thể gọi hook kế tiếp bằng cách gọi hàm CallNextHookEx mà nó có prototype như sau:
CallNextHookEx proto hHook:DWORD, nCode:DWORD, wParam:DWORD, lParam:DWORD
- hHook là hook handle của riêng bạn. Hàm này sử dụng handle này để vượt qua linked list và tìm kiếm hook procedure mà nó sẽ gọi kế tiếp.
- nCode, wParam và lParam bạn có thể truyền ba giá trị này mà bạn nhận được từ Windows cho hàm CallNextHookEx.
Một chú ý quan trọng về remote hooks: hook procedure phải cư trú trong một DLL mà nó sẽ được mapped (ánh xạ) vào trong các processes khác. Khi Windows map DLL vào trong các processes khác, nó sẽ ko map data section(s) vào trong các processes khác đó . Nói tóm lại, tất cả các processes sẽ cùng chia sẽ một bản sao chép code thực thi nhưng chúng sẽ có bản copy riêng DLL's data section cho chính chúng! Đây có thể là một ngạc nhiên lớn làm ta mất cảnh giác. Bạn có thể nghĩ rằng, khi bạn chứa một giá trị vào trong một biến trong data section của một DLL, thì giá trị sẽ được chia sẽ giữa tất cả các processes mà chúng load DLL vào trong process address space của chúng. Điều đó ko đơn giản như thế. Trong một hòan cảnh thông thường, ta cứ tưởng là như vậy khi nó tạo ra một ảo giác mỗi process có chính bản copy DLL cho chính nó. Nhưng ko như thế khi liên quan đến Windows hook. Chúng ta muốn DLL giống hệt trong tất cả các processes, bao gồm cả data. Để giải quyết vấn đề này: bạn phải đánh dấu data section là shared. Bạn có thể làm điều này bởi chỉ định thuộc tính section(s) trong linker switch (bật tắt chế độ linker). Đối với MASM, bạn cần dùng switch này:
/SECTION:
Tên của data section được khởi trị đầu (initialized data) là .data và tên của data section ko cho giá trị đầu (uninitialized data) là .bss. Ví dụ nếu bạn muốn biên dịch một DLL chứa một hook procedure và bạn muốn uninitialized data section được chia sẽ giữa các processes, bạn phải dùng dòng biên dịch như sau:
link /section:.bss,S /DLL /SUBSYSTEM:WINDOWS ..........
S thuộc tính đánh dấu section là shared (chia sẽ) .
Ví dụ :
Có hai modules: một là main program sẽ làm phần GUI và một là DLL sẽ install/uninstall hook.
;--------------------------------------------- This is the source code of the main program ----------------
.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include mousehook.inc
includelib mousehook.lib
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
wsprintfA proto C :DWORD,:DWORD,:VARARG
wsprintf TEXTEQU
.const
IDD_MAINDLG equ 101
IDC_CLASSNAME equ 1000
IDC_HANDLE equ 1001
IDC_WNDPROC equ 1002
IDC_HOOK equ 1004
IDC_EXIT equ 1005
WM_MOUSEHOOK equ WM_USER+6
DlgFunc PROTO :DWORD,:DWORD,:DWORD,:DWORD
.data
HookFlag dd FALSE
HookText db "&Hook",0
UnhookText db "&Unhook",0
template db "%lx",0
.data?
hInstance dd ?
hHook dd ?
.code
start:
invoke GetModuleHandle,NULL
mov hInstance,eax
invoke DialogBoxParam,hInstance,IDD_MAINDLG,NULL,addr DlgFunc,NULL
invoke ExitProcess,NULL
DlgFunc proc hDlg:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD
LOCAL hLib:DWORD
LOCAL buffer[128]:byte
LOCAL buffer1[128]:byte
LOCAL rect:RECT
.if uMsg==WM_CLOSE
.if HookFlag==TRUE
invoke UninstallHook
.endif
invoke EndDialog,hDlg,NULL
.elseif uMsg==WM_INITDIALOG
invoke GetWindowRect,hDlg,addr rect
invoke SetWindowPos, hDlg, HWND_TOPMOST, rect.left, rect.top, rect.right, rect.bottom, SWP_SHOWWINDOW
.elseif uMsg==WM_MOUSEHOOK
invoke GetDlgItemText,hDlg,IDC_HANDLE,addr buffer1,128
invoke wsprintf,addr buffer,addr template,wParam
invoke lstrcmpi,addr buffer,addr buffer1
.if eax!=0
invoke SetDlgItemText,hDlg,IDC_HANDLE,addr buffer
.endif
invoke GetDlgItemText,hDlg,IDC_CLASSNAME,addr buffer1,128
invoke GetClassName,wParam,addr buffer,128
invoke lstrcmpi,addr buffer,addr buffer1
.if eax!=0
invoke SetDlgItemText,hDlg,IDC_CLASSNAME,addr buffer
.endif
invoke GetDlgItemText,hDlg,IDC_WNDPROC,addr buffer1,128
invoke GetClassLong,wParam,GCL_WNDPROC
invoke wsprintf,addr buffer,addr template,eax
invoke lstrcmpi,addr buffer,addr buffer1
.if eax!=0
invoke SetDlgItemText,hDlg,IDC_WNDPROC,addr buffer
.endif
.elseif uMsg==WM_COMMAND
.if lParam!=0
mov eax,wParam
mov edx,eax
shr edx,16
.if dx==BN_CLICKED
.if ax==IDC_EXIT
invoke SendMessage,hDlg,WM_CLOSE,0,0
.else
.if HookFlag==FALSE
invoke InstallHook,hDlg
.if eax!=NULL
mov HookFlag,TRUE
invoke SetDlgItemText,hDlg,IDC_HOOK,addr UnhookText
.endif
.else
invoke UninstallHook
invoke SetDlgItemText,hDlg,IDC_HOOK,addr HookText
mov HookFlag,FALSE
invoke SetDlgItemText,hDlg,IDC_CLASSNAME,NULL
invoke SetDlgItemText,hDlg,IDC_HANDLE,NULL
invoke SetDlgItemText,hDlg,IDC_WNDPROC,NULL
.endif
.endif
.endif
.endif
.else
mov eax,FALSE
ret
.endif
mov eax,TRUE
ret
DlgFunc endp
end start
;----------------------------------------------------- This is the source code of the DLL --------------------
.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
include \masm32\include\user32.inc
includelib \masm32\lib\user32.lib
.const
WM_MOUSEHOOK equ WM_USER+6
.data
hInstance dd 0
.data?
hHook dd ?
hWnd dd ?
.code
DllEntry proc hInst:HINSTANCE, reason:DWORD, reserved1:DWORD
.if reason==DLL_PROCESS_ATTACH
push hInst
pop hInstance
.endif
mov eax,TRUE
ret
DllEntry Endp
MouseProc proc nCode:DWORD,wParam:DWORD,lParam:DWORD
invoke CallNextHookEx,hHook,nCode,wParam,lParam
mov edx,lParam
assume edx:PTR MOUSEHOOKSTRUCT
invoke WindowFromPoint,[edx].pt.x,[edx].pt.y
invoke PostMessage,hWnd,WM_MOUSEHOOK,eax,0
assume edx:nothing
xor eax,eax
ret
MouseProc endp
InstallHook proc hwnd:DWORD
push hwnd
pop hWnd
invoke SetWindowsHookEx,WH_MOUSE,addr MouseProc,hInstance,NULL
mov hHook,eax
ret
InstallHook endp
UninstallHook proc
invoke UnhookWindowsHookEx,hHook
ret
UninstallHook endp
End DllEntry
;---------------------------------------------- This is the makefile of the DLL ------------------------------
NAME=mousehook
$(NAME).dll: $(NAME).obj
Link /SECTION:.bss,S /DLL /DEF:$(NAME).def /SUBSYSTEM:WINDOWS /LIBPATH:c:\masm\lib $(NAME).obj
$(NAME).obj: $(NAME).asm
ml /c /coff /Cp $(NAME).asm
Phân tích:
Ví dụ này sẽ hiển thị một dialog box với 3 edit controls mà chúng sẽ được lắp giá trị là class name, window handle và address của thủ tục window (window procedure) của cửa sổ window mà con trỏ chuột đang nằm trên đó. Có hai buttons, Hook và Exit. Khi bạn nhấn Hook button, chương trình hook mouse input và text trên button thay đổi là Unhook. Khi bạn move (di chuyển) con trỏ chuột (mouse cursor) trên một window, thông tin về window sẽ được hiển thị trong cửa sổ chính của chương trình ví dụ. Khi bạn nhấn Unhook button, chương trình rời bỏ mouse hook.
Chương trình chính dùng một dialog box như cửa sổ chính của nó. Nó định nghĩa một custom message là WM_MOUSEHOOK, thông điệp này sẽ được sử dụng trong main program và thư viện hook DLL. Khi main program nhận thông điệp này, wParam chứa handle của window mà mouse cursor đang trên đó. Tất nhiên , đây là một sự sắp đặt tùy ý (do tôi quy định như thế). Tôi quyết định gởi handle trong tham số wParam của thông điệp là vì đơn giản hóa công việc. Bạn có thể chọn phương pháp truyền tham số thông điệp khác của chính bạn để truyền thông giữa main program và hook DLL.
.if HookFlag==FALSE
invoke InstallHook,hDlg
.if eax!=NULL
mov HookFlag,TRUE
invoke SetDlgItemText,hDlg,IDC_HOOK,addr UnhookText
.endif
Chương trình duy trì một cờ flag (cờ), là HookFlag, để biểu thị trạng thái của hook. Nó là FALSE nếu hook ko được install và là TRUE nếu hook được install .
Khi người sử dụng nhấn Hook button, chương trình kiểm tra xem hook đã cài đặt chưa. Nếu nó chưa cài đặt, nó gọi InstallHook function trong thư viện hook DLL để install nó. Chú ý rằng chúng ta truyền handle của main dialog như một tham số của hàm InstallHook, vì vậy sau này hook DLL mới có thể gởi thông d9ie5p WM_MOUSEHOOK messages đến đúng window yêu cầu, nghĩa là chính window chương trình của bạn.
Khi chương trình được load, hook DLL cũng được loaded. Thực tế , các DLLs được load ngay lập tức sau khi program ở trong memory. Hàm entrypoint của DLL được gọi trước chỉ thị đầu tiên trong main program thực thi. Vì vậy khi main program thực thi các DLL(s) đã được khởi trị đầu tiên. Chúng ta đặt code sau đây trong DLL entrypoint function của hook DLL:
.if reason==DLL_PROCESS_ATTACH
push hInst
pop hInstance
.endif
Code này chỉ lưu handle instance của hook DLL vào một biến tòan cục có tên là hInstance dành để sử dụng trong hàm InstallHook function. Do DLL entrypoint function được gọi trước các hàm khác trong DLL, nên hInstance luôn luôn hợp lệ. Chúng ta đặt hInstance trong .data section để giá trị này được lưu giữ cho mỗi process cơ sở (per-process basis ). Từ đó, khi mouse cursor bay lượn trên một window, hook DLL được mapped vào trong process. Imagine mà ở đó thật sự DLL chiếm cứ vùng addr dự kiến load hook DLL vào, hook DLL sẽ được ánh xạ lại (remapped ) imagine đó vào một address khác. Giá trị của hInstance sẽ được updated đến vùng addr load vào mới ( new load address) của chúng. Khi user nhấn Unhook button và rồi Hook button, SetWindowsHookEx sẽ được gọi một lần nữa . Tuy nhiên, lần này, nó sẽ sử dụng địa chỉ load mới (new load address) như một instance handle mà chúng sẽ bị sai do ở trong process của chương trình ví dụ, vì vậy hook DLL's load address phải được thay đổi. Hook bây giờ sẽ là một hook local mà ở đó bạn chỉ có thể hook mouse events xảy ra trong chính window của bạn. Mong ước khó khăn nhỉ.
InstallHook proc hwnd:DWORD
push hwnd
pop hWnd
invoke SetWindowsHookEx,WH_MOUSE,addr MouseProc,hInstance,NULL
mov hHook,eax
ret
InstallHook endp
InstallHook function của nó rất đơn giản. Nó lưu window handle truyền như một parameter của nó đến một biến toàn cục global variable có tên là hWnd dành để sử dụng sau này. Rồi nó gọi hàm SetWindowsHookEx để install một mouse hook. Return value của SetWindowsHookEx được chứa trong một biến toàn cục global variable có tên là hHook dùng để sử dụng cho hàm UnhookWindowsHookEx.
Sau khi SetWindowsHookEx được gọi , mouse hook họat động. Bất cứ khi nào một mouse event xảy ra trong hệ thống, MouseProc (hook procedure của bạn ) được gọi ngay.
MouseProc proc nCode:DWORD,wParam:DWORD,lParam:DWORD
invoke CallNextHookEx,hHook,nCode,wParam,lParam
mov edx,lParam
assume edx:PTR MOUSEHOOKSTRUCT
invoke WindowFromPoint,[edx].pt.x,[edx].pt.y
invoke PostMessage,hWnd,WM_MOUSEHOOK,eax,0
assume edx:nothing
xor eax,eax
ret
MouseProc endp
Điều đầu tiên nó làm là gọi hàm CallNextHookEx để cho các hooks khác thay đổi đến tiến trình thực hiện biến cố chuột (mouse event). Sau đó, nó gọi hàm WindowFromPoint function để nhận lại handle của window tại tọa độ màn hình chỉ định. Chú ý rằng chúng ta dùng cấu trúc POINT structure trong cấu trúc MOUSEHOOKSTRUCT structure trỏ đến bởi lParam như là tọa độ chuột hiện hành (current mouse coordinate). Sau đó chúng ta gởi window handle đến main program qua đường PostMessage với WM_MOUSEHOOK message. Một điều bạn nên nhớ: Bạn sẽ ko dùng SendMessage bên trong hook procedure, nó có thể gây ra đình chỉ thông điệp. PostMessage đã được gợi dùng ở đây. Cấu trúc MOUSEHOOKSTRUCT structure được định nghĩa dưới đây:
MOUSEHOOKSTRUCT STRUCT DWORD
pt POINT <>
hwnd DWORD ?
wHitTestCode DWORD ?
dwExtraInfo DWORD ?
MOUSEHOOKSTRUCT ENDS
- pt là tọa độ màn hình hiện hành của mouse cursor
- hwnd là handle của window sẽ được nhận thông điệp mouse message. Nó luôn là window dưới mouse cursor nhưng ko luôn như thế. Nếu một window gọi SetCapture, mouse input sẽ được chuyển hướng đến window đó. Bởi lý do đó, tôi ko dùng thành phần hwnd của cấu trúc này nhưng chọn dùng gọi hàm WindowFromPoint là như vậy .
- wHitTestCode chỉ định giá trị hit-test value. Hit-test value cho nhiều thông tin về vị trí mouse cursor hiện hành. Nó chỉ định phần nào của window mà mouse cursor ở trên đó. Dể hòan tòan nắm rõ , hảy tra cứu win32 api reference với thông điệp WM_NCHITTEST message.
- dwExtraInfo chứa thông tin mở rộng kết hợp message. Thông thường giá trị này được set bởi gọi mouse_event và nhận lại bằng cách gọi hàm GetMessageExtraInfo.
Khi main window nhận thông điệp WM_MOUSEHOOK message, nó sử dụng window handle trong wParam (do ta PostMessage với wParam=handle window dưới con chuột) để nhận lại thông tin về window.
.elseif uMsg==WM_MOUSEHOOK
invoke GetDlgItemText,hDlg,IDC_HANDLE,addr buffer1,128
invoke wsprintf,addr buffer,addr template,wParam
invoke lstrcmpi,addr buffer,addr buffer1
.if eax!=0
invoke SetDlgItemText,hDlg,IDC_HANDLE,addr buffer
.endif
invoke GetDlgItemText,hDlg,IDC_CLASSNAME,addr buffer1,128
invoke GetClassName,wParam,addr buffer,128
invoke lstrcmpi,addr buffer,addr buffer1
.if eax!=0
invoke SetDlgItemText,hDlg,IDC_CLASSNAME,addr buffer
.endif
invoke GetDlgItemText,hDlg,IDC_WNDPROC,addr buffer1,128
invoke GetClassLong,wParam,GCL_WNDPROC
invoke wsprintf,addr buffer,addr template,eax
invoke lstrcmpi,addr buffer,addr buffer1
.if eax!=0
invoke SetDlgItemText,hDlg,IDC_WNDPROC,addr buffer
.endif
Để tránh qua loa , chúng ta check text có sẳn trong edit controls và text chúng ta đặt vào trong chúng xem có giống nhau ko. Nếu chúng giống nhau, chúng ta qua mục khác. Vậy chúng ta nhận handle trong edit control chính là wParam.
Chúng ta nhận lại class name bằng cách gọi hàm GetClassName, nhận address của window procedure bằng cách gọi hàm GetClassLong với GCL_WNDPROC và rồi định dạng chúng vào trong strings và đặt chúng vào trong edit controls thích hợp.
invoke UninstallHook
invoke SetDlgItemText,hDlg,IDC_HOOK,addr HookText
mov HookFlag,FALSE
invoke SetDlgItemText,hDlg,IDC_CLASSNAME,NULL
invoke SetDlgItemText,hDlg,IDC_HANDLE,NULL
invoke SetDlgItemText,hDlg,IDC_WNDPROC,NULL
Khi user nhấn Unhook button, chương trình gọi hàm UninstallHook function trong hook DLL. UninstallHook chỉ gọi hàm UnhookWindowsHookEx. Sau đó, nó thay đổi text của button trở lại thành "Hook", HookFlag là FALSE và xóa nội dung của edit controls.
Chú ý linker switch trong the makefile.
Link /SECTION:.bss,S /DLL /DEF:$(NAME).def /SUBSYSTEM:WINDOWS
Nó chỉ định .bss section như một shared section để làm việc tất cả các processes chia sẽ section data ko định trị lúc đầu của hook DLL. Ngòai switch này , bạn hook DLL sẽ ko thực hiện đúng được .
[Iczelion's Win32 Assembly Homepage]
18/12/2005. Fixed 2010
Benina