iT邦幫忙

2023 iThome 鐵人賽

DAY 2
0
Security

Windows Security 101系列 第 2

[Day2] Demystify PE Format

  • 分享至 

  • xImage
  •  

今天要來介紹的是 Windows 作業系統的執行檔 PE,理解 PE Format 可以讓我們不論是分析惡意程式或是逆向工程都可以快速地對分析目標有初步的認識,像是我們可以從 PE Format 判斷這隻 binary 是 32 bits 或 64 bits,也能判斷這隻程式是 DLL 或 EXE。我也會直接以實際的案例來分享 PE Format 可以在哪些地方被使用。

從 hexdump 可以將整個 PE File 分成這四大區塊,分別是 DOS Header、NT Header、Section Header 和 Directories:
https://ithelp.ithome.com.tw/upload/images/20230916/20120098CUWsnRnadr.png

DOS Header

DOS Header 是根據 _IMAGE_DOS_HEADER 這個 struct 定義的

typedef struct _IMAGE_DOS_HEADER
{
     WORD e_magic; // 以 MZ 為開頭
     WORD e_cblp;
     WORD e_cp;
     WORD e_crlc;
     WORD e_cparhdr;
     WORD e_minalloc;
     WORD e_maxalloc;
     WORD e_ss;
     WORD e_sp;
     WORD e_csum;
     WORD e_ip;
     WORD e_cs;
     WORD e_lfarlc;
     WORD e_ovno;
     WORD e_res[4];
     WORD e_oemid;
     WORD e_oeminfo;
     WORD e_res2[10];
     LONG e_lfanew; // 標示 NT Header 在檔案的起始位址
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

惡意程式分析時很常會以 e_magic 的 MZ 字串來判斷程式中是否有夾帶 PE File

NT Header

NT Header 則是根據 _IMAGE_DOS_HEADER 這個 struct 定義的

typedef struct _IMAGE_NT_HEADERS {
  DWORD                   Signature; // "PE/0/0"
  IMAGE_FILE_HEADER       FileHeader;
  IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

其中還包含了 File Header 和 Optional Header 這兩大塊結構:

typedef struct _IMAGE_FILE_HEADER {
  WORD  Machine; // PE可執行的架構,e.g., I386, IA64, AMD64
  WORD  NumberOfSections; // section數量
  DWORD TimeDateStamp;
  DWORD PointerToSymbolTable;
  DWORD NumberOfSymbols;
  WORD  SizeOfOptionalHeader; // Optional Header 的大小
  WORD  Characteristics; // 該 PE 是哪種類型,e.g., system file, DLL, ...
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
typedef struct _IMAGE_OPTIONAL_HEADER {
  WORD                 Magic;
  BYTE                 MajorLinkerVersion;
  BYTE                 MinorLinkerVersion;
  DWORD                SizeOfCode; // .text 段的大小
  DWORD                SizeOfInitializedData;
  DWORD                SizeOfUninitializedData;
  DWORD                AddressOfEntryPoint; // PE 被載入後開始執行的位址,需要加上 image base 才會是正確的 memory address
  DWORD                BaseOfCode; // .text 段的位址
  DWORD                BaseOfData; // .data 段的位址
  DWORD                ImageBase; // PE 被載入時的 memory address
  DWORD                SectionAlignment; // section 要對齊的大小
  DWORD                FileAlignment; 
  WORD                 MajorOperatingSystemVersion;
  WORD                 MinorOperatingSystemVersion;
  WORD                 MajorImageVersion;
  WORD                 MinorImageVersion;
  WORD                 MajorSubsystemVersion;
  WORD                 MinorSubsystemVersion;
  DWORD                Win32VersionValue;
  DWORD                SizeOfImage; // PE 被載入後的記憶體大小,也必須是 SectionAlignment 的倍數
  DWORD                SizeOfHeaders; // 所有 header 的大小 
  DWORD                CheckSum;
  WORD                 Subsystem; // PE 會需要哪種 subsystem 啟動
  WORD                 DllCharacteristics; // PE 啟用哪些(部分)保護也會記錄在這,e.g., DEP, SEH
  DWORD                SizeOfStackReserve;
  DWORD                SizeOfStackCommit;
  DWORD                SizeOfHeapReserve;
  DWORD                SizeOfHeapCommit;
  DWORD                LoaderFlags;
  DWORD                NumberOfRvaAndSizes;
  IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; // 記錄各式各樣的資訊,e.g., ImportAddressTable, ExportAddressTable, ResourceTable 等的位址、大小等資訊 
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

實際上的 Data Directories 會是這樣:
https://ithelp.ithome.com.tw/upload/images/20230916/20120098S3GKVkDeAe.png

記錄了非常多的資訊,最常用到的會是 Export Directory、Import Directory 和 Resource Directory 的 RVA 和 Size,可以用這些資訊分別定位到 ExportAddressTable、ImportAddressTable 和 ResourceTable,這些在靜態分析程式行為的時候非常有用

Section Headers

Section Headers 則是存放了這支 PE File 的程式內容,包含 code section (.text) 和 data section (.data)

typedef struct _IMAGE_SECTION_HEADER {
  BYTE  Name[IMAGE_SIZEOF_SHORT_NAME];
  union {
    DWORD PhysicalAddress;
    DWORD VirtualSize; // 載入後的大小
  } Misc;
  DWORD VirtualAddress; // 加上 image base 後,為載入後的 memory address
  DWORD SizeOfRawData; // 未載入前的大小
  DWORD PointerToRawData; // 在 PE File 中的位置
  DWORD PointerToRelocations;
  DWORD PointerToLinenumbers;
  WORD  NumberOfRelocations;
  WORD  NumberOfLinenumbers;
  DWORD Characteristics; // 載入後 page 的屬性
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

Section Headers 的擺放方式會跟 Data Directories 一樣是採用 array 的方式
https://ithelp.ithome.com.tw/upload/images/20230916/20120098DZhzPQPt04.png

大致介紹完 header 後,來看看實際上 PE 在 CFF Explorer 會是怎麼被解析的
https://ithelp.ithome.com.tw/upload/images/20230916/201200981ldpUOfMTY.png

可以看到 header 和 directory 是分開的,實際上 directory 沒有明確的擺放位址,只能根據 header 去查找,因此除了 header 本身也較為明確的規定外,PE Format 有許多部分是未被明確定義,這也導致在解析 PE File 時需要注意不同的情況。

下一篇,我將運用剛剛介紹的 PE Format,來分析如何製作最小的 PE File!


上一篇
[Day1] Overview & Tools
下一篇
[Day3] The Smallest PE File
系列文
Windows Security 10130
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言