您的位置:首页 > 其它

PE文件结构的一些基础知识

2007-08-02 20:32 435 查看
 
        原来在DOS操作系统上面的可执行文件(COM)只包含代码,程序被载入后第一句就是一条指令。这样让程序很不灵活,所有的代码,数据,堆栈都存放在一起,程序不能跨段操作。所有Windows操作系统为了解决这个问题就引入了PE文件结构。

[align=center] PE文件的基本结构[/align]

[align=left]PE文件头:包括文件的入口,堆栈位置,重定位表等信息[/align]

[align=left]IMAGE_FILE_HEADER结构[/align]

[align=left]MACHINE成员:运行的平台[/align]

[align=left]NumberOfSections:节数[/align]

[align=left]TimeDateStamp:文件创建的日期[/align]

[align=left]SizeOfOptionalHeader:IMAGE_OPTIONAL_HEADER32结构的大小(永远是0x00E0)[/align]

[align=left]Characteristics:属性。一般EXE文件为0x010F,dll文件为0x210E[/align]

[align=left]IMAGE_OPTIONAL_HEADER32结构[/align]

[align=left]AddressOfEntryPoint:文件被执行时的入口RVA。一般为0x00100000[/align]

[align=left]ImageBase:PE文件被映射到内存的基地址。一般EXE:0x00400000 DLL:0x10000000[/align]

[align=left]SectionAlignment/FileAlignment:段加载到内存中的对齐方式/段在PE文件中的对齐方式[/align]

DataDirectory:是由16个IMAGE_DATA_DIRECTORY组成,在这里插入点关于为什么要用到IMAGE_DATA_DIRECTORY的原因。其实PE文件中的数据块按功能其实是可以分成很多部分的,如导入表,导出表以及.const段指定的只读数据,这些不同用途的数据块可能被放到具有同一属性的节中。PE文件就用一系列的IMAGE_DATA_DIRECTORY来分别指明这些数据的位置。下面就把这16个IMAGE_DATA_DIRECTORY所要指定的数据块贴出来。

索    引
索引值在Windows.inc中的预定义值
对应的数据块
0
IMAGE_DIRECTORY_ENTRY_EXPORT
导出表
1
IMAGE_DIRECTORY_ENTRY_IMPORT
导入表
2
IMAGE_DIRECTORY_ENTRY_RESOURCE
资源
3
IMAGE_DIRECTORY_ENTRY_EXCEPTION
异常(具体资料不详)
4
IMAGE_DIRECTORY_ENTRY_SECURITY
安全(具体资料不详)
5
IMAGE_DIRECTORY_ENTRY_BASERELOC
重定位表
6
IMAGE_DIRECTORY_ENTRY_DEBUG
调试信息
7
IMAGE_DIRECTORY_ENTRY_ARCHITECTURE
版权信息
8
IMAGE_DIRECTORY_ENTRY_GLOBALPTR
具体资料不详
9
IMAGE_DIRECTORY_ENTRY_TLS
Thread Local Storage
10
IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG
具体资料不详
11
IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT
具体资料不详
12
IMAGE_DIRECTORY_ENTRY_IAT
导入函数地址表
13
IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT
具体资料不详
14
IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR
具体资料不详
15
未使用
 
IMAGE_DATA_DIRECTORY结构包含数据块的起始RVA和数据块的长度

节表:所有节的地址,属性等信息被定义在节表中。一个节表对应一个节,却2者的顺序一样,节表由一系列的IMAGE_SECTION_HEADER排列而成。一个IMAGE_SECTION_HEADER描述一个节。节表最终以一个全0的IMAGE_SECTION_HEADER结尾。所以IMAGE_SECTION_HEADER要比节多一个。

IMAGE_SECTION_HEADER结构包括节的名称(不能有重复),节被装入内存的RVA,节在磁盘文件中所处的位置,节属性(PAGE_READERWRITE...)



 
到此,PE文件的基本结构就理完了。下面再介绍下导入表。用例子来说明下。先贴一段反汇编代码出来:
 

:00401000 6A00                    push 00000000
:00401002 6800304000                  push 00403000
:00401007 680F304000                  push 0040300F
:0040100C6A00                    push 00000000
:0040100E E807000000                  Call 0040101A       ;MessageBox
:00401013 6A00                    push 00000000
:00401015 E806000000                  Call 00401020       ;ExitProcess
:0040101A FF2508204000            Jmp dword ptr [00402008]
:00401020 FF2500204000            Jmp dword ptr [00402000]
这里的对MessageBox和ExitProcess的调用都是Call到一段子程序,而子程序的第一条指令都是一个跳转。现在我们再来看下0x00402008和0x00402000处到底是什么 。首先得先看下这个地址在什么段中
----------------------------------------------------------
节区名称  节区大小  虚拟地址  Raw_尺寸  Raw_偏移  节区属性
----------------------------------------------------------
.text     00000026  00001000  00000200  00000400  60000020
.rdata    00000092  00002000  00000200  00000600  40000040
.data     00000022  00003000  00000200  00000800  C0000040
发现是.rdata段,而这个段的Raw_偏移是0x00000600,现在找一个16进制编辑器打开PE文件来看看这个地址到底是什么:

0600  76 20 00 00 00 00 00 00-5C 20 00 00 00 00 00 00   v ....../ ......
0610  54 20 00 00 00 00 00 00-00 00 00 00 6A 20 00 00   T ..........j ..
0620  08 20 00 00 4C 20 00 00-00 00 00 00 00 00 00 00   . ..L ..........
0630  84 20 00 00 00 20 00 00-00 00 00 00 00 00 00 00   . ... ..........
0640  00 00 00 00 00 00 00 00-00 00 00 00 76 20 00 00   ............v ..
0650  00 00 00 00 5C 20 00 00-00 00 00 00 BB 01 4D 65   ..../ ........Me
0660  73 73 61 67 65 42 6F 78-41 00 55 53 45 52 33 32   ssageBoxA.USER32
0670  2E 64 6C6C 00 00 75 00-45 78 69 74 50 72 6F 63   .dll..u.ExitProc
0680  65 73 73 00 4B 45 52 4E-45 4C 33 32 2E 64 6C6C   ess.KERNEL32.dll
0690  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
这个地址的内容是0x0276,显然不是真实函数的地址,但是如果把它看成是一个RVA,就是0x00000276,再找到对应到PE文件中的地址0x00000676,找到这个地址,发现再过去两个字节就是这个函数的函数名(ExitProcess)。现在整理下,首先Call a,然后程序跳到Jmp xxxxxxxx的地方,而xxxxxxxx又是指向这个函数的函数名的字符串。似乎没有任何意义。但是如果告诉你,当PE文件被装载的时候,WINDOWS装载器会根据这个用户名找到真正的函数地址,并且把xxxxxxxx替换成真正的函数地址,那么问题就迎刃而解了。
  接下来就说说,如何查找导入表,以及导入表中的数据是如何组织,以便windows装载器能够顺利的进行完转换工作。
  首先导入表的地址可以通过查找IMAGE_OPTIONAL_HEADER32的DataDirectory成员的第2个IMAGE_DATA_DIRECTORY来得到RVA。现在就得到了导入表的数据块,导入表数据块由一系列的IMAGE_IMPORT_DESCRIPTOR组成(一个dll模块对应一个结构,最终以一个全0的结构结束)。
IMAGE_IMPORT_DESCRIPTR最重要的两个成员就是Name1和FirstThunk。Name1就是dll的名字(如Kernal32.DLL)而FirstThunk是经过一系列的转换后,最终就是真实函数的地址。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息