PE
1.pe文件结构
windows pe文件结构
linux elf文件结构
例如我用clion生成的文件batman.exe,用010ediotr打开
#include "stdlib.h"
int main(){
system("pause");
system("ipconfig /all");
system("pause");
}
前两个字节必是MZ
在3C的位置,我们去找这个对应的80
80位置的字节对应的就是PE,代表这是一个可执行文件
PE指纹。
1)dos部分:给dos用的
2)pe文件头:给windows用的
3)节表:决定节数据的内容(说明书)
4)节数据:真正的内容
2.pe文件的两种状态
1)DOS部分
在windows上的winnt.h文件上,有_IMAGE_DOS_HEADER
一个exe文件运行前和运行后的pe格式有差别
DOS MZ文件头是64个字节,如图
DOS Stub(DOS块)大小不确定,是给连接器用的,连接器会插入自己的数据,删掉也不会影响程序的运行
DOS MZ文件头的最后四个字节指向了PE文件头的开始位置
那么Dos块就是两者之间的部分
2)PE文件头
typedef struct _IMAGE_NT_HEADERS {
DWORD Signature; # 四个字节的文件头
IMAGE_FILE_HEADER FileHeader; # 子结构体(标准PE头)
IMAGE_OPTIONAL_HEADER32 OptionalHeader; # 子结构体(扩展PE头)
} IMAGE_NT_HEADERS32,*PIMAGE_NT_HEADERS32;
标准PE头有20个字节
扩展PE头如果是32位,那么长度是224字节
因为这个有个数组,所以大,数组的每个成员是结构体。
在标准PE头中有一个规定扩展PE头大小的(2个字节)
32位的是E0(224),64位的是F0(240)
那么向后找240个字节就是64位的扩展PE头
3)节表
节表里的每个成员是40个字节
第一个成员.text
第二个成员.data
第三个成员.rdata
第四个成员.pdata
第五成员.xdata
再之后就是编译器插入的垃圾其他数据,四个节表成员代表四组数据
扩展PE头有一个成员
这个头指的是DOS 头 + PE 头 + 节表,按照文件对其以后的大小
假设1 + 2 + 3的大小是302个字节
那么SizeOfheaders的大小是在满足这个之后又必须被“文件对其”之后的大小的整数倍,如下
假设这个filealignment是200字节那么我的实际大小就是400字节,是它的整数倍大于302,为了执行的时候效率更高
这里我们在标准PE头之后查2 + 1 2 + 8 4 = 36 个字节之后就是filealignment
这里就是filealignment是10进制的200
那么我们再去找sizeofheader,从文件对其之后再找20个字节
也就是十进制的400,必须是200的整数倍
这个之上都是头的大小,之下再开始就是真正的节数据了
也就是说假如.text块的大小是180字节,那么我们使得它为200字节,剩下的20字节用0填充
在硬盘中的头和在内存中的头是一样的
在硬盘中的第一个节是400开始,但是在内存中的第一个节却不是400,是1000
是因为内存对齐成员属性,在fileaglinment之上的sectionalignment规定了内存对齐
是100
PE的两种状态就是内存中和硬盘中的两种状态
3.DOS头属性说明
总共是64位
除了标红的这两个,其他的都是为16位服务的,现在都是32位或者64位所以已经用不到,可以删掉了
我们把除了第一个和最后一个以外其他都变成0
另存为在桌面,运行,发现正常执行
正常执行
操作系统如何判断是否是可执行文件?
前两个字节是MZ,倒数第四个字节指向的地方是PE头,所以这两处不能改动,否则不能运行
dos块的内容可以更改
不影响程序的运行,所以我们可以添加恶意代码shellcode进去
。
4.标准PE头属性说明
typedef struct _IMAGE_NT_HEADERS {
DWORD Signature; # PE标识
IMAGE_FILE_HEADER FileHeader; # 标准PE头
IMAGE_OPTIONAL_HEADER32 OptionalHeader; # 扩展PE头
} IMAGE_NT_HEADERS32,*PIMAGE_NT_HEADERS32;
如果更改PE标识也无法运行
标准PE头
两个字节的machine:如果是00 00 那么就是可以运行在任何CPU上
system64存储32位的,system32存储64位的
numberofsections 代表有多少个节,也就是后面的0F 00 ,根据小端存储,就是000F,也就是15个节
再往后的4个字节是timedatestamp,是编译器填写的时间戳,和创建时间修改时间无关
636B9534
全部修改也不影响使用,然后跳过8个字节的两个字节是扩展PE头的大小
我们默认不该32位的是224字节(0xE0),64位的是240字节(0xF0)
再之后两个字节就是文件属性
刚好2个字节,16位,代表每个属性
这里的27 00 就是 00 27,转2进制是0000 0000 0010 0111
根据上面那个图
0000 0000 0010 0111
这里数据位是1的,代表上图文件可执行
数据位是5的地方是1,代表可处理大于2GB的的地址,意思就是在32位中内核高2G,应用程序低2G,如果大于2G就说明不是32位的,是64位的
5.扩展PE头属性
32位的PE头和64位的PE头有差异
typedef struct _IMAGE_NT_HEADERS {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32,*PIMAGE_NT_HEADERS32;
typedef struct _IMAGE_NT_HEADERS64 {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER64 OptionalHeader;
} IMAGE_NT_HEADERS64,*PIMAGE_NT_HEADERS64;
差别在 IMAGE_OPTIONAL_HEADER32 和 IMAGE_OPTIONAL_HEADER64
这里最后的NumberOfRvaAndSizes 代表目录项数量,16个,乘以下面的数组长度,16张表(重点)
typedef struct _IMAGE_OPTIONAL_HEADER {
WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
DWORD BaseOfData;
DWORD ImageBase;
DWORD SectionAlignment;
DWORD FileAlignment;
WORD MajorOperatingSystemVersion;
WORD MinorOperatingSystemVersion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsystemVersion;
WORD MinorSubsystemVersion;
DWORD Win32VersionValue;
DWORD SizeOfImage;
DWORD SizeOfHeaders;
DWORD CheckSum;
WORD Subsystem;
WORD DllCharacteristics;
DWORD SizeOfStackReserve;
DWORD SizeOfStackCommit;
DWORD SizeOfHeapReserve;
DWORD SizeOfHeapCommit;
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32,*PIMAGE_OPTIONAL_HEADER32;
Magic 可以判断是32位还是64位,如果转换之后是10B就是32位,是20B就是64位的
AddressOfEntryPoint 是程序的入口,一定要配合ImageBase 一起看(内存镜像基址)
这里我们ImageBase 就是我们的PE在4GB的内存中展开的初始地址,而AddressOfEntryPoint 则是我们程序开始的指向地址
查过16个字节就是183D7(相对地址,相对ImageBase)
再跳过8个字节找ImageBase,就是400000
00 00 40 00
那么在内存中的绝对地址就是 400000 + 183D7 = 4183D7
所以程序之所以能找到入口,就是因为这个
所以加密就是不能让逆向分析人员轻易找到程序的入口,想办法藏起来
SizeOfImage 决定整个PE占整个内存的多大,一定是内存对齐的整数倍
SizeOfHeaders 是按照对齐后的大小
checksum 是校验和,判断文件是否被修改
DllCharacteristics 是判断是否可以被DLL加载
这里之前的3就是代表控制台或者dll文件
40 01 就是 0140 0000 0001 0100 0000
这里就是6和8的位置有对应,就是可以被重定位和兼容DEP(硬件层面堵漏洞)