PE
需要用到的工具
十六进制文本编辑工具:WinHex和UltraEdit
工具论坛随便一搜或者百度就有,这里就不提供了
PS:这两个工具在论坛虚拟机中的工具包里都有集成
工具的作用
首先要明确任何文件在计算机的存储都是0或1(二进制)
上面的两个工具就是用于查看文件在计算机存储的内容
由于0和1比较冗长,所以使用十六进制来显示数据
准备好工具后正式开始学习PE
学习PE前需要先了解一下可执行文件
可执行文件
什么是可执行文件
可执行文件 (executable file) 指的是可以由操作系统进行加载执行的文件
可执行文件的格式
Windows平台
PE(Portable Executable)文件结构
翻译为:可移植(这里的可移植局限于Windows平台,不同版本的Win10 Win7 WinXp等) 可执行
Linux平台
ELF(Executable and Linking Format)文件结构
翻译为:执行 和 连接 格式
常见可执行文件
.exe .sys .dll等都是windows下的可执行文件(它们都遵循PE文件结构的格式)
常见非可执行文件
- .txt .png .mp4等等都是非可执行文件,它们都需要使用其它可执行文件的软件进行加载
- .txt可以使用Notepad、UltraEdit等 文本 工具查看
- .png可以使用PhotoShop等 图片 工具查看
- .mp4可以使用PotPlayer等 播放器 工具查看
为什么要学习PE
- PE文件是Windows可执行程序必须满足的规范
- 对软件的加壳和脱壳都基于PE
- EXE文件如何加载到内存中也涉及PE的知识
- 一个合格的逆向人员,必须熟悉PE
如何识别PE文件
PE文件特征(PE指纹)
前2个字节
分别用UltraEdit打开.exe .dll .sys 文件,观察特征前2个字节
本人使用的三个文件分别为:
.exe:EverEdit.exe(文本编辑工具)
.dll:dbghelp.dll(调试跟踪相关的一个dll,在C:\Windows\System32\dbghelp.dll里拷贝的)
.sys:passTp.sys(自己写的一个驱动程序,作用嘛大家懂的( ̄︶ ̄)↗ )
.exe
.dll
.sys
可以看到这三个文件的头2个字节都是4D 5A(ASCII码为MZ)
3Ch位置数据
再看这三个文件各自位于3Ch位置的数据
.exe
数据为:F0
.dll
数据为F8
.sys
数据为D8
对应数据的位置
.exe
先前的数据为F0,查看对应F0位置的2字节数据
.dll
先前的数据为F8,查看对应F8位置的2字节数据
.sys
先前的数据为D8,查看对应D8位置的2字节数据
可以看到无论是exe、dll还是sys,通过上述方式找到的数据都为:50 45(对应ACSII码为PE)
小总结
如果一个文件,它的头两个字节为4D 5A(ASCII码为MZ),并且通过3Ch位置的数据再找到的位置里的数据为PE则基本可以断定这个文件是Windows下的可执行文件(满足PE结构)
反例
随便拉一个图片png文件进来看看
很显然,开头的两个字节就已经表明它不是一个PE文件了
PS:识别可执行文件不能通过文件的后缀名来判断,而应该采用上述的方式进行判断,因为后缀名是可以改的ˋ( ° ▽、° )
PE文件总体结构
通过前面可以得知如何识别一个文件是否是PE文件
但是前面在前面为什么是查看一个文件的前2个字节和3Ch的位置,以及其它位置数据作用呢
这便是PE结构所规定的
下面是PE文件的总体结构
可以看到先前判断PE文件特征里的头两个字节对应这里的文件头:DOS ‘MZ’ HEADER
后面根据3Ch得到的50 45(对应ACSII码为PE)对应这里的PE文件头中的"PE"
PE在C中的定义
PE文件结构自然是一种数据结构,不过这种数据结构比较复杂
在C语言的winnt.h这个头文件中定义了PE文件结构相关的结构体
可以通过C语言中PE的定义来更好地学习PE
随便创建一个空的控制台项目,然后引入winnt.h这个头文件
#include<winnt.h>
int main(int argc, char* argv[])
{
return 0;
}
然后在头文件这里右键→打开文档<winnt.h>即可
接下来结合winnt.h这个头文件学习PE文件的总体结构:
DOS部首
该部分结构对应winnt.h中的_IMAGE_DOS_HEADER结构体
可以在先前打开的winnt.h中Ctrl+F搜索_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, *PIMAGE_DOS_HEADER;
关于DOS头里的成员的具体说明这里暂且不表,留作的笔记再详细说明
这里主要说明一下各部分的大体作用
首先的这个DOS部首,可以说是Windows的历史遗留问题了,因为Windows程序最早是在DOS系统(16位系统)上运行的
所以该部分主要是给DOS用的(向下兼容)
PE文件头(NT头)
该部分结构对应winnt.h中的_IMAGE_NT_HEADERS结构体
使用同样的方法得到对应的代码:
typedef struct _IMAGE_NT_HEADERS {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
可以看到PE文件头对应的结构体中还包含了其它结构体,这里依旧只介绍大体作用,细节留作之后的笔记
这里的PE文件头相对于先前的DOS部首则是给Windows使用的
块表(节表)
该部分结构对应winnt.h中的_IMAGE_SECTION_HEADER结构体
使用同样的方法得到对应的代码:
typedef struct _IMAGE_SECTION_HEADER {
BYTE Name[IMAGE_SIZEOF_SHORT_NAME];
union {
DWORD PhysicalAddress;
DWORD VirtualSize;
} Misc;
DWORD VirtualAddress;
DWORD SizeOfRawData;
DWORD PointerToRawData;
DWORD PointerToRelocations;
DWORD PointerToLinenumbers;
WORD NumberOfRelocations;
WORD NumberOfLinenumbers;
DWORD Characteristics;
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
块表主要用来表示当前文件一共分为几个部分,和后面的块相对应
块表决定了后面的块,每一块从哪里开始,里面存储的数据是什么等等
块
块部分是由前面的块表决定的,是具体的存储数据的部分
总结