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
    • Invite by email
      Invitee

      This note has no invitees

    • 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
    • Note Insights
    • 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 Versions and GitHub Sync Note Insights Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
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
  • Invite by email
    Invitee

    This note has no invitees

  • 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 file và những gì liên quan ## PE file là gì? * [PE (Portable Executable)](https://en.wikipedia.org/wiki/Portable_Executable) là định dạng tệp nhị phân được sử dụng chủ yếu trong hệ điều hành Windows cho các tệp thực thi **(EXE)**, thư viện liên kết động **(DLL)**, và các loại file chạy khác. Định dạng này mô tả cấu trúc cần thiết để hệ điều hành có thể tải và thực thi chương trình, bao gồm thông tin về mã máy, dữ liệu, thư viện cần thiết, và thông tin định vị các thành phần bên trong tệp. * Tên gọi ``“portable”`` phản ánh tính linh hoạt của định dạng này trong việc hỗ trợ nhiều nền tảng phần cứng, mặc dù nó chủ yếu được sử dụng trên **Windows**. Định dạng **PE** được xây dựng dựa trên định dạng **COFF (Common Object File Format)** và cũng là nền tảng cho định dạng thực thi trong `.NET` (tức là file **.NET PE**). ## 1 số 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 1 file PE ### Cấu trúc cơ bản ![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) ### DOS Header và DOS Stub * Đối với các loại file ảnh như **PNG**, **JPG**, hay âm thanh như **MP3**, **MP4**, hoặc tệp nén như **ZIP**, **RAR**, .... đều có định dạng riêng. Để có thể phân biệt, nhận diện các loại file, người ta sẽ dùng phần đầu để xác định, gọi là **header**. Trong phần **header** này có chứa 1 dãy byte, gọi là **magic byte**, đóng vai trò như **signature** (chữ ký) của file đó. * Ví dụ với **PNG**, thì mọi người chỉ cần search [PNG header signature](https://asecuritysite.com/forensics/png?file=%2Flog%2Fbasn0g01.png) là có ngay. Ở đây mình thử mở bằng **CFF Explorer**. ![Screenshot 2025-05-20 072203](https://hackmd.io/_uploads/BJKIiStbel.png) * Tương tự với các loại file khác. * Còn khi thử mở 1 file **PE** trên **CFF Explorer**: ![Screenshot 2025-05-21 131642](https://hackmd.io/_uploads/B1dGegi-xe.png) * Mở đầu 1 file **PE** đều là **DOS Header** và **DOS Stub**. Trong hệ điều hành **Windows** hiện đại bây giờ, 2 giá trị này không còn quan trọng với **PE** file. Nhưng nếu như nó không quan trọng, thì tại sao vẫn giữ lại trong cấu trúc **PE** file? :::success * Tham khảo ở [đây](https://0xrick.github.io/win-internals/pe3/#dos-header), thì thấy rằng lý do mà nó vẫn còn được xuất hiện là vì mục đích tương thích ngược với các hệ điều hành cũ như **MS-DOS**. Trong giai đoạn chuyển đổi từ **MS-DOS** sang **Windows NT**, nhờ có **DOS Header**, tệp **PE** có thể được nhận dạng như là một file thực thi của **MS-DOS**. * Khi tệp này được chạy trong môi trường **MS-DOS** (thay vì **Windows**), phần tiếp theo là **DOS Stub** sẽ được thực thi thay thế cho chương trình chính (vì **MS-DOS** không hiểu định dạng **PE**). * Ở hình ảnh trên thì có thể thấy, thông báo `"This program cannot be run in DOS mode"` xuất hiện trong **DOS Stub**. ::: * Vậy: :::info * **DOS Header**: $64$ bytes đầu tiên của tất cả các file **PE**. Có tác dụng tệp **PE** có thể được nhận dạng như là một file thực thi của **MS-DOS**. * **DOS Stub**: Là nơi ngay sau **DOS Header**, có chức năng hiển thị thông báo lỗi `"This program cannot be run in DOS mode"` khi chương trình được khởi chạy trong **DOS** mode. ::: * **structure DOS Header** là một cấu trúc được định nghĩa trong các file `windows.inc` hoặc `winnt.h`. Như hình dưới đây: ![image](https://hackmd.io/_uploads/HJQkmxjZgl.png) ![image](https://hackmd.io/_uploads/rJll7eoZgx.png) * Ta thấy 2 bytes magic là `4D, 5A` tương ứng `MZ`, là tên viết tắt của người sáng tạo chính của **MS-DOS** - Mark Zbikowsky. Và giá trị này tương ứng với offset `e_magic`. * Ngoài ra, 1 trường khác cũng quan trọng cần chú ý đó là `e_lfanew`, có vị trí tại offset $0x3C$. ![Screenshot 2025-05-21 134015](https://hackmd.io/_uploads/BkHYSgoZgg.png) * Có thể thấy giá trị tại đó là $0x108$ ![Screenshot 2025-05-21 133808](https://hackmd.io/_uploads/rkmErxs-xl.png) * Giá trị này xác định vị trí bắt đầu của **PE header**. **Windows Loader** sẽ tìm kiếm offset này vì vậy nó có thể bỏ qua **DOS Stub** và đi trực tiếp tới **PE** file. * Có thể thấy tại $0x108$ bắt đầu bằng **PE** (giá trị `50,45` hexa), đây chính là signature của **PE header**. * Trước khi kết thúc phần **DOS Header,DOS Stub** và đi tìm hiểu tiếp về **PE header**, chúng ta có thắc mắc là phần ở giữa **DOS Stub** và **PE header** là gì? * Sau khi ~~nhặt nhạnh~~ ở [đây](https://0xrick.github.io/win-internals/pe3/#rich-header), thì phần đó chính là **Rich-Header**, có ít tài liệu nhắc tới do nó không gây ảnh hưởng gì cho chương trình, chỉ để lưu lại thông tin chương trình biên dịch Visual Studio. ![image](https://hackmd.io/_uploads/ryaPugoZxg.png) ### PE Header * Hay còn gọi là **NT Header**, gồm 3 phần chính là **Signature**, **File Header**, và **Optional Header** được định nghĩa trong file **windows.inc**: ![image](https://hackmd.io/_uploads/Bkg5rqlsWxl.png) ![Screenshot 2025-05-21 140442](https://hackmd.io/_uploads/ByYmiejWle.png) #### **Signature** * Như trên phần **DOS Header,DOS Stub** cũng có giới thiệu rồi, thì nó là giá trị hexa `50,45,00,00` - kí tự **P,E** và theo sau là 2 byte null. Còn tại trong `CFF Explorer` thì hiển thị theo `little-endian` nên ngược nha. ![image](https://hackmd.io/_uploads/ByHnseiZex.png) #### **File Header** * Là 1 structure $20$ bytes, gồm $7$ phần được định nghĩa trong `winnt.h`: ![image](https://hackmd.io/_uploads/ryMW6xsWll.png) ![image](https://hackmd.io/_uploads/rkxJags-eg.png) ##### **Machine** * Dùng để giữ giá trị chỉ định loại bộ xử lý mà file **PE** này được thiết kế để chạy trên. Có thể thấy ở hình dưới đây, bộ xử lý `Intel i386` tương ứng $0x14C$. Ngoài ra còn có các bộ xử lý khác như `AMD64` - $0x8664$ cho kiến trúc $64$ bit, `ARM` - $0x1C0$ cho thiết bị di động và IoT. ![image](https://hackmd.io/_uploads/Hk8jCxi-gx.png) ##### **NumberOfSections** Dùng để lưu trữ số lượng `section` có trong file **PE**. Như hình dưới thì ta thấy có $8$ `section`: ![Screenshot 2025-05-21 142427](https://hackmd.io/_uploads/Hy9Aybi-lx.png) Các `section` thông thường xuất hiện trong file **PE** bao gồm: `.text`: chứa mã thực thi của file **PE**. `.rdata`: chứa các dữ liệu hằng (constant) trong file **PE**. `.data`: chứa dữ liệu đã được khai báo và định nghĩa. `.bss`: chứa dữ liệu đã được khai báo nhưng chưa được định nghĩa. `.rsrc`: chứa các tài nguyên của file **PE**, có thể là icon, file âm thanh, hình ảnh, … `.reloc`: chứa thông tin cần thiết cho quá trình tái định vị (relocation). ![image](https://hackmd.io/_uploads/S1ljeZobxx.png) ##### **TimeDateStamp** * Với độ dài là $4$ bytes, được sử dụng để ghi lại thời điểm mà file **PE** được tạo hoặc chỉnh sửa. Điều này là quan trọng vì nó giúp xác định thời điểm cuối cùng khi file được biên dịch hoặc sửa đổi. Tuy nhiên ta cũng phải lưu ý rằng các hacker có thể thay đổi giá trị của trường này để làm cho quá trình phân tích tĩnh của file **PE** trở nên khó khăn hơn. ![Screenshot 2025-05-21 143128](https://hackmd.io/_uploads/Hykd-ZjWll.png) ##### PointerToSymbolTable và NumberOfSymbols * Trường thông tin này giữ offset của file chỉ đến **COFF symbol table** và số lượng `entry` trong `symbol table` đó. Tuy nhiên chúng được đặt giá trị là $0$ có nghĩa là không có **COFF symbol table**. Điều này được thực hiện là do thông tin **COFF debugging** không được dùng nữa. ##### **SizeOfOptionalHeader** * Được sử dụng để lưu trữ kích thước của `section` **Optional Header**. Do **Optional Header** không có kích thước cố định, thay vào đó, kích thước của nó có thể thay đổi tùy thuộc vào kiến trúc của các tệp tin **PE** khác nhau và theo các tính năng cụ thể mà tệp tin **PE** có hoặc không có. ![Screenshot 2025-05-21 143311](https://hackmd.io/_uploads/SyDvGZjWll.png) ##### **Characteristics** * Với kích thước là 2 byte, trường này đại diện cho một số đặc tính của file **PE**. Để xem và hiểu rõ các thuộc tính này, ta có thể sử dụng công cụ như `CFF Explorer`. Công cụ này không chỉ giúp xem tất cả các thuộc tính có thể xuất hiện mà còn cho phép kiểm tra các thuộc tính mà file **PE** hiện tại đang sở hữu. ![image](https://hackmd.io/_uploads/Hy7y7ZiZll.png) Một vài thuộc tính quan trọng như: * **File is executable**: thuộc tính này chỉ ra rằng file **PE** hiện tại là một file thực thi. * **File is a DLL**: thuộc tính này chỉ ra rằng file **PE** hiện tại là một `dynamic-link library (DLL)`. * **32 bit word machine**: thuộc tính này ta xác định xem file **PE** hiện tại là `32-bit` hay `64-bit`. Ta cũng có thể chỉnh sửa các thuộc tính này bằng cách tick vào các box trong `CFF Explorer`. ![Screenshot 2025-05-21 152326](https://hackmd.io/_uploads/SJiQxGj-le.png) ![Screenshot 2025-05-21 153017](https://hackmd.io/_uploads/B1PUyzo-ex.png) #### **Optional Header** * Đây là vùng chứa nhiều trường thông tin quan trọng cho **Windows loader** khi thực hiện quá trình sao chép và ánh xạ file **PE** lên bộ nhớ của process. ![image](https://hackmd.io/_uploads/Sk8fGMsWgl.png) ##### **magic** * $2$ bytes, giá trị `0x10B` tương ứng file **PE** 32-bit, `0x20B` là **PE** 64-bit. ![image](https://hackmd.io/_uploads/rJNAMGi-ll.png) ##### SizeOfCode * Kích thước của code thực thi. ##### SizeOfInitializedData * Kích thước của dữ liệu được khởi tạo. ##### BaseOfCode * Chứa địa chỉ `RVA` của phần code(2 giá trị **BaseOfCode** và **AddressOfEntryPoint** - có thể giống nhau nếu phần địa chỉ bắt đầu của mã thực thi và địa chỉ bắt đầu của vùng nhớ mã thực thi trùng nhau). ##### **AddressOfEntryPoint** * Kích thước 4 byte. Nó chứa địa chỉ `Relative Virtual Address (RVA)` của câu lệnh đầu tiên mà sẽ được thực thi khi chương trình **PE** Loader sẵn sàng để run **PE** file. Nếu như bạn muốn làm thay đổi luồng thứ tự thực hiện, bạn phải thay đổi lại giá trị trong trường này thành một `RVA` mới và do đó câu lệnh tại giá trị `RVA` mới này sẽ được thực thi đầu tiên. ##### **ImageBase** * Là địa chỉ nạp được ưu tiên cho **PE** file. 99% giá trị này là 0x400000. Nếu như giá trị trong trường này là 0x400000, **PE** Loader sẽ cố gắng cấp phát không gian trong bộ nhớ ảo để sao chép file PE và các section của nó vào trong, bắt đầu tại 0x400000. **Ưu tiên** ở đây không thể nạp file tại địa chỉ đó nếu như có một module nào khác đã chiếm giữ vùng địa chỉ này. Có thể giải thích rằng do trong quá trình tạo một process hoàn chỉnh, không chỉ **Windows** loader nạp module tương ứng với file **PE** mà còn phải nạp các module khác, chẳng hạn như **DLL** và mapped file,... Điều này làm cho địa chỉ 0x400000 có thể đã được sử dụng bởi một module khác, khiến cho **Windows** loader phải tìm một vị trí khác để nạp file **PE** lên. ##### Section Alignment * Là giá trị thể hiện đơn vị căn chỉnh ``(alignment)`` khi ánh xạ các `section` vào bộ nhớ ảo ``(Virtual Memory)``. * Mỗi `section` khi được nạp vào bộ nhớ phải bắt đầu tại một địa chỉ ảo là bội số của giá trị **Section Alignment**. Trên **Windows**, giá trị này thường là 0x1000 (tức 4096 bytes). * Điều này đảm bảo rằng mỗi **section** không bị chồng lấn lên nhau trong không gian địa chỉ ảo, và giúp hệ điều hành quản lý bộ nhớ dễ dàng hơn. * Ví dụ: nếu **SectionAlignment** = 0x1000 (4096 bytes), và một `section` đang ở địa chỉ ảo 0x401000, dù kích thước thực tế của nó chỉ là 10 bytes, thì `section` kế tiếp vẫn phải bắt đầu tại địa chỉ 0x402000 (tức là bội số kế tiếp của 0x1000). ##### File Alignment * **File Alignment** là giá trị thể hiện đơn vị căn chỉnh ``(alignment)`` của các `section` khi chúng nằm trong file thực thi. * Trên **Windows**, giá trị thường gặp là 0x200 (512 bytes) * Ví dụ: nếu **FileAlignment** = 0x200 (512 bytes), và `section` đầu tiên có dữ liệu dài 10 bytes, thì `section` tiếp theo vẫn phải bắt đầu tại offset 0x200, chứ không được viết liền ngay sau 10 byte kia. Tức là các `section` trong file cũng được căn chỉnh theo **FileAlignment**, để khi nạp vào bộ nhớ thì căn chỉnh lại theo **SectionAlignment**. ##### SizeOfImage * Toàn bộ kích thước của **PE image** trong bộ nhớ. Nó là tổng tất cả các `headers` và `sections` được liên kết tới **Section Alignment**. Lưu ý rằng độ lớn của module tương ứng với file **PE** khi nó được ánh xạ vào bộ nhớ của process sẽ khác với kích thước của file **PE** trên đĩa. ##### SizeOfHeader * Kích thước 4 byte chứa độ lớn tất cả các `header` và `section table`. Nói tóm lại, giá trị này là bằng kích thước file trừ đi kích thước được tổng hợp của toàn bộ `sections` trong file. Bạn cũng có thể sử dụng giá trị này như một file offset của `Section` đầu tiên trong **PE** file. ##### Data Directory * **Data Directory** là mảng gồm $16$ mục trong **PE** header ($128$ byte cuối của **OptionalHeader**), mỗi mục chứa `RVA` và kích thước của một bảng dữ liệu quan trọng như `Import/Export/Resource Directory` và cả `IAT(Import Address Table)`. Nếu một mục không tồn tại, cả hai giá trị sẽ là $0$. **Data Directory** giúp hệ điều hành định vị và nạp các thành phần của **PE** file. ![image](https://hackmd.io/_uploads/S1NJG7i-lg.png) * Công cụ như **CFF Explorer** hỗ trợ tra cứu và xác định mỗi **Data Directory** nằm trong `section` nào của file, rất hữu ích cho việc phân tích hoặc unpacking. ![Screenshot 2025-05-21 163758](https://hackmd.io/_uploads/HJldxmjblx.png) ### Section Table * Tiếp sau **PE Header** là **Section Table**. Nghe tên thì chắc chúng ta cũng đoán được ý nghĩa và tầm quan trọng của thành phần này. Nó là 1 structure, mà mỗi phần tử sẽ chứa thông tin về một `section` trong **PE** file. Từ đó **Windows loader** mới xác định được vị trí của các `section` trong file **PE** rồi mới có thể biết được nên nạp các `section` vào những vị trí cụ thể nào trên bộ nhớ. * Đầu tiên là **Section Header**: ![image](https://hackmd.io/_uploads/r1du6Bs-le.png) ![image](https://hackmd.io/_uploads/SyexZg2Wex.png) #### Name * Trường này có kích thước $8$ bytes, là label lưu trữ tên của `section`.Vậy nên tên của `section` không cần kết thúc bằng null và có thể để trống, tối đa thì $8$ ký tự. #### Virtual Size và Virtual Address * Lần lượt là **kích thước theo bytes** và **RVA** của `section` trên không gian địa chỉ ảo của process. ![image](https://hackmd.io/_uploads/SkO4WUj-ge.png) #### SizeOfRawData/Raw Size và PointerToRawData/Raw Address * Lần lượt là **kích thước các `section` trên file** và **offset của `section`** tính từ đầu file **PE** trên đĩa. ![image](https://hackmd.io/_uploads/Hy5LQ8iWxl.png) #### Characteristics * Trường cuối cùng của **Section Table** là **Characteristics**. Trường này giúp xác định các đặc tính của một `section` như `executable code, initialized data, uninitialized data, read, write,....` ![image](https://hackmd.io/_uploads/SyGiEIjbgg.png) * Với `notepad.exe` khi mở bằng **CFF Explorer**: ![Screenshot 2025-05-21 202908](https://hackmd.io/_uploads/S1ayKUobll.png) * Màu hồng là **PointerToRawData** của từng `section` nha. ### PE Sections * Là `section` chứa nội dung chính của file, gồm `.code`, `.data`, ... nằm sau **Section Header** và cũng chính là phần còn lại của file **PE**. Mỗi `section` có một **Header** và một **Body**. **Section Header** thì đã nói ở trên rồi, nhưng những **Section Body** lại không có cấu trúc, chúng có thể được sắp xếp theo bất kì cách nào với điều kiện là **Header** chứa đầy đủ thông tin để có thể giải mã dữ liệu. #### Executable Code Section * **`.text`**: Chứa code thực thi của chương trình. #### Data Sections * **`.data`**: Chứa data được khởi tạo. Có thể là những biến toàn cục, chỉ xuất hiện trên **Stack**, ... * **`.bss`**: Chứa data không được khởi tạo. Có thể là các biến khai báo tĩnh trong 1 hàm hoặc module nguồn nào đó. * **`.radata`**: Chứa data **read-only** được khởi tạo. Ví dụ như `string, constant,...` #### Import Data Section * **`.edata`**: Chứa `export tables` của 1 chương trình hoặc 1 file **DLL**. **Section** này chứa tên và địa chỉ của các hàm trong đó. * **`.idata`**: Chứa `import tables`. **Section** này chứa những thông tin khác nhau của các hàm trong đó, bao gồm cả **Import Directory** và **IAT(Import Address Table)**. #### Base Relocation Section * **`.reloc`**: Chứa thông tin về `relocation`, là những thông tin cần thiết để thực hiện thay đổi **base address** của 1 module khi nó được nạp vào bộ nhớ (hay còn gọi là tái định vị). Cái này quan trọng vì nó đảm bảo các địa chỉ trong chương trình là chính xác và đúng với địa chỉ bộ nhớ của chương trình đang chạy. Tức là đảm bảo việc không xảy ra xung đột địa chỉ khi nạp hoặc lỗi tham chiếu, ... #### Resources Section * **`.rsrc`**: Chứa tài nguyên được dùng bởi chương trình, bao gồm ảnh, icon hay thậm chí các mã nhị phân được nhúng. #### Thread Local Storage * **`.tls`**: cung cấp bộ nhớ riêng cho mỗi luồng thực thi của chương trình. ## Unpack file PE ### Khái niệm * Đây là 1 kỹ thuật mà ta có thể hiểu là file **PE** sẽ bị `pack`, khiến chúng ta không thể đọc và phân tích code được bình thường nữa. * Vậy thì **Unpack** chính là làm ngược lại, khôi phục lại mã gốc hoặc trạng thái thực thi ban đầu của một file đã bị đóng gói (`packed`). ### Mục đích * Trong ngữ cảnh liên quan tới `malware`, việc pack file **PE** giúp che giấu hành vi bên trong bằng cách nén hoặc mã hóa phần nguy hiểm và chỉ giải nén khi chạy. Điều này khiến các công cụ phân tích tĩnh(`static analysis`) như **antivirus** khó phát hiện, vì mã độc thật bị ẩn sau các `section` "rác" vô hại. Vì các phần mềm **antivirus** quét theo hướng tuyến tính, từ **DOS MZ Header**, **PE Header** rồi các `section` trong **section table**. Đồng thời, nó cũng gây khó khăn cho các chuyên gia phân tích mã khi cần reverse engineer hoặc debug file. Đây là kỹ thuật phổ biến để né tránh phát hiện và làm chậm việc phân tích `malware`. ### 1 vài Packer phổ biến * `UPX`, `ASPack`, ... ### Dấu hiệu * Để detect được khi nào file bị **pack**, ta có thể dựa vào mấy dấu hiệu sau: * **Entry point** không nằm trong section `.text`. * Chứa các section lạ như `.UPX`, `.ASPack`, `.petite`. * Chạy bị lỗi trên `disassembler`. * Tồn tại **section** có **entropy** > $7$. (Khi không `pack` thì < $6,7$). Đây khả năng cao là **section** chứa **Packed Data**. * Hoặc đơn giản hơn thì dùng các công cụ **PEiD**, **Detect It Easy**, **Exeinfo PE**, ... ### Các bước cơ bản của kỹ thuật Unpack * Điều đầu tiên, trong chương trình luôn có 1 **Entry point(`EP`)** (nằm trong `.text` và trỏ trực tiếp tới mã gốc) gọi là điểm xuất phát của chương trình, code sẽ được thực thi bắt đầu từ điểm này. Trong trường hợp file bị **pack**, thì **EP** này có thể sẽ thay đổi, nó thường không trỏ đến mã gốc của chương trình nữa mà trỏ đến một đoạn mã được **packer** chèn vào — gọi là `stub`. Mục đích của `stub` là giải nén hoặc giải mã phần **Packed Data**, nơi chứa mã gốc đã bị nén/mã hóa. Sau khi quá trình giải nén hoàn tất, luồng thực thi sẽ được chuyển tiếp tới **OEP (Original Entry Point)**, là vị trí ban đầu mà mã gốc thực sự bắt đầu chạy. ![image](https://hackmd.io/_uploads/Sky_Xk0Wel.png) :::success * Túm lại, hiểu 1 cách không lòng vòng thì : Đối với 1 file đã bị `pack`, nói đến **EP** ta sẽ hiểu là **Entry Point** của file sau khi `pack`. Còn **OEP** là **EP** của file gốc trước khi bị `pack`. ::: * Vậy bước đầu tiên cần làm chính là tìm **OEP**, từ đó ta mới có thể trích xuất được mã gốc và phân tích nó. #### Bước 1: Tìm OEP * Dùng mấy debugger như: **x64dbg**, **OllyDbg** để theo dõi quá trình unpack tại runtime, đặt breakpoint tại vùng nghi ngờ. * Khi thấy code chuyển sang mã gốc (thường recognizable như `push ebp`,`mov ebp, esp`...), thì chú ý, vì đó nhiều khả năng là **OEP**. * Hoặc dùng **PE-Editor** để tìm **EP** hiện tại, rồi debug xem từ **EP** nó jump vào đâu thì khả năng địa chỉ đó là **OEP**. * Ngoài ra, còn có **Scylla**, **ImpREC**, hoặc các plugin cho **x64dbg** cũng hỗ trợ mạnh mẽ cho bước đầu tiên này. #### Bước 2: Fix IAT * Nếu đặt câu hỏi tại sao sau khi tìm được **OEP**, ta lại phải fix **IAT(`Import Address Table`)**? * Theo như mình hiểu, khi một file bị `pack`, bảng **IAT** (chứa địa chỉ các hàm **API Windows** mà chương trình sử dụng, ví dụ **MessageBoxA**, **CreateFileA**,...) thường bị xóa,ẩn hoặc làm rối để tránh bị phân tích tĩnh bởi **IDA**, **Ghidra**. Thay vào đó, packer sẽ: * Ẩn thông tin import thật. * Tạm thời load **API** vào vùng nhớ riêng khi `unpack` tại runtime. * Địa chỉ thật của các hàm chỉ xuất hiện trong quá trình chạy. * .... :::success * Tức là, hiểu 1 cách đơn giản là bước này nhằm xác định các **API** mà chương trình sử dụng, từ đó phân tích được chính xác hành vi **malware**. ::: * Có thể sử dụng plugin cho **OllyDbg** là **Scylla**, **ImportREC (`ImpREC`)**. Sau đó có thể sử dụng **PE-bear / CFF Explorer** (dùng để kiểm tra lại **IAT** sau khi rebuild). * Có thể công nhân hơn là đặt breakpoint tại các **API** của **kernel32.dll**, **user32.dll**,... :))) * Vui vậy thôi chứ nếu tools có vấn đề gì hoặc không detect hết các **API** thì mới làm thế 🤦‍♂️ #### Bước 3: Dump và hoàn thiện file sau unpack * Sau khi fix **IAT** xong, ta có thể `dump` ngay vùng nhớ này ra và copy vào 1 file nào đó bằng công cụ như **Scylla** hoặc **x64dbg**. * Tuy nhiên, đây ta mới chỉ gọi là lấy được file `dump`, chứ chưa phải là file **PE** `unpack` hoàn chỉnh. Vì nó thiếu thông tin quan trọng như **PE** header, size `section`, `offset`, ... Ngoài ra ta `dump` ra các `section` có thể vẫn dư thừa hoặc thiếu dữ liệu. * Vậy nên chúng ta mới đến bước cuối cùng để gọi là hoàn thiện `unpack` file thành công. * Vẫn là lựa chọn dùng **Scylla**, kết hợp với **x64dbg**, **OllyDbg**. Công nhân thì dùng **CFF Explorer** để `mukbang` bằng tay cũng được 🤣 #### Tổng hợp * Trên đây là mình đưa ra các bước cơ bản nhất, có thể nói là không thế thiếu trong quá trình `unpack` 1 file **PE** bị `pack`. * Ngoài ra, thực tế kể cả trong ngữ cảnh **malware** hay **CTF**, thì có thể sẽ khác, thậm chí bypass **anti-debug**, **anti-VM**,..., nhưng tựa chung vẫn sẽ theo trình tự này. ## Tổng kết Vậy ở wu này mình đã trình bày **PE** file là gì, cấu trúc của 1 file **PE**. Sau đó giới thiệu thêm cơ bản về kỹ thuật `pack` và `unpack` **PE** file. 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://learn.microsoft.com/en-us/windows/win32/debug/pe-format https://kienmanowar.wordpress.com/2014/01/16/tim-hieu-pe-file-qua-cac-vi-du-co-ban/ https://en.wikipedia.org/wiki/Portable_Executable https://0xrick.github.io/win-internals/pe3/ https://hub.whitehub.net/tim-hieu-ve-cau-truc-pe-file/ https://hackmd.io/@3ud4jm0nj4/ByDRNMkHn https://hackmd.io/@0r3o/BJrxqpvc6 https://hackmd.io/@Wh04m1/r1Pzr-M96 https://hackmd.io/@3ud4jm0nj4/BkOr12W0o https://hackmd.io/@antoinenguyen09/Hy0a2mb0t https://hackmd.io/@3ud4jm0nj4/BkOr12W0o

    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