PE 文件格式启发式学习Word下载.docx
《PE 文件格式启发式学习Word下载.docx》由会员分享,可在线阅读,更多相关《PE 文件格式启发式学习Word下载.docx(13页珍藏版)》请在冰豆网上搜索。
![PE 文件格式启发式学习Word下载.docx](https://file1.bdocx.com/fileroot1/2022-12/16/11086101-d3e2-43d3-a7a3-9dfe9a78be01/11086101-d3e2-43d3-a7a3-9dfe9a78be011.gif)
CheckSum;
Subsystem;
DllCharacteristics;
SizeOfStackReserve;
SizeOfStackCommit;
SizeOfHeapReserve;
SizeOfHeapCommit;
LoaderFlags;
z
NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY
DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
}
IMAGE_OPTIONAL_HEADER32,
*PIMAGE_OPTIONAL_HEADER32;
1.3
慢点,别一下子贴那么多东西,我还没有找到
的位置呢,
告诉我怎样找
贴上那个_IMAGE_OPTIONAL_HEADER结构好说话,它的位置紧跟在
MAGE_FILE_HEADER
之后
告诉你个小技巧,那个Magic对NT
x86来讲总是010B,在头文件找到那个010b,就是IMAGE_OPTIONAL_HEADER32
结构的地址。
1.4
问题越来越多了。
还没有说清呢,又出来一个IMAGE_FILE_HEADER。
先不管IMAGE_FILE_HEADER
先按你的小技巧,在头部找到010b,
因为是little
endial,
在ultraedit
中要找0b
01.
好,找到了,离那个
50
45
00
(ascii
PE)相距不远,在偏移D8处,按你所说SectionAlignment和FileAlignment
应该在结构第9个,第10个DWORD
处。
好,找到了,在f8处有00001000,
FC处为00
02
(我已经考虑了endian,以后不用提醒了)。
呀,进步不小吗?
这样一下子你就把IMAGE_OPTIONAL_HEADER32
中所有的东西都找出来了。
1.5
是的,我可以把Optional
header中所有东西都找出来,但我现在除了刚才介绍的第9个DWORD为内存对齐大小,第10个DWORD为文件对齐大小,其它我都不知道是干什么的?
别着急,其实还是很容易理解的,从字面意义就能猜大概。
不过我们现在还不是通读Optional
header的时候,还是拣我们最关心的问题插手吧。
1.6
还是回到text
段上来吧,刚才你对text段大小,位置,属性分析的头头是到
你是从那看出来的?
1.6
是从section
中看出来的,每一个section,
都有一个section
描述其位置,大小,属性。
section
的结构是这样定义的
#define
IMAGE_SIZEOF_SHORT_NAME
8
_IMAGE_SECTION_HEADER
Name[IMAGE_SIZEOF_SHORT_NAME];
union
PhysicalAddress;
VirtualSize;
Misc;
VirtualAddress;
SizeOfRawData;
PointerToRawData;
PointerToRelocations;
PointerToLinenumbers;
NumberOfRelocations;
NumberOfLinenumbers;
Characteristics;
IMAGE_SECTION_HEADER,
*PIMAGE_SECTION_HEADER;
1.7
呦,慢点,怎么又往外甩结构,我很菜!
哦,不太多,还行吧。
不过你还是告诉我具体位置在哪吧,我好拿结构和数据对对号。
好,正是这种学习方法。
你一定能学会的。
节表头是一个数组,它把所有节的位置,长度,属性放在了一起
紧跟在option
之后,所以你从文件头部往下找就可以了。
看到IMAGE_SECTION_HEADER结构的第一个成员了吗,它是
Name【8】
这是节名称,你要找的text
段名字就是
.text,
你看ultraedit
ascii
码区离文件开始不远的地方,有一个.text,
对应的二进制
数据是2E
74
65
78
74,
这就是text
端IMAGE_SECTION_HEADER处
1.8
原来玄机在这里呀。
我试试看。
哦,看见了,在1B8处。
前8个
字节是节名称。
后面的00
28
到底是物理地址还是虚拟大小,
(偷偷的,虚拟大小,表示内存中只有0x28个字节有效,其它全是0),在后面00
10
是虚拟相对地址
俗称RVA,
就是在内存中相对与起始地址的偏移。
再后面00
为SizeOfRawData,
就是文件中大小,再后面
04
是
PointerToRawData,是文件的偏移
后面有三个DWORD
全是0,他们
是重定位信息和行号,很好,EXE文件可以不用管这些。
最后一个
60
20
代表属性可读,可写,是代码。
好,我终于理解你的第一句话了。
不解释一下,我怎么能一下子听的懂呢!
谢谢你。
那么我又有问题了。
那程序针真是搜索这个.text字符串找到Text
节表头吗?
不是。
前面说过,节表头紧随Optional
之后。
1.9
结构变量太多,我数了一下都没数清,到底占多少个字节呢?
正等着你这一问呢?
是啊,数都数不清,纵是现在记住了将来也容易忘。
估计微软也想到了这一点,他把OPTION
的大小放到了
_IMAGE_FILE_HEADER
的一个变量中,
下面是_IMAGE_FILE_HEADER
的定义
Machine;
NumberOfSections;
TimeDateStamp;
PointerToSymbolTable;
NumberOfSymbols;
SizeOfOptionalHeader;
IMAGE_FILE_HEADER,
*PIMAGE_FILE_HEADER;
SizeOfOptionalHeader
一般总是0xE0
1.10
我今天已经学了不少东西了,看样子后面还很多的样子。
再问最后一个问题。
FILE_HEADER
在文件什么位置呢。
这个简单,就在PE标识符后面。
看到了吗,在C0处,ascii
是PE.
二进制是50
00
代学生:
哦,看到了,今天10个问题已经满了,我还想学,可是有点累了。
。
代老师:
今天就到这里吧,好好休息一下。
接问题1.10后的第11个问题
2.11
干脆就把位置问题先问到底吧。
PE标识符
54
总是在文件偏移00c0
处吗。
基本上可以这么说,主要是因为前面的部分是dos
头部和dos
体
dos
头部
IMAGE_DOS_HEADER
的结构我就不贴了,因为dos
已经离我们远去,它
已经失掉了意义,dos
体也几乎是固定不变的了。
这部分的作用是当你拿这个
PE程序到dos
系统上运行时,dos
执行会在控制台上打印一行提示信息,
"
this
program
cannot
be
run
in
DOS
mode"
然后停在哪。
总比你一运行,DOS
就hang
机强多了。
如果拿PE代码在DOS
下直接执行,不用说那肯定hang
机。
微软就是怕这个事情发生才用了这么一个措施。
现在你只需要记住一件事,文件头两个字母是MZ标记,在3c偏移地址,00
c0
指的是NT
的文件偏移,如果这个偏移处的标识正好是PE.
可以肯定,这个文件
就是PE
文件了。
如果在3c偏移地址处存其它DWORD
地址,那就到所指定的地址去找
如果该处正好ASCII
PE"
此处就是NT的header
2.12
怎么有这么多header,
能否概要总结一下:
好的。
在文件开头部分是
_IMAGE_DOS_HEADER
,小名MZ
header,
我们已经不用关心它了。
只要关心地址偏移0x3c
处,该处存有
_IMAGE_NT_HEADER
的偏移。
在dos
和
之间是dos
体,我们也不用关心它了。
到底是什么样呢?
它实际是PE00标识+
NT_FILE_HEADER+NT_OPTION_HEADER
以下是它的结构声明。
_IMAGE_NT_HEADERS
Signature;
//这里的标记是
PE00
IMAGE_FILE_HEADER
FileHeader;
//NT
包含FILE
和Option
header
IMAGE_OPTIONAL_HEADER32
OptionalHeader;
IMAGE_NT_HEADERS32,
*PIMAGE_NT_HEADERS32;
2.13
这样对header有了一个总体认识,它占据着文件开始部分。
反正它是死的,而且每个文件只有
一个,有上面各个header
的结构定义,无非是存储这一些数据,指针。
估计详细分析一下,
它也跑不了了。
我们还是抓主要的,主要的分析清了,可能顺便就把头中的相关结构变量分析了。
还是回到节表上来。
上次已经找到了节表头,前面说是在OptionalHeader下面,现在也可以
说是在NT
header下面。
其中以.text
居首,根据节表头结构,从.text
偏移一个节表结构,
我们看到了第二个ascii
字符
“.rdata"
不远的地方还要一个”.data"
正好也偏移一个节表头结构“,
还有一个"
.rsrc"
再往后就是全0了,那么这是否是说,这个结构数组含有4个结构元素呢?
正是如此,在_IMAGE_FILE_HEADER
中有一项定义了该数值
//x86
的machine代码是
01
4c
(hello.exe
中00c4处)
hello.exe
中
是
04
与你数的完全一致。
对照一下问题1.9的_IMAGE_FILE_HEADER
和
的,你会很容易辨别的。
2.14
我对照过了,知道了它在文件中的位置。
干脆把NT
FILE
Header结构中的其他数据也分析一下吧。
反正也不多。
好,我再把该结构抄过来:
//答2.12已经说了,x86
总是01
4c
//hello.exe
是00
//时戳。
表示你的文件是何时生成的。
不过这个DWORD是用秒数表示的。
//调试信息,hello
中为全0
//答1.9已经说了,OptionalHeader
大小总是0xe0
是010F,
看标志有5个bit
是1,那就是说5个属性为真了。
FileHeader其它项都好理解,没有什么关键的东西,只有这个属性稍微麻烦一点,最多也不超过16个属性。
顺便问一下,你在ultraedit
中看着这个FILE_HEADER
吗?
哦哦,看着呢,我的光标就停在这个010F
标志处呢。
好,继续。
16个属性一下都说出来也太多,先学习hello.exe
的这5个吧。
bit0:
文件不包含重定位信息。
bit1:
文件可以运行。
bit2:
文件不包含行号信息
bit3:
文件不包含符号信息。
bit8:
32bit
机器上运行。
怎样,这你个属性很好理解吧,可执行文件不需要重定位,行号及符号信息。
在32bit机器上运行。
说了半天原来没有一个关键性东西,我还以为有多神秘呢!
是的,搞懂了它有时也觉得失望,其实,懂了也就这么简单。
2.15
再问一个关键性问题,看起来有点菜。
我以为,有text段,有data段就可以了,
那么.rdata,
.rsrc
是干什么用的呢
这个问题确实很关键,这正是PE
文件与以往文件的差别所在。
其实只有text段,data段,pe文件是不可以运行的,因为PE文件的运行总是要调用
系统文件,而系统文件都是以DLL
文件格式存在的,所以你必须要在文件中有动态链接
信息。
2.16
太复杂了,什么是动态连接信息,什么是动态连接库,听说过但没有真正理解。
动态连接是多进程操作系统引进的一个概念。
在DOS时代,单任务是没有动态连接的。
在DOS
时代,连接器总是把库文件直接连接到可执行文件中。
叫静态连接。
这种做法
在单任务时是可以接受的,无非是每个连接的文件都包含一个库文件,造成磁盘空间
的一点浪费。
但静态连接在多任务时代不可以接受。
例如每个进程都会调用
kernel32.Dll
如果采用静态连接,造成磁盘空间浪费不说,假若系统有20个进程,系统中就将会
有20份kernel32.dll,
内存的浪费将是不可容忍的,动态连接的概念就是保证系统中
只有一份DLL,同时各个进程又都能够很好的运行。
好在动态连接是加载器的功能,
我们程序不用刻意去做什么,所以用起来也不是太复杂。
2.17
哦!
是这样,我原来以为链接程序都把事情处理好了,原来还没有,多进程中还要由
加载器进行动态连接。
那我们怎样使用动态连接呢?
当我们用汇编语言或C
C++或者其它语言开发是,生成的PE文件对系统库的调用都是
动态连接。
我们并没有做什么。
当你想调用自己生成的DLL(第三方DLL),可以采用隐含动态连接或者显示动态连接来
加载DLL.
听起来很炫用起来很简单,隐含链接就跟使用系统dll
一样,你只要在文件
中包含第三方头文件(好引用它的函数啊。
)在连接选项里设置第三方的lib,dll位置
链接程序就帮你搞定了。
显示动态连接是在你的程序里用loadlibrary
加载DLL,
用getprocess
获取DLL中函数
地址,然后用函数指针调用第三方函数。
2.18
哦.
听你的意思看来使用DLL
也是很简单的。
系统DLL使用我们不用管,怎样使用第三方
DLL以后再说吧,我现在的重点是想搞明白PE
的文件格式。
那么既然它调用了
kernel32.Dll
中的函数。
而这个函数的地址连接程序不知道,只能由加载器在运行时动态加载。
那么,加载器是怎么知道要加载那个DLL,
要执行DLL中的那个程序呢?
这个问题问到点子上去了。
搞清了这个问题,PE
格式就可以说入门了。
我们还是结合hello.exe
实例说吧。
有HIEW
软件吗,准备一下。
好:
1.
用hiew
打开hello.exe
2.
按F4,
选Decode.
3.
按F5,
敲入偏移400(我们上面分析过,text
段在400)
干脆我把代码贴过来吧,
双//
是我注释的。
00000400:
6A00
push
000
00000402:
6800304000
000403000
;
@0
00000407:
680C304000
00040300C
@0♀"
0000040C:
0000040E:
E809000000
call
00000041C
//hiew
分析出,这是MessageBoxA
00000413:
90
nop
00000414:
00000415:
00000417:
E806000000
000000422
分析出,这是ExitProcess
0000041C:
FF2508204000
jmp
d,[00402008]
00000422:
FF2500204000
d,[00402000]
---------------------------------------------------------------------------
我们分析
Messagebox
吧。
40e
处:
41c,
41c
ds:
[00402008]
00402008.
这是虚拟内存地址。
我们用hiew
找到它。
具体操作如下:
选hex.
要看数据,选hex
合适
敲入偏移600.
在hiew
中看到了下一行。
.00402000:
76
00-00
00-5C
2.19
喂,慢点,打扰一下,我这个人就喜欢刨根问底。
你怎么知道要敲入600呢?
是这样,调用DLL采用动态连接,动态连接信息是放在.rdata
段,叫只读数据段。
你刚才不是问.rdata,
是干什么的吗?
我现在才讲到了.rdata
段
要想知道.rdata
在哪,你的问.rdata
的节表头。
我们已经讲过所有节表头组成一个
数组,紧跟在NT
Header(总header)之后。
.rdata
是第二项节表头,其名字是ascii码
很明显的。
我把它copy
到这啦。
00001e0:
2e72
6461
7461
0000
9200
0020
.rdata.......
..
00001f0:
0002
0006
................
0000200:
4000
0040
2e64
6174
6100
....@..@.data...
这里根据
答1.