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

深度解析:動態漢化 Windows 的「陷阱」技術原理

     編輯:VC 教學

前言:尋找漢化核心的關鍵

在 Windows 早期發展歷程中(從 2.0 到 95/NT),國產漢化系統如長城、長青、DBWin、中文之星(CStar)與四通利方(RichWin)層出不窮。其中以 RichWin 影響力最大、技術最成熟,而 CStar 與其同源,核心技術高度相似。兩者對外宣稱的核心優勢在於獨特的「陷阱」技術——即動態修改 Windows 核心代碼以實現漢化支援。本文將從模組調用機制與重定位概念切入,深入剖析這一技術的實現原理,並提供示例源碼說明如何動態攔截並修改系統 API 行為。

一、工具發現:異常的導入表與重定位項

使用 Visual C++ 附帶的 EXEHDR 工具檢視 NE 格式的核心漢化模組(如 WSENGINE.DLL 或 CHINESE.DLL),以 CStar 1.20 為例,會發現兩個顯著異常點:

  1. 龐大的導入函數表:該 DLL 導入了大量 GDI、USER、KERNEL、KEYBOARD、DISPLAY 等核心模組的關鍵函數,如 ExtTextOut、CreateFont、GetCharWidth、GetMessage、IsDBCSLeadByte 等,幾乎覆蓋了所有文字輸出、字體管理、鍵盤輸入與系統訊息處理的關鍵節點。
  2. 極少的重定位項:輸出顯示僅有 35 relocations,遠低於同等規模的正常 DLL。這意味著該模組幾乎不依賴系統載入器進行地址修正,而是採用了自行管理代碼地址的機制——這正是「陷阱」技術的核心跡象。

二、原理剖析:模組調用與 IAT 重定向

1. Windows 模組調用機制回顧

在 16 位 Windows(及 9x 兼容層)中,可執行文件採用 NE 格式。模組間調用通過「輸入表」實現:調用者不直接包含被調用代碼的絕對地址,而是通過一個索引查找輸入表,由系統載入器在載入時填入目標函數的實際入口地址。這一機制為運行時動態修改調用目標提供了可能。

2. 「陷阱」技術的實現邏輯

所謂「陷阱」,本質上是在內存中修改目標模組的輸入地址表(IAT)或直接修改代碼段的調用指令,將原本指向系統原生 API(如 GDI.ExtTextOut)的調用重定向到漢化模組自定義的處理函數。具體步驟如下:

  • 定位目標模組:遍歷系統模組列表,找到 GDI.EXE、USER.EXE 等核心模組的基地址。
  • 解析輸入表:解析目標模組的 NE 頭部,找到輸入表位置與結構。
  • 修改入口地址:將需攔截的 API 對應的輸入表條目修改為漢化模組內部處理函數的地址(需確保代碼段可寫,或通過 VirtualProtect 修改頁屬性)。
  • 實現處理函數:在漢化模組中實現同樣簽名的函數,執行漢化邏輯(如字體替換、雙字節字符處理、輸入法協調等),最後可選擇調用原 API(需保存原地址)或完全替換行為。

三、關鍵難點與解決方案

1. 代碼段寫保護繞過

系統核心模組代碼段通常屬性為只讀/執行。修改 IAT 需臨時修改頁屬性為可寫(PAGE_EXECUTE_READWRITE),修改後恢復。在 16 位環境下可通過 PrestoChangoSelector 或直接操作描述符表實現;32 位下使用 VirtualProtect。

2. 16/32 位混合調用的兼容

Windows 9x 內部大量 32 位 API 通過 Thunk 層調用 16 位實現。漢化模組需同時攔截兩層入口,或直接攔截 16 位核心入口以覆蓋所有上層調用。這要求處理函數需支持混合調用約定(如 PASCAL / CDECL)與參數轉換。

3. 重定位項極少的秘密

正常 DLL 載入時需大量重定位修正內部絕對地址引用。漢化模組通過純相對寻址、避免全局變量、關鍵地址運行時動態計算等手法,將重定位需求降至最低,確保在任意基地址載入時無需系統修正即可正確運行,這對於注入至任意進程空間至關重要。

四、示例:動態修改 ExtTextOut 入口

// 核心邏輯示意(簡化版,針對 16 位 NE 格式)
typedef struct _IMPORT_ENTRY {
    WORD  offset;   // 模組內調用指令偏移
    WORD  module;   // 目標模組索引
    WORD  proc;     // 目標函數序號/名稱索引
} IMPORT_ENTRY;

BOOL InstallTrap(HMODULE hTargetMod, LPCSTR lpApiName, FARPROC pNewProc) {
    // 1. 獲取目標模組 NE 頭部
    NE_HEADER* pNe = (NE_HEADER*)hTargetMod;
    IMPORT_ENTRY* pImportTable = (IMPORT_ENTRY*)((BYTE*)hTargetMod + pNe->ne_enttab);
    int nEntries = pNe->ne_centtab / sizeof(IMPORT_ENTRY); // 簡化計算

    // 2. 遍歷導入表查找目標 API
    for (int i = 0; i < nEntries; i++) {
        if (MatchImportName(pImportTable[i], lpApiName)) {
            // 3. 修改頁屬性可寫
            DWORD oldProtect;
            VirtualProtect(&pImportTable[i].proc, sizeof(WORD), PAGE_READWRITE, &oldProtect);
            
            // 4. 保存原地址(可存入全局表供鏈式調用)
            g_OriginalProcs[lpApiName] = (FARPROC)pImportTable[i].proc;
            
            // 5. 寫入新函數地址(需轉換為目標模組上下文的調用門地址)
            pImportTable[i].proc = (WORD)GetThunkAddress(pNewProc);
            
            // 6. 恢復頁屬性
            VirtualProtect(&pImportTable[i].proc, sizeof(WORD), oldProtect, &oldProtect);
            return TRUE;
        }
    }
    return FALSE;
}

// 漢化處理函數示例
int FAR PASCAL HookedExtTextOut(HDC hdc, int x, int y, UINT fuOptions,
    CONST RECT* lprc, LPCSTR lpString, int cbCount, CONST INT* lpDx) {
    // 1. 雙字節字符串檢測與分割
    // 2. 字體關聯與替換邏輯
    // 3. 調用原始 ExtTextOut 繪製
    return g_OriginalExtTextOut(hdc, x, y, fuOptions, lprc, lpString, cbCount, lpDx);
}

五、總結與啟示

「陷阱」技術展示了早期 Windows 系統開放性極高、邊界模糊的特點:應用層代碼可直接重塑系統核心行為。雖然現代 Windows(NT 內核)通過內核模式簽名、PatchGuard、CFG 等機制徹底封堵了此類用戶態修改,但其核心思想——運行時二進制重寫、熱補丁、API Hooking——仍廣泛應用於調試器、性能分析器、安全防護軟件及兼容層(如 Wine)中。理解這一歷史技術,有助於掌握現代 Hook 框架(Detours, MinHook, Frida)的底層邏輯與演變脈絡。

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