PE文件格式詳解,第三講,可選頭文件格式,以及節表
作者:IBinary
出處:http://www.cnblogs.com/iBinary/
版權所有,歡迎保留原文鏈接進行轉載:)
一丶可選頭結構以及作用
typedef struct _IMAGE_OPTIONAL_HEADER {
WORD Magic; /*機器型號,判斷是PE是32位還是64位*/ BYTE MajorLinkerVersion; /*連接器版本號高版本*/ BYTE MinorLinkerVersion; /*連接器版本號低版本,組合起來就是 5.12 其中5是高版本,C是低版本*/ DWORD SizeOfCode; /*代碼節的總大小(512為一個磁盤扇區)*/ DWORD SizeOfInitializedData; /*初始化數據的節的總大小,也就是.data*/ DWORD SizeOfUninitializedData; /*未初始化數據的節的大小,也就是 .data ? */ DWORD AddressOfEntryPoint; /*程序執行入口(OEP) RVA(相對偏移)*/ DWORD BaseOfCode; /*代碼的節的起始RVA(相對偏移)也就是代碼區的偏移,偏移+模塊首地址定位代碼區*/ DWORD BaseOfData; /*數據結的起始偏移(RVA),同上*/ DWORD ImageBase; /*程序的建議模塊基址(意思就是說作參考用的,模塊地址在哪里)*/
DWORD SectionAlignment; /*內存中的節對齊*/ DWORD FileAlignment; /*文件中的節對齊*/ WORD MajorOperatingSystemVersion; /*操作系統版本號高位*/ WORD MinorOperatingSystemVersion; /*操作系統版本號低位*/ WORD MajorImageVersion; /*PE版本號高位*/ WORD MinorImageVersion; /*PE版本號低位*/ WORD MajorSubsystemVersion; /*子系統版本號高位*/ WORD MinorSubsystemVersion; /*子系統版本號低位*/ DWORD Win32VersionValue; /*32位系統版本號值,注意只能修改為4 5 6表示操作系統支持nt4.0 以上,5的話依次類推*/ DWORD SizeOfImage; /*整個程序在內存中占用的空間(PE映尺寸)*/ DWORD SizeOfHeaders; /*所有頭(頭的結構體大小)+節表的大小*/ DWORD CheckSum; /*校驗和,對於驅動程序,可能會使用*/ WORD Subsystem; /*文件的子系統 :重要*/ WORD DllCharacteristics; /*DLL文件屬性,也可以成為特性,可能DLL文件可以當做驅動程序使用*/ DWORD SizeOfStackReserve; /*預留的棧的大小*/ DWORD SizeOfStackCommit; /*立即申請的棧的大小(分頁為單位)*/ DWORD SizeOfHeapReserve; /*預留的堆空間大小*/ DWORD SizeOfHeapCommit; /*立即申請的堆的空間的大小*/ DWORD LoaderFlags; /*與調試有關*/ DWORD NumberOfRvaAndSizes; /*下面的成員,數據目錄結構的項目數量*/ IMAGE_DATA_DIRECTORY DataDirectory[16];/*數據目錄,默認16個,16是宏,這里方便直接寫成16*/ } IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
需要注意的成員:
1.PE類型
這個有宏定義了
#define IMAGE_NT_OPTIONAL_HDR32_MAGIC 0x10b /*32位PE*/ #define IMAGE_NT_OPTIONAL_HDR64_MAGIC 0x20b /*64位PE*/ #define IMAGE_ROM_OPTIONAL_HDR_MAGIC 0x107 /*其它,單片機*/
2丶.OEP,程序執行入口位置.
我們利用昨天寫的程序,可以完成一個反調試.
思路:
1.修改OEP偏移,置為0位置處(也就是MZ的位置)
2.在MZ位置后面添加我們自己的代碼
3.添加完成之后,繼續跳到以前OEP的位置.
首先,看PE文件的值,OEP的偏移位置是00001008偏移,那么OD調試,看下位置在哪里.
我們知道了入口偏移是00401008位置,那么我們就知道了模塊首地址是00400000
公式 00401008 - 1008 = 00400000 因為我們知道1008是相對於模塊地址來的所以可以求出模塊地址,我們跳轉過去
可以看出,前邊正好是4D5A,那么我們可以修改一下,添加自己的代碼,首先4D5A正好是匯編代碼
那么我們可以去平棧,然后跳轉到我們以前的OEP位置.
修改成下邊那樣
首先,我們以前講DOS頭的時候說過,如果這個EXE文件運行在32位系統下,那么DOS頭中就地一個和最后一個成員有用,那么后面我們隨便修改.
上面代碼很簡單,首先棧平衡,然后跳轉到我們以前代碼執行位置.
文件中(PE)我們把后面的二進制都修改為我們的代碼
入后偏移(RVA)修改為0000000
運行我們的程序,和調試我們的程序
運行程序:
可以正常運行
調試程序:
程序出錯,反調試了
二丶數據目錄
數據目錄,主要是存放各種表格的,看下
typedef struct _IMAGE_DATA_DIRECTORY { DWORD VirtualAddress; 虛擬地址(表格位置) DWORD Size; 大小 } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
關於表格,這里有很多宏定義.
#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 // Export Directory #define IMAGE_DIRECTORY_ENTRY_IMPORT 1 // Import Directory #define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 // Resource Directory #define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 // Exception Directory #define IMAGE_DIRECTORY_ENTRY_SECURITY 4 // Security Directory #define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 // Base Relocation Table #define IMAGE_DIRECTORY_ENTRY_DEBUG 6 // Debug Directory // IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7 // (X86 usage) #define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7 // Architecture Specific Data #define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 // RVA of GP #define IMAGE_DIRECTORY_ENTRY_TLS 9 // TLS Directory #define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 // Load Configuration Directory #define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 // Bound Import Directory in headers #define IMAGE_DIRECTORY_ENTRY_IAT 12 // Import Address Table #define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 // Delay Load Import Descriptors #define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14 // COM Runtime descriptor
它是按照位來計算的.
三丶節表
在NT頭下面,緊跟着的是節表
節表是什么意思? 可以理解為分區,就是幾個區
那么意思就是保存了區
那么我們猜想一下,都需要什么成員
地址
地址大小
文件中的地址
文件大小
等等....
看下節表的信息吧
typedef struct _IMAGE_SECTION_HEADER { BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; /*節區的名字*/ union { DWORD PhysicalAddress; DWORD VirtualSize; /*節區的尺寸*/ } Misc; DWORD VirtualAddress; /*虛擬地址 節區的RVA地址(偏移)*/ DWORD SizeOfRawData; /*在文件中對齊的尺寸*/ DWORD PointerToRawData; /*在文件中的偏移*/ DWORD PointerToRelocations; /*在OBJ文件中使用*/ DWORD PointerToLinenumbers; /*行號表位置,調試使用*/ WORD NumberOfRelocations; /*在OBJ文件中使用*/ WORD NumberOfLinenumbers; /*行號表的數量*/ DWORD Characteristics; /*節的屬性*/ } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
重要成員
1.節的尺寸
2.虛擬地址,RVA(偏移)
3.文件中的大小
4.文件中的偏移
5.節的屬性
其中節的屬性有很多,(表達這個分區是一個什么樣的分區,代碼區,常量區等等)
看下宏定義(按位來的,可以看下第二講的最后關於文件屬性的講解,其中講解了這個怎么按位來)
#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_LNK_OTHER 0x00000100 // Reserved. #define IMAGE_SCN_LNK_INFO 0x00000200 // Section contains comments or some other type of information. // IMAGE_SCN_TYPE_OVER 0x00000400 // Reserved. #define IMAGE_SCN_LNK_REMOVE 0x00000800 // Section contents will not become part of image. #define IMAGE_SCN_LNK_COMDAT 0x00001000 // Section contents comdat. // 0x00002000 // Reserved. // IMAGE_SCN_MEM_PROTECTED - Obsolete 0x00004000 #define IMAGE_SCN_NO_DEFER_SPEC_EXC 0x00004000 // Reset speculative exceptions handling bits in the TLB entries for this section. #define IMAGE_SCN_GPREL 0x00008000 // Section content can be accessed relative to GP #define IMAGE_SCN_LNK_NRELOC_OVFL 0x01000000 // Section contains extended relocations. #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.
其中保留的沒有寫.
作者:IBinary
出處:http://www.cnblogs.com/iBinary/
版權所有,歡迎保留原文鏈接進行轉載:)