目录linux
PE 格式是Windows系统下组织可执行文件的格式。PE文件由文件头和对应的数据组成。目标是在不一样的架构下装载器和编程工具不用重写。编程
PE中一大特色是不连续的位置大部分记录的都是相对地址(RVA),相对的是PE文件中记录的基地址(image base)的偏移量。进程是程序的执行状态的实体,每一个进程都有本身独立的内存空间(编址)PE和内核等一块儿编制,因此image base也不老是肯定的。windows
结构(参考:加密与解密)数据结构
参考:参考: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(第一个和最后一个)函数
上图3CH是e_lfanew内容指向PE头地址内容位0000000E(小端存储)。工具
咱们能够看到DOS头和PE头是间隔的那块区域叫DOS stub这个是存储16位程序的,对于3二、64位系统无用this
PNTHeader = ImageBase + (dosHeader->e_lfanew)加密
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
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_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