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

Windows 動態中文化核心技術深度解析:「陷阱」技術原理與實作

     編輯:關於C++

四通利方 RichWin、中文之星 CStar 等經典中文化產品,其核心技術被稱為「陷阱」技術——即動態修改 Windows 核心程式碼以實現中文顯示與輸入。本文從 Windows 模組呼叫機制與重定位概念切入,深度剖析此技術原理,並提供示例原始碼。

發現:異常的重定位資訊

使用 Visual C++ 工具 EXEHDR 分析 CStar 的 CHINESE.DLL,發現兩大異常:

  1. 資料區段包含重定位資訊——通常僅程式碼區段才有重定位表。
  2. 重定位目標函數高度集中——全涉及文字輸出(ExtTextOut)、字型建立(CreateFont)、鍵盤轉換(ToAscii、AnsiToOem)、字串比較(lstrcmp、CharUpper)等文字處理 API。

這揭示了中文化必須攔截修改的關鍵函數集合。

Windows 模組呼叫機制與重定位基礎

三大核心模組依賴關係

  • KERNEL:系統核心,不依賴其他模組(Kernel.exe、Krnl286.exe、Krnl386.exe)。
  • GDI:圖形裝置介面,依賴 KERNEL(Gdi.exe)。
  • USER:使用者介面服務,依賴 KERNEL、GDI 及驅動程式(User.exe)。

這三者實為動態連結庫,幾乎所有 API 函數皆藏身其中。

重定位機制

程式載入記憶體前,對 API 的呼叫均為動態連結,無法定址。載入時依據重定位表修正位址。重定位項記錄:目標模組、函數序號、呼叫位置偏移。EXEHDR /v 輸出範例:

PTR 0442 imp GDI.351  ; 本區段 0442h 處呼叫 GDI 第 351 號函數

查詢 GDI 匯出表可知第 351 號為 ExtTextOut。

動態中文化原理:重導向 API 呼叫

  1. 在自身模組重寫涉及文字處理的關鍵函數(如 MyExtTextOut)。
  2. 修改 Windows 核心模組中對原函數的呼叫指令,使其跳轉至自訂函數。
  3. 自訂函數處理中文邏輯後,視需求呼叫原函數或自行返回。

實證驗證

分析 RichWin 運行前後 GDI.EXE 程式碼區段:ExtTextOut 入口處原為 B8 FF 05 45 55,運行 RichWin 後變為 EA 08 08 8F 3D(反組譯為 JMP 3D8F:0808),目標正是 RichWin WSENGINE.DLL 內的重寫函數。退出 RichWin 後自動恢復原碼。

「陷阱」技術關鍵實作步驟

1. 取得目標函數位址

在資料區段定義函數指標,編譯器自動產生重定位項:

FARPROC FarProcFunc = ExtTextOut; // 載入後 FarProcFunc 即為真實位址

2. 突破程式碼區段寫保護

使用未公開 API AllocCStoDSAlias 取得與程式碼區段基址相同的可寫資料區段別名:

WORD FAR PASCAL AllocCStoDSAlias(WORD code_sel);

3. 修改前 5 位元組為遠跳轉指令

*(lpStr + wOffset) = 0xEA;        // JMP FAR 指令碼
*((LPLONG)(lpStr + wOffset + 1)) = (LONG)MyExtTextOut; // 目標位址

4. 恢復原始程式碼

卸載時將保存的原始 5 位元組寫回。

完整示例程式碼

// relocate.c - 動態修改 ExtTextOut 示例
#include <windows.h>
#include <dos.h>

BOOL WINAPI MyExtTextOut(HDC hDC, int x, int y, UINT nInt1, const RECT FAR* lpRect,
                         LPCSTR lpStr, UINT nInt2, int FAR* lpInt);

WORD FAR PASCAL AllocCStoDSAlias(WORD code_sel);

typedef struct {
    FARPROC lpFarProcReplace; // 替代函數位址
    FARPROC lpFarProcWindows; // Windows 原函數位址
    BYTE bOld;                // 原第 1 位元組
    LONG lOld;                // 原後 4 位元組
} FUNC;

FUNC Func = {MyExtTextOut, ExtTextOut};

int PASCAL WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR lpCmd, int nShow) {
    HANDLE hMemCode = HIWORD((LONG)Func.lpFarProcWindows);
    WORD wOffset = LOWORD((LONG)Func.lpFarProcWindows);
    WORD hMemData = AllocCStoDSAlias(hMemCode);
    LPSTR lpStr = (LPSTR)GlobalLock(hMemData);
    LPLONG lpLong = (LPLONG)(lpStr + wOffset + 1);

    // 保存原始碼
    Func.bOld = *(lpStr + wOffset);
    Func.lOld = *lpLong;

    // 修改為 JMP MyExtTextOut
    *(lpStr + wOffset) = 0xEA;
    *lpLong = (LONG)Func.lpFarProcReplace;
    GlobalUnlock(hMemData);
    MessageBox(NULL, "已改為自訂函數", "提示", MB_OK);

    // 恢復原始碼
    hMemData = AllocCStoDSAlias(hMemCode);
    lpStr = (LPSTR)GlobalLock(hMemData);
    lpLong = (LPLONG)(lpStr + wOffset + 1);
    *(lpStr + wOffset) = Func.bOld;
    *lpLong = Func.lOld;
    GlobalUnlock(hMemData);
    MessageBox(NULL, "已恢復 Windows 原函數", "提示", MB_OK);
    return 1;
}

// 自訂 ExtTextOut:在螢幕繪製點陣字元示意
BOOL WINAPI MyExtTextOut(HDC hDC, int x, int y, UINT nInt1, const RECT FAR* lpRect,
                         LPCSTR lpStr, UINT nInt2, int FAR* lpInt) {
    // ... 繪製簡單點陣圖形略 ...
    return TRUE;
}

模組定義檔

NAME RELOCATE
EXETYPE WINDOWS
CODE PRELOAD MOVEABLE DISCARDABLE
DATA PRELOAD MOVEABLE MULTIPLE
HEAPSIZE 1024
EXPORTS

結語與延伸應用

「陷阱」技術實現了任意 Windows 函數呼叫的動態重導向,不僅是中文化的基石,亦可延伸至:多語系動態切換、不同內碼制式(Big5/GB/Unicode)即時轉換顯示、API 監控與攔截工具開發等領域。雖然現代 Windows (NT 核心) 已透過 Unicode 原生支援多語言,但此技術在系統級 Hook、熱修補、安全防護等領域仍具參考價值。

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