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 | 未使用 | |
节表:所有节的地址,属性等信息被定义在节表中。一个节表对应一个节,却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是经过一系列的转换后,最终就是真实函数的地址。
相关文章推荐
- 20170910WindowsPrj05_文件基础操作_PE结构
- linux下多进程的文件拷贝与进程相关的一些基础知识
- PE文件格式详解(一)――基础知识
- 20170910WindowsPrj05_文件基础操作_PE结构
- Swift: iOS底层基础知识-文件目录结构
- HTML基础知识一(HTML、常见方式、文档结构、<meta>、 网站文件命名、文本修饰、文本样式、段落标记、居中标记、水平线标记、特殊字符、列表、图像)
- 文件基础操作-PE结构
- Linux基础知识之---文件系统层级结构
- 【Linux基础学习之四】文件系统的基础知识总结(主要目录结构、文件类型、mount、硬连接)
- 20170910WindowsPrj05_文件基础操作_PE结构
- c#基础知识---常用的一些用来文件操作的类
- 20170910WindowsPrj05_文件基础操作_PE结构
- PE文件结构的一些相关定义
- 20170910WindowsPrj05_文件基础操作_PE结构
- 自己总结的一些常数据结构基础知识
- linux 目录结构+常用命令+压缩命令+vim使用+及一些基础知识(非常好)
- 【PE结构】由浅入深PE基础学习-菜鸟手动查询导出表、相对虚拟地址(RVA)与文件偏移地址转换(FOA)
- linux 目录结构+常用命令+压缩命令+vim使用+及一些基础知识(非常好)
- 20170910WindowsPrj05_文件基础操作_PE结构
- 计算机体系结构的一些基础知识