如图1所示,一个完整的PE(Portable Executable)文件由DOS头,PE文件头,块表,块和调试信息(调试版本有效)组成。
图1 PE文件格式
在DOS头部,以e_magic开头,它的值是固定的”0x
DOS Stub即为一句话“This program Cannot be run in DOS mode”。
typedef struct _IMAGE_DOS_HEADER {
// DOS .EXE header
WORD
e_magic;
// Magic number
WORD
e_cblp;
// Bytes on last page of file
WORD
e_cp;
// Pages in file
WORD
e_crlc;
// Relocations
WORD
e_cparhdr;
// Size of header in paragraphs
WORD
e_minalloc;
// Minimum extra paragraphs needed
WORD
e_maxalloc;
// Maximum extra paragraphs needed
WORD
e_ss;
// Initial (relative) SS value
WORD
e_sp; // Initial SP value
WORD
e_csum;
// Checksum
WORD
e_ip;
// Initial IP value
WORD
e_cs;
// Initial (relative) CS value
WORD
e_lfarlc;
// File address of relocation table
WORD
e_ovno;
// Overlay number
WORD
e_res[4];
// Reserved words
WORD
e_oemid;
// OEM identifier (for e_oeminfo)
WORD
e_oeminfo;
// OEM information; e_oemid specific
WORD
e_res2[10];
// Reserved words
LONG
e_lfanew;
// File address of new exe header
}
IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
在DOS头部,有一个结构成员e_lfanew指向了真正的PE头。值得注意的是PE文件头中的IMAGE_OPTIONAL_HEADER32是一个非常重要的结构,PE文件中的导入表、导出表、资源、重定位表等数据的位置和长度都保存在这个结构里。
typedef struct _IMAGE_NT_HEADERS {
DWORD Signature; //PE头签名PE\0\0
IMAGE_FILE_HEADER
FileHeader;//PE文件头
IMAGE_OPTIONAL_HEADER32
OptionalHeader;//PE扩展头
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
typedef struct _IMAGE_FILE_HEADER {
WORD
Machine; //
WORD
NumberOfSections; //PE节数量-0007个节
DWORD
TimeDateStamp; //时间戳E72B4FA9
DWORD
PointerToSymbolTable; //指向符号表0000
DWORD
NumberOfSymbols; //符号表数量0000
WORD
SizeOfOptionalHeader; //扩展PE头大小00E0
WORD Characteristics; //文件属性
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
typedef struct _IMAGE_OPTIONAL_HEADER {
//
// Standard fields.
//
WORD
Magic; //010B-IMAGE_NT_OPTIONAL_HDR32_MAGIC
BYTE
MajorLinkerVersion; //
BYTE
MinorLinkerVersion; //00-连接器小版本号
DWORD
SizeOfCode; //
DWORD
SizeOfInitializedData; //
DWORD
SizeOfUninitializedData; //00000000(0)-未初始化数据大小
DWORD
AddressOfEntryPoint; //000110AA程序入口地址
DWORD
BaseOfCode; //00001000程序段基地址
DWORD
BaseOfData; //00001000数据段基地址
//
// NT additional fields.
//
DWORD
ImageBase; //镜像加载基地址00400000
DWORD
SectionAlignment; //节对其0001000(4096)
DWORD
FileAlignment; //文件对齐0000200(512)
WORD
MajorOperatingSystemVersion; //操作系统主版本号0005
WORD
MinorOperatingSystemVersion; //操作系统小版本号0001
WORD
MajorImageVersion; //镜像主版本号0000
WORD
MinorImageVersion; //镜像小版本号0000
WORD
MajorSubsystemVersion; //子系统主版本号0005
WORD
MinorSubsystemVersion; //子系统小版本号0001
DWORD
Win32VersionValue; //0
DWORD
SizeOfImage; //镜像大小00022000
DWORD
SizeOfHeaders; //头大小0400
DWORD
CheckSum; //0
WORD
Subsystem; //03-IMAGE_SUBSYSTEM_WINDOWS_CUI
WORD
DllCharacteristics;
DWORD
SizeOfStackReserve; //栈初始化大小010000
DWORD
SizeOfStackCommit; //栈提交大小01000
DWORD
SizeOfHeapReserve; //堆初始化大小010000
DWORD
SizeOfHeapCommit; //堆提交大小01000
DWORD
LoaderFlags; //0
DWORD
NumberOfRvaAndSizes; //10(16)
IMAGE_DATA_DIRECTORY
DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];//数据目录表
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
在PE头,最开始的值是一个PE文件特有的签名,即“PE\0\
l WORD Machine;//该文件运行所需要的CPU,对于Intel平台是14Ch
l WORD NumberOfSections;//文件的节数目
l DWORD TimeDateStamp;//文件创建日期和时间
l DWORD PointerToSymbolTable;//用于调试
l DWORD NumberOfSymbols;//符号表中符号个数
l WORD SizeOfOptionalHeader;//OptionalHeader 结构大小
l WORD Characteristics;//文件信息标记,区分文件是exe还是dll
在Optional Header里,包含了如下信息:
WORD Magic;//标志字(总是010bh)
BYTE MajorLinkerVersion;//连接器高版本号
BYTE MinorLinkerVersion;//连接器低版本号
DWORD SizeOfCode;//代码段大小
DWORD SizeOfInitializedData;//已初始化数据块大小
DWORD SizeOfUninitializedData;//未初始化数据块大小
DWORD AddressOfEntryPoint;//PE装载器准备运行的PE文件的第一个指令的RVA,
//若要改变整个执行的流程,可以将该值指定到新的RVA,这样新RVA处的指令首先
//被执行。
DWORD BaseOfCode;//代码段起始RVA
DWORD BaseOfData;//数据段起始RVA
DWORD ImageBase;//PE文件的装载地址
DWORD SectionAlignment;//块对齐因子
DWORD FileAlignment;//文件块对齐因子
WORD MajorOperatingSystemVersion;//所需操作系统高位版本号
WORD MinorOperatingSystemVersion;// 所需操作系统低位版本号
WORD MajorImageVersion;//用户自定义高位版本号
WORD MinorImageVersion;//用户自定义低位版本号
WORD MajorSubsystemVersion;//win32子系统版本。若PE文件是专门为Win32设计的
WORD MinorSubsystemVersion;//该子系统版本必定是4.0否则对话框不会有3维立体感
DWORD Win32VersionValue;//保留值,系统没用到的,一般被作为是否感染的标志
DWORD SizeOfImage;//内存中整个PE映像体的尺寸
DWORD SizeOfHeaders;//所有头+节表的大小
DWORD CheckSum;//校验和
WORD Subsystem;//NT用来识别PE文件属于哪个子系统
WORD DllCharacteristics;// 用来表示一个DLL映像是否为进程和线程的初始化及终止包含入口点的标记
DWORD SizeOfStackReserve;//
DWORD SizeOfStackCommit;//
DWORD SizeOfHeapReserve;//
DWORD SizeOfHeapCommit;//
//堆栈大小 这些域控制要保留的地址空间数量,并且负责栈和默认堆的申请。在默认//情况下,栈和堆都拥有1个页面的申请值以及16个页面的保留值
DWORD LoaderFlags;// 告知装载器是否在装载时中止和调试,或者默认地正常运行
DWORD NumberOfRvaAndSizes;// 该字段标识了接下来的DataDirectory数组个数。
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
//IMAGE_DATA_DIRECTORY 结构数组。每个结构给出一个重要数据结构的RVA,比如//引入地址表等
图2 PE文件格式全览
在Optional Header里,有一个重要的数组即DataDirectory数组。在这个数组里面,保存着很多重要的表,比如导入表(IMP),导出表(EMP),IAT表等。
导入表记录了PE使用了多少库函数。导入表的结构如下:
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
union {
DWORD Characteristics;
DWORD OriginalFirstThunk;// 指向一个
IMAGE_THUNK_DATA 结构数组的RVA
}
DWORD TimeDateStamp;// 文件生成的时间
DWORD ForwarderChain;// 这个数据一般为0,可以不关心
DWORD Name1; // RVA,指向DLL名字的指针,ASCII字符串
DWORD FirstThunk; //指向一个 IMAGE_THUNK_DATA
结构数组的RVA,这个数据与IAT所指向的地址一致
}IMAGE_IMPORT_DESCRIPTOR,*PIMAGE_IMPORT_DESCRIPTOR
IMAGE_THUNK_DATA 这是一个DWORD类型的集合。通常我们将其解释为指向一个 IMAGE_IMPORT_BY_NAME 结构的指针,其定义如下:
IMAGE_THUNK_DATA{
union {
PBYTE ForwarderString;
PDWORD Function;
DWORD Ordinal;//判定当前结构数据是不是以序号为输出的,如果是的话该值为
//0x800000000,此时PIMAGE_IMPORT_BY_NAME不可做为名称使用
PIMAGE_IMPORT_BY_NAME
AddressOfData;
}u1;
} IMAGE_THUNK_DATA,*PIMAGE_THUNK_DATA;
typedef struct _IMAGE_IMPORT_BY_NAME{
WORD Hint;// 函数输出序号
BYTE Name1[1];//输出函数名称
} IMAGE_IMPORT_BY_NAME,*PIMAGE_IMPORT_BY_NAME
节表头结构:
typedef struct _IMAGE_SECTION_HEADER {
BYTE Name[IMAGE_SIZEOF_SHORT_NAME];//节表名称,如“.text”
//IMAGE_SIZEOF_SHORT_NAME=8
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;
1.概述(general)
-------
所有的节在载入内存后都按“SectionAlignment”(节对齐)对齐,在文件中则以“FileAlignment”(文件对齐)对齐。节由节头中的相关项来描述:在文件中你可通过“PointerToRawData”(原始数据指针)来找到,在内存中你可通过“VirtualAddress”(虚拟地址)来找到;长度由“SizeOfRawData”(原始数据长度)决定。
根据节中包含的内容,可分为好几种节。大多数(并非所有)情况下,节中至少由一个数据目录,并在可选头的数据目录数组中有一个指针指向它。
2.代码节(code section)
------------------------
首先,我将提到代码节。此节,至少,要将“IMAGE_SCN_CNT_CODE”(含有代码节)、“IMAGE_SCN_MEM_EXECUTE”(内存可执行节)和“IMAGE_SCN_MEM_READ”(内存可读节)等标志位设为1,并且“AddressOfEntryPoint”(入口点地址)将指向节中的某个地方,指向开发者希望首先执行的那个函数的开始处。
“BaseOfCode”(代码基址)通常指向这一节的开始处,但是,如果一些非代码字节被放在代码之前的话,它也可能指向节中靠后的某个地方。
通常,除了可执行代码外,本节没有别的东东,并且通常只有一个代码节,但是不要太迷信这一点。
典型的节名有“.text”、“.code”、“AUTO”之类。
3.数据节(data section)
------------------------
我们要讨论的下一件事情就是已初始化变量;本节包含的是已初始化的静态变量(象“static int i = 5;”)。它将,至少,使“IMAGE_SCN_CNT_INITIALIZED_DATA”(含有已初始化数据节)、“IMAGE_SCN_MEM_READ”(内存可读节)和“IMAGE_SCN_MEM_WRITE”(内存可写节)等标志位被置为1。
一些链接器可能会将常量放在没有可写标志位的它们自己的节中。如果有一部分数据可共享,或者有其它的特定情况,那么可能会有更多的节,且它们的合适的标志位会被设置。
不管是一节,还是多节,它们都将处于从“BaseOfData”(数据基址)到“BaseOfData”+“SizeOfInitializedData”(数据基址+已初始化数据的大小)的范围之内。
典型的名称有“.data”、“.idata”、“DATA”、等等。
4.BSS节(bss section)
----------------------
其后就是未初始化的数据(一些象“static int k;”之类的静态变量);本节十分象已初始化的数据,但它的“PointerToRawData”(文件偏移量)却为0,表明它的内容不存储在文件中;并且“IMAGE_SCN_CNT_UNINITIALIZED_DATA”(含有未初始化数据节)而不是“IMAGE_SCN_CNT_INITIALIZED_DATA”(含有已初始化数据节)标志位被置为1,表明在载入时它的内容应该被置为0。这就意味着,在文件中只有节头,没有节身;节身将由加载器创建,并全部为0字节。
它的长度由“SizeOfUninitializedData”(未初始化数据大小)确定。
典型的名称有“.bss”、“BSS”之类。
有些节数据“没有”被数据目录指向。它们的内容和结构是由编译器而不是链接器提供。
(栈段和堆段不是二进制文件中的节,它们是由加载器根据可选头中的栈大小和堆大小项来创建的。)
ELF(Executable and Linking Format)是一种对象文件的格式,用于定义不同类型的对象文件(Object files)中都放了什么东西、以及都以什么样的格式去放这些东西。
ELF文件格式提供了两种视图:连接视图和运行视图。在两种视图中,ELF头部(ELF Header)都位于文件的开始部分,位置固定,保存了路线图(road map),描述了该文件的组织情况。
图3 ELF文件格式
在链接视图中,程序头部表(program header table)为可选。从程序的执行来看文件格式,程序头部表告诉系统如何来创建一个进程的内存映象。被用来建立进程映象(执行一个程序)的文件必须要有一个程序头部表,可重定位文件不需要这个头部表。
节区(section)保存着目标文件的信息,从链接视图看,包括指令,数据,符号表和重定位信息等等。从执行视图看,一个段通常包含几个节区,同样保存着指令,数据,符号表和重定位等信息。
节区头部表(section header table)包含了描述节区的信息。每个节区在这个表中有一个入口,该入口给出了节区的名字,大小等等信息。链接过程中的文件必须有一个节区头部表,而在执行视图中这个节区头部表为可选。
目标文件格式支持8位字节/32位体系结构。不过这种格式是可以扩展的,因此,目标文件以某些机器独立的格式来表达某些控制数据,使得能够以一种的公共的方式来识别和解释其内容。目标文件中的其它数据使用目标处理器的编码结构,而不管文件在何种机器上创建。
Copyright 2011-2020 © MallocFree. All rights reserved.