Windows Message Manager ช่วยให้แอปพลิเคชันสามารถเข้าถึงโฟลว์ข้อความที่ได้รับการควบคุม
กลไกที่เรียกว่า hook (HOOK) ของ 'c4 hook มีหลายประเภท แต่ละประเภทใช้เพื่อจับประเภทหรือช่วงของข้อความที่เฉพาะเจาะจง เช่น ข้อความบนคีย์บอร์ด ข้อความเมาส์ เป็นต้น ที่นี่เราใช้เฉพาะการใช้ hooks ของแป้นพิมพ์เพื่อหารือเกี่ยวกับวิธีการเขียนโปรแกรม DLL ภายใต้ DELPHI และวิธีการติดตั้งและใช้ฟังก์ชัน hooks ของแป้นพิมพ์ในโปรแกรมของคุณเอง นอกจากนี้ เรายังหารือถึงวิธีการแบ่งปันข้อมูลเมื่อโปรแกรมต่างๆ ใช้ไฟล์ DLL เดียวกัน .
1. คำแนะนำในการเขียนฟังก์ชันตัวกรองตะขอ
เนื่องจากฟังก์ชันตัวกรอง hook ต้องอยู่ในโมดูลอิสระ นั่นหมายความว่าเราต้องสร้างกรอบงาน DLL ก่อน จากนั้นจึงเพิ่มโค้ดฟังก์ชัน hook และโค้ดฟังก์ชันอื่นๆ ที่เกี่ยวข้องลงไป ต่อไปนี้เราจะนำการเขียนฟังก์ชัน hook filter ของแป้นพิมพ์มาเป็นตัวอย่างเพื่อแสดงให้เห็น ขั้นตอนเฉพาะมีดังนี้:
1. ขั้นแรกให้สร้างตะกร้า DLL 2
2. เขียนฟังก์ชันตัวกรองตะขอแป้นพิมพ์ของคุณเอง
ฟังก์ชันตัวกรอง hook ต้องเป็นฟังก์ชันเรียกกลับ และฟังก์ชันจะมีรูปร่างดังต่อไปนี้:
functionKeyHookPRoc(
iCode:จำนวนเต็ม;
wพารามิเตอร์:WPARAM;
lParam:LPARAM ): LRESULT; ส่งออก;
เพิ่มฟังก์ชันการประมวลผล hook แป้นพิมพ์ของคุณเองลงในกรอบงาน DLL ที่สร้างขึ้นเพื่อจัดการข้อความบนแป้นพิมพ์
รหัสมีดังนี้:…
if(iCode>=0) ให้เริ่มต้น
ผลลัพธ์:=0; //คืนค่าการเริ่มต้น
//เพิ่มรหัสของคุณเองที่นี่
จบอย่างอื่น
เริ่ม
ผลลัพธ์:=CallNextHook(hOldKeyHook,iCode,wParam,lParam);
//hOldKeyHook เป็นฟังก์ชันตัวกรองแป้นพิมพ์ต้นฉบับที่บันทึกไว้หรือไม่
จบ;
3. ติดตั้งฟังก์ชั่นตัวกรองตะขอคีย์บอร์ด
เมื่อต้องการติดตั้ง hook ฟังก์ชันตัวกรอง _fd ควรเรียกใช้ฟังก์ชัน SetWindowsHookEx (ฟังก์ชันการติดตั้ง SetWindowsHook hook สำหรับ Windows 3.0 ล้าสมัยแล้ว) ต้นแบบของฟังก์ชันนี้มีดังนี้:
HHOOK ตั้ง WindowsHookEx(
int idHook // ติดตั้งแล้ว?_b3 ชนิดย่อย
HOOKPROC lpfn, //Hook filter??f ที่อยู่หมายเลข
HINSTANCE hMod, // ตัวจัดการงาน
DWord dwThreadId // วัตถุประสงค์ของ hook
-
ควรสังเกตว่า: ?_a8 มักจะเรียกใช้ฟังก์ชัน MakeProcInstance เพื่อรับที่อยู่รายการของคำนำของฟังก์ชันเอาต์พุต และจากนั้น ใช้ที่อยู่นี้เป็นพารามิเตอร์ตัวที่สอง lpfn ของ SetWindowsHookEx อย่างไรก็ตาม เนื่องจาก Delphi ให้ "การโทรกลับอัจฉริยะ" จึงสามารถละเว้น MakeProcInstance และชื่อฟังก์ชันตัวกรอง hook สามารถใช้เป็นที่อยู่รายการได้โดยตรง
ด้วยวิธีนี้ เมื่อฟังก์ชัน _c3GetMessage หรือ PeekMessage ของแอปพลิเคชันอ่านข้อความจากคิวข้อความหรือมีข้อความสำคัญ (WM_KEYDOWN หรือ WM_KEYUP) ที่จะประมวลผล ระบบจะเรียกใช้ฟังก์ชันตัวกรอง hook KeyHookProc เพื่อประมวลผลข้อความบนแป้นพิมพ์
4. ถอนการติดตั้งฟังก์ชันตัวกรองตะขอ
เมื่อไม่จำเป็นต้องใช้ฟังก์ชัน hook อีกต่อไป ควรเรียก UnHookWindowsHookProc เพื่อถอนการติดตั้ง hook ที่ติดตั้งเพื่อปล่อยทรัพยากรระบบ
รายการโปรแกรมทั้งหมดมีดังนี้?_ba
ห้องสมุด KEYHOOK;
ใช้วินโดวส์;
const BUFFER_SIZE=16*1024;
const HOOK_MEM_FILENAME='ตัวอย่าง KEY_HOOK_MEM_FILE';
const HOOK_MUTEX_NAME ='ตัวอย่าง KEY_HOOK_MUTEX_NAME';
พิมพ์
TShared=บันทึก
คีย์: อาร์เรย์ [0..BUFFER_SIZE] ของ Char;
KeyCount : จำนวนเต็ม;
จบ;
PSshared=^TShared;
var
MemFile,HookMutex: THandle;
hOldKeyHook: HHook;
ProcSaveExit: ตัวชี้;
แบ่งปันแล้ว: PSshared;
// ฟังก์ชั่นตัวกรองตะขอคีย์บอร์ด
ฟังก์ชั่น KeyHookProc (iCode: Integer; wParam: WPARAM ; lParam: LPARAM): LRESULT
; ส่งออก;
const KeyPressMask = $80000000;
เริ่ม
ถ้า iCode < 0 แล้ว
ผลลัพธ์ := CallNextHookEx (hOldKeyHook, iCode, wParam, lParam)
เริ่มอย่างอื่น
ถ้า ((lParam และ KeyPressMask)= 0) จากนั้น // กดปุ่ม
เริ่ม
Shared^.Keys[Shared^.KeyCount]:=Char(wParam และ $00ff);
Inc (แชร์ ^.KeyCount);
ถ้า Shared^.KeyCount>=BUFFER_SIZE-1 แล้ว Shared^.KeyCount:=0;
จบ;
รหัส iCode:=-1;
ผลลัพธ์ := CallNextHookEx (hOldKeyHook, iCode, wParam, lParam);
จบ;
จบ;
//ตั้งค่าฟังก์ชันตัวกรองเบ็ด
ฟังก์ชั่น EnableKeyHook: BOOL;
เริ่ม
Shared^.KeyCount:=0; //เตรียมใช้งานตัวชี้แป้นพิมพ์
ถ้า hOldKeyHook=0 ให้เริ่มต้น
hOldKeyHook := SetWindowsHookEx(WH_KEYBOARD,
คีย์ฮุคโปรค,
อินสแตนซ์
0);
จบ;
ผลลัพธ์ := (hOldKeyHook <> 0);
จบ;
// เลิกทำฟังก์ชันตัวกรอง hook
ฟังก์ชั่น DisableKeyHook: BOOL; ส่งออก;
เริ่ม
ถ้า hOldKeyHook<> 0 แล้ว
เริ่ม
UnHookWindowsHookEx(hOldKeyHook); // ปลดตะขอคีย์บอร์ด
hOldKeyHook:= 0;
แบ่งปัน^.KeyCount:=0;
จบ;
ผลลัพธ์ := (hOldKeyHook = 0);
จบ;
//รับจำนวนการกดแป้นพิมพ์ในบัฟเฟอร์ของแป้นพิมพ์
ฟังก์ชั่น GetKeyCount : จำนวนเต็มส่งออก;
เริ่ม
ผลลัพธ์:=Shared^.KeyCount;
จบ;
//รับคีย์ของบัฟเฟอร์แป้นพิมพ์
ฟังก์ชั่น GetKey (ดัชนี: จำนวนเต็ม) : Char ;
เริ่ม
ผลลัพธ์:=Shared^.Keys[ดัชนี];
จบ;
//ล้างบัฟเฟอร์คีย์บอร์ด
ขั้นตอนการส่งออก ClearKeyString;
เริ่ม
แบ่งปัน^.KeyCount:=0;
จบ;
//กระบวนการออกจากกระบวนการ DLL
ขั้นตอน KeyHookExit;
เริ่ม
ถ้า hOldKeyHook <> 0 แล้ว DisableKeyHook;
UnMapViewOfFile (Shared); // ปล่อยไฟล์อิมเมจหน่วยความจำ
CloseHandle(MemFile); // ปิดไฟล์รูปภาพ
ExitProc := ProcSaveExit;
จบ;
ส่งออก // กำหนดฟังก์ชันเอาต์พุต
เปิดใช้งานKeyHook,
ปิดการใช้งานKeyHook,
รับคีย์เคาท์,
เคลียร์คีย์สตริง,
รับคีย์;
เริ่ม
// ส่วนการเริ่มต้น DLL
HookMutex:=CreateMutex(ไม่มี,True,HOOK_MUTEX_NAME);
// แบ่งปันหน่วยความจำโดยการสร้างไฟล์ภาพหน่วยความจำ
MemFile:=OpenFileMapping(FILE_MAP_WRITE,False,
HOOK_MEM_FILENAME);
ถ้า MemFile=0 แล้ว
MemFile:=CreateFileMapping($FFFFFFFF,nil,PAGE_READWRITE,0,
ขนาดของ (TShared) ,HOOK_MEM_FILENAME);
แบ่งปันแล้ว:=MapViewOfFile(MemFile,File_MAP_WRITE,0,0,0);
ReleaseMutex(HookMutex);
CloseHandle (HookMutex);
ProcSaveExit := ExitProc; // บันทึก ExitProc ของ DLL
ExitProc := @KeyHookExit; // ตั้งค่า ExitProc ใหม่ของ DLL
จบ.
//สิ้นสุดซอร์สโค้ด
2. ใช้ฟังก์ชันตัวกรองตะขอแป้นพิมพ์ที่เตรียมไว้ในโปรแกรมของคุณเอง
หลังจากที่คอมไพล์ฟังก์ชัน hook แล้วจะใช้งานได้ง่ายมาก: ขั้นแรกให้เรียก SetWindowsHookEx เพื่อติดตั้งฟังก์ชันตัวกรอง hook ของคุณเอง และในขณะเดียวกันก็บันทึกที่อยู่ฟังก์ชันตัวกรอง hook ดั้งเดิม ในเวลานี้ ฟังก์ชัน hook เข้ามามีบทบาท และจะจัดการข้อความบนแป้นพิมพ์ตามความต้องการของคุณ เมื่อโปรแกรมทำงานเสร็จแล้วหรือไม่จำเป็นต้องตรวจสอบข้อความบนแป้นพิมพ์อีกต่อไป ให้เรียกใช้ฟังก์ชัน UnHookWindowsHookProc เพื่อถอนการติดตั้งฟังก์ชัน hook ที่ติดตั้งไว้ และเรียกคืนที่อยู่ฟังก์ชันตัวกรอง hook เดิม
ต่อไปนี้เป็นตัวอย่างของการใช้ฟังก์ชัน hook ที่คอมไพล์ไว้ด้านบน:
หน่วย หน่วยที่ 1;
อินเตอร์เฟซ
การใช้งาน
Windows, ข้อความ, SysUtils, คลาส, กราฟิก, การควบคุม, แบบฟอร์ม, กล่องโต้ตอบ,
StdCtrls, ExtCtrls;
พิมพ์
TForm1 = คลาส (TForm)
Memo1: TMemo;
แผง 1: TPanel;
bSetHook: TButton;
bCancelHook: TButton;
bReadKeys: TButton;
bClearKeys: TButton;
แผง2: TPanel;
ขั้นตอน bSetHookClick (ผู้ส่ง: TObject);
ขั้นตอน bCancelHookClick (ผู้ส่ง: TObject);
ขั้นตอน bReadKeysClick (ผู้ส่ง: TObject);
ขั้นตอน bClearKeysClick (ผู้ส่ง: TObject);
จบ;
var Form1: TForm1;
การดำเนินการ
{$R *.DFM}
ฟังก์ชั่น EnableKeyHook : BOOL ; ภายนอก 'KEYHOOK.DLL';
ฟังก์ชั่น DisableKeyHook : BOOL ; ภายนอก 'KEYHOOK.DLL';
ฟังก์ชัน GetKeyCount : จำนวนเต็ม ; ภายนอก 'KEYHOOK.DLL';
ฟังก์ชั่น GetKey (idx: จำนวนเต็ม) : Char ; ภายนอก 'KEYHOOK.DLL';
ขั้นตอน ClearKeyString; ภายนอก 'KEYHOOK.DLL';
ขั้นตอน TForm1.bSetHookClick (ผู้ส่ง: TObject); // ตั้งค่าตะขอคีย์บอร์ด7ó
เริ่ม
เปิดใช้งานKeyHook;
bSetHook.Enabled :=เท็จ;
bCancelHook.Enabled:=จริง;
bReadKeys.Enabled :=จริง;
bClearKeys.Enabled :=จริง;
Panel2.Caption:='ตั้งค่าตะขอคีย์บอร์ดแล้ว';
จบ;
ขั้นตอน TForm1.bCancelHookClick (ผู้ส่ง: TObject); // ถอนการติดตั้งตะขอคีย์บอร์ด
เริ่ม
ปิดการใช้งานKeyHook;
bSetHook.Enabled :=จริง;
bCancelHook.Enabled:=False;
bReadKeys.Enabled :=เท็จ;
bClearKeys.Enabled :=เท็จ;
Panel2.Caption:='ไม่ได้ตั้งค่า Keyboard hook';
จบ;
ขั้นตอน TForm1.bReadKeysClick (ผู้ส่ง: TObject); // รับประวัติการกดแป้นพิมพ์
var i:จำนวนเต็ม;
เริ่ม
Memo1.Lines.Clear; // แสดงประวัติการกดแป้นพิมพ์ใน Memo1
สำหรับ i:=0 ถึง GetKeyCount-1 ทำ
Memo1.Text:=Memo1.Text+GetKey(i);
จบ;
ขั้นตอน TForm1.bClearKeysClick (ผู้ส่ง: TObject); // ล้างประวัติการกดแป้นพิมพ์
เริ่ม
Memo1.เคลียร์;
ClearKeyString;
จบ;
จบ.
//สิ้นสุดซอร์สโค้ด
3. การใช้หน่วยความจำที่ใช้ร่วมกันใน DLL ภายใต้ Windows95
ในไฟล์ DLL ซึ่งมีฟังก์ชัน hook ด้านบนอยู่ จำเป็นต้องใช้หน่วยความจำที่ใช้ร่วมกัน กล่าวคือ บันทึกการกดแป้นพิมพ์ทั้งหมดจะถูกจัดเก็บไว้ในเซ็กเมนต์ข้อมูลเดียวกัน ทำไมเราถึงทำเช่นนี้? เนื่องจากวิธีการเรียก DLL ของ Windows95 แตกต่างจาก Windows3.X เมื่อแต่ละเธรดล็อกอินเข้าสู่ไลบรารีลิงก์แบบไดนามิก เธรดนั้นจะส่งผ่านตัวจัดการอินสแตนซ์ใหม่ (นั่นคือ หมายเลขอ้างอิงของส่วนข้อมูล DLL) ไปยังไลบรารีลิงก์แบบไดนามิก ซึ่งช่วยให้อินสแตนซ์ต่างๆ ของ DLL ไม่รบกวนซึ่งกันและกัน แต่จะนำมาซึ่งปัญหาบางประการเมื่ออินสแตนซ์ DLL ทั้งหมดใช้ชุดตัวแปรร่วมกัน เพื่อที่จะแก้ไขปัญหานี้ เราแก้ไขได้ที่นี่โดยการสร้างไฟล์ที่แมปหน่วยความจำ นั่นคือการใช้ OpenFileMapping, CreateFileMapping และ
MapViewOfFile สามฟังก์ชั่นเพื่อให้บรรลุ วิธีใช้:
-
MemFile เป็นประเภท THandle, Shared เป็นประเภทตัวชี้ และ HOOK_MEM_FILENAME เป็นสตริงคงที่
-
MemFile:=OpenFileMapping(FILE_MAP_WRITE,False,
HOOK_MEM_FILENAME); //เปิดไฟล์ที่แมปหน่วยความจำ
ถ้า MemFile=0 แล้ว //หากการเปิดล้มเหลว?_c2 สร้างไฟล์ที่แมปหน่วยความจำ
MemFile:=CreateFileMapping($FFFFFFFF,nil,PAGE_READWRITE,0,
ขนาดของ (TShared) ,HOOK_MEM_FILENAME);
//แมปไฟล์กับตัวแปร
แบ่งปันแล้ว:=MapViewOfFile(MemFile,File_MAP_WRITE,0,0,0);
จนถึงตอนนี้ คุณรู้อยู่แล้วว่าการรวบรวมฟังก์ชัน hook ใน Delphi นั้นง่ายเพียงใด สุดท้ายนี้ผมขอเตือนทุกคนว่า ถึงแม้ฟังก์ชัน hook จะค่อนข้างทรงพลัง แต่หากใช้ไม่ถูกต้องจะส่งผลเสียต่อประสิทธิภาพของระบบอย่างมาก ดังนั้น พยายามหลีกเลี่ยงการใช้ system hooks เมื่อต้องใช้ความระมัดระวังเป็นพิเศษจึงจะส่งผลต่อการทำงานของระบบน้อยที่สุด
[จบข้อความฉบับเต็ม]