Keylogger
WinAPI
Last updated
WinAPI
Last updated
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 đó.
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ó 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:
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.
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 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,…
Để 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):
WH_CALLWNDPROC
4
Thread or global
WH_CALLWNDPROCRET
12
Thread or global
WH_CBT
5
Thread or global
WH_DEBUG
9
Thread or global
WH_FOREGROUNDIDLE
11
Thread or global
WH_GETMESSAGE
3
Thread or global
WH_JOURNALPLAYBACK
1
Global only
WH_JOURNALRECORD
0
Global only
WH_KEYBOARD
2
Thread or global
WH_KEYBOARD_LL
13
Global only
WH_MOUSE
7
Thread or global
WH_MOUSE_LL
14
Global only
WH_MSGFILTER
-1
Thread or global
WH_SHELL
10
Thread or global
WH_SYSMSGFILTER
6
Global only
– 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).
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.
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.
SetWindowsHookExW: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowshookexw
KeyboardProc
vàLowLevelKeyboardProc
đều là những callback function được sử dụng trong Windows API để xử lý các sự kiện bàn phím, nhưng chúng khác nhau về cách hoạt động và mục đích sử dụng.
KeyboardProc
KeyboardProc
là một callback function được sử dụng trong các hook bàn phím (keyboard hooks) cấp cao. Nó được gọi bởi hệ thống mỗi khi có sự kiện bàn phím xảy ra, như nhấn hoặc thả một phím. Hook cấp cao này cho phép các ứng dụng tương tác với dữ liệu đầu vào từ bàn phím trước khi chúng được xử lý bởi các ứng dụng khác hoặc bởi hệ thống.
Mục đích: Thường được sử dụng để theo dõi hoặc thay đổi các sự kiện bàn phím.
Hạn chế: Có thể không bắt được một số phím hoặc tổ hợp phím đặc biệt, và nó không thể hoạt động ở mức độ thấp nhất của hệ thống như
LowLevelKeyboardProc
.
LowLevelKeyboardProc
LowLevelKeyboardProc
là một callback function dùng trong các hook bàn phím cấp thấp. Nó cung cấp một cách để theo dõi hoặc xử lý các sự kiện bàn phím ở mức độ thấp nhất, ngay cả trước khi hệ điều hành xử lý chúng.
Mục đích: Được sử dụng để theo dõi hoặc thay đổi các sự kiện bàn phím ở mức độ thấp nhất, thích hợp cho việc phát triển các ứng dụng như phần mềm chống keylogger, phần mềm tùy chỉnh bàn phím, v.v.
Hạn chế: Vì hoạt động ở mức độ thấp, nó đòi hỏi quyền truy cập hệ thống cao hơn và có thể khó triển khai hơn. Nó cũng có thể ảnh hưởng đến hiệu suất hệ thống nếu không được sử dụng một cách cẩn thận.
Tóm tắt
Sự khác biệt chính:
KeyboardProc
hoạt động ở mức độ ứng dụng hoặc hệ thống cao hơn, trong khiLowLevelKeyboardProc
hoạt động ở mức độ thấp nhất, cho phép tương tác sớm hơn với sự kiện bàn phím.Ứng dụng:
LowLevelKeyboardProc
thích hợp cho các tác vụ đòi hỏi sự kiểm soát cao độ và tinh tế hơn đối với các sự kiện bàn phím, trong khiKeyboardProc
phù hợp với việc xử lý các sự kiện bàn phím ở mức độ cao hơn và ít nhạy cảm hơn.
LowLevelKeyboardProc: https://learn.microsoft.com/en-us/windows/win32/winmsg/lowlevelkeyboardproc
KBDLLHOOKSTRUCT structure: https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-kbdllhookstruct
Đoạn code bạn đưa ra là một phần của một chương trình C++ thường được sử dụng trong việc lập trình Windows API, đặc biệt là trong việc xử lý các sự kiện bàn phím thông qua Low Level Keyboard Hook.
Giải thích chi tiết:
KBDLLHOOKSTRUCT: Là một cấu trúc do Windows API định nghĩa, chứa thông tin về một sự kiện bàn phím. Cụ thể, nó chứa các thông tin như mã của phím được nhấn hoặc thả, trạng thái của phím, thời gian sự kiện xảy ra, và một số thông tin khác.
lParam
: Là một tham số được truyền vào callback function của Low Level Keyboard Hook. Trong ngữ cảnh này,lParam
chứa địa chỉ của mộtKBDLLHOOKSTRUCT
chứa thông tin về sự kiện bàn phím hiện tại.
reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam)
: Đây là một phép ép kiểu trong C++.reinterpret_cast
được sử dụng để thay đổi kiểu dữ liệu của một con trỏ sang một kiểu con trỏ khác mà không thực hiện bất kỳ kiểm tra kiểu nào ở thời điểm biên dịch. Trong trường hợp này, nó ép kiểulParam
(có lẽ là mộtLPARAM
hoặc một con trỏ kiểuvoid*
) sang một con trỏ kiểuKBDLLHOOKSTRUCT*
. Điều này cho phép truy cập trực tiếp và sử dụng dữ liệu của sự kiện bàn phím thông qua con trỏs
.Việc sử dụng
reinterpret_cast
trong trường hợp này thường là cần thiết vì callback function cho Low Level Keyboard Hook được gọi bởi Windows với các tham số có kiểu dữ liệu cụ thể, vàlParam
cần được ép kiểu để có thể truy cập vào dữ liệu của nó một cách chính xác theo cấu trúcKBDLLHOOKSTRUCT
.
MapVirtualKeyW: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-mapvirtualkeyw
Virtual-Key Codes: https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
SendInput: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendinput
ToUnicode: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-tounicode
ToUnicodeEx: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-tounicodeex
GetKeyboardLayout: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getkeyboardlayout
CreateFileW: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew
SetFilePointer: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-setfilepointer
WriteFile: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-writefile
RegCreateKeyW: https://learn.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regcreatekeyw
RegCreateKeyExW: https://learn.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regcreatekeyexw
https://learn.microsoft.com/en-us/windows/win32/secauthz/enabling-and-disabling-privileges-in-c--
RegSetValueExW: https://learn.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regsetvalueexw
© 2024,Pham Quoc Trung. All rights reserved.