PE文件是windows操作系统下使用的可执行文件格式。PE文件是指32位的可执行文件,也称为PE32。64位的可执行文件被称为PE+或PE32+,是PE文件的一中扩展形式。
1、PE文件格式
2、基本结构
3、VA&RVA
4、DOS头
5、DOS存根
6、NT头
7、NT头:文件头
8、NT头:可选头
9、节区头
PE文件格式 种类
主扩展名
可执行系列
EXE,SCR
库系列
DLL、OCX、CPL、DRV
驱动系列
SYS、VXD
对象文件系列
OBJ
除OBJ(对象)文件之外的其它文件都是可执行的。DLL、SYS文件等虽然不能直接在shell中运行,但可以通过调试器、服务等执行。
下面以notepad.exe程序举例,使用HXD打开该程序。从中可以看出一些PE的特征,例如MZ等。
基本结构 从DOS头到节区头是PE头部分,其下的节区合称PE体。文件中使用偏移(offset),内存中使用VA(Virtual Address 虚拟地址)来表示位置。文件加载到内存时,情况就会发送变化。文件的内容一般可分为代码(.text)、数据(.data)、资源(.rsrc)节,分别保存。
VA&RVA VA是指进程虚拟内存的绝对地址,RVA指从某个基准位置开始的相对地址。
VA与RVA满足下面的换算关系。
RVA+ImageBase=VA
PE头内部信息大多以RVA形式存在。原因在于,PE文件(主要是DLL)加载到进程虚拟内存的特定位置时,该位置可能已经加载其它的PE文件。此时必须通过重定位将其加载到空白的位置,若是PE头信息使用的时VA,则无法正常访问。
DOS头 微软考虑到PE文件对DOS文件的兼容性,在PE头的最前面添加了IMAGE_DOS_HEADER结构体,用来拓展已有的DOS EXE头。
IMAGE_DOS_HEADER结构体
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结构体的大小为64字节。在该结构体中有两个重要成员:e_magic(word:2个字节)、e_lfanew(long:8个字节)。
所有PE文件在开始部分(e_magic)都会有DOS签名(“MZ”)。当值被修改时,程序无法运行。
e_magic: DOS签名(signature,4D5A=>ASCII值“MZ”)
e_lfanew: 指示NT头的偏移(根据不同文件拥有可变值) 所有的PE文件在开始部分(e_magic)都有DOS签名("MZ")。e_lfanew指向NT头所在的位置(IMAGE_NT_HEADER)。
使用HxD打开abexcrackme2.exe,查看IMAGE_DOS_HEADER结构体(64个字节大小)。根据PE规范,文件开始的两个字节为4D5A,e_lfanew的为00 00 00 B8,而不是B8 00 00 00(提示:Inter的CPU以小端序标识法)。
如图所示:
DOS存根 DOS存根在DOS头下方,是一个可选项,且大小不固定。DOS存根由代码与数据混合而成。
如图所示:
同时,0000003F+DOS存根大小+1等于NT头(IMAGE_NT_HEADERS)所在的地址
0000003F+78(hex)+1=000000B8
NT头 IMAGE_NT_HEADERS结构体如下:
//x86
typedef struct _IMAGE_NT_HEADERS {
DWORD Signature;//签名结构体
IMAGE_FILE_HEADER FileHeader;//文件头
IMAGE_OPTIONAL_HEADER32 OptionalHeader;//可选头
}
//x64
typedef struct _IMAGE_NT_HEADERS64 {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER64 OptionalHeader;
} IMAGE_NT_HEADER结构体由三个成员组成Signature、FileHeader、OptionalHeader。其中第一个为Signature结构体,其值为50450000h。另外两个成员分别为文件头(FileHeader)与可选头(OptionalHeader)结构体。用HxD打开一个编译的exe程序,可以看见其IMAGE_NT_HEADERS。IMAGE_NT_HEADERS结构体的大小为F8
NT头:文件头 IMAGE_FILE_HEADER结构体主要是表现文件大致属性的结构体。主要成员如下:
//x86 x64相同
typedef struct _IMAGE_FILE_HEADER {
WORD Machine; //运行平台
WORD NumberOfSections; //区块数目
DWORD TimeDateStamp; /文件日期时间戳
DWORD PointerToSymbolTable; //指向符号表
DWORD NumberOfSymbols; //符号表中的符号数量
WORD SizeOfOptionalHeader; //映像可选头结构的大小
WORD Characteristics; //文件特征值
} IMAGE_FILE_HEADER结构体中有以下几个重要成员。
1、Macine
每一个CPU都拥有唯一的Machine码,兼容32位Inter x86的Macine为14C。下图是定义在winnt.h文件中的Machine码。
2、NumberOfSections
用来指出文件中存在的节区数量。该值一定要大于0,且当定义的节区数量与实际节区不同时,运行错误。
NumberOfSections值为3,证实abexcrackme2.exe中的节区数量为3个。
3、TimeDateStamp
该值不影响文件运行,用来记录创建文件的时间。但可以利用Delphi等工具修改时间。
使用PEID查看文件的时间戳
python编写脚本转换为可读时间格式
import time
#十六进制时间戳
hextime = '0x37E78EB0'
#十六进制转十进制
print("时间戳为:",hextime)
timeStamp = int(hextime,16)
#格式化时间戳为本地的时间
timeArray = time.localtime(timeStamp)
otherStyleTime = time.strftime("%Y-%m-%d %H:%M:%S", timeArray)
print(otherStyleTime) 查看时间与转换时间相同
6、SizeOfOptionalHeader
指明在IMAGE_NT_HEADERS中紧跟在FileHeader后的OptionalHeader的大小,对于32位的PE文件而言,这个值的大小通常为0x00E0。
7、Characteristics
用来标识文件属性,文件是否是可运行的形态、是否为DLL文件等信息,,以bit or形式组合起来。
普通EXE文件的值为0x010F,DLL文件的值为0x0210。
HxD查看IMAGE_FILE_HEADER结构体(小端序标识法)。
NT头:可选头 IMAGE_OPTIONAL_HEADER32是PE头结构中最大的。主要成员如下:
typedef struct _IMAGE_OPTIONAL_HEADER {
//
// Standard fields.
//
WORD Magic;//标志字
BYTE MajorLinkerVersion;//链接器主版本号
BYTE MinorLinkerVersion;//链接器次版本号
DWORD SizeOfCode;//所有含有代码的区块的大小
DWORD SizeOfInitializedData;//所有初始化数据区块的大小
DWORD SizeOfUninitializedData;//所有未初始化数据区块的大小
DWORD AddressOfEntryPoint;//程序执行人口 RVA
DWORD BaseOfCode;//代码区块起始RVA
DWORD BaseOfData;//数据区块起始RVA
//
// NT additional fields.
//
DWORD ImageBase;//程序默认载人基地址
DWORD SectionAlignment;//内存中区块的对齐值
DWORD FileAlignment;//文件中区块的对齐值
WORD MajorOperatingSystemVersion;//操作系统主版本号
WORD MinorOperatingSystemVersion;//操作系统次版本号
WORD MajorImageVersion;//用户自定义主版本号
WORD MinorImageVersion;//用户自定义次版本号
WORD MajorSubsystemVersion;//所需子系统主版本号
WORD MinorSubsystemVersion;//所需子系统次版本号
DWORD Win32VersionValue;//保留,通常被设置为0
DWORD SizeOfImage;//映像载入内存后的总尺寸
DWORD SizeOfHeaders;//MS-DOS头部、PE文件头、区块表总大小
DWORD CheckSum;//映像校验和
WORD Subsystem;//文件子系统
WORD DllCharacteristics;//显示 DLL特性的旗标
DWORD SizeOfStackReserve;初始化时栈的大小
DWORD SizeOfStackCommit;//初始化时实际提交栈的大小
DWORD SizeOfHeapReserve;//初始化时保留堆的大小
DWORD SizeOfHeapCommit;//初始化时实际保留堆的大小
DWORD LoaderFlags;//与调试相关,默认值为0
DWORD NumberOfRvaAndSizes;//数据目录表的项数
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];//数据目录表
} 1、Magic
为IMAGE_OPTIONAL_HEADER32结构体时,Magic码为10B;为IMAGE_OPTIONAL_HEADER64结构体时,Magic码为20B。
2、AddressOfEntryPoint
AddressOfEntryPoint持有EP的RVA值。该值指出程序最先执行的代码起始地址,相当重要。
3、ImageBase
进程虚拟内存的范围是0FFFFFFF ( 32位系统)。PE文件被加载到如此大的内存中时,ImageBase指出文件的优先装人地址。
EXE、DLL文件被装载到用户内存的O~FFFFF中,SYS文件被载人内核内存的800000-FFFFFF中。一般而言,使用开发工具( VB/VC++/Delphi)创建好EXE文件后,其ImageBase的值为00400000,DLL 文件的ImageBase值为10000000 (当然也可以指定为其他值)。执行PE文件时,PE装载器先创建进程,再将文件载人内存,然后把EIP寄存器的值设置为ImageBase+AddressOfEntryPoint。
4、SectionAlignment, FileAlignment
PE文件的Body部分划分为若干节区,这些节存储着不同类别的数据。FileAlignment指定了节区在磁盘文件中的最小单位,而SectionAlignment则指定了节区在内存中的最小单位(一个文件中,FileAlignment与SectionAlignment的值可能相同,也可能不同)。磁盘文件或内存的节区大小必定为FileAlignment或SectionAlignment值的整数倍。
5、SizeOflmage与SizeOfHeader
加载PE文件到内存时,SizeOflmage指定了PE Image在虚拟内存中所占空间的大小。一般而言,文件的大小与加载到内存中的大小是不同的。
SizeOfHeader用来指出整个PE头的大小。该值也必须是FileAlignment的整数倍。第一节区所在位置与SizeOfHeader距文件开始偏移的量相同。
6、Subsystem
Subsystem值用来区分系统驱动文件( .sys)与普通的可执行文件( .exe,*.dll)。
其值代表含义如下:
值
含义
备注
1
Driver文件
系统驱动(.sys)
2
GUI文件
窗口应用程序(.exe)
3
CUI文件
控制台应用程序(.exe)
abexcrackme2.exe中Subsystem的值为2
windows的驱动文件在C:\Windows\System32\drivers目录下。其值为1。
控制台应用程序hello.exe的值为3
HxD查看abexcrackme2.exe的IMAGE_OPTIONAL_HEADER结构体
7、NumberOfRvaAndSizes
NumberOfRvaAndSizes用来指定DataDirectory( IMAGE_ OPTIONAL_ HEADER32结构体的最后一个成员)数组的个数。虽然结构体定义中明确指出了数组个数为IMAGE NUMBEROF_DIRECTORY ENTRIES(16),但是PE装载器通过查看NumberOfRvaAndSizes值来识别数组大小,换言之,数组大小也可能不是16。
8、DataDirectory
DataDirectory是由IMAGE DATA_ DIRECTORY结构体组成的数组,数组的每项都有被定义的值。需要注意的是IMPORT与EXPORT Directory,它们是PE头中非常重要的部分。
以下为DataDirectory的数组项:
DataDirectory[0] = EXPORT Directory
DataDirectory[1] = IMPORT Directory
DataDirectory[2] = RESOURCE Directory
DataDirectory[3] = EXCEPTION Directory
DataDirectory[4] = SECURITY Di rectory
DataDirectory[5] = BASERELOC Directory
DataDirectory[6] = DEBUG Directory
DataDirectory[7] = COPYRIGHT Directory
DataDi rectory[8] = GLOBALPTR Directory
DataDirectory[9] = TLS Directory
DataDirectory[A] = LOAD CONFIG Directory
DataDirectory[B] = BOUND IMPORT Directory
DataDirectory[C] = IAT Directory
DataDirectory[D] = DELAY IMPORT Directory
DataDirectory[E] = COM DESCRIPTOR Directory
DataDirectory[F] = Reserved Directory
节区头 IMAGE_SECTION_HEADER结构数组存在PE文件头与原始数据之间。包含每个块在映像中的信息(如位置、长度、属性),分别指向不同的区块实体。全部有效结构的最后以一个空的IMAGE_SECTION_HEADER结构作为结束,所以节表中总的IMAGE_SECTION_HEADER结构数量等于节的数量加一。
另外,节表中 IMAGE_SECTION_HEADER 结构的总数总是由PE文件头NumberOfSections 字段来指定的。
以下为IMAGE_SECTION_HEADER结构数组的具体信息:
typedef struct _IMAGE_SECTION_HEADER {
BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; //块名,例如.text
union {
DWORD PhysicalAddress;
DWORD VirtualSize; //内存中节区所占大小
} Misc;
DWORD VirtualAddress; //内存中节区的起始地址
DWORD SizeOfRawData; //磁盘文件中节区所占大小
DWORD PointerToRawData; //磁盘文件中节区的起始地址
DWORD PointerToRelocations;
DWORD PointerToLinenumbers;//行号表在文件中的偏移值,文件调试的信息
WORD NumberOfRelocations;
WORD NumberOfLinenumbers;//该块在行号表中的行号数目
DWORD Characteristics; //节区属性
} 使用PEview查看 abexcrackme2.exe的节区头信息。
.text信息
.data信息
.rsrc信息
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2022-8-13 19:18
被顾忧编辑
,原因: