Một vài ví dụ của định dạng PE là .exe
, .dll
, .sys
và .scr
.
Note
Các file có thể thực thi chẳng hạn như
.exe
hoặc.dll
có thể được gọi là các image.
PE Structure
Cấu trúc tổng quan của định dạng PE:
Có thể thấy, định dạng PE bao gồm các header, các data directory và các section.
DOS Header (IMAGE_DOS_HEADER
)
Header đầu tiên của file PE luôn bắt đầu bằng hai byte là 0x4D
và 0x5A
(ở dạng ASCII là MZ
). Hai byte này chính là signature của DOS header.
Cấu trúc của DOS header được định nghĩa như sau:
typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header
WORD e_magic; // Magic number
WORD e_cblp; // Bytes on last page of file
WORD e_cp; // Pages in file
WORD e_crlc; // Relocations
WORD e_cparhdr; // Size of header in paragraphs
WORD e_minalloc; // Minimum extra paragraphs needed
WORD e_maxalloc; // Maximum extra paragraphs needed
WORD e_ss; // Initial (relative) SS value
WORD e_sp; // Initial SP value
WORD e_csum; // Checksum
WORD e_ip; // Initial IP value
WORD e_cs; // Initial (relative) CS value
WORD e_lfarlc; // File address of relocation table
WORD e_ovno; // Overlay number
WORD e_res[4]; // Reserved words
WORD e_oemid; // OEM identifier (for e_oeminfo)
WORD e_oeminfo; // OEM information; e_oemid specific
WORD e_res2[10]; // Reserved words
LONG e_lfanew; // Offset to the NT header
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
Hai thành phần quan trọng là:
e_magic
: signature bytese_lfanew
: offset đến NT header.
DOS Stub
Là một đoạn chương trình in ra thông điệp “This program cannot be run in DOS mode” trong trường hợp chương trình được nạp ở chế độ DOS (hay “Disk-based Operating System”). Thông điệp này có thể thay đổi bởi lập trình viên trong quá trình biên dịch. DOS stub không phải là một header.
NT Header (IMAGE_NT_HEADERS
)
Là một header quan trọng vì nó là sự kết hợp của hai loại header khác: FileHeader
và OptionalHeader
cũng như là chứa một lượng lớn thông tin về file PE. Tương tự với DOS
header, NT header cũng có signature bytes là chuỗi “PE” (0x50
và 0x45
). Tuy nhiên, do signature có kiểu là DWORD nên nó được đệm thêm bằng 2 byte rỗng: 0x50450000
.
NT header có thể được truy cập thông qua thành phần e_lfanew
của DOS header.
Cấu trúc của NT header thay đổi tùy thuộc vào từng loại kiến trúc máy tính:
Phiên bản 32-bit:
typedef struct _IMAGE_NT_HEADERS {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
Phiên bản 64-bit:
typedef struct _IMAGE_NT_HEADERS64 {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER64 OptionalHeader;
} IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64;
Có thể thấy, hai phiên bản này khác nhau ở OptionalHeader
.
File Header (IMAGE_FILE_HEADER
)
Có thể được truy cập thông qua thành phần FileHeader
của NT header và có cấu trúc như sau:
typedef struct _IMAGE_FILE_HEADER {
WORD Machine;
WORD NumberOfSections;
DWORD TimeDateStamp;
DWORD PointerToSymbolTable;
DWORD NumberOfSymbols;
WORD SizeOfOptionalHeader;
WORD Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
Những thành phần quan trọng là:
NumberOfSections
: số lượng các section ở trong file PE file.Characteristics
: chứa một số thuộc tính về file chẳng hạn như thuộc tính cho biết file là DLL hay console application.SizeOfOptionalHeader
: kích thước của optional header tiếp theo.TimeDateStamp
: thời gian và ngày tháng mà file PE được tạo ra.PointerToSymbolTable
: offset đến symbol table (nếu có).NumberOfSymbols
: số lượng các symbol có trong symbol table.
Seealso
Optional Header (IMAGE_OPTIONAL_HEADER
)
Là thành phần quan trọng cho việc thực thi của file PE mặc dù nó có tên là “optional”. Nó được gọi là “optional” vì một số tệp định dạng PE không chứa header này.
Tương tự với file header, optional header cũng có hai phiên bản dành cho kiến trúc 32-bit và kiến trúc 64-bit. Cả hai đều có các thành phần tương tự nhau và sự khác biệt lớn nhất là kích thước của các thành phần này. Ngoài ra, phiên bản 32-bit có một số thành phần mà phiên bản 64-bit không có.
Một số thành phần quan trọng của IMAGE_OPTIONAL_HEADER
và IMAGE_OPTIONAL_HEADER64
:
Magic
: mô tả trạng thái của image file (32 hoặc 64-bit).MajorLinkerVersion
vàMinorLinkerVersion
: phiên bản của linker được sử dụng để tạo ra file PE.MajorOperatingSystemVersion
: phiên bản hệ điều hành chính (chẳng hạn như 10, 11) tối thiểu cần thiết để chạy file PE.MinorOperatingSystemVersion
: phiên bản hệ điều hành phụ (chẳng hạn như 1511, 1507, 1607) tối thiểu cần thiết để chạy file PE.MajorImageVersion
vàMinorImageVersion
: phiên bản của file PE.SizeOfCode
,SizeOfInitializedData
vàSizeOfUninitializedData
: kích thước của.text
section, các vùng dữ liệu đã được khởi tạo và các vùng dữ liệu chưa được khởi tạo.AddressOfEntryPoint
: offset đến entry point của file (thường là hàmmain
)BaseOfCode
vàBaseOfData
: offset đến phần.text
và phần.data
.ImageBase
: địa chỉ mà ứng dụng sẽ được nạp vào bộ nhớ. Tuy nhiên, do cơ chế ASLR (Address Space Layout Randomization) của Windows, địa chỉ này thường thay đổi mỗi lần chạy, vì Windows PE loader ánh xạ file tới một địa chỉ khácSizeOfImage
: kích thước của toàn bộ imageDataDirectory
: một trong những thành phần quan trọng nhất của optional header. Nó là một mảng cácIMAGE_DATA_DIRECTORY
.
Cấu trúc của IMAGE_DATA_DIRECTORY
:
typedef struct _IMAGE_DATA_DIRECTORY {
DWORD VirtualAddress;
DWORD Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
Kích thước của DataDirectory
là kích thước của IMAGE_NUMBEROF_DIRECTORY_ENTRIES
với giá trị cố định là 16. Từng phần tử của trong mảng chứa thông tin chi tiết về một data directory.
Một data directory có thể được truy cập thông qua các chỉ số sau:
#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 // Export Directory
#define IMAGE_DIRECTORY_ENTRY_IMPORT 1 // Import Directory
#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 // Resource Directory
#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 // Exception Directory
#define IMAGE_DIRECTORY_ENTRY_SECURITY 4 // Security Directory
#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 // Base Relocation Table
#define IMAGE_DIRECTORY_ENTRY_DEBUG 6 // Debug Directory
#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7 // Architecture Specific Data
#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 // RVA of GP
#define IMAGE_DIRECTORY_ENTRY_TLS 9 // TLS Directory
#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 // Load Configuration Directory
#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 // Bound Import Directory in headers
#define IMAGE_DIRECTORY_ENTRY_IAT 12 // Import Address Table
#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 // Delay Load Import Descriptors
#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14 // COM Runtime descriptor
Hai data directory quan trọng là:
Export Directory
: chứa địa chỉ của các hàm và biến được export từ file thực thi. Các file thực thi khác có thể sử dụng các địa chỉ này để truy cập đến các hàm và các biến. Data directory này thường được tìm thấy ở trong các DLL. Ví dụ:kernel32.dll
exportCreateFileA
.Import Address Table
: chứa địa chỉ của các hàm và biến được import vào file thực thi. Chúng được sử dụng để truy cập vào các hàm và các biến của các file thực thi khác. Ví dụ:Application.exe
importCreateFileA
từkernel32.dll
.
PE Sections
Các section trong PE file chứa các thành phần như code, dữ liệu, hoặc resource (ví dụ: icon hoặc bitmap) tạo nên chương trình. Mỗi section có một tên duy nhất, và số lượng section không cố định vì chúng có thể được thêm, xóa, hoặc hợp nhất bởi các compiler tùy thuộc vào các thiết lập khác nhau. Ngoài ra, một số section có thể được thêm thủ công hoặc linh hoạt sau quá trình biên dịch.
Một số section phổ biến:
.text
: Chứa mã máy (machine code) của chương trình..data
: Lưu trữ dữ liệu đã khởi tạo, bao gồm các biến được khởi tạo trong code..rdata
: Chứa dữ liệu chỉ đọc (read-only data), chẳng hạn các biến được khai báo với từ khóaconst
..idata
: Chứa tham chiếu đến các hàm và biến được import từ các DLL..reloc
: Chứa thông tin về relocation, giúp sửa chữa địa chỉ bộ nhớ để chương trình có thể nạp mà không gặp lỗi..rsrc
: Lưu trữ resource, chẳng hạn như icon, bitmap, và các tài nguyên khác.
Mỗi PE section đều có một IMAGE_SECTION_HEADER
nằm bên dưới các NT header chứa các thông tin liên quan đến section đó.
Cấu trúc của IMAGE_SECTION_HEADER
:
typedef struct _IMAGE_SECTION_HEADER {
BYTE Name[IMAGE_SIZEOF_SHORT_NAME];
union {
DWORD PhysicalAddress;
DWORD VirtualSize;
} Misc;
DWORD VirtualAddress;
DWORD SizeOfRawData;
DWORD PointerToRawData;
DWORD PointerToRelocations;
DWORD PointerToLinenumbers;
WORD NumberOfRelocations;
WORD NumberOfLinenumbers;
DWORD Characteristics;
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
Một số thành phần quan trọng:
Name
: tên của section (chẳng hạn như.text
,.data
,.rdata
).PhysicalAddress
hoặcVirtualSize
(Union Data Type): kích thước của section trong bộ nhớ.VirtualAddress
: offset của section trong bộ nhớ.SizeOfRawData
: kích thước của section trong file PE (bytes).PointerToRelocations
: offset đến các relocation của section.NumberOfRelocations
: số lượng các relocation của section.Characteristics
: chứa các flag giúp mô tả các thuộc tính của section.
Seealso
Tham khảo thêm 0xRick’s Blog:
- PE Overview - https://0xrick.github.io/win-internals/pe2/
- DOS Header, DOS Stub and Rich Header - https://0xrick.github.io/win-internals/pe3/
- NT Headers - https://0xrick.github.io/win-internals/pe4/
- Data Directories, Section Headers and Sections - https://0xrick.github.io/win-internals/pe5/
- PE Imports (Import Directory Table, ILT, IAT) - https://0xrick.github.io/win-internals/pe6/
Related
list
from outgoing([[MalDev - Portable Executable Format]])
sort file.ctime asc