PE文件详解
来源:互联网 发布:银戒指淘宝 编辑:程序博客网 时间:2024/05/17 00:12
参考数据书籍:《加密与解密(第三版)》
PE文件学习笔记整理:
自己学习PE文件的一个总结,只是一个起步,对PE文件的概念的一个学习吧,属于比较菜的阶段,学习完这一部分之后就要先放下一段时间来做更正要的事情了。PE文件之后的应用之后再慢慢的学习吧。希望可以给我同时也给你们带来一些帮助。
图是来自己截的图,有书上的,有自己实际操作的。如果需要的话可以Q我我发给你,包括这个word。
PE文件格式总览:
PE文件使用的是一个平面地址空间,所有代码和数据都被合并在一起,组成一个很大的结构。文件的内容被分割为不同的区块(Section,又名区段,节等),区块中包含代码或数据,各个区块按页边界来对齐,区块没有大小限制,是一个连续的结构。每个块都有它自己在内存中的一套属性,区块是相同属性的数据的一个集合。
PE文件不是作为单一内存映射文件被装入内存的。PE文件通过PE装载器遍历PE文件并决定文件的哪一部分被映射,这种映射方式是将文件较高的偏移位置映射到较高的内存地址,当磁盘文件被装入内存中,磁盘上的数据结构布局和内存中的数据结构布局是一致的。这样的好处是在磁盘中的的内容,在载入内存中后也可以找到同样的信息,但数据间的相对位置可能改变。
DOS头,PE头,块表在内存中的偏移和在磁盘中的偏移是一致的。其余的内存偏移和磁盘偏移是需要转换的。
各种地址的概念:
基址(Image Base):PE文件装入内存后的起始地址。
相对虚拟地址(Relative Virtual Address,RVA):在内存中相对于PE文件装入地址的偏移位置,是一个相对地址。
虚拟地址(Virtual Address,VA):装入内存中的实际地址。
文件偏移(Fill Offset):PE文件存储在磁盘上时,相对于文件头的偏移位置。16进制文件编辑器打开后的地址为文件偏移地址。
虚拟地址(VA) = 基址(Image Base) + 相对虚拟地址)(RVA)
将RVA转换为File
设:VK为相对虚拟地址RVA与文件偏移地址File
VA=ImageBase+RVA
File
File
PE文件各部分结构(为winnt.h中的结构体定义)
实例程序为VC++6.0自动生成的窗口程序
所标注的偏移地址为16进制偏移地址
MS-DOS头:
DOS MZ头与DOS stub合称DOS文件头
typedef struct _IMAGE_DOS_HEADER {
在16进制编译器中如下:
结构体第一个为DOS头标志,在16进制编译器中查看为
4D 5A 90 00
最后一个为LONG型数据指向PE头的RVA,也就是在000000E0的位置是PE头。
PE文件头:
PE头定义如下:
IMAGE_NT_HEADERS STRUCT
{
}
PE头包含2个结构体,IMAGE_FILE_HEADER 与 IMAGE_OPTIONAL_HEADER32
typedef struct _IMAGE_FILE_HEADER {
WORD
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
在此文件中
通过DOS头部最后一个数据找到PE头的偏移地址为000000E0,找到PE头位置。
从上图可以看出,区块数目为0006,也就是6个区块,可以在LoadPE中看到:
可选头大小(SizeOfOptionalHeader)为00E0,都可以在LoadPE中得到验证。
IMAGE_OPTIONAL_HEADER结构:
00E0转化为10进制为224字节,也就是IMAGE_OPTIONAL_HEADER结构的大小为224字节。
此部分为IMAGE_OPTIONAL_HEADER结构部分。
typedef struct _IMAGE_OPTIONAL_HEADER {
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
从上面可以看出以下数据:
OEP的位置为 000017B0
Image Base为 00400000
SectionAlignment:当被装入内存时的区块大小。每个区块被装入的地址必定是本字段指定数值的整数倍。默认的对齐尺寸是目标CPU的页尺寸。对于运行在windows 9x/Me 下的用户模式可执行文件,最小的对齐尺寸是一页1000h(4kb)。从上面可以得出此程序的内存对齐为1000h
FileAlignment:磁盘上PE文件内的区块对齐大小,组成块得原始数据必须保证从本字段的倍数地址开始。从上面可以看出此文件的文件对齐为1000h。
NumberOfRvaAndSizes为00000010,转化为十进制为16,从Windows NT发布以来一直为16.
下面从LoadPE中看一下。
DataDirectory[16]:数据目录表,由数个相同的IMAGE_DATA_DIRECTORY结构组成,指向输出表,输入表,资源块等数据。
typedef struct _IMAGE_DATA_DIRECTORY {
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
从前8个字节可以看出此exe文件的没有导出表,此后的部分可以与LoadPE中的数据一一对应。
区块:
区块表:
typedef struct _IMAGE_SECTION_HEADER {
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
在IMAGE_FILE_HEADER结构的第二个数据标识了文件的区块数目,在偏移为6h的地方可以找到区块数目为0006,也就是说这个实例成有有6个区段,如下图所示:
每个区段的长度为40个字节。
拿第一个区段举例,从上图可以看出:
区段的名称为:.text段
区段的尺寸为:00021000
区块的RVA为:00001000
区块的文件对齐后大小为:00021000
区块的文件偏移为:00001000,
区块的属性为:60000020
Characteristics:块属性。该字段是一组指出块属性(如代码/数据/可读/可写等)的标志。
多个标志值求或即为Characteristics的值。这些标志中的很多都可以通过连接器的/SECTION选项设置。
字段值
IMAGE_SCN_CNT_CODE
IMAGE_SCN_CNT_INITIALIZED_DATA
00000040h |
IMAGE_SCN_CNT_UNINITIALIZED_DATA
00000080h |
IMAGE_SCN_MEM_DISCARDABLE
02000000h | 不再需要它了,比如.reloc(重定位块)
IMAGE_SCN_MEM_SHARED
IMAGE_SCN_MEM_EXECUTE
IMAGE_SCN_MEM_READ
IMAGE_SCN_MEM_WRITE
上图为LoadPE中区段表的截图
数据目录表中的数据,是在区段中的数据。而区段表的数据,是指向区段边界的数据。
区块的名字可以随意改变,以下只是常见的一些区段名称。
常见区段:
.text
.data
.rdata
.idata
.edata
.rsrc
.bss
.crt
.tls
.reloc
.sdata
.srdata 相对于全局指针的可被定为的“短的”只读数据。
.pdata
.debug$S
.debug$T
.debug$P
.drectve
.didat
当编程从PE文件中读取需要的内容时,如输入表、输出表等,不能以区块名称作为参考,正确的方法是按照数据目录表中的字段进行定位。在数据目录表中,找到数据的RVA,对比区段表,查看所找数据是在哪一个区段当中。
可以创建和命名自己的区块。
区块的对齐值:
文件偏移与虚拟地址转换:
设:VK为相对虚拟地址RVA与文件偏移地址File
VA=ImageBase+RVA
File
File
也可以使用LoadPE直接转换:
输入表:
输入函数的调用:
在IMAGE_DATA_DIRECTORY(数据目录表的第二项)为输入表,
RVA为0002A000
大小为0000003C
在LoadPE中可以得到确认:
与区段表进行对比后可以看出,导入表在.idata段当中。
输入表是一个IMAGE_IMPORT_DESCRIPTOR(简称IID)数组开始的。每个被PE文件隐式链接进来的DLL都有一个IID。在这个数组中,没有字段指出该结构数组的项数,但是他的最后一个单元为NULL,可以由此计算项数。
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
DWORD
DWORD
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;
OriginalFirstThunk与FirstThunk都是IMAGE_THUNK_DATA结构的数组。
typedef struct _IMAGE_THUNK_DATA32 {
} IMAGE_THUNK_DATA32;
每一个IMAGE_THUNK_DATA元素对应于一个从可执行文件输入的函数。两个数组的结束是通过一个值为0的IMAGE_THUNK_DATA元素来表示的。当IMAGE_THUNK_DATA值的最高位为1时,表示函数以序号方式输入,这时低31位(或者64位可执行文件的低63位)被看做一个函数序号。当双子的最高位为0时,表示函数以字符串类型的函数名方式输入,这时双字的值是一个RVA,指向一个IMAGE_IMPORT_BY_NAME结构。
typedef struct _IMAGE_IMPORT_BY_NAME {
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
输入地址表(IAT)
文件偏移:28000-(2A000 – 28000)=28000
输入表的文件偏移为28000.
找到28000的位置,输入表是一个结构体数组,每个结构体20个字节,以20个字节的00为结束,由上图可以看出,此程序由两个DLL,从LoadPE看一下:
Name是指向DLL的指针,是一个RVA地址,我们可以查找一下
第一个IID的第4个成员为0002A4F2,转换成文件偏移为0000284F2
可以看出为USER32.dll,与LoadPE中的数据相同。
OriginalFirstThunk指向的IMAGE_THUNK_DATA数组位置,可以看出有21个函数。
再查找下FirstThunk指向的IMAGE_THUNK_DATA数组位置,
两个结构完全相同,也是有21个函数,我们在LoadPE中查看一下:
与LoadPE中USER32.dll中的函数个数相同。
查找第一个为
与LoadPE中显示相同。
在程序运行前,它的FirstThunk字段值也是指向一个地址串,而且和OriginalFirstThunk字段值指向的INT是重复的。系统在程序初始化时根据OriginalFirstThunk的值找到函数名,调用GetProcAddress函数且根据函数名取得函数的入口地址,然后用函数入口地址取代FirstThunk指向的地址传中对应的值(IAT)
这是一个从内存中抓取出来的,是PE文件映射到内存中的状态,上图为IAT。
此图为INT,两者已经不一样。
输入表部分的内容有点乱……
输出表:
输出表格式如下:
typedef struct _IMAGE_EXPORT_DIRECTORY {
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
输出表为如上部分。
NumberOfFunctions
NumberOfNames
AddressOfFunctions
addressOfNames
AddressOfNameOrdinals
与LoadPE中数据进行比较,完全一致。
最后一个数据:
基址重定位:
在数据目录表中查找重定位表的RVA
typedef struct _IMAGE_BASE_RELOCATION {
//
} IMAGE_BASE_RELOCATION;
typedef IMAGE_BASE_RELOCATION UNALIGNED * PIMAGE_BASE_RELOCATION;
在16进制编译器中找到重定位表:
TypeOffset:是一个数组,数组每项大小为两个字节,共16位。又分为高4位与低12位,高4位代表重定位类型;低12位是重定位地址,他与VirtualAddress相加既是指向PE影响中需要修改的地址数据的指针。
图中最后4位用于对齐。
重定位数据
高4位
低12位
加上virtualAddress
转化成文件偏移
查找到的重定位数据。此为PE影响中需要修改的地址数据。
资源:
资源目录结构:
typedef struct _IMAGE_RESOURCE_DIRECTORY {
//
} IMAGE_RESOURCE_DIRECTORY, *PIMAGE_RESOURCE_DIRECTORY;
资源目录入口结构:
typedef struct _IMAGE_RESOURCE_DIRECTORY_ENTRY {
};
typedef struct _IMAGE_RESOURCE_DIR_STRING_U {
} IMAGE_RESOURCE_DIR_STRING_U, *PIMAGE_RESOURCE_DIR_STRING_U;
资源数据入口:
typedef struct _IMAGE_RESOURCE_DATA_ENTRY {
} IMAGE_RESOURCE_DATA_ENTRY, *PIMAGE_RESOURCE_DATA_ENTRY;
- PE文件详解-----PE文件的简介
- PE文件详解------PE文件结构剖析
- PE文件详解之PE文件头
- PE文件详解之PE文件头
- 收藏.PE文件详解
- PE文件详解
- PE文件结构详解
- PE文件详解
- PE文件详解
- PE 文件详解-节表
- PE文件详解
- PE文件结构详解
- PE文件详解
- PE文件结构详解
- PE文件详解
- PE文件详解二
- PE文件结构详解
- PE文件之PE映像尺寸详解
- php json_decode返回数据js的处理
- VC++中的C运行时库浅析
- Android SDK Android NDK 官方下载地址
- C语言指针讲解
- android 进程监控 top
- PE文件详解
- [Leetcode] Pascal's Triangle II
- 【转】PHP 使用header函数设置HTTP头的示例方法 表头
- C/C++程序编译运行生成过程分析
- 电商后台项目总结
- mount: unknown filesystem type 'ntfs'
- 企业网站项目
- 菜鸟也能解决android中的OOM问题
- 你好,2013