首页
社区
课程
招聘
[原创]浅学PE结构(一)
发表于: 2022-8-13 19:15 5332

[原创]浅学PE结构(一)

2022-8-13 19:15
5332

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 被顾忧编辑 ,原因:
收藏
免费 4
支持
分享
最新回复 (1)
雪    币: 129
活跃值: (1095)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
请问110处的校验和采用什么校验方法???
谢谢指教!!!
2022-9-2 04:30
0
游客
登录 | 注册 方可回帖
返回
//