DLL 注入是將自訂動態連結庫載入目標進程位址空間,進而攔截系統呼叫、修改行為或擴充功能的技術。本文詳細解析基於 Windows Hook 機制 (SetWindowsHookEx) 的 DLL 注入完整流程,包含注入端 (Caller) 與被注入 DLL (Inject.dll) 的核心程式碼與關鍵細節。
FindWindow/FindWindowEx 取得目標視窗控制代碼。GetWindowThreadProcessId 取得目標執行緒 ID。Inject.dll 匯出的 SetInjectHook(ThreadID) 安裝 Hook。GetMsgProc) 在目標執行緒內建立自訂表單 (TInjectForm) 作為控制入口。FindWindow 找到注入表單,發送自訂訊息 (WM_APP) 控制目標視窗。procedure TestHook;
var
hMain, hChild, hInjectForm: HWND;
dwThreadID: DWORD;
Msg: TMsg;
begin
// 1. 尋找目標主視窗 (以 Program Manager 為例)
hMain := FindWindow('Progman', nil);
// 2. 尋找子視窗
hChild := FindWindowEx(hMain, 0, nil, nil);
// 3. 取得目標執行緒 ID
dwThreadID := GetWindowThreadProcessId(hChild, nil);
// 4. 安裝 Hook (載入 Inject.dll 並呼叫 SetInjectHook)
SetInjectHook(dwThreadID);
// 5. 等待訊息迴圈 (確保 Hook 程序已執行)
GetMessage(Msg, 0, 0, 0);
// 6. 尋找注入建立的控制表單
hInjectForm := FindWindow(nil, 'InjectForm');
// 7. 發送控制訊息:wParam=目標視窗控制代碼, lParam=控制參數
SendMessage(hInjectForm, WM_APP, hChild, Integer(True));
// 8. 關閉注入表單並清理
SendMessage(hInjectForm, WM_CLOSE, 0, 0);
Sleep(500);
Assert(not IsWindow(hInjectForm));
// 9. 移除 Hook
SetDipsHook(0); // 對應 SetInjectHook(0)
end;
library Inject;
uses
Windows, Forms, UnitInjectForm; // 包含 TInjectForm 定義
var
g_hHook: HHook = 0;
g_dwThreadIDInject: DWORD = 0;
g_hInjectForm: HWND = 0;
fFirstTime: Boolean = True;
function SetInjectHook(dwThreadID: DWORD): Boolean; stdcall;
begin
Result := False;
if dwThreadID <> 0 then
begin
// 安裝 Hook
Assert(g_hHook = 0);
g_dwThreadIDInject := GetCurrentThreadId;
// WH_GETMESSAGE: 攔截 GetMessage/PeekMessage
g_hHook := SetWindowsHookEx(WH_GETMESSAGE, @GetMsgProc, HInstance, dwThreadID);
Result := g_hHook <> 0;
if Result then
begin
// 發送空訊息觸發 Hook 程序建立表單
Result := PostThreadMessage(dwThreadID, WM_NULL, 0, 0);
Sleep(500); // 等待表單建立完成
end;
end
else
begin
// 移除 Hook
Assert(g_hHook <> 0);
Result := UnhookWindowsHookEx(g_hHook);
g_hHook := 0;
end;
end;
exports
SetInjectHook name 'SetInjectHook';
function GetMsgProc(nCode: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
var
InjectForm: TInjectForm;
begin
if fFirstTime then
begin
fFirstTime := False;
// 在目標執行緒建立表單 (必須在 Hook 程序內建立)
InjectForm := TInjectForm.Create(nil);
g_hInjectForm := InjectForm.Handle;
end;
// 必須呼叫下一個 Hook,否則訊息鏈斷裂
Result := CallNextHookEx(g_hHook, nCode, wParam, lParam);
end;
unit UnitInjectForm;
interface
uses Windows, Messages, Forms, Controls;
type
TInjectForm = class(TForm)
private
procedure WMApp(var Msg: TMessage); message WM_APP;
end;
implementation
procedure TInjectForm.WMApp(var Msg: TMessage);
var
TargetWnd: HWND;
Enable: Boolean;
begin
TargetWnd := HWND(Msg.WParam);
Enable := Boolean(Msg.LParam);
// 此處在目標進程執行緒內,可直接操作目標視窗
// 範例:啟用/停用目標視窗
EnableWindow(TargetWnd, Enable);
// 回傳結果給 Caller (可選)
Msg.Result := 1;
end;
end.
| 技術點 | 說明 |
|---|---|
| Hook 類型 | WH_GETMESSAGE 攔截訊息迴圈,確保在目標執行緒內執行程式碼 |
| 表單建立時機 | 首次 Hook 程序被呼叫時 (fFirstTime 旗標) |
| 跨進程通訊 | Caller 用 FindWindow 找到注入表單,發送 WM_APP 自訂訊息 |
| 清理順序 | 發送 WM_CLOSE → 等待 → UnhookWindowsHookEx |
| 注意事項 | 1. 目標進程必須有訊息迴圈 (GUI 應用程式) 2. 32/64 位元必須匹配 (32-bit DLL 注入 32-bit 進程) 3. 需處理例外情況防止目標進程當機 4. 某些安全軟體會攔截 Hook 行為 |
Hook 與 DLL 注入屬進階系統程式技術,僅供學習、除錯、合法擴充功能使用。惡意注入、竊取資料、繞過授權等行為違法且違反道德,嚴禁濫用。