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

PE 資源結構深度解析:Resource Directory、Directory Entry、Data Entry 完整格式與 Delphi 記錄宣告

     編輯:Delphi

概述

PE 檔案資源區 (`.rsrc` section) 採三層樹狀結構:**Type → Name/ID → Language**。本文依照 Microsoft PE/COFF 規格,詳細拆解各層資料結構,並給出對應 Delphi `packed record` 宣告,便於開發資源檢視/編輯工具。

層級 1:Resource Directory Table (頂層/子目錄通用)

// 對應 IMAGE_RESOURCE_DIRECTORY
PIMAGE_RESOURCE_DIRECTORY = ^IMAGE_RESOURCE_DIRECTORY;
IMAGE_RESOURCE_DIRECTORY = packed record
  Characteristics: DWORD;       // 保留,通常 0
  TimeDateStamp: DWORD;         // 資源建立時間戳
  MajorVersion: WORD;           // 主版本
  MinorVersion: WORD;           // 次版本
  NumberOfNamedEntries: WORD;   // 以字串命名的項目數
  NumberOfIdEntries: WORD;      // 以數字 ID 的項目數
  // 緊接著 NumberOfNamedEntries + NumberOfIdEntries 個 IMAGE_RESOURCE_DIRECTORY_ENTRY
end;

層級 2:Resource Directory Entry (目錄項目)

// 對應 IMAGE_RESOURCE_DIRECTORY_ENTRY
PIMAGE_RESOURCE_DIRECTORY_ENTRY = ^IMAGE_RESOURCE_DIRECTORY_ENTRY;
IMAGE_RESOURCE_DIRECTORY_ENTRY = packed record
  // Name/ID 聯合體:高位元判斷類型
  NameOrId: DWORD;              // 高位=1 → Name (RVA);高位=0 → ID (低 16 位)
  // Offset/Subdirectory 聯合體
  OffsetToData: DWORD;          // 高位=1 → 子目錄 RVA (Mask $7FFFFFFF);高位=0 → Data Entry RVA
end;

輔助函數:

const
  IMAGE_RESOURCE_NAME_IS_STRING = $80000000;
  IMAGE_RESOURCE_DATA_IS_DIRECTORY = $80000000;
  IMAGE_OFFSET_STRIP_HIGH = $7FFFFFFF;

function IsStringName(Entry: IMAGE_RESOURCE_DIRECTORY_ENTRY): Boolean;
begin
  Result := (Entry.NameOrId and IMAGE_RESOURCE_NAME_IS_STRING) <> 0;
end;

function GetId(Entry: IMAGE_RESOURCE_DIRECTORY_ENTRY): Word;
begin
  Result := Entry.NameOrId and $FFFF;
end;

function GetNameRva(Entry: IMAGE_RESOURCE_DIRECTORY_ENTRY): DWORD;
begin
  Result := Entry.NameOrId and IMAGE_OFFSET_STRIP_HIGH;
end;

function IsSubdirectory(Entry: IMAGE_RESOURCE_DIRECTORY_ENTRY): Boolean;
begin
  Result := (Entry.OffsetToData and IMAGE_RESOURCE_DATA_IS_DIRECTORY) <> 0;
end;

function GetDataRvaOrSubdirRva(Entry: IMAGE_RESOURCE_DIRECTORY_ENTRY): DWORD;
begin
  Result := Entry.OffsetToData and IMAGE_OFFSET_STRIP_HIGH;
end;

資源類型 ID 對照表 (根目錄 ID 含義)

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

層級 3:Resource Data Entry (資料實體)

// 對應 IMAGE_RESOURCE_DATA_ENTRY
PIMAGE_RESOURCE_DATA_ENTRY = ^IMAGE_RESOURCE_DATA_ENTRY;
IMAGE_RESOURCE_DATA_ENTRY = packed record
  OffsetToData: DWORD;  // 資料 RVA (相對 ImageBase)
  Size: DWORD;          // 資料大小 (位元組)
  CodePage: DWORD;      // 碼頁 (通常 0 或 1252)
  Reserved: DWORD;      // 保留,必須 0
end;

字串名稱儲存格式 (Name String)

// 緊接在 Directory Entry 之後 (RVA 指向此處)
PIMAGE_RESOURCE_DIR_STRING_U = ^IMAGE_RESOURCE_DIR_STRING_U;
IMAGE_RESOURCE_DIR_STRING_U = packed record
  Length: WORD;                 // 字元數 (不含結束符)
  NameString: array[0..0] of WideChar; // Unicode 字串
end;

遍歷演算法偽代碼

procedure EnumResources(DirRVA: DWORD; Level: Integer);
var
  Dir: PIMAGE_RESOURCE_DIRECTORY;
  Entry: PIMAGE_RESOURCE_DIRECTORY_ENTRY;
  I: Integer;
  Name: string;
begin
  Dir := PIMAGE_RESOURCE_DIRECTORY(ImageBase + DirRVA);
  Entry := PIMAGE_RESOURCE_DIRECTORY_ENTRY(Dir + 1); // 緊接在結構後
  for I := 0 to Dir.NumberOfNamedEntries + Dir.NumberOfIdEntries - 1 do
  begin
    if IsStringName(Entry^) then
      Name := WideCharToString(PWideChar(ImageBase + GetNameRva(Entry^)) + 2) // 跳過 Length WORD
    else
      Name := '#' + IntToStr(GetId(Entry^));

    if IsSubdirectory(Entry^) then
      EnumResources(GetDataRvaOrSubdirRva(Entry^), Level + 1)
    else
    begin
      // 到達 Data Entry
      DataEntry := PIMAGE_RESOURCE_DATA_ENTRY(ImageBase + GetDataRvaOrSubdirRva(Entry^));
      // DataEntry.OffsetToData + ImageBase = 實際資料位址
      // DataEntry.Size = 大小
      ProcessResource(Name, Level, DataEntry);
    end;
    Inc(Entry);
  end;
end;

實務提醒

  • 資源區可能不在 `.rsrc` section (惡意軟體常見),建議掃描所有 section 找 `IMAGE_RESOURCE_DIRECTORY` 特徵。
  • Delphi 專案資源 (*.res) 編譯後即嵌入此結構,可用 FindResource/LoadResource/LockResource API 存取,無需手工解析。
  • 64 位 PE (PE32+) 結構相同,僅 ImageBase、RVA 為 64 位;Delphi 64 位編譯器下 `DWORD` 仍為 32 位,`Pointer`/`NativeUInt` 為 64 位,注意類型轉換。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved