“稀饭”大佬给我发的一份资料,我觉得在学习汇编前还是有必要了解一下PE文件结构。
这张图很清楚:
+----------------------+------------------ | Dos Header |64字节(0x40) | | +----------------------+------------------ | Dos Stub | | | +----------------------+------------------ | |--------------->+----------------------+ | | | Signature | | | | | | Pe Header |248字节(0xF8) +----------------------+ | | | File Header | | | | | | | +----------------------+ | | | Optional Header |--------------->+----------------------+ +----------------------+----------------| | | Magic | | section headers 1 |40字节(0x28) +----------------------+ | | | | +----------------------+ +----------------------+---------------- | . . . | | section headers 2 | | | | | +----------------------+ +----------------------+ | DataDirectory | | .................. | +----------------------| | | | | +----------------------+ +----------------------+ | | section 1 | | | | +----------------------+ +----------------------+ | Export Directory |8字节(0x08) | section 2 | +----------------------+ | | | Import Directory | +----------------------+ +----------------------+ | .................. | | . . . | | | +----------------------+ +----------------------+ | . . . | +----------------------+
//sdk winnt.h //dos header/// #define IMAGE_DOS_SIGNATURE 0x5A4D // MZ typedef struct _IMAGE_DOS_HEADER // DOS .EXE header { WORD e_magic; // +0x00 Magic number 魔法数字 "MZ" * WORD e_cblp; // +0x02 Bytes on last page of file 文件最后一页的字节数 WORD e_cp; // +0x04 Pages in file 文件的页数 WORD e_crlc; // +0x06 Relocations 重定位 WORD e_cparhdr; // +0x08 Size of header in paragraphs 段中头的大小 WORD e_minalloc; // +0x0a Minimum extra paragraphs needed 需要的最少额外段 WORD e_maxalloc; // +0x0c Maximum extra paragraphs needed 需要的最多额外段 WORD e_ss; // +0x0e Initial (relative) SS value 初始的(相对的)SS寄存器值 WORD e_sp; // +0x10 Initial SP value 初始的SP寄存器值 WORD e_csum; // +0x12 Checksum 校验和 WORD e_ip; // +0x14 Initial IP value 初始的IP寄存器值 WORD e_cs; // +0x16 Initial (relative) CS value 初始的(相对的)CS寄存器值 WORD e_lfarlc; // +0x18 File address of relocation table 重定位表在文件中的地址 WORD e_ovno; // +0x1a Overlay number 交叠数 WORD e_res[4]; // +0x1c Reserved words 保留 WORD e_oemid; // +0x24 OEM identifier (for e_oeminfo) WORD e_oeminfo; // +0x26 OEM information; e_oemid specific WORD e_res2[10]; // +0x28 Reserved words 保留 LONG e_lfanew; // +0x3c File address of new exe header 新exe头在文件中的地址 * // +0x40 } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER; // size 64字节(0x40) /// ///
///
//dos stub
sizeof(dos stub) = (大小不固定,因便宜器而改变)
DOS stub实际上是个有效的 EXE,在不支持 PE文件格式的操作系统中,它将简单显示一个错误提示,
类似于字符串 "This program requires Windows" 或者程序员可根据自己的意图实现完整的 DOS代码。
通常我们也不对 DOS stub 太感兴趣: 因为大多数情况下它是由汇编器/编译器自动生成。
通常,它简单调用中断21h服务9来显示字符串"This program cannot run in DOS mode"。
//
//
//sdk winnt.h
//pe header
typedef struct _IMAGE_NT_HEADERS {
DWORD Signature; // +0x00 pe文件签(4Byte)名 ASCII="pe/0/0" *
IMAGE_FILE_HEADER FileHeader; // +0x04 PE文件头(IMAGE_FILE_HEADER)
IMAGE_OPTIONAL_HEADER OptionalHeader; // +0x18 PE文件可选头(IMAGE_OPTIONAL_HEADER)
} IMAGE_NT_HEADERS, *PIMAGE_NT_HEADERS; // size 248字节(0xF8)
//file header
typedef struct _IMAGE_FILE_HEADER {
WORD Machine; // +0x00 机器类型对于intel 386=0x014c *
WORD NumberOfSections; // +0x02 文件节数目 *
DWORD TimeDateStamp; // +0x04 时间戳文件创建的时间
DWORD PointerToSymbolTable; // +0x08 用于调试
DWORD NumberOfSymbols; // +0x0c 用于调试
WORD SizeOfOptionalHeader; // +0x10 指明IMAGE_OPTIONAL_HEADER的大小 *
WORD Characteristics; // +0x12 特征集合 *
// +0x14
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER; // size 20字节(0x14)
//optional header
typedef struct _IMAGE_OPTIONAL_HEADER
{
WORD Magic; // +0x00 魔法数字 * 0x010B
BYTE MajorLinkerVersion; // +0x02 连接器主版本号
BYTE MinorLinkerVersion; // +0x03 连接器副版本号
DWORD SizeOfCode; // +0x04 可执行代码的大小
DWORD SizeOfInitializedData; // +0x08 已初始化数据的大小
DWORD SizeOfUninitializedData; // +0x0c 未初始化数据的大小
DWORD AddressOfEntryPoint; // +0x10 入口点地址 *
DWORD BaseOfCode; // +0x14 可执行代码的偏移值(“代码基址”)
DWORD BaseOfData; // +0x18 已初始化数据的偏移值(“数据基址”)
DWORD ImageBase; // +0x1c 映象文件基址 *
DWORD SectionAlignment; // +0x20 内存对其 * 2^n
DWORD FileAlignment; // +0x24 文件对其 * 2^n
WORD MajorOperatingSystemVersion; // +0x28 OS主版本号
WORD MinorOperatingSystemVersion; // +0x2a OS副版本号
WORD MajorImageVersion; // +0x2c 映象文件主版本号
WORD MinorImageVersion; // +0x2e 映象文件副版本号
WORD MajorSubsystemVersion; // +0x30 子系统主版本号 *
WORD MinorSubsystemVersion; // +0x32 子系统副版本号
DWORD Win32VersionValue; // +0x34 Win32版本值
DWORD SizeOfImage; // +0x38 映像文件大小 *
DWORD SizeOfHeaders; // +0x3c 所有类型的头的大小的总和 * (指向第一个节)
DWORD CheckSum; // +0x40 校验和
WORD Subsystem; // +0x44 子系统 *
WORD DllCharacteristics; // +0x46 DLL标记(0非dll文件,1 dll文件) *
DWORD SizeOfStackReserve; // +0x48 保留栈的大小
DWORD SizeOfStackCommit; // +0x4c 指定栈的大小
DWORD SizeOfHeapReserve; // +0x50 保留堆的大小
DWORD SizeOfHeapCommit; // +0x54 指定堆的大小
DWORD LoaderFlags; // +0x58 加载器标志
DWORD NumberOfRvaAndSizes; // +0x5c RVA大小的总和 *
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; // +0x60
// +0xe0
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32; //size 224字节(0xE0)
typedef struct _IMAGE_DATA_DIRECTORY
{
DWORD VirtualAddress; // +0x00 RVA
DWORD Size; // +0x04 The size in bytes
// +0x08
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
0 [ 0] RVA [size] of Export Directory
208 [ 28] RVA [size] of Import Directory
0 [ 0] RVA [size] of Resource Directory
0 [ 0] RVA [size] of Exception Directory
0 [ 0] RVA [size] of Certificates Directory
0 [ 0] RVA [size] of Base Relocation Directory
0 [ 0] RVA [size] of Debug Directory
0 [ 0] RVA [size] of Architecture Directory
0 [ 0] RVA [size] of Special Directory
0 [ 0] RVA [size] of Thread Storage Directory
0 [ 0] RVA [size] of Load Configuration Directory
0 [ 0] RVA [size] of Bound Import Directory
1E0 [ 8] RVA [size] of Import Address Table Directory
0 [ 0] RVA [size] of Delay Import Directory
0 [ 0] RVA [size] of Reserved Directory
0 [ 0] RVA [size] of Reserved Directory
//
//
//sdk winnt.h
//Section header
#define IMAGE_SIZEOF_SHORT_NAME 8 //定义一个常量
typedef struct _IMAGE_SECTION_HEADER {
UCHAR Name[IMAGE_SIZEOF_SHORT_NAME]; // +0x00 名字数组
union { // 共用体标志
ULONG PhysicalAddress; // 节的物理地址
ULONG VirtualSize; // +0x08 节的虚拟大小 <--(文件中的值)
} Misc;
ULONG VirtualAddress; // +0x0c 节被载入到的地址空间中的虚拟地址
ULONG SizeOfRawData; // +0x10 下个FileAlignment'倍数的大小
ULONG PointerToRawData; // +0x14 位于文件中的节身的位置的偏移量,如果是0,节数据不包含在文件内,在加载时被确定
ULONG PointerToRelocations; // +0x18 重定位指针
ULONG PointerToLinenumbers; // +0x1c 行数指针
USHORT NumberOfRelocations; // +0x20 重定位数目
USHORT NumberOfLinenumbers; // +0x22 行数数目
ULONG Characteristics; // +0x24 定义节的特征
// +0x28
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER; // size 40字节(0x28)
值 定义
0x00000020 代码节
0x00000040 已初始化数据节
0x00000080 未初始化数据节
0x04000000 不可缓存节
0x08000000 不可分页节
0x10000000 共享节
0x20000000 可执行节
0x40000000 可读节
0x80000000 可写节
注意:
如果一个PE文件里,对应了N个节,那么IMAGE_SECTION_HEADER结构也是对应N个
另外为了标记IMAGE_SECTION_HEADER结构已经结束了,都会在最后一个IMAGE_SECTION_HEADER附加一个
成员全部为零的IMAGE_SECTION_HEADER结构。
WINXP下运行PE的程序最小要求IMAGE_SECTION_HEADER为>=2,WIN2k则没有这个要求
//IMAGE_IMPORT_DESCRIPTOR typedef struct _IMAGE_IMPORT_DESCRIPTOR { union { DWORD Characteristics; // 0 for terminating null import descriptor DWORD OriginalFirstThunk; // RVA to original unbound IAT (PIMAGE_THUNK_DATA) }; DWORD TimeDateStamp; // 0 if not bound, // -1 if bound, and real date/time stamp // in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND) // O.W. date/time stamp of DLL bound to (Old BIND) DWORD ForwarderChain; // -1 if no forwarders DWORD Name; DWORD FirstThunk; // RVA to IAT (if bound this IAT has actual addresses) } IMAGE_IMPORT_DESCRIPTOR; typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR; DWORD OriginalFirstThunk //它是一个RVA(32位),指向一个以0结尾的、由IMAGE_THUNK_DATA(换长数据)的RVA构成的数组,其每个IMAGE_THUNK_DATA(换长数据)元素都描述一个函数。此数组永不改变。 DWORD TimeDateStamp //它是一个具有好几个目的的32位的时间戳。让我们先假设时间戳为0,一些高级的情况将在以后处理。 DWORD ForwarderChain //它是输入函数列表中第一个中转的、32位的索引。中转也是高级的东东。对初学者先将所有位设为-1。 DWORD Name //它是一个DLL文件的名称(0结尾的ASCII码字符串)的、32位的RVA。 DWORD FirstThunk //它也是一个RVA(32位),指向一个0结尾的、由IMAGE_THUNK_DATA(换长数据)的RVA构成的数组,其每个IMAGE_THUNK_DATA(换长数据)元素都描述一个函数。此数组是输入地址表的一部分,并且可以改变。
注意:IMAGE_IMPORT_DESCRIPTOR结构个数是和需要应入的DLL文件个数对应的,比如应入了N个DLL文件,那么就有N个IMAGE_IMPORT_DESCRIPTOR结构
最后用一个成员全为零的IMAGE_IMPORT_DESCRIPTOR结构来标记结束。
typedef struct _IMAGE_THUNK_DATA32 { union { DWORD ForwarderString; // PBYTE DWORD Function; // PDWORD DWORD Ordinal; DWORD AddressOfData; // PIMAGE_IMPORT_BY_NAME } u1; } IMAGE_THUNK_DATA32; typedef IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32;
对应一个32位的值
DWORD ForwarderString; 标记位对应一个比特位,当为1时候,表示函数用序号引入,为0时,表示用名字引入
注意:IMAGE_THUNK_DATA32结构个数是和需要引入函数个数对应的,比如引入了N个函数,那么就有N个IMAGE_THUNK_DATA32结构
最后用一个成员全为零的IMAGE_THUNK_DATA32结构来标记结束。
typedef struct _IMAGE_IMPORT_BY_NAME { WORD Hint; //Hint字段的内容是可选的,如果它不是0,则它也表示函数的序号 BYTE Name[1]; //变长数组,保存的是一个以NULL结尾的字符串,也就是函数名。 } IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;