giangnd
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
      • Invitee
    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Versions and GitHub Sync Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
Invitee
Publish Note

Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

Your note will be visible on your profile and discoverable by anyone.
Your note is now live.
This note is visible on your profile and discoverable online.
Everyone on the web can find and read all notes of this public team.
See published notes
Unpublish note
Please check the box to agree to the Community Guidelines.
View profile
Engagement control
Commenting
Permission
Disabled Forbidden Owners Signed-in users Everyone
Enable
Permission
  • Forbidden
  • Owners
  • Signed-in users
  • Everyone
Suggest edit
Permission
Disabled Forbidden Owners Signed-in users Everyone
Enable
Permission
  • Forbidden
  • Owners
  • Signed-in users
Emoji Reply
Enable
Import from Dropbox Google Drive Gist Clipboard
   owned this note    owned this note      
Published Linked with GitHub
Subscribed
  • Any changes
    Be notified of any changes
  • Mention me
    Be notified of mention me
  • Unsubscribe
Subscribe
--- tags: [Technique] --- # PE Parser ## Mở đầu * Trong phần trước, mình đã bóc tách **PE file** dưới góc nhìn cấu trúc và có trình bày tại [đây](https://hackmd.io/XHiqM5aXTzqsHBpm4rj4hg). Nhưng lý thuyết thôi thì chưa đủ – lần này, hãy cùng nhau code tay một **PE parser**, không cần tới **CFF Explorer** hay **PEview**. Điều này không chỉ giúp ta hiểu **PE** ở tầng thấp hơn, mà còn có thể xây dựng công cụ của chính mình để phục vụ **reverse**, **malware analysis**, hoặc **fuzzing**. * Thực chất thì một số **PE Parser** phổ biến chính là **CFF Explorer**, **PE-bear**, ... * Nhưng chúng ta có bao giờ đặt ra câu hỏi, trong ngữ cảnh **CTF** nói riêng, hay phát triển rộng hơn trong lĩnh vực **malware**, thì có phải lúc nào chúng ta nhặt được 1 con `.exe` nào đấy, mà ném nó vào **CFF Explorer** hay **PE-bear** thì đều giúp chúng ta có thông tin đầy đủ của các trường không? Ý mình là có thể file bị pack, hay mất 1 vài trường quan trọng khiến cho khó phân tích tiếp, .... * Đó chính là lý do, mà ít nhất những người học ``"rì vợt"`` như chúng ta, luôn cần có trong tay 1 code **PE Parser**, để phòng cho những trường hợp bất tiện như này, đúng không nào? ## 1 vài khái niệm ### VA * **VA(`Virtual Address`)**, là địa chỉ ảo khi chương trình được load vào bộ nhớ. * Ví dụ, load file vào **IDA** thấy địa chỉ hàm `main()` là `0x401000`, thì đó là **VA**. ### **Base Address** * Là địa chỉ cơ sở của chương trình khi nạp vào bộ nhớ. Thường có giá trị là `0x400000` trên **Windows x86**. ### RVA * **RVA(`Relative Virtual Address`)** là địa chỉ ảo tương đối của file. Được tính toán như sau: **RVA = VA - Base Address**. ### IAT * **IAT(`Import Address Table`)** là 1 bảng sử dụng để lưu trữ địa chỉ của các **API** mà chương trình gọi trong lúc thực thi. Điều này giúp thuận tiện hơn trong quá trình trong việc quản lý, truy xuất, resolve, ... các **API** so với việc địa chỉ được lưu 1 cách rời rạc. ### File Offset * Là thuật ngữ chỉ vị trí của một trường, hoặc một giá trị nào đó trong `raw file`( ví dụ như `e_magic` có `file offset` là $0x00$), nghĩa là vị trí này khi file chưa chạy vẫn ở trong bộ nhớ vật lý. * Cách tính `file offset`: **```File_offset = RVA - pSect->VirtualAddress + pSect->PointerToRawData```** ### Cấu trúc file PE ![image](https://hackmd.io/_uploads/B1kQ68vbxl.png) * Cấu trúc cơ bản của 1 file **PE** chia thành 2 phần chính: * **Header**: * DOS MZ header * DOS Stub * PE Header * Section table * **Section**: * code * data ![image](https://hackmd.io/_uploads/ryJlyPwZgg.png) * Có lẽ chỉ `"overview"` 1 chút như thế, còn chi tiết hơn thì trước mình đã có 1 writeup về phần này rồi. Mình để ở [đây](https://hackmd.io/XHiqM5aXTzqsHBpm4rj4hg?view) nhaaa. * Hoặc đơn giản hơn thì bảng dưới đây cũng cung cấp đủ: | **Thuật ngữ** | **Ý nghĩa** | **Ví dụ / Ghi chú** | | ------------------------------------ | --------------------------------------------------------------- | ---------------------------------------------- | | **VA** *(Virtual Address)* | Địa chỉ ảo của thành phần khi chương trình được load vào bộ nhớ | VD: `main()` tại `0x401000` trong IDA | | **Base Address** | Địa chỉ bắt đầu khi chương trình được nạp vào RAM | Thường là `0x400000` (Windows x86) | | **RVA** *(Relative Virtual Address)* | Địa chỉ ảo tương đối so với `Base Address` | `RVA = VA - Base Address` | | **IAT** *(Import Address Table)* | Bảng lưu địa chỉ các API Windows mà chương trình sử dụng | Hỗ trợ resolve tên hàm sang địa chỉ thực | | **File Offset** | Vị trí byte trong file PE khi còn nằm trên đĩa | VD: `e_magic` ở offset `0x00` | | *(Công thức)* | `FileOffset = RVA - VirtualAddress + PointerToRawData` | Dùng để tìm vị trí thực của dữ liệu trong file | ## Code ... ### Part 1: Định nghĩa các structures, constants #### Các types ```py typedef unsigned char BYTE; typedef unsigned short WORD; typedef unsigned long DWORD; typedef unsigned long long QWORD; typedef unsigned long LONG; typedef __int64 LONGLONG; typedef unsigned __int64 ULONGLONG; ``` #### Các Constants ```py #define ___IMAGE_NT_OPTIONAL_HDR32_MAGIC 0x10b #define ___IMAGE_NT_OPTIONAL_HDR64_MAGIC 0x20b #define ___IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16 #define ___IMAGE_DOS_SIGNATURE 0x5A4D #define ___IMAGE_DIRECTORY_ENTRY_EXPORT 0 #define ___IMAGE_DIRECTORY_ENTRY_IMPORT 1 #define ___IMAGE_DIRECTORY_ENTRY_RESOURCE 2 #define ___IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 #define ___IMAGE_DIRECTORY_ENTRY_SECURITY 4 #define ___IMAGE_DIRECTORY_ENTRY_BASERELOC 5 #define ___IMAGE_DIRECTORY_ENTRY_DEBUG 6 #define ___IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7 #define ___IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 #define ___IMAGE_DIRECTORY_ENTRY_TLS 9 #define ___IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 #define ___IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 #define ___IMAGE_DIRECTORY_ENTRY_IAT 12 #define ___IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 #define ___IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14 #define ___IMAGE_SIZEOF_SHORT_NAME 8 #define ___IMAGE_SIZEOF_SECTION_HEADER 40 ``` * Giải thích: | Macro | Giá trị | Giải thích ngắn gọn | | ----------------------------------------- | -------: | ---------------------------------------------------------------------------- | | `___IMAGE_DOS_SIGNATURE` | `0x5A4D` | Magic của file PE ở DOS header: `"MZ"` (chữ cái đầu tên **Mark Zbikowski**) | | `___IMAGE_NT_OPTIONAL_HDR32_MAGIC` | `0x10B` | Magic xác định là **PE32** (32-bit) | | `___IMAGE_NT_OPTIONAL_HDR64_MAGIC` | `0x20B` | Magic xác định là **PE32+** (64-bit) | | `___IMAGE_NUMBEROF_DIRECTORY_ENTRIES` | `16` | Số lượng phần tử trong `DataDirectory[]` của Optional Header | | `___IMAGE_DIRECTORY_ENTRY_EXPORT` | `0` | Export Table – thông tin các hàm/biến được export từ PE | | `___IMAGE_DIRECTORY_ENTRY_IMPORT` | `1` | Import Table – chứa thông tin các DLL và hàm được import | | `___IMAGE_DIRECTORY_ENTRY_RESOURCE` | `2` | Resource Table – icon, dialog, string table, bitmap,... | | `___IMAGE_DIRECTORY_ENTRY_EXCEPTION` | `3` | Exception Table – dùng cho cơ chế xử lý ngoại lệ (SEH/VEH) | | `___IMAGE_DIRECTORY_ENTRY_SECURITY` | `4` | Digital Signature – xác thực tính hợp lệ của PE (Authenticode) | | `___IMAGE_DIRECTORY_ENTRY_BASERELOC` | `5` | Base Relocation Table – hỗ trợ relocate khi PE không được load tại ImageBase | | `___IMAGE_DIRECTORY_ENTRY_DEBUG` | `6` | Debug Data – thông tin phục vụ debugger (PDB) | | `___IMAGE_DIRECTORY_ENTRY_ARCHITECTURE` | `7` | Dự phòng cho kiến trúc đặc biệt (Itanium) | | `___IMAGE_DIRECTORY_ENTRY_GLOBALPTR` | `8` | Địa chỉ GlobalPtr (ít được dùng, chỉ với CPU cũ) | | `___IMAGE_DIRECTORY_ENTRY_TLS` | `9` | Thread Local Storage – khởi tạo dữ liệu theo từng thread riêng | | `___IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG` | `10` | Load Config Table – chứa thông tin cấu hình bảo mật (CFG, SEH table, ...) | | `___IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT` | `11` | Bound Import Table – tối ưu tốc độ load DLL bằng cách lưu cache | | `___IMAGE_DIRECTORY_ENTRY_IAT` | `12` | Import Address Table – lưu địa chỉ **đã resolve** của các hàm import | | `___IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT` | `13` | Delay Load Table – cho phép chỉ resolve import khi cần dùng | | `___IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR` | `14` | CLR Header – bắt buộc với PE thuộc .NET assembly | | `___IMAGE_SIZEOF_SHORT_NAME` | `8` | Kích thước tối đa của tên section trong `SectionHeader` | | `___IMAGE_SIZEOF_SECTION_HEADER` | `40` | Kích thước mỗi `Section Header`, cố định là 40 byte | #### Các structures ##### IMAGE_DOS_HEADER ```py typedef struct __IMAGE_DOS_HEADER { WORD e_magic; // Magic number 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; // Offset tới PE Header } ___IMAGE_DOS_HEADER, * ___PIMAGE_DOS_HEADER; ``` * Đây là header đầu tiên trong file **PE**. * Được dùng bởi **DOS stub** để hiển thị thông báo (ví dụ: ``"This program cannot be run in DOS mode"``). * Trường quan trọng nhất là `e_lfanew`: offset tới `IMAGE_NT_HEADERS`, đánh dấu bắt đầu phần **PE** thật sự. ##### IMAGE_DATA_DIRECTORY ```py typedef struct __IMAGE_DATA_DIRECTORY { DWORD VirtualAddress; DWORD Size; } ___IMAGE_DATA_DIRECTORY, * ___PIMAGE_DATA_DIRECTORY; ``` * Mỗi phần tử trong `DataDirectory[]` của **Optional Header** là một `IMAGE_DATA_DIRECTORY`. * Dùng để lưu `RVA` và kích thước của: * `Import Table` * `Export Table` * `Base Relocations` * ... ##### IMAGE_OPTIONAL_HEADER32 và IMAGE_OPTIONAL_HEADER64 ```py typedef struct __IMAGE_OPTIONAL_HEADER { WORD Magic; BYTE MajorLinkerVersion; BYTE MinorLinkerVersion; DWORD SizeOfCode; DWORD SizeOfInitializedData; DWORD SizeOfUninitializedData; DWORD AddressOfEntryPoint; DWORD BaseOfCode; DWORD BaseOfData; DWORD ImageBase; DWORD SectionAlignment; DWORD FileAlignment; WORD MajorOperatingSystemVersion; WORD MinorOperatingSystemVersion; WORD MajorImageVersion; WORD MinorImageVersion; WORD MajorSubsystemVersion; WORD MinorSubsystemVersion; DWORD Win32VersionValue; DWORD SizeOfImage; DWORD SizeOfHeaders; DWORD CheckSum; WORD Subsystem; WORD DllCharacteristics; DWORD SizeOfStackReserve; DWORD SizeOfStackCommit; DWORD SizeOfHeapReserve; DWORD SizeOfHeapCommit; DWORD LoaderFlags; DWORD NumberOfRvaAndSizes; ___IMAGE_DATA_DIRECTORY DataDirectory[___IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; } ___IMAGE_OPTIONAL_HEADER32, * ___PIMAGE_OPTIONAL_HEADER32; typedef struct __IMAGE_OPTIONAL_HEADER64 { WORD Magic; BYTE MajorLinkerVersion; BYTE MinorLinkerVersion; DWORD SizeOfCode; DWORD SizeOfInitializedData; DWORD SizeOfUninitializedData; DWORD AddressOfEntryPoint; DWORD BaseOfCode; ULONGLONG ImageBase; DWORD SectionAlignment; DWORD FileAlignment; WORD MajorOperatingSystemVersion; WORD MinorOperatingSystemVersion; WORD MajorImageVersion; WORD MinorImageVersion; WORD MajorSubsystemVersion; WORD MinorSubsystemVersion; DWORD Win32VersionValue; DWORD SizeOfImage; DWORD SizeOfHeaders; DWORD CheckSum; WORD Subsystem; WORD DllCharacteristics; ULONGLONG SizeOfStackReserve; ULONGLONG SizeOfStackCommit; ULONGLONG SizeOfHeapReserve; ULONGLONG SizeOfHeapCommit; DWORD LoaderFlags; DWORD NumberOfRvaAndSizes; ___IMAGE_DATA_DIRECTORY DataDirectory[___IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; } ___IMAGE_OPTIONAL_HEADER64, * ___PIMAGE_OPTIONAL_HEADER64; ``` * Chứa thông tin quan trọng để loader xử lý **PE**: **entry point, image base, section alignment, heap/stack sizes**, ... * `DataDirectory[]`: bảng $16$ phần tử chứa **RVA** và `size` cho các bảng phụ như **Import Table, Export Table, Relocations, Resources**... ##### IMAGE_FILE_HEADER ```py 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; ``` * Đây là phần **File Header** trong **PE**. * Chứa thông tin như số lượng `section`, loại máy, thời gian build, ... ##### IMAGE_NT_HEADERS32 và IMAGE_NT_HEADERS64 ```py typedef struct __IMAGE_NT_HEADERS64 { DWORD Signature; ___IMAGE_FILE_HEADER FileHeader; ___IMAGE_OPTIONAL_HEADER64 OptionalHeader; } ___IMAGE_NT_HEADERS64, * ___PIMAGE_NT_HEADERS64; typedef struct __IMAGE_NT_HEADERS { DWORD Signature; ___IMAGE_FILE_HEADER FileHeader; ___IMAGE_OPTIONAL_HEADER32 OptionalHeader; } ___IMAGE_NT_HEADERS32, * ___PIMAGE_NT_HEADERS32; ``` * Phần header chính của **PE** file. * Nếu `OptionalHeader.Magic == 0x20b` → dùng **IMAGE_NT_HEADERS64**. ##### IMAGE_IMPORT_DESCRIPTOR ```py typedef struct __IMAGE_IMPORT_DESCRIPTOR { union { DWORD Characteristics; DWORD OriginalFirstThunk; } DUMMYUNIONNAME; DWORD TimeDateStamp; DWORD ForwarderChain; DWORD Name; DWORD FirstThunk; } ___IMAGE_IMPORT_DESCRIPTOR, * ___PIMAGE_IMPORT_DESCRIPTOR; ``` * Cấu trúc mô tả từng **DLL** được import. * `Name → RVA` tới tên **DLL**. * `FirstThunk / OriginalFirstThunk` → mô tả hàm import. ##### IMAGE_IMPORT_BY_NAME ```py typedef struct __IMAGE_IMPORT_BY_NAME { WORD Hint; char Name[100]; } ___IMAGE_IMPORT_BY_NAME, * ___PIMAGE_IMPORT_BY_NAME; ``` * Cấu trúc mô tả một hàm được import theo tên. ##### IMAGE_BASE_RELOCATION ```py typedef struct __IMAGE_BASE_RELOCATION { DWORD VirtualAddress; DWORD SizeOfBlock; } ___IMAGE_BASE_RELOCATION, * ___PIMAGE_BASE_RELOCATION; ``` * Dùng cho phần **Base Relocation Table**. * Cho phép sửa đổi địa chỉ nếu không load tại đúng **ImageBase** gốc. ##### IMAGE_SECTION_HEADER ```py 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ỗi section trong **PE** được mô tả bằng một struct này. * Chứa thông tin mapping giữa file offset và virtual memory. ##### RICH_HEADER_INFO ```py typedef struct __RICH_HEADER_INFO { int size; char* ptrToBuffer; int entries; } RICH_HEADER_INFO, * PRICH_HEADER_INFO; ``` * Đây là struct lưu meta thông tin về phần `Rich Header`. * `size`: Kích thước của **Rich Header** (tính bằng byte). * `ptrToBuffer`: Một con trỏ tới bộ đệm chứa dữ liệu của **Rich Header**. * `entries`: Số lượng mục trong **Rich Header**. ##### RICH_HEADER_ENTRY ```py typedef struct __RICH_HEADER_ENTRY { WORD prodID; WORD buildID; DWORD useCount; } RICH_HEADER_ENTRY, * PRICH_HEADER_ENTRY; ``` * Một entry trong vùng RICH, cho thấy công cụ nào đã tham gia build file (ví dụ: `Microsoft Linker`, `VS Compiler`...) * Phân tích **Rich Header** là cách để truy ngược lại quá trình build file **PE** — hữu ích khi phân tích mã độc hoặc xác định môi trường build trong forensic. * `prodID`: Type ID / Product ID. * `buildID`: Build ID. * `useCount`: Số lần thành phần đó được sử dụng trong quá trình build file. ##### RICH_HEADER ```py typedef struct __RICH_HEADER { PRICH_HEADER_ENTRY entries; } RICH_HEADER, * PRICH_HEADER; ``` * Đây là container chứa các **RICH_HEADER_ENTRY**, thường sau khi đã giải mã thành công vùng **RICH**. * `entries`: Con trỏ trỏ tới mảng các entry **RICH_HEADER_ENTRY**. ##### ILT_ENTRY_64 – Import Lookup Table (64-bit) ```py typedef struct __ILT_ENTRY_64 { union { DWORD ORDINAL : 16; DWORD HINT_NAME_TABE : 32; } FIELD_2; DWORD ORDINAL_NAME_FLAG : 1; } ILT_ENTRY_64, * PILT_ENTRY_64; ``` * Dùng để biểu diễn một phần tử trong bảng `Import Lookup` (trong **PE**), ở chế độ 64-bit. * `ORDINAL_NAME_FLAG = 1`: dùng số thứ tự (ordinal) thay vì tên **API**. * `HINT_NAME_TABE`: offset đến vùng chứa tên hàm cần import (từ `.idata`). ##### ILT_ENTRY_32 – Import Lookup Table (32-bit) ```py typedef struct __ILT_ENTRY_32 { union { DWORD ORDINAL : 16; DWORD HINT_NAME_TABE : 32; DWORD ORDINAL_NAME_FLAG : 1; } FIELD_1; } ILT_ENTRY_32, * PILT_ENTRY_32; ``` * Tương tự như trên, nhưng cho định dạng 32-bit **PE**. ##### BASE_RELOC_ENTRY ```py typedef struct __BASE_RELOC_ENTRY { WORD OFFSET : 12; WORD TYPE : 4; } BASE_RELOC_ENTRY, * PBASE_RELOC_ENTRY; ``` * Biểu diễn một entry trong **Base Relocation Table** — thứ giúp **Windows** di chuyển (relocate) file **PE** khi không load đúng địa chỉ gốc (`ImageBase`). ### Part 2: Định nghĩa các Class và Constructors * Định dạng **PE64**: ```py class PE64FILE ``` * Lớp này đại diện cho một file **PE** 64-bit. * Trong project parser, sẽ có hai class **PE32FILE** và **PE64FILE**, chỉ khác nhau ở kiểu dữ liệu nhưng logic xử lý thì giống nhau. #### **Public Member** ```py public: PE64FILE(char* _NAME, FILE* Ppefile); void PrintInfo(); ``` * **Constructor**: nhận vào tên file (`char* _NAME`) và con trỏ **FILE*** đến file nhị phân đang mở. * **PrintInfo()**: phương thức công khai duy nhất, dùng để in ra thông tin toàn bộ file **PE** sau khi **parse**. #### **Private Member** * **Metadata**: ```py char* NAME; FILE* Ppefile; int _import_directory_count, _import_directory_size; int _basreloc_directory_count; ``` * Lưu thông tin tổng quát như: * Tên file * Con trỏ **FILE** * Số lượng entry của bảng import và relocation. * **DOS Header**: ```py // HEADERS ___IMAGE_DOS_HEADER PEFILE_DOS_HEADER; ___IMAGE_NT_HEADERS64 PEFILE_NT_HEADERS; // DOS HEADER DWORD PEFILE_DOS_HEADER_EMAGIC; LONG PEFILE_DOS_HEADER_LFANEW; ``` * `e_magic`: `0x5A4D` (chữ 'MZ'). * `e_lfanew`: offset đến **PE** header. * **Rich Header**: ```py // RICH HEADER RICH_HEADER_INFO PEFILE_RICH_HEADER_INFO; RICH_HEADER PEFILE_RICH_HEADER; ``` * Chứa thông tin về vùng **Rich Header**. * **NT Headers**: ```py // NT_HEADERS.Signature DWORD PEFILE_NT_HEADERS_SIGNATURE; // NT_HEADERS.FileHeader WORD PEFILE_NT_HEADERS_FILE_HEADER_MACHINE; WORD PEFILE_NT_HEADERS_FILE_HEADER_NUMBER0F_SECTIONS; WORD PEFILE_NT_HEADERS_FILE_HEADER_SIZEOF_OPTIONAL_HEADER; ``` * Bao gồm: * `Signature` ``"PE\0\0"`` * `FileHeader` (architecture, số lượng section, ...) * `OptionalHeader` (các thông số quan trọng của vùng nhớ) * **Optional Header**: ```py // NT_HEADERS.OptionalHeader DWORD PEFILE_NT_HEADERS_OPTIONAL_HEADER_MAGIC; DWORD PEFILE_NT_HEADERS_OPTIONAL_HEADER_SIZEOF_CODE; DWORD PEFILE_NT_HEADERS_OPTIONAL_HEADER_SIZEOF_INITIALIZED_DATA; DWORD PEFILE_NT_HEADERS_OPTIONAL_HEADER_SIZEOF_UNINITIALIZED_DATA; DWORD PEFILE_NT_HEADERS_OPTIONAL_HEADER_ADDRESSOF_ENTRYPOINT; DWORD PEFILE_NT_HEADERS_OPTIONAL_HEADER_BASEOF_CODE; ULONGLONG PEFILE_NT_HEADERS_OPTIONAL_HEADER_IMAGEBASE; DWORD PEFILE_NT_HEADERS_OPTIONAL_HEADER_SECTION_ALIGNMENT; DWORD PEFILE_NT_HEADERS_OPTIONAL_HEADER_FILE_ALIGNMENT; DWORD PEFILE_NT_HEADERS_OPTIONAL_HEADER_SIZEOF_IMAGE; DWORD PEFILE_NT_HEADERS_OPTIONAL_HEADER_SIZEOF_HEADERS; ``` * Các thông số quan trọng ảnh hưởng đến cách file được load vào bộ nhớ: `EntryPoint`, `ImageBase`, `Alignment`, `SizeOfImage`, ... * **Data Directory Entries**: ```py ___IMAGE_DATA_DIRECTORY PEFILE_EXPORT_DIRECTORY; ___IMAGE_DATA_DIRECTORY PEFILE_IMPORT_DIRECTORY; ___IMAGE_DATA_DIRECTORY PEFILE_RESOURCE_DIRECTORY; ___IMAGE_DATA_DIRECTORY PEFILE_EXCEPTION_DIRECTORY; ___IMAGE_DATA_DIRECTORY PEFILE_SECURITY_DIRECTORY; ___IMAGE_DATA_DIRECTORY PEFILE_BASERELOC_DIRECTORY; ___IMAGE_DATA_DIRECTORY PEFILE_DEBUG_DIRECTORY; ___IMAGE_DATA_DIRECTORY PEFILE_ARCHITECTURE_DIRECTORY; ___IMAGE_DATA_DIRECTORY PEFILE_GLOBALPTR_DIRECTORY; ___IMAGE_DATA_DIRECTORY PEFILE_TLS_DIRECTORY; ___IMAGE_DATA_DIRECTORY PEFILE_LOAD_CONFIG_DIRECTORY; ___IMAGE_DATA_DIRECTORY PEFILE_BOUND_IMPORT_DIRECTORY; ___IMAGE_DATA_DIRECTORY PEFILE_IAT_DIRECTORY; ___IMAGE_DATA_DIRECTORY PEFILE_DELAY_IMPORT_DIRECTORY; ___IMAGE_DATA_DIRECTORY PEFILE_COM_DESCRIPTOR_DIRECTORY; ``` * Đây là các thành phần nằm trong **Optional Header** (`DataDirectory[16]`) → mỗi cái chỉ đến một bảng/section như `Import Table`, `Export Table`, `TLS`, `Reloc`, `Debug`... * **Section Headers**: ```py // SECTION HEADERS ___PIMAGE_SECTION_HEADER PEFILE_SECTION_HEADERS; ``` * Mỗi section (`.text`, `.data`, `.rsrc`, ...) được mô tả bằng 1 entry trong **Section Table**. * Con trỏ trỏ tới mảng các `IMAGE_SECTION_HEADER`. * **Import và Reloc Table**: ```py // IMPORT TABLE ___PIMAGE_IMPORT_DESCRIPTOR PEFILE_IMPORT_TABLE; // BASE RELOCATION TABLE ___PIMAGE_BASE_RELOCATION PEFILE_BASERELOC_TABLE; ``` * Dùng để lưu bảng `import descriptor` và bảng `relocation` sau khi phân tích. #### Các phương thức xử lý * **Address Resolution**: ```py // ADDRESS RESOLVERS int locate(DWORD VA); DWORD resolve(DWORD VA, int index); ``` * `locate`: xác định file offset từ một `virtual address`. * `resolve`: dùng để tra một địa chỉ ảo và ánh xạ nó về offset thực trong file. * Các phương thức **parser**: ```py // PARSERS void ParseFile(); void ParseDOSHeader(); void ParseNTHeaders(); void ParseSectionHeaders(); void ParseImportDirectory(); void ParseBaseReloc(); void ParseRichHeader(); ``` * Phân tích từng phần của **PE** file: * **DOS Header** * **NT Headers** * **Section Table** * **Import Table** * **Relocation Table** * **Rich Header** #### Các hàm in thông tin ```py // PRINT INFO void PrintFileInfo(); void PrintDOSHeaderInfo(); void PrintRichHeaderInfo(); void PrintNTHeadersInfo(); void PrintSectionHeadersInfo(); void PrintImportTableInfo(); void PrintBaseRelocationsInfo(); ``` * Tương ứng với từng phần **parser**, sau khi phân tích xong sẽ gọi hàm tương ứng để in thông tin ra màn hình. * Như đã nói ở trên, thì định dạng 32 bit cũng như 64 bit hoi, chỉ khác nhau ở kiểu dữ liệu nhưng logic xử lý thì giống nhau. ### Part 3: Triển khai class PE64FILE / PE32FILE #### 1. Constructor PE64FILE::PE64FILE ```py PE64FILE::PE64FILE(char* _NAME, FILE* _Ppefile) { NAME = _NAME; Ppefile = _Ppefile; ParseFile(); } ``` * Lưu tên file và con trỏ **FILE**. * Gọi `ParseFile()` để phân tích toàn bộ cấu trúc file **PE** sau khi mở. #### 2. Hàm hỗ trợ địa chỉ: `locate()` và `resolve()` ```py // ADDRESS RESOLVERS int PE64FILE::locate(DWORD VA) { int index; for (int i = 0; i < PEFILE_NT_HEADERS_FILE_HEADER_NUMBER0F_SECTIONS; i++) { if (VA >= PEFILE_SECTION_HEADERS[i].VirtualAddress && VA < (PEFILE_SECTION_HEADERS[i].VirtualAddress + PEFILE_SECTION_HEADERS[i].Misc.VirtualSize)) { index = i; break; } } return index; } DWORD PE64FILE::resolve(DWORD VA, int index) { return (VA - PEFILE_SECTION_HEADERS[index].VirtualAddress) + PEFILE_SECTION_HEADERS[index].PointerToRawData; } ``` * `locate(VA)`: Tìm section chứa địa chỉ ảo **VA** * `resolve(VA, index)`: Chuyển **VA** thành offset trong file * ----> Đây là cơ chế ánh xạ `VirtualAddress` ↔ `FileOffset`. #### 3. Parse*() – Phân tích từng vùng trong PE * Theo đúng thứ tự phân tích các phương thức **parser**: ```py // PARSERS void ParseFile(); void ParseDOSHeader(); void ParseNTHeaders(); void ParseSectionHeaders(); void ParseImportDirectory(); void ParseBaseReloc(); void ParseRichHeader(); ``` ##### ParseDOSHeader() ```py void PE64FILE::ParseDOSHeader() { fseek(Ppefile, 0, SEEK_SET); fread(&PEFILE_DOS_HEADER, sizeof(___IMAGE_DOS_HEADER), 1, Ppefile); PEFILE_DOS_HEADER_EMAGIC = PEFILE_DOS_HEADER.e_magic; PEFILE_DOS_HEADER_LFANEW = PEFILE_DOS_HEADER.e_lfanew; } ``` * Đọc đầu file, trích `e_magic`, `e_lfanew`. * `e_lfanew` là offset đến **NT Header**. ##### ParseNTHeaders() ```py void PE64FILE::ParseNTHeaders() { fseek(Ppefile, PEFILE_DOS_HEADER.e_lfanew, SEEK_SET); fread(&PEFILE_NT_HEADERS, sizeof(PEFILE_NT_HEADERS), 1, Ppefile); PEFILE_NT_HEADERS_SIGNATURE = PEFILE_NT_HEADERS.Signature; PEFILE_NT_HEADERS_FILE_HEADER_MACHINE = PEFILE_NT_HEADERS.FileHeader.Machine; PEFILE_NT_HEADERS_FILE_HEADER_NUMBER0F_SECTIONS = PEFILE_NT_HEADERS.FileHeader.NumberOfSections; PEFILE_NT_HEADERS_FILE_HEADER_SIZEOF_OPTIONAL_HEADER = PEFILE_NT_HEADERS.FileHeader.SizeOfOptionalHeader; PEFILE_NT_HEADERS_OPTIONAL_HEADER_MAGIC = PEFILE_NT_HEADERS.OptionalHeader.Magic; PEFILE_NT_HEADERS_OPTIONAL_HEADER_SIZEOF_CODE = PEFILE_NT_HEADERS.OptionalHeader.SizeOfCode; PEFILE_NT_HEADERS_OPTIONAL_HEADER_SIZEOF_INITIALIZED_DATA = PEFILE_NT_HEADERS.OptionalHeader.SizeOfInitializedData; PEFILE_NT_HEADERS_OPTIONAL_HEADER_SIZEOF_UNINITIALIZED_DATA = PEFILE_NT_HEADERS.OptionalHeader.SizeOfUninitializedData; PEFILE_NT_HEADERS_OPTIONAL_HEADER_ADDRESSOF_ENTRYPOINT = PEFILE_NT_HEADERS.OptionalHeader.AddressOfEntryPoint; PEFILE_NT_HEADERS_OPTIONAL_HEADER_BASEOF_CODE = PEFILE_NT_HEADERS.OptionalHeader.BaseOfCode; PEFILE_NT_HEADERS_OPTIONAL_HEADER_IMAGEBASE = PEFILE_NT_HEADERS.OptionalHeader.ImageBase; PEFILE_NT_HEADERS_OPTIONAL_HEADER_SECTION_ALIGNMENT = PEFILE_NT_HEADERS.OptionalHeader.SectionAlignment; PEFILE_NT_HEADERS_OPTIONAL_HEADER_FILE_ALIGNMENT = PEFILE_NT_HEADERS.OptionalHeader.FileAlignment; PEFILE_NT_HEADERS_OPTIONAL_HEADER_SIZEOF_IMAGE = PEFILE_NT_HEADERS.OptionalHeader.SizeOfImage; PEFILE_NT_HEADERS_OPTIONAL_HEADER_SIZEOF_HEADERS = PEFILE_NT_HEADERS.OptionalHeader.SizeOfHeaders; PEFILE_EXPORT_DIRECTORY = PEFILE_NT_HEADERS.OptionalHeader.DataDirectory[___IMAGE_DIRECTORY_ENTRY_EXPORT]; PEFILE_IMPORT_DIRECTORY = PEFILE_NT_HEADERS.OptionalHeader.DataDirectory[___IMAGE_DIRECTORY_ENTRY_IMPORT]; PEFILE_RESOURCE_DIRECTORY = PEFILE_NT_HEADERS.OptionalHeader.DataDirectory[___IMAGE_DIRECTORY_ENTRY_RESOURCE]; PEFILE_EXCEPTION_DIRECTORY = PEFILE_NT_HEADERS.OptionalHeader.DataDirectory[___IMAGE_DIRECTORY_ENTRY_EXCEPTION]; PEFILE_SECURITY_DIRECTORY = PEFILE_NT_HEADERS.OptionalHeader.DataDirectory[___IMAGE_DIRECTORY_ENTRY_SECURITY]; PEFILE_BASERELOC_DIRECTORY = PEFILE_NT_HEADERS.OptionalHeader.DataDirectory[___IMAGE_DIRECTORY_ENTRY_BASERELOC]; PEFILE_DEBUG_DIRECTORY = PEFILE_NT_HEADERS.OptionalHeader.DataDirectory[___IMAGE_DIRECTORY_ENTRY_DEBUG]; PEFILE_ARCHITECTURE_DIRECTORY = PEFILE_NT_HEADERS.OptionalHeader.DataDirectory[___IMAGE_DIRECTORY_ENTRY_ARCHITECTURE]; PEFILE_GLOBALPTR_DIRECTORY = PEFILE_NT_HEADERS.OptionalHeader.DataDirectory[___IMAGE_DIRECTORY_ENTRY_GLOBALPTR]; PEFILE_TLS_DIRECTORY = PEFILE_NT_HEADERS.OptionalHeader.DataDirectory[___IMAGE_DIRECTORY_ENTRY_TLS]; PEFILE_LOAD_CONFIG_DIRECTORY = PEFILE_NT_HEADERS.OptionalHeader.DataDirectory[___IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG]; PEFILE_BOUND_IMPORT_DIRECTORY = PEFILE_NT_HEADERS.OptionalHeader.DataDirectory[___IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT]; PEFILE_IAT_DIRECTORY = PEFILE_NT_HEADERS.OptionalHeader.DataDirectory[___IMAGE_DIRECTORY_ENTRY_IAT]; PEFILE_DELAY_IMPORT_DIRECTORY = PEFILE_NT_HEADERS.OptionalHeader.DataDirectory[___IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT]; PEFILE_COM_DESCRIPTOR_DIRECTORY = PEFILE_NT_HEADERS.OptionalHeader.DataDirectory[___IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR]; } ``` * Đọc **NT_HEADERS** tại vị trí `e_lfanew`. * Trích **Signature**, **FileHeader**, **OptionalHeader**. * Lưu lại toàn bộ **DataDirectory** như **Import**, **Export**, **Reloc**, **TLS**, v.v. ##### ParseSectionHeaders() ```py void PE64FILE::ParseSectionHeaders() { PEFILE_SECTION_HEADERS = new ___IMAGE_SECTION_HEADER[PEFILE_NT_HEADERS_FILE_HEADER_NUMBER0F_SECTIONS]; for (int i = 0; i < PEFILE_NT_HEADERS_FILE_HEADER_NUMBER0F_SECTIONS; i++) { int offset = (PEFILE_DOS_HEADER.e_lfanew + sizeof(PEFILE_NT_HEADERS)) + (i * ___IMAGE_SIZEOF_SECTION_HEADER); fseek(Ppefile, offset, SEEK_SET); fread(&PEFILE_SECTION_HEADERS[i], ___IMAGE_SIZEOF_SECTION_HEADER, 1, Ppefile); } } ``` * Lặp qua số **section** (`NumberOfSections`) để đọc từng `IMAGE_SECTION_HEADER`. * Đặt con trỏ `PEFILE_SECTION_HEADERS[i]` cho từng entry. ##### ParseImportDirectory() ```py void PE64FILE::ParseImportDirectory() { DWORD _import_directory_address = resolve(PEFILE_IMPORT_DIRECTORY.VirtualAddress, locate(PEFILE_IMPORT_DIRECTORY.VirtualAddress)); _import_directory_count = 0; while (true) { ___IMAGE_IMPORT_DESCRIPTOR tmp; int offset = (_import_directory_count * sizeof(___IMAGE_IMPORT_DESCRIPTOR)) + _import_directory_address; fseek(Ppefile, offset, SEEK_SET); fread(&tmp, sizeof(___IMAGE_IMPORT_DESCRIPTOR), 1, Ppefile); if (tmp.Name == 0x00000000 && tmp.FirstThunk == 0x00000000) { _import_directory_count -= 1; _import_directory_size = _import_directory_count * sizeof(___IMAGE_IMPORT_DESCRIPTOR); break; } _import_directory_count++; } PEFILE_IMPORT_TABLE = new ___IMAGE_IMPORT_DESCRIPTOR[_import_directory_count]; for (int i = 0; i < _import_directory_count; i++) { int offset = (i * sizeof(___IMAGE_IMPORT_DESCRIPTOR)) + _import_directory_address; fseek(Ppefile, offset, SEEK_SET); fread(&PEFILE_IMPORT_TABLE[i], sizeof(___IMAGE_IMPORT_DESCRIPTOR), 1, Ppefile); } } ``` * Lấy **RVA** của `Import Directory` → `resolve()` → `offset`. * Duyệt từng `IMAGE_IMPORT_DESCRIPTOR` đến khi gặp ``{0,0}`` kết thúc. * Sau đó đọc toàn bộ các `entries` vào mảng. ##### ParseBaseReloc() ```py void PE64FILE::ParseBaseReloc() { DWORD _basereloc_directory_address = resolve(PEFILE_BASERELOC_DIRECTORY.VirtualAddress, locate(PEFILE_BASERELOC_DIRECTORY.VirtualAddress)); _basreloc_directory_count = 0; int _basereloc_size_counter = 0; while (true) { ___IMAGE_BASE_RELOCATION tmp; int offset = (_basereloc_size_counter + _basereloc_directory_address); fseek(Ppefile, offset, SEEK_SET); fread(&tmp, sizeof(___IMAGE_BASE_RELOCATION), 1, Ppefile); if (tmp.VirtualAddress == 0x00000000 && tmp.SizeOfBlock == 0x00000000) { break; } _basreloc_directory_count++; _basereloc_size_counter += tmp.SizeOfBlock; } PEFILE_BASERELOC_TABLE = new ___IMAGE_BASE_RELOCATION[_basreloc_directory_count]; _basereloc_size_counter = 0; for (int i = 0; i < _basreloc_directory_count; i++) { int offset = _basereloc_directory_address + _basereloc_size_counter; fseek(Ppefile, offset, SEEK_SET); fread(&PEFILE_BASERELOC_TABLE[i], sizeof(___IMAGE_BASE_RELOCATION), 1, Ppefile); _basereloc_size_counter += PEFILE_BASERELOC_TABLE[i].SizeOfBlock; } } ``` * Lấy **RVA** bảng **Reloc** → **offset**. * Duyệt qua từng `IMAGE_BASE_RELOCATION` block (có `SizeOfBlock`). * Tính toán số lượng entries trong mỗi `block = (SizeOfBlock - 8)/2`. Cụ thể hơn thì chính là `ENTRIES = (BLOCKSIZE - sizeof(___IMAGE_BASE_RELOCATION)) / sizeof(WORD);` tại hàm `PrintBaseRelocationsInfo()`. ##### ParseRichHeader() ```py void PE64FILE::ParseRichHeader() { char* dataPtr = new char[PEFILE_DOS_HEADER_LFANEW]; fseek(Ppefile, 0, SEEK_SET); fread(dataPtr, PEFILE_DOS_HEADER_LFANEW, 1, Ppefile); int index_ = 0; for (int i = 0; i <= PEFILE_DOS_HEADER_LFANEW; i++) { if (dataPtr[i] == 0x52 && dataPtr[i + 1] == 0x69) { index_ = i; break; } } if (index_ == 0) { printf("Error while parsing Rich Header."); PEFILE_RICH_HEADER_INFO.entries = 0; return; } char key[4]; memcpy(key, dataPtr + (index_ + 4), 4); int indexpointer = index_ - 4; int RichHeaderSize = 0; while (true) { char tmpchar[4]; memcpy(tmpchar, dataPtr + indexpointer, 4); for (int i = 0; i < 4; i++) { tmpchar[i] = tmpchar[i] ^ key[i]; } indexpointer -= 4; RichHeaderSize += 4; if (tmpchar[1] = 0x61 && tmpchar[0] == 0x44) { break; } } char* RichHeaderPtr = new char[RichHeaderSize]; memcpy(RichHeaderPtr, dataPtr + (index_ - RichHeaderSize), RichHeaderSize); for (int i = 0; i < RichHeaderSize; i += 4) { for (int x = 0; x < 4; x++) { RichHeaderPtr[i + x] = RichHeaderPtr[i + x] ^ key[x]; } } PEFILE_RICH_HEADER_INFO.size = RichHeaderSize; PEFILE_RICH_HEADER_INFO.ptrToBuffer = RichHeaderPtr; PEFILE_RICH_HEADER_INFO.entries = (RichHeaderSize - 16) / 8; delete[] dataPtr; PEFILE_RICH_HEADER.entries = new RICH_HEADER_ENTRY[PEFILE_RICH_HEADER_INFO.entries]; for (int i = 16; i < RichHeaderSize; i += 8) { WORD PRODID = (uint16_t)((unsigned char)RichHeaderPtr[i + 3] << 8) | (unsigned char)RichHeaderPtr[i + 2]; WORD BUILDID = (uint16_t)((unsigned char)RichHeaderPtr[i + 1] << 8) | (unsigned char)RichHeaderPtr[i]; DWORD USECOUNT = (uint32_t)((unsigned char)RichHeaderPtr[i + 7] << 24) | (unsigned char)RichHeaderPtr[i + 6] << 16 | (unsigned char)RichHeaderPtr[i + 5] << 8 | (unsigned char)RichHeaderPtr[i + 4]; PEFILE_RICH_HEADER.entries[(i / 8) - 2] = { PRODID, BUILDID, USECOUNT }; if (i + 8 >= RichHeaderSize) { PEFILE_RICH_HEADER.entries[(i / 8) - 1] = { 0x0000, 0x0000, 0x00000000 }; } } delete[] PEFILE_RICH_HEADER_INFO.ptrToBuffer; } ``` * Đọc vùng giữa **DOS header** và **NT header** để tìm chuỗi ``"Rich"``. * Trích ra mảng **RICH_HEADER_ENTRY**, chứa: `prodID`, `buildID`, `useCount`. #### 4. Các hàm Print*() – In thông tin ra màn hình ##### PrintFileInfo() ```py void PE64FILE::PrintFileInfo() { printf(" FILE: %s\n", NAME); printf(" TYPE: 0x%X (PE32+)\n", PEFILE_NT_HEADERS_OPTIONAL_HEADER_MAGIC); } ``` * In tên file, loại **PE32+** (64-bit) ##### PrintDOSHeaderInfo() ```py void PE64FILE::PrintDOSHeaderInfo() { printf(" DOS HEADER:\n"); printf(" -----------\n\n"); printf(" Magic: 0x%X\n", PEFILE_DOS_HEADER_EMAGIC); printf(" File address of new exe header: 0x%X\n", PEFILE_DOS_HEADER_LFANEW); } ``` * In `e_magic`, `e_lfanew`. ##### PrintRichHeaderInfo() ```py void PE64FILE::PrintRichHeaderInfo() { printf(" RICH HEADER:\n"); printf(" ------------\n\n"); for (int i = 0; i < PEFILE_RICH_HEADER_INFO.entries; i++) { printf(" 0x%X 0x%X 0x%X: %d.%d.%d\n", PEFILE_RICH_HEADER.entries[i].buildID, PEFILE_RICH_HEADER.entries[i].prodID, PEFILE_RICH_HEADER.entries[i].useCount, PEFILE_RICH_HEADER.entries[i].buildID, PEFILE_RICH_HEADER.entries[i].prodID, PEFILE_RICH_HEADER.entries[i].useCount); } } ``` * In từng **RICH_HEADER_ENTRY**: `prodID`, `buildID`, `useCount`. ##### PrintNTHeadersInfo() ```py void PE64FILE::PrintNTHeadersInfo() { printf(" NT HEADERS:\n"); printf(" -----------\n\n"); printf(" PE Signature: 0x%X\n", PEFILE_NT_HEADERS_SIGNATURE); printf("\n File Header:\n\n"); printf(" Machine: 0x%X\n", PEFILE_NT_HEADERS_FILE_HEADER_MACHINE); printf(" Number of sections: 0x%X\n", PEFILE_NT_HEADERS_FILE_HEADER_NUMBER0F_SECTIONS); printf(" Size of optional header: 0x%X\n", PEFILE_NT_HEADERS_FILE_HEADER_SIZEOF_OPTIONAL_HEADER); printf("\n Optional Header:\n\n"); printf(" Magic: 0x%X\n", PEFILE_NT_HEADERS_OPTIONAL_HEADER_MAGIC); printf(" Size of code section: 0x%X\n", PEFILE_NT_HEADERS_OPTIONAL_HEADER_SIZEOF_CODE); printf(" Size of initialized data: 0x%X\n", PEFILE_NT_HEADERS_OPTIONAL_HEADER_SIZEOF_INITIALIZED_DATA); printf(" Size of uninitialized data: 0x%X\n", PEFILE_NT_HEADERS_OPTIONAL_HEADER_SIZEOF_UNINITIALIZED_DATA); printf(" Address of entry point: 0x%X\n", PEFILE_NT_HEADERS_OPTIONAL_HEADER_ADDRESSOF_ENTRYPOINT); printf(" RVA of start of code section: 0x%X\n", PEFILE_NT_HEADERS_OPTIONAL_HEADER_BASEOF_CODE); printf(" Desired image base: 0x%X\n", PEFILE_NT_HEADERS_OPTIONAL_HEADER_IMAGEBASE); printf(" Section alignment: 0x%X\n", PEFILE_NT_HEADERS_OPTIONAL_HEADER_SECTION_ALIGNMENT); printf(" File alignment: 0x%X\n", PEFILE_NT_HEADERS_OPTIONAL_HEADER_FILE_ALIGNMENT); printf(" Size of image: 0x%X\n", PEFILE_NT_HEADERS_OPTIONAL_HEADER_SIZEOF_IMAGE); printf(" Size of headers: 0x%X\n", PEFILE_NT_HEADERS_OPTIONAL_HEADER_SIZEOF_HEADERS); printf("\n Data Directories:\n"); printf("\n * Export Directory:\n"); printf(" RVA: 0x%X\n", PEFILE_EXPORT_DIRECTORY.VirtualAddress); printf(" Size: 0x%X\n", PEFILE_EXPORT_DIRECTORY.Size); printf("\n * Import Directory:\n"); printf(" RVA: 0x%X\n", PEFILE_IMPORT_DIRECTORY.VirtualAddress); printf(" Size: 0x%X\n", PEFILE_IMPORT_DIRECTORY.Size); printf("\n * Resource Directory:\n"); printf(" RVA: 0x%X\n", PEFILE_RESOURCE_DIRECTORY.VirtualAddress); printf(" Size: 0x%X\n", PEFILE_RESOURCE_DIRECTORY.Size); printf("\n * Exception Directory:\n"); printf(" RVA: 0x%X\n", PEFILE_EXCEPTION_DIRECTORY.VirtualAddress); printf(" Size: 0x%X\n", PEFILE_EXCEPTION_DIRECTORY.Size); printf("\n * Security Directory:\n"); printf(" RVA: 0x%X\n", PEFILE_SECURITY_DIRECTORY.VirtualAddress); printf(" Size: 0x%X\n", PEFILE_SECURITY_DIRECTORY.Size); printf("\n * Base Relocation Table:\n"); printf(" RVA: 0x%X\n", PEFILE_BASERELOC_DIRECTORY.VirtualAddress); printf(" Size: 0x%X\n", PEFILE_BASERELOC_DIRECTORY.Size); printf("\n * Debug Directory:\n"); printf(" RVA: 0x%X\n", PEFILE_DEBUG_DIRECTORY.VirtualAddress); printf(" Size: 0x%X\n", PEFILE_DEBUG_DIRECTORY.Size); printf("\n * Architecture Specific Data:\n"); printf(" RVA: 0x%X\n", PEFILE_ARCHITECTURE_DIRECTORY.VirtualAddress); printf(" Size: 0x%X\n", PEFILE_ARCHITECTURE_DIRECTORY.Size); printf("\n * RVA of GlobalPtr:\n"); printf(" RVA: 0x%X\n", PEFILE_GLOBALPTR_DIRECTORY.VirtualAddress); printf(" Size: 0x%X\n", PEFILE_GLOBALPTR_DIRECTORY.Size); printf("\n * TLS Directory:\n"); printf(" RVA: 0x%X\n", PEFILE_TLS_DIRECTORY.VirtualAddress); printf(" Size: 0x%X\n", PEFILE_TLS_DIRECTORY.Size); printf("\n * Load Configuration Directory:\n"); printf(" RVA: 0x%X\n", PEFILE_LOAD_CONFIG_DIRECTORY.VirtualAddress); printf(" Size: 0x%X\n", PEFILE_LOAD_CONFIG_DIRECTORY.Size); printf("\n * Bound Import Directory:\n"); printf(" RVA: 0x%X\n", PEFILE_BOUND_IMPORT_DIRECTORY.VirtualAddress); printf(" Size: 0x%X\n", PEFILE_BOUND_IMPORT_DIRECTORY.Size); printf("\n * Import Address Table:\n"); printf(" RVA: 0x%X\n", PEFILE_IAT_DIRECTORY.VirtualAddress); printf(" Size: 0x%X\n", PEFILE_IAT_DIRECTORY.Size); printf("\n * Delay Load Import Descriptors:\n"); printf(" RVA: 0x%X\n", PEFILE_DELAY_IMPORT_DIRECTORY.VirtualAddress); printf(" Size: 0x%X\n", PEFILE_DELAY_IMPORT_DIRECTORY.Size); printf("\n * COM Runtime Descriptor:\n"); printf(" RVA: 0x%X\n", PEFILE_COM_DESCRIPTOR_DIRECTORY.VirtualAddress); printf(" Size: 0x%X\n", PEFILE_COM_DESCRIPTOR_DIRECTORY.Size); } ``` * In `Signature`, `FileHeader`, `OptionalHeader` * In toàn bộ các **DataDirectory** (`Export`, `Import`, `Reloc`, `TLS`...) ##### PrintSectionHeadersInfo() ```py void PE64FILE::PrintSectionHeadersInfo() { printf(" SECTION HEADERS:\n"); printf(" ----------------\n\n"); for (int i = 0; i < PEFILE_NT_HEADERS_FILE_HEADER_NUMBER0F_SECTIONS; i++) { printf(" * %.8s:\n", PEFILE_SECTION_HEADERS[i].Name); printf(" VirtualAddress: 0x%X\n", PEFILE_SECTION_HEADERS[i].VirtualAddress); printf(" VirtualSize: 0x%X\n", PEFILE_SECTION_HEADERS[i].Misc.VirtualSize); printf(" PointerToRawData: 0x%X\n", PEFILE_SECTION_HEADERS[i].PointerToRawData); printf(" SizeOfRawData: 0x%X\n", PEFILE_SECTION_HEADERS[i].SizeOfRawData); printf(" Characteristics: 0x%X\n\n", PEFILE_SECTION_HEADERS[i].Characteristics); } } ``` * In thông tin từng section: * `.text`, `.data`, `.rdata`, ... * `VirtualAddress`, `PointerToRawData`, `Size`, `Characteristics`. ##### PrintImportTableInfo() ```py void PE64FILE::PrintImportTableInfo() { printf(" IMPORT TABLE:\n"); printf(" ----------------\n\n"); for (int i = 0; i < _import_directory_count; i++) { DWORD NameAddr = resolve(PEFILE_IMPORT_TABLE[i].Name, locate(PEFILE_IMPORT_TABLE[i].Name)); int NameSize = 0; while (true) { char tmp; fseek(Ppefile, (NameAddr + NameSize), SEEK_SET); fread(&tmp, sizeof(char), 1, Ppefile); if (tmp == 0x00) { break; } NameSize++; } char* Name = new char[NameSize + 2]; fseek(Ppefile, NameAddr, SEEK_SET); fread(Name, (NameSize * sizeof(char)) + 1, 1, Ppefile); printf(" * %s:\n", Name); delete[] Name; printf(" ILT RVA: 0x%X\n", PEFILE_IMPORT_TABLE[i].DUMMYUNIONNAME.OriginalFirstThunk); printf(" IAT RVA: 0x%X\n", PEFILE_IMPORT_TABLE[i].FirstThunk); if (PEFILE_IMPORT_TABLE[i].TimeDateStamp == 0) { printf(" Bound: FALSE\n"); } else if (PEFILE_IMPORT_TABLE[i].TimeDateStamp == -1) { printf(" Bound: TRUE\n"); } printf("\n"); DWORD ILTAddr = resolve(PEFILE_IMPORT_TABLE[i].DUMMYUNIONNAME.OriginalFirstThunk, locate(PEFILE_IMPORT_TABLE[i].DUMMYUNIONNAME.OriginalFirstThunk)); int entrycounter = 0; while (true) { ILT_ENTRY_64 entry; fseek(Ppefile, (ILTAddr + (entrycounter * sizeof(QWORD))), SEEK_SET); fread(&entry, sizeof(ILT_ENTRY_64), 1, Ppefile); BYTE flag = entry.ORDINAL_NAME_FLAG; DWORD HintRVA = 0x0; WORD ordinal = 0x0; if (flag == 0x0) { HintRVA = entry.FIELD_2.HINT_NAME_TABE; } else if (flag == 0x01) { ordinal = entry.FIELD_2.ORDINAL; } if (flag == 0x0 && HintRVA == 0x0 && ordinal == 0x0) { break; } printf("\n Entry:\n"); if (flag == 0x0) { ___IMAGE_IMPORT_BY_NAME hint; DWORD HintAddr = resolve(HintRVA, locate(HintRVA)); fseek(Ppefile, HintAddr, SEEK_SET); fread(&hint, sizeof(___IMAGE_IMPORT_BY_NAME), 1, Ppefile); printf(" Name: %s\n", hint.Name); printf(" Hint RVA: 0x%X\n", HintRVA); printf(" Hint: 0x%X\n", hint.Hint); } else if (flag == 1) { printf(" Ordinal: 0x%X\n", ordinal); } entrycounter++; } printf("\n ----------------------\n\n"); } } ``` * In từng DLL được import: `"KERNEL32.dll"`, ``"USER32.dll"`` ... * In **ILT/IAT**, phân biệt dùng **Ordinal** hay **Name**. * Duyệt qua **ILT_ENTRY_64**, phân biệt qua **ORDINAL_NAME_FLAG**. ##### PrintBaseRelocationsInfo() ```py void PE64FILE::PrintBaseRelocationsInfo() { printf(" BASE RELOCATIONS TABLE:\n"); printf(" -----------------------\n"); int szCounter = sizeof(___IMAGE_BASE_RELOCATION); for (int i = 0; i < _basreloc_directory_count; i++) { DWORD PAGERVA, BLOCKSIZE, BASE_RELOC_ADDR; int ENTRIES; BASE_RELOC_ADDR = resolve(PEFILE_BASERELOC_DIRECTORY.VirtualAddress, locate(PEFILE_BASERELOC_DIRECTORY.VirtualAddress)); PAGERVA = PEFILE_BASERELOC_TABLE[i].VirtualAddress; BLOCKSIZE = PEFILE_BASERELOC_TABLE[i].SizeOfBlock; ENTRIES = (BLOCKSIZE - sizeof(___IMAGE_BASE_RELOCATION)) / sizeof(WORD); printf("\n Block 0x%X: \n", i); printf(" Page RVA: 0x%X\n", PAGERVA); printf(" Block size: 0x%X\n", BLOCKSIZE); printf(" Number of entries: 0x%X\n", ENTRIES); printf("\n Entries:\n"); for (int i = 0; i < ENTRIES; i++) { BASE_RELOC_ENTRY entry; int offset = (BASE_RELOC_ADDR + szCounter + (i * sizeof(WORD))); fseek(Ppefile, offset, SEEK_SET); fread(&entry, sizeof(WORD), 1, Ppefile); printf("\n * Value: 0x%X\n", entry); printf(" Relocation Type: 0x%X\n", entry.TYPE); printf(" Offset: 0x%X\n", entry.OFFSET); } printf("\n ----------------------\n\n"); szCounter += BLOCKSIZE; } } ``` * In các Block trong `Reloc Table`: * Mỗi block có **Page RVA** và các entry con. * Mỗi entry hiển thị: `Type`, `Offset` #### 5. ParseFile() và PrintInfo() ```py // MAIN void PE64FILE::ParseFile() { // PARSE DOS HEADER ParseDOSHeader(); // PARSE RICH HEADER ParseRichHeader(); //PARSE NT HEADERS ParseNTHeaders(); // PARSE SECTION HEADERS ParseSectionHeaders(); // PARSE IMPORT DIRECTORY ParseImportDirectory(); // PARSE BASE RELOCATIONS ParseBaseReloc(); } void PE64FILE::PrintInfo() { printf("\n\n"); PrintFileInfo(); printf("\n ----------------------------------\n\n"); PrintDOSHeaderInfo(); printf("\n ----------------------------------\n\n"); PrintRichHeaderInfo(); printf("\n ----------------------------------\n\n"); PrintNTHeadersInfo(); printf("\n ----------------------------------\n\n"); PrintSectionHeadersInfo(); printf("\n ----------------------------------\n\n"); PrintImportTableInfo(); printf("\n ----------------------------------\n\n"); PrintBaseRelocationsInfo(); printf("\n ----------------------------------\n\n"); return; } ``` * Chỉ là chạy tuần tự từng **parser**, sau đó in tất cả thông tin đã được phân tích thuiii. * Như ở trên mình cũng có nói rồi á, kiến trúc 32 bit về logic xây dựng tương tự chỉ khác nhau ở kiểu dữ liệu thoi. ### Linh tinh ... * Logic chính của code **PE Parser** này chỉ có vậy, cuối cùng chỉ là 1 vài hàm nữa đơn giản hơn. Dưới đây là entry point cho chương trình parser **PE file** hoàn chỉnh: ```py #include <iostream> #include <fstream> #include "PEFILE.h" int main(int argc, char* argv[]) { if (argc != 2) { printf("Usage: %s [path to executable]\n", argv[0]); return 1; } FILE * PpeFile; fopen_s(&PpeFile, argv[1], "rb"); if (PpeFile == NULL) { printf("Can't open file.\n"); return 1; } if (INITPARSE(PpeFile) == 1) { exit(1); } else if (INITPARSE(PpeFile) == 32) { PE32FILE PeFile_1(argv[1], PpeFile); PeFile_1.PrintInfo(); fclose(PpeFile); exit(0); } else if (INITPARSE(PpeFile) == 64) { PE64FILE PeFile_1(argv[1], PpeFile); PeFile_1.PrintInfo(); fclose(PpeFile); exit(0); } return 0; } ``` * Phân tích thì có lẽ không cần thiết, nhưng mình vẫn sẽ giải thích qua qua mấy mẩu code: ```py if (argc != 2) { printf("Usage: %s [path to executable]\n", argv[0]); return 1; } ``` * Kiểm tra truyền đúng đối số (đường dẫn file). ```py FILE * PpeFile; fopen_s(&PpeFile, argv[1], "rb"); if (PpeFile == NULL) { printf("Can't open file.\n"); return 1; } ``` * Mở file chế độ `rb` → gán vào **PpeFile**. Không mở được thì báo lỗi. ```py if (INITPARSE(PpeFile) == 1) { exit(1); } else if (INITPARSE(PpeFile) == 32) { PE32FILE PeFile_1(argv[1], PpeFile); PeFile_1.PrintInfo(); fclose(PpeFile); exit(0); } else if (INITPARSE(PpeFile) == 64) { PE64FILE PeFile_1(argv[1], PpeFile); PeFile_1.PrintInfo(); fclose(PpeFile); exit(0); } ``` * Nếu `INITPARSE()` trả về $1$ → lỗi khi xác định định dạng **PE**. * Nếu file là **PE32 / PE64**: * Tạo đối tượng `PE32FILE / PE64FILE` để phân tích. * Gọi `PrintInfo()` để in thông tin ra console. * Đóng file. * Chi tiết hàm `INITPARSE()`: ```py #include "PEFILE.h" // INITIAL PARSE // int INITPARSE(FILE* PpeFile) { ___IMAGE_DOS_HEADER TMP_DOS_HEADER; WORD PEFILE_TYPE; fseek(PpeFile, 0, SEEK_SET); fread(&TMP_DOS_HEADER, sizeof(___IMAGE_DOS_HEADER), 1, PpeFile); if (TMP_DOS_HEADER.e_magic != ___IMAGE_DOS_SIGNATURE) { printf("Error. Not a PE file.\n"); return 1; } fseek(PpeFile, (TMP_DOS_HEADER.e_lfanew + sizeof(DWORD) + sizeof(___IMAGE_FILE_HEADER)), SEEK_SET); fread(&PEFILE_TYPE, sizeof(WORD), 1, PpeFile); if (PEFILE_TYPE == ___IMAGE_NT_OPTIONAL_HDR32_MAGIC) { return 32; } else if (PEFILE_TYPE == ___IMAGE_NT_OPTIONAL_HDR64_MAGIC) { return 64; } else { printf("Error while parsing IMAGE_OPTIONAL_HEADER.Magic. Unknown Type.\n"); return 1; } } ``` * Chức năng chính xác định **PE file**: * Không hợp lệ (`return 1`) * **PE32** (32-bit) (`return 32`) * **PE32+** (64-bit) (`return 64`) * Được rồi. Vậy có thể nói tổng thể xây dựng ~~1 mẩu~~ code **PE Parser** đã xong ~~(khá là chubby)~~. Thực chất là mình ~~bú~~ của [0xRick](https://0xrick.github.io/win-internals/pe8/), tham khảo và sau đó giải thích lại logic code theo những kiến thức mình biết thôi. Hẹ hẹ hẹ.... ## Build và kiểm chứng code ### Build file * Đây là cây thư mục mà khi mình ~~git clone~~ của [0xRick](https://0xrick.github.io/win-internals/pe8/). Cá nhân mình thấy thì gộp lại thành 1 file rồi compile thì không vấn đề gì, nhưng chia nhỏ ra để tiện theo dõi cũng oke, nên mình vẫn giữ nguyên trạng thái thư mục, không thay đổi gì cả :)) ![image](https://hackmd.io/_uploads/S1ZBWR7Ixe.png) ### Build 32 bit * Bước 1: Từ **Start Menu**, tìm `x86 Native Tools Command Prompt for VS 2022` * Bước 2: Di chuyển về đúng thư mục. * Bước 3: Compile với `cl.exe`. Như sau: `cl /EHsc /FePE_Parser_32bit.exe PE-Parser.cpp PEFILE.cpp PE32FILE.cpp PE64FILE.cpp` * `/EHsc`: bật xử lý exception **C++** * `/FePE_Parser_32bit.exe`: đặt tên output là `PE_Parser_32bit.exe` ### Build 64 bit * Bước 1: Từ **Start Menu**, tìm `x64 Native Tools Command Prompt for VS 2022` * Bước 2: Di chuyển về đúng thư mục. * Bước 3: Compile với `cl.exe`. Như sau: `cl /EHsc /FePE_Parser_64bit.exe PE-Parser.cpp PEFILE.cpp PE32FILE.cpp PE64FILE.cpp` * `/EHsc`: bật xử lý exception **C++** * `/FePE_Parser_64bit.exe`: đặt tên output là `PE_Parser_64bit.exe` ### Test ... * Sau khi compile và chạy thử với file `notepad.exe`, thì output khá dài nên không patse vô đây được, mình chỉ đưa ra các thông tin mà quan trọng thôi nhe. Các hàm hay logic thì ở trên mình cũng có nói, phần này chủ yếu là kết hợp với **CFF Explorer** để xem code chạy đúng không nha. 😶‍🌫️ ### Type và file ![image](https://hackmd.io/_uploads/S1ojiAmLgg.png) * Yea không có gì bàn cãi :)) * Nếu `0x10B` thì là `PE32` nha :)) ### DOS HEADER ![image](https://hackmd.io/_uploads/ryav2AX8gl.png) * Magic: `0x5A4D` * Đây là `e_magic` trong **IMAGE_DOS_HEADER** * `0x5A4D = "MZ"`: dấu hiệu nhận diện một file **EXE** hợp lệ * Luôn nằm ở offset `0x0` của file. ![Screenshot 2025-07-15 204941](https://hackmd.io/_uploads/Hy8phC7Ige.png) * `0xF0` đây là `e_lfanew` trong **IMAGE_DOS_HEADER**, offset chỉ tới `NT Headers` * Tức là tại offset `0xF0`, sẽ thấy ``"PE\0\0"``. ![Screenshot 2025-07-15 205629](https://hackmd.io/_uploads/rkvvRAXUee.png) ### RICH HEADER ![image](https://hackmd.io/_uploads/H15kykEUge.png) * **Rich Header** là một phần ẩn nằm giữa `DOS Stub` và `NT Header` ----> **Rich Header** luôn nằm trước offset `e_lfanew` ![Screenshot 2025-07-15 211739](https://hackmd.io/_uploads/r1bw714Iee.png) * Nó không được **Microsoft** công khai tài liệu nhưng chứa thông tin về: * Phiên bản Linker * Công cụ build (e.g. VS version) * Số lượng module được build (compilers, linkers, assemblers...) * Kiểu máy (target machine) * Một vài `ProductID` tiêu biểu: | ProductID (Dec) | Hex | Công cụ MSVC | | --------------- | ----- | ------------------------- | | `0xC9 = 201` | `201` | MASM Assembler | | `0x93 = 147` | `147` | `cl.exe` Compiler (C/C++) | | `0x101 = 257` | `257` | Linker | | `0x103 = 259` | `259` | Export | | `0x104 = 260` | `260` | Import | | `0x108 = 264` | `264` | .NET Assembly tools | | `0xFF = 255` | `255` | Unknown / filler | * Dựa trên **Rich Header**, file `notepad.exe` được build bằng: * `CL.exe` (147) → biên dịch C++ * `Link.exe` (257) * `ALINK.exe` (258) * Các builder khác: `Resource`, `Import`, `Export` * Build bằng: `MSVC v142` (VS 2022) – build `30795` * So sánh với 1 vài thông tin ở **Detect It Easy** thì khá khớp nha :)) ![image](https://hackmd.io/_uploads/rypFbFVUee.png) ### NT HEADERS ```py NT HEADERS: ----------- PE Signature: 0x4550 File Header: Machine: 0x8664 Number of sections: 0x7 Size of optional header: 0xF0 Optional Header: Magic: 0x20B Size of code section: 0x28000 Size of initialized data: 0x31000 Size of uninitialized data: 0x0 Address of entry point: 0x19A0 RVA of start of code section: 0x1000 Desired image base: 0x40000000 Section alignment: 0x1000 File alignment: 0x1000 Size of image: 0x5A000 Size of headers: 0x1000 Data Directories: * Export Directory: RVA: 0x0 Size: 0x0 * Import Directory: RVA: 0x30900 Size: 0x3FC * Resource Directory: RVA: 0x3A000 Size: 0x1E1D0 * Exception Directory: RVA: 0x37000 Size: 0x1464 * Security Directory: RVA: 0x0 Size: 0x0 * Base Relocation Table: RVA: 0x59000 Size: 0x2F8 * Debug Directory: RVA: 0x2E450 Size: 0x54 * Architecture Specific Data: RVA: 0x0 Size: 0x0 * RVA of GlobalPtr: RVA: 0x0 Size: 0x0 * TLS Directory: RVA: 0x0 Size: 0x0 * Load Configuration Directory: RVA: 0x29790 Size: 0x140 * Bound Import Directory: RVA: 0x0 Size: 0x0 * Import Address Table: RVA: 0x298D0 Size: 0xB68 * Delay Load Import Descriptors: RVA: 0x30438 Size: 0xE0 * COM Runtime Descriptor: RVA: 0x0 Size: 0x0 ---------------------------------- ``` * Phân tích qua các giá trị: * `PE Signature: 0x4550` cái này thì thôi nha :))) #### File Header (COFF Header) ![image](https://hackmd.io/_uploads/SJhWBFNUle.png) * Giá trị: `0x8664 = IMAGE_FILE_MACHINE_AMD64` ----> File được compile cho kiến trúc `x64` (64-bit) * `Number of sections (0x7)` * `Size of optional header (0xF0)` * Ta có thể xác nhận thông qua thông tin mà **CFF Explorer** dưới đây nha: ![Screenshot 2025-07-16 085033](https://hackmd.io/_uploads/HJek8YELxl.png) #### Optional Header * `Magic (0x20B) = IMAGE_NT_OPTIONAL_HDR64_MAGIC`, đây là `PE32+`. * `Size of code section (0x28000)`: Tổng kích thước của tất cả các section chứa code. * `Size of initialized data (0x31000)`: Tổng kích thước data đã được khởi tạo. * `Address of entry point (0x19A0)`: `RVA = 0x19A0`, địa chỉ tương đối của hàm `main/entry point`. * `RVA of start of code section (0x1000)`: `RVA` của section chứa code đầu tiên. * `Section alignment (0x1000) & File alignment (0x1000)`: alignment trong memory và alignment trong file. * `Size of image (0x5A000)`: Tổng kích thước của image khi được load vào memory. * `Size of headers (0x1000)`: Kích thước của tất cả headers (`DOS + NT + Section Headers`) * Đối chiếu các giá trị đó trong **CFF Explorer** juan nha: ![image](https://hackmd.io/_uploads/rJ0lcFEUeg.png) * Còn các **Data Directories** thì verify tương tự nha, thực chất nó cũng chuẩn đó. ![image](https://hackmd.io/_uploads/HyarcFELlx.png) #### Section Headers ```py SECTION HEADERS: ---------------- * .text: VirtualAddress: 0x1000 VirtualSize: 0x27F42 PointerToRawData: 0x1000 SizeOfRawData: 0x28000 Characteristics: 0x60000020 * .rdata: VirtualAddress: 0x29000 VirtualSize: 0xA608 PointerToRawData: 0x29000 SizeOfRawData: 0xB000 Characteristics: 0x40000040 * .data: VirtualAddress: 0x34000 VirtualSize: 0x26C0 PointerToRawData: 0x34000 SizeOfRawData: 0x1000 Characteristics: 0xC0000040 * .pdata: VirtualAddress: 0x37000 VirtualSize: 0x1464 PointerToRawData: 0x35000 SizeOfRawData: 0x2000 Characteristics: 0x40000040 * .didat: VirtualAddress: 0x39000 VirtualSize: 0xF8 PointerToRawData: 0x37000 SizeOfRawData: 0x1000 Characteristics: 0xC0000040 * .rsrc: VirtualAddress: 0x3A000 VirtualSize: 0x1E1D0 PointerToRawData: 0x38000 SizeOfRawData: 0x1F000 Characteristics: 0x40000040 * .reloc: VirtualAddress: 0x59000 VirtualSize: 0x2F8 PointerToRawData: 0x57000 SizeOfRawData: 0x1000 Characteristics: 0x42000040 ``` * Các giá trị y hệt luôn. ![image](https://hackmd.io/_uploads/SyUJoYELel.png) ### Linh tinh ... * Tiếp sau đó là các giá trị cũng chính xác như ở trên mình đã phân tích. Cụ thể hơn thì là `Import Table` - danh sách các `API functions` mà **PE file** sẽ gọi từ các `DLL`, như là `GDI32.dll`, `USER32.dll`, ..., sau đó là các [Universal CRT (Universal C Runtime)](https://support.microsoft.com/en-us/topic/update-for-universal-c-runtime-in-windows-c0514201-7fe6-95a3-b0a5-287930f3560c) và [Windows API Set DLLs](https://learn.microsoft.com/en-us/windows/win32/apiindex/windows-apisets), chủ yếu là phục vụ runtime. ~~(theo mình tìm hiểu là thế)~~ :)) * **CFF Explorer** cũng cho ra kết quả tương tự: ![image](https://hackmd.io/_uploads/SJiSaFVIex.png) ## Tổng kết * Cá nhân mình thấy vọc vạch tự code hay tham khảo từ các nguồn về **PE Parser** này cũng hay, nhưng theo mình thấy thì dùng **tools** vẫn tiện lợi và dễ dàng theo dõi hơn. Còn kha khá nguồn code hay mình tìm được để ở dưới, hoặc các bạn cũng có thể tìm được rất nhiều, nhưng chung quy lại về output thì mình nghĩ cũng không quá khác gì nhau đâu. * Vậy ở wu này mình đã trình bày code **PE Parser** (~~thực ra là đi bú của [0xRick](https://0xrick.github.io/win-internals/pe8/))~~, mình có điểm qua cấu trúc của 1 file **PE**, kết hợp với **CFF Explorer** để kiểm chứng tính đúng đắn của code. Trong lúc tổng hợp wu có thể có những sai sót, các bạn đọc tham khảo có thể góp ý để wu mình hoàn thiện hơn. Tới đây là kết thúc wu rồi, chúc các bạn 1 ngày tốt lành và mong wu này giúp ích cho các bạn!!! ## Refs https://0xrick.github.io/win-internals/pe8/ https://www.ired.team/miscellaneous-reversing-forensics/windows-kernel-internals/pe-file-header-parser-in-c++ https://chuongdong.com/reverse%20engineering/2020/08/15/PE-Parser/ https://github.com/trailofbits/pe-parse https://bladchan.github.io/2023/08/03/%E4%B8%80%E6%AD%A5%E4%B8%80%E6%AD%A5%E6%89%8B%E6%90%93%E4%B8%80%E4%B8%AA%E7%AE%80%E5%8D%95%E7%9A%84PE%E8%A7%A3%E6%9E%90%E5%99%A8/

Import from clipboard

Paste your markdown or webpage here...

Advanced permission required

Your current role can only read. Ask the system administrator to acquire write and comment permission.

This team is disabled

Sorry, this team is disabled. You can't edit this note.

This note is locked

Sorry, only owner can edit this note.

Reach the limit

Sorry, you've reached the max length this note can be.
Please reduce the content or divide it to more notes, thank you!

Import from Gist

Import from Snippet

or

Export to Snippet

Are you sure?

Do you really want to delete this note?
All users will lose their connection.

Create a note from template

Create a note from template

Oops...
This template has been removed or transferred.
Upgrade
All
  • All
  • Team
No template.

Create a template

Upgrade

Delete template

Do you really want to delete this template?
Turn this template into a regular note and keep its content, versions, and comments.

This page need refresh

You have an incompatible client version.
Refresh to update.
New version available!
See releases notes here
Refresh to enjoy new features.
Your user state has changed.
Refresh to load new user state.

Sign in

Forgot password

or

By clicking below, you agree to our terms of service.

Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
Wallet ( )
Connect another wallet

New to HackMD? Sign up

Help

  • English
  • 中文
  • Français
  • Deutsch
  • 日本語
  • Español
  • Català
  • Ελληνικά
  • Português
  • italiano
  • Türkçe
  • Русский
  • Nederlands
  • hrvatski jezik
  • język polski
  • Українська
  • हिन्दी
  • svenska
  • Esperanto
  • dansk

Documents

Help & Tutorial

How to use Book mode

Slide Example

API Docs

Edit in VSCode

Install browser extension

Contacts

Feedback

Discord

Send us email

Resources

Releases

Pricing

Blog

Policy

Terms

Privacy

Cheatsheet

Syntax Example Reference
# Header Header 基本排版
- Unordered List
  • Unordered List
1. Ordered List
  1. Ordered List
- [ ] Todo List
  • Todo List
> Blockquote
Blockquote
**Bold font** Bold font
*Italics font* Italics font
~~Strikethrough~~ Strikethrough
19^th^ 19th
H~2~O H2O
++Inserted text++ Inserted text
==Marked text== Marked text
[link text](https:// "title") Link
![image alt](https:// "title") Image
`Code` Code 在筆記中貼入程式碼
```javascript
var i = 0;
```
var i = 0;
:smile: :smile: Emoji list
{%youtube youtube_id %} Externals
$L^aT_eX$ LaTeX
:::info
This is a alert area.
:::

This is a alert area.

Versions and GitHub Sync
Get Full History Access

  • Edit version name
  • Delete

revision author avatar     named on  

More Less

Note content is identical to the latest version.
Compare
    Choose a version
    No search result
    Version not found
Sign in to link this note to GitHub
Learn more
This note is not linked with GitHub
 

Feedback

Submission failed, please try again

Thanks for your support.

On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

Please give us some advice and help us improve HackMD.

 

Thanks for your feedback

Remove version name

Do you want to remove this version name and description?

Transfer ownership

Transfer to
    Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

      Link with GitHub

      Please authorize HackMD on GitHub
      • Please sign in to GitHub and install the HackMD app on your GitHub repo.
      • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
      Learn more  Sign in to GitHub

      Push the note to GitHub Push to GitHub Pull a file from GitHub

        Authorize again
       

      Choose which file to push to

      Select repo
      Refresh Authorize more repos
      Select branch
      Select file
      Select branch
      Choose version(s) to push
      • Save a new version and push
      • Choose from existing versions
      Include title and tags
      Available push count

      Pull from GitHub

       
      File from GitHub
      File from HackMD

      GitHub Link Settings

      File linked

      Linked by
      File path
      Last synced branch
      Available push count

      Danger Zone

      Unlink
      You will no longer receive notification when GitHub file changes after unlink.

      Syncing

      Push failed

      Push successfully