关于PE头的分析,写得好的要数Matt Pietrek的文章了。我今天写的主题就是学习这篇文章的一点体会,希望对大家在分析PE文件有所帮助,同时加深自己对PE头的理解。
PE头的总体结构如下:
图1、PE头总体结构
在各个字段前面的数字代表在此结构体内的相对位移。
好了,下面让我们来看看具体分析过程:
1、用UltraEdit打开一个EXE文件(DLL文件也一样,只不过是起始地址不一样而已)。
2、找到如图一所示的字段“Signature”,对于PE文件,它的值是"PE",如图二.
图二、 一个EXE文件前面的字节情况
3、从"PE"这个字符所在位置往下数120个字节。在图二中"PE"所在位置是d0h + 8;然后往下数120个字节就是图三了。
图三:IMAGE_IMPORT_DESCRIPTOR的RVA
我们由上图得到 IMAGE_IMPORT_DESCRIPTOR 的RVA = 0x0003c000(Intel是Little Endian.即从后向前读)
IMAGE_IMPORT_DESCRIPTOR 结构体描述如下:
DWORD Characteristics 这个域在以前可能是一个标志。现在Microsoft已经更改了它的意义但是并没有同时更新WINNT.H文件。它实际是一个指针数组的偏移地址(RVA)。其中的每个指针都指向一个IMAGE_IMPORT_BY_NAME结构。 DWORD TimeDateStamp 指示文件创建日期的日期/时间戳。 DWORD ForwarderChain 这个域与函数转发(Forward)有关。转发就是把对一个DLL中的某个函数的调用转到另一个DLL的某个函数上。例如在Windows NT上,KERNEL32.DLL就将它的一些导出函数转发到了NTDLL.DLL中。一个应用程序看起来好像调用的是KERNEL32.DLL中的函数,但实际上它调用的是NTDLL.DLL中的函数。这个域包含了FirstThunk数组(马上就要讲到)的索引。被这个域索引的函数会被转发到另一个DLL上。不幸的是,函数是如何转发的这种格式并未公开。转发函数的例子很难找到。 DWORD Name 这是一个以NULL结尾的ASCII字符串的RVA,这个字符串包含导入的DLL的名称。常见的例子是“KERNEL32.DLL”和“USER32.DLL”。 PIMAGE_THUNK_DATA FirstThunk 这个域是IMAGE_THUNK_DATA共用体的偏移地址(RVA)。几乎在所有情况下,这个共用体都是作为指向IMAGE_IMPORT_BY_NAME结构的指针。如果这个域不是这些指针之一,那推测它应该是那个被导入的DLL所导出的一个序数值。从文档上看并不清楚是否可以只通过序数而不通过名称就能导入函数。4、打开windbg。噢,还没有,那赶快一下个吧.点这里下载。具体使用自己学吧,我也只会一点。
5、打开windgb.FILE->Open Executalbe(或Ctrl+E),选择我们刚才用UE分析的那个EXE文件。
6、View->Memory(或Alt+5),输入刚才那个地址,但要加上应用程序被加载的地址。(一般WIN32下面的应用程序的Base Address 是0x400000。所以最后输入的地址是:0x0043c000.如图四。然后我们根据上面关于IMAGE_IMPORT_DESCRIPTOR 的结构体说明可知,Characteristics = 0x3c028,FirstThunk = 0x3c2a0。
图四:得到IMAGE_IMPORT_DESCRIPTOR结构体内的每一个成员的RVA
7、一步一步靠近。。。。。
8、因为第一项(前4个字节)是结构体内有关导入函数名的RVA = 0x0003c028
9、我们再一次在那个"Virtual"后的那Edit栏内输入0x0043c028(不知怎么来的,晕)
10、那我们就会看到如下的情况,图五。
图五:函数导入的RVA = 0x0003c4a2
11、最后一在Virtual后的那个Edit栏内输入0x043c4a2.会看到最后的美景。
注:
RVA:Relative virtual Address(相对地址)Virtual不译了。
有点估计大家有所不明白,就是第3步中,为什么就偏移120个字节,而不是其他的。关于这个数字是这样算出来的。首先,4 + 20 + 96,这些是 IMAGE_DATA_DIRECTORY 这个类型数组前的偏移字节数,而再加8字节,是因为DataDirectory数组第一项是Export Symbols的RVA,第二项才是 Import Symbols,而数组的每一项是8字节,由此而来。
edit by ytfrdfiw 03-25-08 :
上面的那段关于Export symbols的解释可能不是太清楚,应该这样说: 这个数组名为 DataDirectory 的数组目前是16个类型为IMAGE_DATA_DIRECTORY 的元素,但第索引为1的元素(即DataDirectory[1]的元素)是我们要函数Import symbols,这些元素的函数为什么是这样,估计要问MS了。。所有各项为:
IMAGE_DIRECTORY_ENTRY_EXPORT (0)输出符号目录用于DLL
IMAGE_DIRECTORY_ENTRY_IMPORT (1)输入符号目录
IMAGE_DIRECTORY_ENTRY_RESOURCE (2)资源目录
IMAGE_DIRECTORY_ENTRY_EXCEPTION (3)异常目录
IMAGE_DIRECTORY_ENTRY_SECURITY (4)安全目录
IMAGE_DIRECTORY_ENTRY_BASERELOC (5)重定位表
IMAGE_DIRECTORY_ENTRY_DEBUG (6)调试目录
IMAGE_DIRECTORY_ENTRY_COPYRIGHT (7)描述版权串
IMAGE_DIRECTORY_ENTRY_GLOBALPTR (8)机器值
IMAGE_DIRECTORY_ENTRY_TLS (9)Thread local storage目录
IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG (10)Load configuration 目录
IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (11)Bound import directory目录
IMAGE_DIRECTORY_ENTRY_IAT (12)Import Address Table输入地址表目录
参考文章:
PE文件格式,qduwg翻译 看雪学院整理
Matt Pietrek,March 1994,Peering inside the PE: A tour of the Win32 Portable Executable File Format
然希望对你有所帮助,也希望你能提出意见,谢谢!