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

PE 資源區結構深度研究:資源目錄樹與資料項解析

     編輯:VC 教學

前言

PE 檔案中的資源區(.rsrc)採用三層樹狀結構組織:**類型 → 名稱/ID → 語言**,每層皆為 IMAGE_RESOURCE_DIRECTORY 節點,葉節點指向實際資料 IMAGE_RESOURCE_DATA_ENTRY。本文詳細解析資源目錄佈局、關鍵結構體、遍歷算法與手動修改資源實戰技巧。

資源區整體佈局

.rsrc 區段
+--------------------------+
| Resource Directory (根)   |  IMAGE_RESOURCE_DIRECTORY
+--------------------------+
| Type Directory Entries   |  IMAGE_RESOURCE_DIRECTORY_ENTRY[] (類型層)
+--------------------------+
| Name/ID Directory        |  IMAGE_RESOURCE_DIRECTORY (名稱/ID層)
+--------------------------+
| Language Directory       |  IMAGE_RESOURCE_DIRECTORY (語言層)
+--------------------------+
| Resource Data Entries    |  IMAGE_RESOURCE_DATA_ENTRY[] (葉節點)
+--------------------------+
| Raw Resource Data        |  位元組資料 (圖示、位圖、字串表、版本資訊等)
+--------------------------+

核心結構體定義

1. 資源目錄表頭


typedef struct _IMAGE_RESOURCE_DIRECTORY {
    DWORD Characteristics;       // 保留,通常 0
    DWORD TimeDateStamp;         // 建立時間
    WORD  MajorVersion;
    WORD  MinorVersion;
    WORD  NumberOfNamedEntries;  // 以名稱索引的項目數
    WORD  NumberOfIdEntries;     // 以 ID 索引的項目數
    // 緊接著 IMAGE_RESOURCE_DIRECTORY_ENTRY[NumberOfNamedEntries + NumberOfIdEntries]
} IMAGE_RESOURCE_DIRECTORY;

2. 資源目錄項


typedef struct _IMAGE_RESOURCE_DIRECTORY_ENTRY {
    union {
        DWORD NameOffset;        // 高位 0:字串名稱 (指向 IMAGE_RESOURCE_DIR_STRING_U)
        DWORD Id;                // 高位 1:整數 ID
    };
    union {
        DWORD OffsetToData;      // 高位 0:指向資料項 (IMAGE_RESOURCE_DATA_ENTRY)
        DWORD OffsetToDirectory; // 高位 1:指向下一層目錄 (IMAGE_RESOURCE_DIRECTORY)
    };
} IMAGE_RESOURCE_DIRECTORY_ENTRY;

關鍵位操作:

  • NameOffset & 0x80000000 → 1=ID, 0=字串名稱
  • OffsetToData & 0x80000000 → 1=子目錄, 0=資料項

3. 資源資料項


typedef struct _IMAGE_RESOURCE_DATA_ENTRY {
    DWORD OffsetToData;   // 資料相對 .rsrc 區段起始的 RVA
    DWORD Size;           // 資料大小
    DWORD CodePage;       // 代碼頁 (0=Unicode/CP_ACP)
    DWORD Reserved;       // 保留
} IMAGE_RESOURCE_DATA_ENTRY;

4. 字串名稱結構


typedef struct _IMAGE_RESOURCE_DIR_STRING_U {
    WORD  Length;         // 字元數 (非位元組)
    WCHAR NameString[1];  // 變長 Unicode 字串
} IMAGE_RESOURCE_DIR_STRING_U;

資源類型預定義常數

ID常數說明
1RT_CURSOR游標
2RT_BITMAP位圖
3RT_ICON圖示 (單一幀)
4RT_MENU選單
5RT_DIALOG對話框模板
6RT_STRING字串表
7RT_FONTDIR字型目錄
8RT_FONT字型
9RT_ACCELERATOR加速鍵表
10RT_RCDATA自訂原始資料
11RT_MESSAGETABLE訊息表
12RT_GROUP_CURSOR游標組
14RT_GROUP_ICON圖示組 (含多尺寸/色深)
16RT_VERSION版本資訊 (VS_VERSIONINFO)
17RT_DLGINCLUDE對話框包含
19RT_PLUGPLAYPlug&Play
20RT_VXDVxD
21RT_ANICURSOR動畫游標
22RT_ANIICON動畫圖示
23RT_HTMLHTML 資源
24RT_MANIFEST應用程式資訊清單

遍歷資源樹算法


void EnumResources(PBYTE rsrcBase, PIMAGE_RESOURCE_DIRECTORY dir, int level = 0)
{
    WORD named = dir->NumberOfNamedEntries;
    WORD id    = dir->NumberOfIdEntries;
    PIMAGE_RESOURCE_DIRECTORY_ENTRY entry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(dir + 1);

    for (int i = 0; i < named + id; i++, entry++)
    {
        // 解析類型/名稱/ID
        BOOL isName = (entry->NameOffset & 0x80000000) == 0;
        LPSTR typeName;
        if (isName)
        {
            PIMAGE_RESOURCE_DIR_STRING_U str = (PIMAGE_RESOURCE_DIR_STRING_U)(rsrcBase + entry->NameOffset);
            typeName = WideCharToAnsi(str->NameString, str->Length);
        }
        else
        {
            typeName = IntToStr(entry->Id);
        }

        // 判斷是子目錄還是資料項
        if (entry->OffsetToData & 0x80000000)
        {
            // 子目錄:遞歸
            PIMAGE_RESOURCE_DIRECTORY subDir = (PIMAGE_RESOURCE_DIRECTORY)(rsrcBase + (entry->OffsetToData & 0x7FFFFFFF));
            EnumResources(rsrcBase, subDir, level + 1);
        }
        else
        {
            // 資料項:葉節點
            PIMAGE_RESOURCE_DATA_ENTRY dataEntry = (PIMAGE_RESOURCE_DATA_ENTRY)(rsrcBase + entry->OffsetToData);
            PBYTE data = rsrcBase + dataEntry->OffsetToData;
            DWORD size = dataEntry->Size;
            // 此處可讀取/修改/導出資料
        }
    }
}

手動新增/替換資源實戰步驟

  1. 定位 .rsrc 區段(DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE])。
  2. 解析根目錄,找到或建立目標類型節點(如 RT_BITMAP)。
  3. 在類型節點下找到或建立名稱/ID 節點。
  4. 在名稱/ID 節點下找到或建立語言節點(預設 0x0409 en-US 或 0x0804 zh-CN)。
  5. 在語言節點下新增 IMAGE_RESOURCE_DATA_ENTRY,指向資料區。
  6. 將資料寫入區段尾端,更新 SizeOfRawData、VirtualSize、對齊。
  7. 修正所有受影響目錄的 NumberOfNamedEntries/IdEntries 計數。
  8. 若區段空間不足,需擴展區段或新增區段(涉及 PE 結構重組)。

常見工具與程式庫

  • Resource Hacker:GUI 查看/修改/導出/導入資源
  • ResEdit:開源資源編輯器
  • Windows API:BeginUpdateResource/UpdateResource/EndUpdateResource(官方標準方式)
  • libpeconv / pe-parse / LIEF:程式化操作
  • Python + pefile:腳本自動化 pe.write()

版本資源 (VS_VERSIONINFO) 結構

資源類型 RT_VERSION (16),ID 通常為 1,資料為結構化二進位:

VS_VERSIONINFO
  - FixedFileInfo (結構化版本號、檔案旗標、OS、類型等)
  - StringFileInfo
      - StringTable (語言/代碼頁)
          - CompanyName, FileDescription, FileVersion, InternalName, LegalCopyright, OriginalFilename, ProductName, ProductVersion 等
  - VarFileInfo
      - Translation (語言/代碼頁陣列)

修改版本資訊建議使用 VerSetResourceFile 系列 API 或 UpdateResource 替換整個 RT_VERSION 資源。

資源區修改風險與防護

風險後果緩解
區段空間不足覆蓋其他區段、檔案損壞檢查剩餘空間,必要時擴展區段或新增區段
對齊錯誤載入失敗、資料截斷嚴格遵循 FileAlignment (通常 0x200) 對齊
目錄計數不一致資源枚舉器漏讀/誤讀修改後重新計算所有層級 NumberOfNamedEntries/IdEntries
數位簽名失效Authenticode 驗證失敗修改後重新簽名

深入掌握 PE 資源目錄樹結構,是實現資源編輯器、殼程式圖示同步、版本資訊自動化、惡意軟體資源分析等進階技術的鑰匙。

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