Thứ Năm, 28 tháng 6, 2012

Win32 – Giới thiệu kĩ thuật Hook và các khái niệm cơ bản

Hook là một cơ chế mà một ứng dụng có thể chặn các sự kiện, như các thông điệp, thao tác chuột, bàn phím. Hàm dùng để chặn một loại sự kiện riêng biệt được gọi là hook procedure/ Hook function. Mỗi khi nhận được sự kiện, hook procedure có thể thay đổi và thậm chí hủy bỏ các sự kiện đó.

Cơ chế của Hook

Như đã nói ở trên, có nhiều loại hook (như chuột, bàn phím) và hệ điều hành luôn duy trì một danh sách các hook procedure cho mỗi loại đó. Mỗi danh sách các Hook procedure này được gọi là hook chain. Bản chất của hook chain là một dãy các con trỏ hàm trỏ đến các Hook procedure.
Khi hệ thống thực một sự kiện nào đó, nó sẽ tìm kiếm trong hook chain tương ứng với sự kiện đó. Nếu một hook procedure phù hợp được tìm thấy, hệ thống sẽ thực hiện nó và chỉ lấy lại quyền điều khiển sau khi hook chain kết thúc. Vì thế khi một hook procedure thực hiện xong, nó phải thực hiện việc chuyển quyền điều khiển cho hook procedure kế tiếp trong hook chain.
Tuy nhiên cơ chế này còn tùy thuộc vào loại hook. Như một số loại hook chỉ có thể theo dõi các thông điệp, vì vậy cho dù hook procedure có chuyển quyền điều khiển cho hook procedure kế tiếp hay không, hệ thống vẫn sẽ tự động làm việc này.
Một điểm cần lưu ý là hook sẽ làm chậm hệ thống, vì thế bạn chỉ nên cài đặt hook khi cần thiết và loại bỏ nó khi đã hoàn tất công việc.

Các loại Hook (Hook Type)

Có nhiều loại hook được phân biệt dựa vào các sự kiện, thông điệp mà Hook procedure can thiệp vào. Danh sách dưới đây liệt kê các loại hook kèm với link dẫn đến tham khảo từ MSDN:

Ứng dụng của Hook

Hook được ứng dụng khá nhiều trong lập trình và là kĩ thuật không thể thiếu nếu như bạn muốn thực hiện một số ứng dụng điển hình sau:
-       Tạo các chương trình record và play back macro.
-       Tạo các ứng dụng computer-based training (CBT)
-       Bắt và giả lập các thông điệp bàn phím, chuột
-       Cung cấp chức năng Help (F1) cho ứng dụng
-       Xác định trạng thái rỗi của ứng dụng để thực hiện công việc nào đó.
-       Tạo các chức năng debug.

Phạm vi của Hook

Dựa theo lọai hook bạn có thể cài đặt hook procedure có phạm vi cục bộ (local hook/thread hook) hoặc toàn cục (global hook). Tất cả các loại hook đểu có thể được cài đặt để trở thành global hook, một số cho phép chọn lựa phạm vi hook dựa vào các tham số trong hàm cài đặt (SetWindowsHookEx). Ta sẽ xem chi tiết hơn trong phần kế tiếp. Hiện tại bạn cần phân biệt đặc điểm của hai loại hook này (theo phạm vi ảnh hưởng):
-       Local hook (thread hook): chỉ có ảnh hưởng trong phạm vi một thread.
-       Global hook: có ảnh hưởng trong toàn hệ thống. Trường hợp này, hook procedure phải được chứa trong một thư viện DLL.

Hook Procedure

Hook procedure là một loại callback function (hàm hồi quy). Hệ thống sẽ gọi các hàm này khi các sự kiện, thông điệp tương ứng với loại hook. Mỗi loại hook có một hook procedure khác nhau nhưng đều có cùng tham số như cú pháp bên dưới. Với mỗi hook procedure khác nhau thì việc xét các giá trị tham số cũng khác nhau.
Danh sách hook procedure tương ứng với từng loại hook được liệt kê trong phần giới thiệu về hàm SetWindowsHookEx.
Cú pháp chung:
LRESULT CALLBACK HookProc(int nCode, WPARAM wParam, LPARAM lParam)
Tham số:
-       nCode: giá trị giúp xác định cách xử lý thông điệp. Nếu nCode nhỏ hơn 0, hook procedure sẽ gọi CallNextHookEx. Ngược lại sẽ thực hiện các công việc xử lý trước khi gọi CallNextHookEx
-       wParam và lParam: tùy theo loại hook sẽ có ý nghĩa khác nhau. Ví dụ nếu bạn hook bàn phím thì wParam sẽ có giá trị của phím được nhấn, lParam sẽ chứa các thông tin về số lần nhấn, các phím tắt, trạng thái phím,…

Các hàm Win32 API cần thiết

Để sử dụng hook, ta cần sử dụng ba hàm API của Windows là:
-       SetWindowsHookEx: cài đặt một hook procedure vào một hook chain.
-       CallNextHookEx: chuyển quyền điều khiển cùng các thông tin hook cho hook procedure kế tiếp trong hook chain. Bạn có thể không sử dụng hàm này tuy nhiên chỉ khi nào bạn muốn chặn các hook procedure còn lại trong hook chain.
-       UnhookWindowsHookEx: gỡ hook procedure ra khỏi hook chain được cài đặt bởi SetWindowsHookEx.
Phần cú pháp khai báo có một số từ khóa phía trước phần biến mà bạn cần chú ý:
__in: input parameter
__out output parameter
__in_opt: optional input parameter
SetWindowsHookEx
Cú pháp:
HHOOK WINAPI SetWindowsHookEx(
__in  int idHook,
__in  HOOKPROC lpfn,
__in  HINSTANCE hMod,
__in  DWORD dwThreadId
);
Giá trị trả về:
Handle của hook nếu thành công, ngược lại trả về NULL. Giá trị này cần thiết để sử dụng hàm UnhookWindowsHookEx. Trong trường hợp này bạn có thể dùng hàm GetLastErrorđể lấy thông tin lỗi. Để xem thông tin lỗi, bạn có thể truy cập vào trang sau: List of System Error Codes
Tham số:
-       idHook:  loại hook, bao gồm các giá trị sau (đã được liệt kê trong phần Hook Type):
NameValueScopreHook procedure
WH_CALLWNDPROC4Thread or globalCallWndProc
WH_CALLWNDPROCRET12Thread or globalCallWndRetProc
WH_CBT5Thread or globalCBTProc
WH_DEBUG9Thread or globalDebugProc
WH_FOREGROUNDIDLE11Thread or globalForegroundIdleProc
WH_GETMESSAGE3Thread or globalGetMsgProc
WH_JOURNALPLAYBACK1Global onlyJournalPlaybackProc
WH_JOURNALRECORD0Global onlyJournalRecordProc
WH_KEYBOARD2Thread or globalKeyboardProc
WH_KEYBOARD_LL13Global onlyLowLevelKeyboardProc
WH_MOUSE7Thread or globalMouseProc
WH_MOUSE_LL14Global onlyLowLevelMouseProc
WH_MSGFILTER-1Thread or globalMessageProc
WH_SHELL10Thread or globalShellProc
WH_SYSMSGFILTER6Global onlySysMsgProc
-       lpfn: một con trỏ đến hook procedure. Nếu là global hook thì tham số này phải trỏ đến một hook procedure trong một DLL. Ngược lại thì có thể trỏ đến một đoạn mã đóng vai trò hook procedure trong process hiện tại.
-       hMod: handle của DLL chứa hook procedure. Trong trường hợp local hook thì tham số này được đặt là NULL.
-       dwThreadId: định danh của thread mà hook procedure sẽ gắn vào. Nếu giá trị này là 0 thì mọi thread sẽ bị ảnh hưởng (global), ngược lại thì chỉ có thread được xác định là bị ảnh hưởng (local).
CallNextHookEx
Cú pháp:
LRESULT WINAPI CallNextHookEx(
__in_opt  HHOOK hhk,
__in      int nCode,
__in      WPARAM wParam,
__in      LPARAM lParam
);
Giá trị trả về:
Tham số:
-       hhk: handle của hook lấy được từ SetWindowsHookEx, tuy nhiên tham số này thực sự không cần thiết.
-       nCode: tham số này được gọi tên là hook code. Giá trị của nó sẽ quyết định hook procedure có thực hiện các công việc xử lý không. Nếu giá trị này là âm, bạn cần gọi hàm CallNextHookEx để chuyển tới hook procedure kế tiếp và bỏ qua bước xử lý (xem phần hook procedure). Giá trị này có được do hệ thống quyết định và bạn không nên thay đối giá trị.
-       wParam / lParam: các tham số lưu trữ thông tin cần thiết cho hook procedure kế tiếp.
UnhookWindowsEx
Cú pháp:
BOOL WINAPI UnhookWindowsHookEx(
__in  HHOOK hhk
);
Giá trị trả về: Một số khác 0 nếu thành công, ngược lại là 0.
Tham số:
-       hhk: handle của hook sẽ được loại ra khỏi hook chain, lấy từ SetWindowsHookEx.

Kết luận

Như vậy bạn đã hiểu các khái niệm cơ bản về hook cũng như ứng dụng của nó. Trong bài viết tới tôi sẽ hướng dẫn một vài ví dụ đơn giản để áp dụng hook trong C#. Để biết thêm chi tiết và xem ví dụ bạn có thể tham khảo các link sau:
-       How to set a Windows hook in Visual C# .NET (http://support.microsoft.com/kb/318804)

Không có nhận xét nào: