PE格式;应用于32位windows系统(这种结构,每两位组成一个数据,但高位在后,低位于前,如4c01 =14Ch)
PE的基本结构:
调试信息 |
块 |
块表 |
PE文件头 |
DOS部首 |
一.任一个PE文件都以一个DOS部首开始!存在的意义类似于一个if then else结构,判断此PE文件是否适应当前操作系统,将简单地显示一个错误信息,除此之外跳向PE文件头继续执行。PE文件头以下将会是我们着重分析的部分。
二.PE文件头以PE/0/0字串开始!其后是映象文件头,再后是可选映象文件头!
1.PE/0/0:占4位空间,即 5045 0000
2.映象文件头:共长20位!
(1) 可执行文件的CPU类型,长2位。如 4C01 =14Ch
机器 |
Intel i386 |
Mips r3000 |
Mips r4000 |
Alpha axp |
Power pc |
标志 |
14Ch |
162h |
166h |
184h |
1F0h |
(2) 块数目,长2位。如 0300 =03h
(3) 时间日期标记,长4位。如 2C97 B83D
(4) 调试信息,长4位。 0000 0000
(5) 符号数,长4位。 0000 0000
(6) 可选部首长度,长2位,在OBJ中,为0, 执行文件中指向可选映象头结构的长度。如 E000 =E0h
(7) 文件属性,长2位,有选择地通过几值的运算得到!如 0F01
特征值 |
含义 |
1h |
重定位信息被除去 |
2h |
文件可执行 |
4h |
行号被除去 |
8h |
符号被除去 |
80h |
处理机的低位字节是相反的 |
100h |
32位机器 |
200h |
.DBG文件的调试信息被除去 |
400h |
如果映象文件是在可移动媒体中,则先复制到交换文件后再运行 |
800h |
如果映象文件是在网络中,则复制到交换文件后才运行 |
1000h |
系统文件 |
2000h |
文件是DLL文件 |
4000h |
文件只能运行在单处理器上 |
8000h |
处理机的高位字节是相反的 |
所以,我们把所有的映象文件头例子合起来,就是这样
长20位:
4c01 0300 2c97 b83d 0000 0000 0000 0000 e000 0f01
3.可选映象头:(一个可选结构)长112位!
(1)幻数,说明文件是ROM映象,长度2位,一般是0B01 =10Bh 数,说明文件是ROM映象,长度2位,一般是0B01 =10Bh
(2)链接程序的主版本号,长1位, 如 05
(3)链接程序的次版本号,长1位, 如 0C
(4)代码段大小,长度4位,如 0002 0000
(5)已初始化数据块的大小,长度4位,如 0004 0000
(6)未初始化数据块的大小,长度4位,如 0000 0000
(7)程序开始执行的入口地址,长度4位,如 0010 0000
(8) 代码段的入口地址,长度4位,如 0010 0000
(9) 数据段的起始地址,长度4位,如 0020 0000
(10) 可执行文件默认装入的基地址,长4位,如 0000 4000
所以(10)和(8)组成了我们熟悉的401000
(11) 内存中块的对齐值,长度4位,一般默认为0010 0000 =1000h
(12) 文件块的对齐值,长度4位,如 0002 0000
(13) 要求操作系统的最低版本号的主版本号,长2位,如 0400
(14) 要求操作系统的最低版本号的次版本号,长2位,如 0000
(15) 该可执行文件的主版本号,长2位,如 0400
(16) 该可执行文件的次版本号,长2位,如 0000
(17) 要求最低子系统版本的主版本号,长2位,如 0400
(18) 要求最低子系统版本的次版本号,长2位,如 0400
(19) 保留,长4位, 0000 0000
(20) 映象装入内存后的总尺寸,长4位,如 3830 0000
(21) 部首及块表的大小,长4位,如 0004 0000
(22) CRC检验和,一般的exe可以为0,但重要的dll必须要有,长4位, 如 A720 0000
(23) 程序使用的用户接口子系统,长2位,如 0200
值 |
子系统 |
0 |
未知 |
1 |
不需要子系统 |
2 |
图形接口子系统 |
3 |
字符子系统 |
5 |
OS/2字符子系统 |
7 |
POSIX字符子系统 |
8 |
保留 |
(24) DllMain()函数何时被调用,长2位,默认为0000
(25) 为线程保留的堆栈大小,长4位,如 0000 1000
(26) 当线程初始化时提交堆栈的大小,长4位, 如 0010 0000
(27) 为进程的默认堆栈保留的内存,长4位, 如 0000 1000
(28) 进程初始化时提交的默认堆大小,长4位,如 0010 0000
(29) 与调试无关,长4位,默认为 0000 0000
(30) 数据目录的项数,长4位,如 1000 0000
(31) 数据目录表,长8×16位,如其名,是数据的目录表,包含16组数据目录,每组长8位,前4位是地址,后4位是大小!
序号 |
成员 |
长度 |
示例 |
1 |
Export table |
8 |
0000 0000 0000 0000 |
2 |
Import table |
8 |
4020 3c00 0000 0000 |
3 |
Resources table |
8 |
0000 0000 0000 0000 |
4 |
Exception table |
8 |
0000 0000 0000 0000 |
5 |
Security table |
8 |
0000 0000 0000 0000 |
6 |
Base relocation table |
8 |
0000 0000 0000 0000 |
7 |
Debug |
8 |
0000 0000 0000 0000 |
8 |
Copyright |
8 |
0000 0000 0000 0000 |
9 |
Global ptr |
8 |
0000 0000 0000 0000 |
10 |
Thread local strorage(TLS) |
8 |
0000 0000 0000 0000 |
11 |
Load configuration |
8 |
0000 0000 0000 0000 |
12 |
Bound import |
8 |
0000 0000 0000 0000 |
13 |
Import address table(IAT) |
8 |
0020 0000 4000 0000 |
14 |
Delay import |
8 |
0000 0000 0000 0000 |
15 |
Com escriptor |
8 |
0000 0000 0000 0000 |
16 |
保留 |
8 |
0000 0000 0000 0000 |
本次示例中表无输出表,输入表地址0x2040 大小0x3C,输入地址表(IAT)地址0x2000 大小0x40[看得出来,输入地址表刚好就是输入表前面的那0x40空间,:]]
于是,我们把可选映象头所有31项例子组合起来,将是一个完整的可先映象头在PE体中的结构:长6x16+8x16位
0b01 050c 0002 0000 0004 0000 0000 0000 0010 0000 0010 0000 0020 0000 0000 4000 0010 0000 0002 0000 0400 0000 0400 0000 0400 0000 0000 0000 3830 0000 0004 0000 a720 0000 0200 0000 0000 1000 0010 0000 0000 1000 0010 0000 0000 0000 1000 0000 0000 0000 0000 0000 4020 0000 3c00 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0020 0000 4000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
4.接下来是块表:块表将是不定总长度的了,但块单项长度是定的,长20位,总长将是块数x20!
(1)块名,是长8位的ASCII码名,如 2E74 6578 7400 0000 =[.text]
(2)该块的真实长度,长4位,如 9A01 0000
(3)该块装载到内存中的RVA,长4位, exe中默认为1000h 即 0010 0000
(4)该块在磁盘文件中所占的在小,长4位, 如 00002 00000
(5)该块在磁盘文件中的偏移,长4位,如 0004 0000
(6)此部分在exe中无意义,占4位, 0000 0000
(7)行号表在文件中的偏移值,是文件的调试信息,占4位,如 0000 0000
(8)exe中无意义,长2位,0000
(9)该块在行号表中的行号数目,长2位, 可以如 0000 0000
(10)块属性,长4位,是多个标志值求或的值
字段值 |
用途 |
00000020h |
包含执行代码,常与10000000h一起设置 |
00000040h |
包含已初始代的数据 |
00000080h |
包含未初始化的数据 |
02000000h |
可丢弃块属性 |
10000000h |
共享块属性 |
20000000h |
可执行块属性 |
40000000h |
可读 |
80000000h |
可写 |
常用的主要以
C0000040h 可读可写,包含初始化数据
E0000020h 可读可写并可执行
60000020h 可读可执行
所以,块表单项属性20位为:(以.text块表为例)
2e74 6578 7400 000 9a01 0000 0010 0000 0002 0000 0004 0000 0000 0000 0000 0000 0000 0000 2000 00060
ok, 从PE/0/0到块的长度是定的,所以
PE结构体实例代码如下:
Dos部首 |
if then else结构的东东! |
PE/0/0 |
5045 0000 |
映象文件头 |
4c01 0300 2c97 b83d 0000 0000 0000 0000 e000 0f01 |
可选映象文件头(不含数据目录表) |
0b01 050c 0002 0000 0004 0000 0000 0000 0010 0000 0010 0000 0020 0000 0000 4000 0010 0000 0002 0000 0400 0000 0400 0000 0400 0000 0000 0000 3830 0000 0004 0000 a720 0000 0200 0000 0000 1000 0010 0000 0000 1000 0010 0000 0000 0000 1000 0000 |
可选映象文件头之数据目录表 |
0000 0000 0000 0000 4020 0000 3c00 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0020 0000 4000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 |
块表(单项,以text块为例) |
2e74 6578 7400 000 9a01 0000 0010 0000 0002 0000 0004 0000 0000 0000 0000 0000 0000 0000 2000 00060 |
块 |
真正执行的程序功能代码 |
连起来就是:
……从PE/0/0标志开始 5045 0000 4c01 0300 2c97 b83d 0000 0000 0000 0000 e000 0f01 0b01 050c 0002 0000 0004 0000 0000 0000 0010 0000 0010 0000 0020 0000 0000 4000 0010 0000 0002 0000 0400 0000 0400 0000 0400 0000 0000 0000 3830 0000 0004 0000 a720 0000 0200 0000 0000 1000 0010 0000 0000 1000 0010 0000 0000 0000 1000 0000 0000 0000 0000 0000 4020 0000 3c00 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0020 0000 4000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 2e74 6578 7400 000 9a01 0000 0010 0000 0002 0000 0004 0000 0000 0000 0000 0000 0000 0000 2000 00060到第一块属性表例 …… |
这就是所谓的PE结构,并不复杂!
怎么才叫已知PE格式呢,看完这一PE通俗篇!
附带1.输入表结构通俗性理解篇
输入地址表 |
输入表 |
函数名表 |
输入表和输入地址表位置在上面PE格式中的可选映象头之数据目录表中可以找到,这里不再重复(现在也可回头去看看,小学时的老师就说过了:不清楚的查字典!)。
1. 输入表:
故名思意,一张表,跟块表一样,是一张表,由多个单项组成,但所有单项是结构是统一 的,相同。输入表,有多少函数输入,就有多少项,结果相同!
表数据各4位,共20位!
实例:
指向函数对应地址 |
时间标志(可忽略) |
链接索引(一般为0) |
指向dll名 |
指向函数名 |
8C20 0000 |
0000 0000 |
0000 0000 |
7421 0000 |
1020 0000 |
指向输入地址表空间 |
|
|
|
指向函数名表空间 |
77DFBD20 |
|
|
|
LoadIconA |
即: 8c20 0000 0000 0000 0000 7421 0000这样就表示一项输入函数信息!
用简单的汇编指令来表示(还是那句话,不清楚的查字典):
dword ptr DS:[208C]=‘77DFBD20’
dword ptr DS:[2174]=‘user32.dll’
dword ptr DS:[2010]=‘LoadIconA’
另外,这种表示法在汇编里面基本上是不成立的,只是借指令意思来说明!:)
即通过这张表把函数名与地址联系在一起来了!
2.输入地址表就是一些4位长的表示地址的数据组成的一张表!
2. 存贮函数名的空间!
附带2.绑定输入
算是绑定dll吧,可选映象头之数据目录表中有绑定输入项,指向绑定输入起始地址!
结构:
绑定结构表 |
Dll名表 |
1. 绑定结构名表,是表,由单项或多项组成,有多少Dll引入就有多少项,每一项结构一样!长8位,前4位是时间/日期戳,第5、6位指向dll名表中相应dll名(是相对偏移,相对于绑定输入起始地址的偏移),最后2位保留。
例:
时间/日期戳 |
指向dll名表中相应dll名 |
保留 |
75DA 3837 |
3800 |
0000 |
即75da 3837 3800 0000
2.Dll名表是dll名存贮空间!