逆向-PE頭解析

PE頭解析

PE 格式是Windows系統下組織可執行文件的格式。PE文件由文件頭和對應的數據組成。目標是在不一樣的架構下裝載器和編程工具不用重寫。編程

PE中一大特色是不連續的位置大部分記錄的都是相對地址(RVA),相對的是PE文件中記錄的基地址(image base)的偏移量。進程是程序的執行狀態的實體,每一個進程都有本身獨立的內存空間(編址)PE和內核等一塊兒編制,因此image base也不老是肯定的。windows

結構(參考:加密與解密)數據結構

數據結構

IMAGE_DOS_HEADER

參考:參考:http://www.openrce.org/reference_library/files/reference/PE%20Format.pdf架構

IMAGE_DOS_HEADER STRUCT
{
+0h     WORD    e_magic //Magic DOS signature MZ(4Dh 5Ah) DOS可執行文件標記
+2h     WORD    e_cblp //Bytes on last page of file
+4h     WORD    e_cp //Pages in file
+6h     WORD    e_crlc //Relocations
+8h     WORD    e_cparhdr //Size of header in paragraphs
+0ah    WORD    e_minalloc //Minimun extra paragraphs needs
+0ch    WORD    e_maxalloc //Maximun extra paragraphs needs
+0eh    WORD    e_ss //intial(relative)SS value DOS代碼的初始化堆棧SS
+10h    WORD    e_sp //intial SP value DOS代碼的初始化堆棧指針SP
+12h    WORD    e_csum //Checksum
+14h    WORD    e_ip // intial IP value DOS代碼的初始化指令入口[指針IP]
+16h    WORD    e_cs //intial(relative)CS value DOS代碼的初始堆棧入口
+18h    WORD    e_lfarlc //File Address of relocation table
+1ah    WORD    e_ovno // Overlay number
+1ch    WORD    e_res[4] //Reserved words
+24h    WORD    e_oemid // OEM identifier(for e_oeminfo)
+26h    WORD    e_oeminfo // OEM information;e_oemid specific
+29h    WORD    e_res2[10] // Reserved words
+3ch    DWORD   e_lfanew //Offset to start of PE header PE頭相對於文件的偏移地址
} IMAGE_DOS_HEADER ENDS

對於PE來講DOS頭是爲了兼容16位程序的,如今都是3二、64位因此咱們只關心這個結構體中的兩個成員(16位系統中PE頭和內容是冗餘數據)ide

e_magic、e_lfanew(第一個和最後一個)函數

  • e_magic 是個標誌MZ 0x4D5A 判斷是不是PE文件用(不是惟一)
  • e_lfanew PE頭相對於文件的偏移地址

上圖3CH是e_lfanew內容指向PE頭地址內容位0000000E(小端存儲)。工具

咱們能夠看到DOS頭和PE頭是間隔的那塊區域叫DOS stub這個是存儲16位程序的,對於3二、64位系統無用this

PNTHeader = ImageBase + (dosHeader->e_lfanew)加密

IMAGE_NT_HEADERS

typedef struct _IMAGE_NT_HEADERS {
+0      hDWORD  Signature //
+4h     IMAGE_FILE_HEADER FileHeader //
+18h    IMAGE_OPTIONAL_HEADER32 OptionalHeader //
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

#define IMAGE_NT_SIGNATURE                  0x00004550
  • Signature固定爲0x00004550

IMAGE_FILE_HEADER

typedef struct _IMAGE_FILE_HEADER {
+04h    WORD    Machine;//04h相對於_IMAGE_NT_HEADERS的,運行平臺
+06h    WORD    NumberOfSections;//文件的區塊數(*重要)
+08h    DWORD   TimeDateStamp;//文件建立時間 和unix時間戳同樣int(secound(now-19700101))
+0cH    DWORD   PointerToSymbolTable;//指向符號表(主要用於調試)
+10H    DWORD   NumberOfSymbols;//符號表中符號個數(同上)
+14H    WORD    SizeOfOptionalHeader;//IMAGE_OPTIONAL_HEADER32 結構大小(*重要)IMAGE_OPTIONAL_HEADER是長度可變的。
+16H    WORD    Characteristics;//文件屬性多,種屬性經過 「或運算」 同時擁有
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

IMAGE_OPTIONAL_HEADER(*重要)

由IMAGE_FILE_HEADER的SizeOfOptionalHeader決定大小(可變長)
typedef struct _IMAGE_OPTIONAL_HEADER {
    //
    // Standard fields.
    //

+18h    WORD Magic; // 標誌字, ROM 映像(0107h),普通可執行文件(010Bh)
+1Ah    BYTE MajorLinkerVersion; // 連接程序的主版本號
+1Bh    BYTE MinorLinkerVersion; // 連接程序的次版本號
+1Ch    DWORD SizeOfCode; // 全部含代碼的節的總大小
+20h    DWORD SizeOfInitializedData; // 全部含已初始化數據的節的總大小
+24h    DWORD SizeOfUninitializedData; // 全部含未初始化數據的節的大小
+28h    DWORD AddressOfEntryPoint; // 程序執行入口RVA
+2Ch    DWORD BaseOfCode; // 代碼的區塊的起始RVA
+30h    DWORD BaseOfData; // 數據的區塊的起始RVA

    //
    // NT additional fields.
    //

+34h    DWORD ImageBase; // 文件在內存中的的首選裝載地址。
+38h    DWORD SectionAlignment; // 內存中的區塊的對齊大小
+3Ch    DWORD FileAlignment; // 文件中的區塊的對齊大小
+40h    WORD MajorOperatingSystemVersion; // 要求操做系統最低版本號的主版本號
+42h    WORD MinorOperatingSystemVersion; // 要求操做系統最低版本號的副版本號
+44h    WORD MajorImageVersion; // 可運行於操做系統的主版本號
+46h    WORD MinorImageVersion; // 可運行於操做系統的次版本號
+48h    WORD MajorSubsystemVersion; // 要求最低子系統版本的主版本號
+4Ah    WORD MinorSubsystemVersion; // 要求最低子系統版本的次版本號
+4Ch    DWORD Win32VersionValue; // 莫須有字段,不被病毒利用的話通常爲0
+50h    DWORD SizeOfImage; // 映像裝入內存後的總尺寸
+54h    DWORD SizeOfHeaders; // 全部頭 + 區塊表的尺寸大小
+58h    DWORD CheckSum; // 映像的校檢和
+5Ch    WORD Subsystem; // 可執行文件指望的子系統
+5Eh    WORD DllCharacteristics; // DllMain()函數什麼時候被調用,默認爲 0
+60h    DWORD SizeOfStackReserve; // 初始化時的棧大小
+64h    DWORD SizeOfStackCommit; // 初始化時實際提交的棧大小
+68h    DWORD SizeOfHeapReserve; // 初始化時保留的堆大小
+6Ch    DWORD SizeOfHeapCommit; // 初始化時實際提交的堆大小
+70h    DWORD LoaderFlags; // 與調試有關,默認爲 0
+74h    DWORD NumberOfRvaAndSizes; // 下邊數據目錄的項數,這個字段自Windows NT 發佈以來 // 一直是16
+78h    IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];// 數據目錄表
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

ImageBase文件在內存中載入地址,若是有文件佔據這個位置裝載器會進行應用基址重定位。 對於EXE文件來講,因爲每一個文件老是使用獨立的虛擬地址空間通常不會被別的文件搶佔。 對於DLL文件來講,因爲多個DLL文件所有使用宿主EXE文件的地址空間,不能保證優先裝入地址沒有被別的DLL使用,因此DLL文件中必須包含重定位信息,對應

#define IMAGE_FILE_RELOCS_STRIPPED           0x0001  // Relocation info stripped from file.
 IMAGE_FILE_HEADER ->Characteristics(能夠看到下面的數字位數不一樣,並且都是一、二、四、8他們總共加起來就是16看看二進制就知道了他們佔的不是同一位與一下就能取到相對應位,linux的文件屬性一、二、4也是同樣的)
EXE文件的默認優先裝入地址被定爲00400000h,而DLL文件的默認優先裝入地址被定爲10000000h

  • AddressOfEntryPoint字段 : 程序執行入口RVA,imageBase+AddressOfEntryPoint就是程序運行的時候首先執行代碼處。通常指向.text節。

  • SectionAlignment:程序載入內存後區塊(節)對齊大小,每一個節被載入內存後必須和CPU內存頁對齊(方便設置內存頁屬性)最小1Kh(4KB)

  • FileAlignment:PE文件在磁盤上的對齊大小,最小爲200h(512byte)一個扇區大小。

    關於對齊能夠用winhex打開磁盤上的notpad.exe和內存中的notpad.exe,notpad裝載的時候就被拉伸了。

  • IMAGE_DATA_DIRECTORY

    這個是個結構存儲的對應表位置和大小,至於這些表有什麼,作什麼須要下面詳細解釋。

    typedef struct _IMAGE_DATA_DIRECTORY {
        DWORD   VirtualAddress; //表首地址的RVA
        DWORD   Size;  //表長度
    } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

    下面是微軟的文檔上截取的,其中咱們比較關心的是導入表、重定位表(DLL等)

    Size Field Description
    8 Export Table The export table address and size. For more information see .edata Section (Image Only).
    8 Import Table The import table address and size. For more information, see The .idata Section.
    8 Resource Table The resource table address and size. For more information, see The .rsrc Section.
    8 Exception Table The exception table address and size. For more information, see The .pdata Section.
    8 Certificate Table The attribute certificate table address and size. For more information, see The Attribute Certificate Table (Image Only).
    8 Base Relocation Table The base relocation table address and size. For more information, see The .reloc Section (Image Only).
    8 Debug The debug data starting address and size. For more information, see The .debug Section.
    8 Architecture Reserved, must be 0
    8 Global Ptr The RVA of the value to be stored in the global pointer register. The size member of this structure must be set to zero.
    8 TLS Table The thread local storage (TLS) table address and size. For more information, The .tls Section.
    8 Load Config Table The load configuration table address and size. For more information, The Load Configuration Structure (Image Only).
    8 Bound Import The bound import table address and size.
    8 IAT The import address table address and size. For more information, see Import Address Table.
    8 Delay Import Descriptor The delay import descriptor address and size. For more information, see Delay-Load Import Tables (Image Only).
    8 CLR Runtime Header The CLR runtime header address and size. For more information, see The .cormeta Section (Object Only).
    8 Reserved, must be zero

區塊

區塊由區塊表映射,區塊表緊跟IMAGE_NT_HEADERS;多少個區塊表由_IMAGE_NT_HEADERS.FileHeader.NumberOfSections指定。

typedef struct _IMAGE_SECTION_HEADER {
    BYTE    Name[IMAGE_SIZEOF_SHORT_NAME];   //8字節的name
    union {
            DWORD   PhysicalAddress;
            DWORD   VirtualSize;          
    } Misc;
    DWORD   VirtualAddress;//區塊RVA
    DWORD   SizeOfRawData;//文件對齊後的尺寸
    DWORD   PointerToRawData;//文件中的偏移
    DWORD   PointerToRelocations;//
    DWORD   PointerToLinenumbers;
    WORD    NumberOfRelocations;
    WORD    NumberOfLinenumbers;
    DWORD   Characteristics;//區塊屬性
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

#define IMAGE_SIZEOF_SHORT_NAME              8
  • Name八個字節的不必定以「\0」借位的字符串表示節的名字

  • Misc、PhysicalAddress、VirtualSize叫什麼都行是未對齊前節的大小(能夠不許確,改了也沒事,編譯器生成的)

  • 節在內存中的偏移地址,RVA。

  • SizeOfRawData 節在文件中對齊的尺寸

  • PointerToRawData 節在磁盤文件中的便宜,若是須要本身裝載PE(不用操做系統裝載)這個值有不少用處。

  • Characteristics 節屬性(代碼/數據,可讀/可寫)

    經常使用
    #define IMAGE_SCN_CNT_CODE                   0x00000020  // Section contains code.
    #define IMAGE_SCN_CNT_INITIALIZED_DATA       0x00000040  // Section contains initialized data.
    #define IMAGE_SCN_CNT_UNINITIALIZED_DATA     0x00000080  // Section contains uninitialized data.
    #define IMAGE_SCN_MEM_DISCARDABLE            0x02000000  // Section can be discarded.
    #define IMAGE_SCN_MEM_NOT_CACHED             0x04000000  // Section is not cachable.
    #define IMAGE_SCN_MEM_NOT_PAGED              0x08000000  // Section is not pageable.
    #define IMAGE_SCN_MEM_SHARED                 0x10000000  // Section is shareable.
    #define IMAGE_SCN_MEM_EXECUTE                0x20000000  // Section is executable.
    #define IMAGE_SCN_MEM_READ                   0x40000000  // Section is readable.
    #define IMAGE_SCN_MEM_WRITE                  0x80000000  // Section is writeable.
    關於區塊的對齊

    區塊對齊分兩部分,FileAlignment、SectionAlignment分別表明文件中和內存中的對齊,爲了有效讀取和設置屬性規定,FileAlignment如今好多程序都和SectionAlignment同樣大小了。SectionAlignment內存對齊值在x86上通常是內存頁大小4kB(1000H)在x64當中是8KB(2000H)。

    文件偏移與虛擬地址的轉換

    FileAlignment、SectionAlignment不一樣須要將磁盤中的PE文件拉伸。

FileOffset=VA-RVA-k

參考:加密與解密
https://docs.microsoft.com/zh-cn/windows/win32/debug/pe-format

相關文章
相關標籤/搜索