公司禁止使用 QQ、MSN 等聊天軟體時,員工常感困擾。利用 Windows CBT (Computer-Based Training) 鉤子,可在聊天視窗建立/啟動時攔截並修改其標題列文字(如將「與 XX 聊天中」改為「我的文件」),配合圖示替換,達到視覺偽裝效果。本文示範完整的 DLL 鉤子實作與呼叫方式。
wParam 為視窗控制代碼#32770 為標準對話框類別)與標題關鍵字,確認為目標聊天視窗後呼叫 SetWindowText 修改標題library HookPrj;
uses
SysUtils, Classes, QQTitleHook in 'QQTitleHook.pas';
exports
EnableWheelHook, DisableWheelHook;
begin
hkQQChat := 0;
end.
unit QQTitleHook;
interface
uses Windows, Messages, SysUtils, Dialogs, CommCtrl, StrUtils;
var
hkQQChat: HHOOK; // 鉤子控制代碼
hwQQChat: HWND; // 聊天視窗控制代碼
tlQQChat: string; // 原始標題
clsName: string; // 視窗類別名
buf: array[0..1023] of Char;
const
csQQ = '#32770'; // QQ 聊天視窗類別名
function TitleHookProc(Code: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
function EnumWindowsTitleFunc(Handle: THandle; lParam: LPARAM): Boolean; stdcall;
function EnableWheelHook: Boolean; stdcall; export;
function DisableWheelHook: Boolean; stdcall; export;
implementation
// 鉤子處理函數
function TitleHookProc(Code: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
begin
Result := 0;
if Code < 0 then begin
Result := CallNextHookEx(hkQQChat, Code, wParam, lParam);
Exit;
end
else if Code = HCBT_ACTIVATE then begin
hwQQChat := HWND(wParam);
GetClassName(hwQQChat, buf, 1024);
clsName := string(buf);
if clsName = csQQ then
EnumWindows(@EnumWindowsTitleFunc, hwQQChat); // 進一步篩選
end;
end;
// 列舉視窗回呼:確認標題關鍵字並修改
function EnumWindowsTitleFunc(Handle: THandle; lParam: LPARAM): Boolean; stdcall;
begin
Result := True;
if (Handle = lParam) and (GetWindowText(Handle, buf, 256) > 0) then begin
tlQQChat := string(buf);
// QQ: "與XXX聊天中" -> "我的文件"
if (Pos('與', tlQQChat) > 0) and (Pos('聊天中', tlQQChat) > 0) then begin
tlQQChat := AnsiReplaceStr(tlQQChat, '與', '我的文件');
tlQQChat := AnsiReplaceStr(tlQQChat, '聊天中', '');
SetWindowText(Handle, PChar(tlQQChat));
end;
// QQ 群: "群 -XXX" / "高級群 -XXX" -> "我的文件"
if (Pos('群 -', tlQQChat) > 0) or (Pos('高級群 -', tlQQChat) > 0) then begin
tlQQChat := AnsiReplaceStr(tlQQChat, '群 -', '我的文件');
tlQQChat := AnsiReplaceStr(tlQQChat, '高級', '');
SetWindowText(Handle, PChar(tlQQChat));
end;
// MSN: "XXX - 對話" -> "我的文件"
if Pos(' - 對話', tlQQChat) > 0 then begin
tlQQChat := AnsiReplaceStr(tlQQChat, ' - 對話', '我的文件');
SetWindowText(Handle, PChar(tlQQChat));
end;
end;
end;
// 安裝鉤子
function EnableWheelHook: Boolean; stdcall;
begin
if hkQQChat = 0 then begin
hkQQChat := SetWindowsHookEx(WH_CBT, @TitleHookProc, HInstance, 0); // 0 = 全系統鉤子
Result := True;
end else Result := False;
end;
// 卸載鉤子
function DisableWheelHook: Boolean; stdcall;
begin
if hkQQChat <> 0 then begin
UnhookWindowsHookEx(hkQQChat);
hkQQChat := 0;
Result := True;
end else Result := False;
end;
end.
procedure TForm1.btnInstallHookClick(Sender: TObject);
begin
if EnableWheelHook then begin
Hide; // 隱藏主視窗,駐留背景
ShowMessage('鉤子已安裝,QQ/MSN 視窗標題將被自動修改');
end;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
DisableWheelHook; // 退出時務必卸載
end;
| 面向 | 說明 |
|---|---|
| 效能考量 | #32770 類別視窗極多,EnumWindows 遍歷開銷大。建議增加額外判斷條件(如視窗尺寸、特定子控制項、進程名稱)縮小範圍 |
| 相容性 | QQ/MSN 版本更新可能變更類別名或標題格式,需持續維護特徵碼 |
| 安全性 | 全系統 CBT 鉤子屬高風險操作,殺毒軟體可能攔截;簽名驗證、數位簽章可降低誤報 |
| 法律/道德 | 繞過公司政策可能違反僱傭合約;本技術僅供學習 Windows 鉤子機制,請勿用於規避合法監管 |
| 現代 Windows | Vista+ 引入 UIPI (User Interface Privilege Isolation),低完整性進程無法鉤住高完整性進程視窗,需提升權限或使用其他 IPC 機制 |
SendMessage(WM_SETICON) 或資源修改工具替換視窗圖示,強化偽裝HCBT_CREATEWND 可在視窗建立早期植入子類化,監控聊天內容(涉隱私合規風險極高)Windows 鉤子機制威力強大,但「能力越大,責任越大」。掌握 WH_CBT 等鉤子類型的運作細節,有助於開發視窗管理、自動化測試、無障礙輔助等合法實用工具。