| 方案 | 原理 | 優點 | 缺點 |
|---|---|---|---|
| 網卡混雜模式 | 驅動層抓取所有封包 | 全網路可見 | 需驅動簽名、資料量大、權限高 |
| API Hook (用戶態) | 注入 DLL 修改目標進程 IAT/EAT | 精準針對單進程、無需驅動 | 僅限用戶態、可能被反 Hook 偵測 |
| 代理 DLL | 替換系統 DLL (如 ws2_32.dll) | 透明攔截 | 系統穩定性風險大、簽名問題 |
本文實作**用戶態 API Hook**:訊息 Hook 注入 → GetProcAddress 取得真實位址 → 覆寫 JMP 指令 → 執行自訂邏輯 → 恢復呼叫真實 API。
library Hook;
uses SysUtils, Windows, Messages, APIHook;
type
PData = ^TData;
TData = record Hook: THandle; Hooked: Boolean; end;
var DLLData: PData;
{--- 訊息 Hook 程序 ---}
procedure HookProc(nCode, wParam, lParam: LongWord); stdcall;
begin
if not DLLData^.Hooked then begin HookAPI; DLLData^.Hooked := True; end;
CallNextHookEx(DLLData^.Hook, nCode, wParam, lParam);
end;
{--- 安裝/卸載 ---}
function InstallHook(TargetWnd: LongWord): Boolean; stdcall;
var Tid: LongWord;
begin
Result := False;
Tid := GetWindowThreadProcessId(TargetWnd, nil);
DLLData^.Hook := SetWindowsHookEx(WH_GETMESSAGE, @HookProc, HInstance, Tid);
Result := DLLData^.Hook > 0;
end;
procedure UnHook; stdcall;
begin UnHookAPI; UnhookWindowsHookEx(DLLData^.Hook); end;
{--- DLL 入口:共享記憶體儲存全域狀態 ---}
procedure MyDLLHandler(Reason: Integer);
var FHandle: THandle;
begin
case Reason of
DLL_PROCESS_ATTACH:
begin
FHandle := CreateFileMapping($FFFFFFFF, nil, PAGE_READWRITE, 0, $FFFF, 'MYDLLDATA');
if FHandle = 0 then FHandle := OpenFileMapping(FILE_MAP_ALL_ACCESS, False, 'MYDLLDATA');
DLLData := MapViewOfFile(FHandle, FILE_MAP_ALL_ACCESS, 0, 0, 0);
end;
DLL_PROCESS_DETACH:
if Assigned(DLLData) then begin UnmapViewOfFile(DLLData); DLLData := nil; end;
end;
end;
exports InstallHook, UnHook, HookProc;
begin DLLProc := @MyDLLHandler; MyDLLHandler(DLL_PROCESS_ATTACH); DLLData^.Hooked := False; end.
unit APIHook;
interface uses SysUtils, Windows, WinSock;
type
TSockProc = function(s: TSocket; var Buf; Len, Flags: Integer): Integer; stdcall;
TJmpCode = packed record
JmpCode: Byte; // $E9 (JMP REL32) 或 $B8 (MOV EAX) + FF E0 (JMP EAX)
Address: TSockProc;
MovEAX: array[0..2] of Byte;
end;
var
OldSend, OldRecv: TSockProc;
JmpCode: TJmpCode;
OldCode: array[0..1] of TJmpCode;
AddSend, AddRecv: Pointer;
ProcessHandle: THandle;
implementation
{--- MySend: 攔截發送 ---}
function MySend(s: TSocket; var Buf; Len, Flags: Integer): Integer; stdcall;
var Dw: Cardinal;
begin
// 處理發送資料 (此處僅 Beep 示意)
MessageBeep(1000);
WriteProcessMemory(ProcessHandle, AddSend, @OldCode[0], 8, Dw); // 恢復原指令
Result := OldSend(s, Buf, Len, Flags); // 呼叫真實 Send
JmpCode.Address := @MySend;
WriteProcessMemory(ProcessHandle, AddSend, @JmpCode, 8, Dw); // 重新 Hook
end;
{--- MyRecv: 攔截接收 ---}
function MyRecv(s: TSocket; var Buf; Len, Flags: Integer): Integer; stdcall;
var Dw: Cardinal;
begin
MessageBeep(1000);
WriteProcessMemory(ProcessHandle, AddRecv, @OldCode[1], 8, Dw);
Result := OldRecv(s, Buf, Len, Flags);
JmpCode.Address := @MyRecv;
WriteProcessMemory(ProcessHandle, AddRecv, @JmpCode, 8, Dw);
end;
{--- 安裝 Hook ---}
procedure HookAPI;
var Dw: Cardinal; H: THandle;
begin
ProcessHandle := GetCurrentProcess;
H := LoadLibrary('ws2_32.dll');
AddSend := GetProcAddress(H, 'send');
AddRecv := GetProcAddress(H, 'recv');
// JMP 短指令模板 (x86)
JmpCode.JmpCode := $B8; // MOV EAX, addr
JmpCode.MovEAX[0] := $FF; JmpCode.MovEAX[1] := $E0; JmpCode.MovEAX[2] := 0; // JMP EAX
ReadProcessMemory(ProcessHandle, AddSend, @OldCode[0], 8, Dw);
JmpCode.Address := @MySend; WriteProcessMemory(ProcessHandle, AddSend, @JmpCode, 8, Dw);
ReadProcessMemory(ProcessHandle, AddRecv, @OldCode[1], 8, Dw);
JmpCode.Address := @MyRecv; WriteProcessMemory(ProcessHandle, AddRecv, @JmpCode, 8, Dw);
OldSend := AddSend; OldRecv := AddRecv;
end;
{--- 卸載 Hook ---}
procedure UnHookAPI;
var Dw: Cardinal;
begin
WriteProcessMemory(ProcessHandle, AddSend, @OldCode[0], 8, Dw);
WriteProcessMemory(ProcessHandle, AddRecv, @OldCode[1], 8, Dw);
end;
end.
type
TInstallHook = function(Wnd: THandle): Boolean; stdcall;
TUnHook = procedure; stdcall;
procedure TForm1.Button1Click(Sender: TObject);
var H: THandle; Wnd: THandle;
begin
Wnd := FindWindow(nil, '目標視窗標題');
if not IsWindow(Wnd) then begin ShowMessage('找不到視窗'); Exit; end;
H := LoadLibrary('Hook.dll');
@InstallHook := GetProcAddress(H, 'InstallHook');
@UnHook := GetProcAddress(H, 'UnHook');
if InstallHook(Wnd) then ShowMessage('Hook 成功');
end;
procedure TForm1.Button2Click(Sender: TObject);
begin UnHook; end;
CreateFileMapping + MapViewOfFile 實現 DLL 多進程實例共享 Hooked 狀態WriteProcessMemory 寫入 8 字節 (x86 JMP 指令長度),執行自訂 → 恢復 → 呼叫原函數 → 重新 Hook此模板展示經典 **DLL 注入 + Inline Hook** 攔截 ws2_32.dll!send/recv。理解訊息 Hook 注入機制、共享記憶體跨進程狀態、JMP 指令覆寫、原子恢復/重 Hook 流程,是 Windows 逆向與安全研究核心技能。實戰務必處理 x86/x64 差異、多執行緒競爭、例外安全、反偵測等工程化細節。