pe文件格式详解,pe文件的总体结构
本内容由系统网小编为大家分享,Windows系统安装教程、办公系统、软件怎么使用、软件使用教程、办公软件攻略等信息。
1、PE文件的结构
1.什么是可执行文件?
可执行文件(英语:Executable file)是一个可以由操作系统载入和执行的文件。
可执行文件的格式:
PE和ELF非常相似,它们都来自相同的可执行文件格式COFF
事实上,在Windows平台,由Visual C++编译器生成的目标文件仍然是COFF格式,可执行的文件则是PE格式
微软的64位Windows平台的PE文件结构叫做PE32+,它将原来的32位字段转换成64位字段。
2、PE文件的特征
确定文件是否是PE文件不仅应该看到文件的副名,而且应该通过PE指纹
使用UE打开一个exe文件,发现该文件的第一个两个字母是MZ,0x3C位置存储一个地址,并且发现“PE”存储在该位置,因此基本上可以确定修改的文件是PE文件
这些重要信息(“MZ”和“PE”)验证文件是否是一个PE文件,这些信息叫做PE指纹。
3. PE文件的整体结构
这将 PE 文件的主要部分列为四个部分,其中可以首先介绍模糊的概念,然后详细解释
节点或块或块都是意义,后者将相互交换使用
下面是从二进制级别的PE文件结构的概览
PE文件映射到内存
存储在磁盘上的PE文件的结构与载入内存的文件不同。
当PE文件通过Windows加载器载入内存后,内存中的版本称为模块(Module)。
映射文件的初始地址称为模块处理(hModule),也称为基本地址(ImageBase)。
模块手柄与其他手柄不同 吗?
文件数据通常为512字节(1节)整齐(目前超过4k),32位内存通常为4k(1页),512D=200H,4096D=100H
文件中的块的大小是200H的整数,内存中的块的大小是100H的整数,映射后实际数据的大小是不变的,其余部分可以用0填充
PE文件头部(DOS头部+PE头部)与块表之间没有空隙,但两者之间存在空隙,大小取决于匹配参数
当VC编译器默认编译时,exe文件基础地址为0x400,DLL文件基础地址为0x100
VA:虚拟内存地址
RVA:相对的虚拟地址是与基地址相对的迁移地址
FOA:文件传输地址
5、DOS部分
DOS MZ文件头实际上是一个结构(IMAGE_DOS_HEADER),占有64字节
DOS字符串在16位元系统中被使用,其中DOS字符串在32位元系统中成为冗余数据,但还有两个重要的成员:e_magic字段(偏差0x0)和e_lfanew字段(偏差0x3C)。
e_magic保存了“MZ”字符,e_lfanew保存了PE字节地址,通过这个地址找到PE字节并获取PE文件标识符“PE”。
e_magic和e_lfanew是验证 PE指纹的重要字段,而其他字段大多不使用(可以填充任意的数据)
DOS Stub区域的数据由链接器填充(它可以填充数据本身的意图)并且是一个小代码,可以在DOS下运行。
这段代码的唯一作用是向终端输出一行字:“This program cannot be run in DOS”(“e_cs”和“e_ip”指向)
然后退出程序,表明该程序不能在DOS下运行。
6、PE文件头(PE Header)
PE头是一个结构(IMAGE_NT_HEADERS32),包含另外两个结构,占有4B + 20B + 224B
Signature字段设置为0x00004550,ANCII码字符是“PE00”,标识PE文件头的开始,PE标识不能破坏。
1.IMAGE_FILE_HEADER结构
IMAGE_FILE_HEADER(映像文件头或标准PE头)结构包含PE文件的一些基本信息,该结构在微软的官方文档中被称为标准通用对象文件格式(Common Object File Format,COFF)头
重要字段: NumberOfSections,SizeOfOptionalHeader
相应的结构是下图的紫色部分
0x014C表示x86CPU上运行; 0x007表示当前ex有7节点;
0x00E0描述IMAGE_OPTIONAL_HEADER32为224字节;
0x030F(00 0011 00 11)表示文件属性,结合下列相应位置和1
2.IMAGE_OPTIONAL_HEADER结构
IMAGE_OPTIONAL_HEADER(可选视频标题或扩展PE标题)是可选结构, IMAGE_FILE_HEADER结构的扩展
IMAGE_FILE_HEADER结构记录的OptionalHeader字段(可能不准确)
重要字段:
AddressOfEntryPoint: 以下显示的程序输入地址是32C40H
ImageBase:内存镜基地址,400H以下
FileAlignment:文件对齐,下图为200H
SectionAlignment:内存对齐,下图为1000H
数据目录[16]:由若干相同的IMAGE_DATA_DIRECTORY结构组成的数据目录表,
指向输出表、输入表、资源块,重定位表等(后面详解这里先跳过)
ImageBase + AddressOfEntryPoint = 程序实际上运行输入地址(实际的装载地址相当于ImageBase)。
0x400 + 0x32C40 = 0x432C40 (使用OD程序发现它从这个地址开始)
应用程序:在PE文件空白区域中添加代码,让程序先执行添加代码,然后跳进程序输入
思路:
① 在PE的空白区构造一段代码(call -> E8)
2修改新的代码输入地址( IMAGE_OPTIONAL_HEADER ).AddressOfEntryPoint)
③ 新增代码执行后,跳回入口地址(jmp -> E9)
7、块表
块表是 IMAGE_SECTION_HEADER结构的集合,每个包含40个 IMAGE_SECTION_HEADER结构的字符。
每个 IMAGE_SECTION_HEADER结构包含与它关联的块的信息,如位置、长度和属性。
重要字段: Name[8], VirtualSize, VirtualAddress, SizeOfRawData, PointerToRawData, Characteristics
IMAGE_FILE_HEADER中的NumberOfSections字段是否记录当前文件中的节点数目?
31C80H代表了装载的内存代码块的排列;100H代表了装载到内存RVA100H的代码块;
31E00H表示文件同步后代码块的大小;400H表示文件中的代码块的移动
60020H代表代码块属性(0110 00 00 00 00 00 00 0010 00) 请参阅下面的表,查看具有可读和可执行属性的代码
更多属性参考: https://docs.Microsoft.com/zh-cn/windows/win32/api/winnt/ns-winnt-image_section_header
8.将RVA转换为FOA
RVA: 相对虚拟地址, FOA: 文件转移地址.
计算步骤:
1 计算 RVA = 虚拟存储地址 - ImageBase
2如果RVA位于PE头部:FOA==RVA
3 确定 RVA 的 哪个 部分 位置 :
RVA >= 节.VirtualAddress (节在内存对齐后RVA )
RVA <= 节.VirtualAddress + 当前节内存对齐后的大小
偏移 = RVA - node.Virtual Address;
4 FOA = Section.PointerToRawData +偏移量;
应用举例:
具有初始值的全球变量的初始值存储在PE文件中,这是您修改的文件中的全球变量的数据值
你需要找到在文件中存储全球变量的位置,然后修改它
2、输出表和输入表
可选PE头的最后一个字段(扩展PE头)DataDirectory[16]表示一个由16个相同的IMAGE_DATA_DIRECTORY结构组成的数据目录表,每个结构都指向输出表、输入表、资源块等
1,输出表(抽出表)当创建一个DLL时,您实际上创建了一个允许EXE或其他DLL被调用的功能集
DLL文件通过输出表向系统提供信息,例如输出函数的名称、序列号和输入地址。
数据目录表的第一个成员是指输出表。
在文件中找到输出表(使用DllDemo.Take dll作为例子。请参见图)。
成功地在文件极化0C00H上找到输出表,如下:
注:如果文件匹配和内存匹配为4k,则不需要地址转换。2输出表大小指输出表大小及其子表大小,以及
输出表实际上是一个40字节结构(IMAGE_EXPORT_DIRECTORY)。输出表的结构如下:
重要字段: Name,Base,NumberOfNames,AddressOfFunctions,AddressOfNames,AddressOfNameOrdinals
过程分析:
//功能:将动态链接库载入内存HMODULE WINAPI LoadLibrary(LPCTSTR lpFileName // 模块的文件名);
/*函数:检索指定动态链接库(DLL)中的输出库函数地址*/FARPROC GetProcAddress(HMODULE hModule, // DLL模块处理器(模块基础地址)LPCSTR lpProcName // 指定函数的函数名称或序列值);
PE负载器调用GetProcAddress来在DLL中找到DlIDemo.API函数MsgBox,
系统定位DlI Demo.DLL的输出表(IMAGE_EXPORT_DIRECTORY)结构,获取输出函数名称表(ENT)的初始地址,
对名字进行二进制查找,直到发现字符串“MsgBox”为止,PE装载器发现MsgBox是数组的第1个条目后,加载器从输出序数表
中读取相应的第1个值,这个值是MsgBox的在函数地址表(EAT)的索引。使用索引在EAT取值得到MsgBox的RVA1008h。
添加DllDemo到1008h。DLL的装载地址获取MsgBox的实际地址。
注: 如果lpProcName是一个序列,则必须通过Base字段确定初始序列,并且序列-Base的差值作为参考得到函数RVA地址(注意序列和参考在这里)
注:输出序列表存储索引值而不是序列,而实际序列是Base+索引值
例如,写一个简单的添加函数(int add(int a, int b))并创建A.dll
分析A.进口DLL表
当用序列(12)获取函数地址时,它将12Base=0作为输出函数地址表的索引值
使用A.dll
2.输入表(输入表)
在PE文件映射到内存后,Windows将相应的DLL文件加载,EXE文件通过输入表在相应的DLL中找到进口函数,从而完成程序的正常操作
数据目录表的第二个成员是输入表,当前的文件依赖于多个模块,有多个输入表,并且不断丢弃。
如何找到输入表?
上面的图显示,当前的文件只依赖一个模块,只依赖一个进口表,如果多个模块持续存储到20 0 语句持续出现为止。
输入表实际上是一个20字节结构 IMAGE_IMPORT_DESCRIPTOR
重要字段:
Name:DLL(依赖模块)名字的指针。是一个以“00”结尾的ASCII字符的RVA地址。
OriginalFirstThunk: RVA包含一个面向输入的名称表(INT)。
FirstThunk:包含输入地址表(IAT)的RVA。IAT是一个image_THUNK_DATA结构的集合。
IMAGE_THUNK_DATA结构实际上只占4字节
如果IMAGE_THUNK_DATA32的最大位数为1,则下位31位元代表函数的输出序列,
否则,四个字符是RVA,指向IMAGE_IMPORT_BY_NAME结构
IMAGE_IMPORT_BY_NAME结构字段仅包含四个字节,这些字节存储输入函数的相关信息
从上面看,我们可以通过导入表获取当前文件依赖模块的名称和函数名称吗?
INT和IAT的内容在这里相同,为什么? 稍后解释
INT和IAT的内容在PE文件未加载时相同。
PE运载器将文件插入内存,并将实际函数地址(GetProcAddress)填入IAT
例如:
3、重定位表
如果PE文件未被载入优先地址(ImageBase),则该文件中的每个绝对地址需要修改。
需要修正的地址有很多,可以在文件中使用重定位表记录这些绝对地址的位置,在载入内存后若载入基地址与ImageBase不同再进行修正,若相同就不需要修正这些地址。
数据目录项的第6个结构,指向重定位表(Relocation Table)
重置表由单独的重置单元组成,每个单元记录需要重置的地址在4KB(一页)内存中。
每个移位数据块的大小必须与 DWORD (4 字节)的大小相符,它们以 IMAGE_BASE_RELOCATION 结构开始,格式如下
这些领域可能很难直接理解,但你可以在后面看到一个完全清晰的例子
虽然有许多类型的迁移,x86可执行的文件中,所有基本地址迁移类型是 IMAGE_REL_BASED_HIGHLOW。
在移位组的末尾,出现 typeIMAGE_REL_BASED_ABSOLUTE 的移位。这些移位除了填充即可使下一个 MAGE_BASE_RELOCATION 以 4- 字节边界线排列。
对于IA-64可执行的文件,重置类型总是显示为IMAGE_REL_BASED_DIR64。
有趣的是,虽然IA-64的EXE页面大小是8KB,但基础地址的迁移仍然是4KB的块
所有移位块都以MAGE_BASE_RELOCATION结构结束,VisualAddress字段为0。
示例分析:
继续使用DllDemo.Example: dll
使用该工具以下列方式定位文件的定位表
查看下文的重置表信息
下面实际分析
在CODE节中以下列方式确定当前RVA
所以
100Fh(RVA) → 60Fh(FOA)
1023h(RVA) → 623h(FOA)
60Fh及623h点至0040200h及00403030h点,分别为需要迁移的数据
在执行PE文件之前,当重置时,负载器使用PE文件的实际视频地址在内存中减少PE文件所需的视频地址,并根据不同类型的重置而增加相应的地址数据的差值。
您可以看到重置表的作用:一旦文件被载入内存,重置表记录的RVA会找到需要重置的数据
通过页基地址获得完整的RVA+页间极化地址,将重置表大大缩小。
4、资源
Windows程序的各个接口称为资源,包括加速键(Accelerator)、位图(Bitmap)、光标(Cursor)、对话框(Dialog Box)、图标(Icon)、菜单(Menu)、串表(String Table)、工具栏(Toolbar)和版本信息(Version Information)等。
在定义资源时,你可以用字符串作为名称来识别资源,或者用ID来识别资源
资源分类
- 标准资源类型
- 非标准资源类型
如果资源类型的高级别为1,则表示相应的资源类型是非标准的新类型
数据目录项目第三个结构是指资源表,而不是直接指资源数据,而是以磁盘目录的形式定位资源数据
资源表是一个四层二进制序列树结构。
每个节点由一个资源目录结构和以下几个资源目录项目组成。
这两个结构构成资源目录结构单元(目录块)
资源目录结构(IMAGE_RESOURCE_DIRECTORY)为16字节,定义如下
资源目录项目结构(IMAGE_RESOURCE_DIRECTORY_ENTRY)为8字节,包含2个字段,定义如下。
重要字段:
名称字段: 定义目录项的名称或ID.
OffsetToData字段:是一个指针。
在第三层目录结构中的OffsetToData将指向image_RESOURCE_DATA_ENTRY结构。
结构描述了资源数据的位置和大小,并定义如下。
重要字段:
OffsetToData:指向资源数据(RVA)
大小:资源数据的长度
实例分析:
在文件中定位资源
由于当前的exe文件匹配和内存匹配为4k,RVA不需要传输FOA
所以:
该图标的实际资源数据为4100h,而尺寸为2E8h。
RVA菜单的实际资源数据为4400h,尺寸为5Ah。
图标群的实际资源数据RVA为43E8h,尺寸为14h。
使用工具验证
可以清楚地看到根目录有三个资源目录项目(图标、菜单、图标组)
第二层是资源ID或资源名称
第三层为2052表的代码页ID简化中文,1033表的英文
实际资源数据的右下图标
XTw.com.Cn系统网专业应用软件下载教程,免费windows10系统,win11,办公软件,OA办公系统,OA软件,办公自动化软件,开源系统,移动办公软件等信息,解决一体化的办公方案。
免责声明:本文中引用的各种信息及资料(包括但不限于文字、数据、图表及超链接等)均来源于该信息及资料的相关主体(包括但不限于公司、媒体、协会等机构)的官方网站或公开发表的信息。内容仅供参考使用,不准确地方联系删除处理!
联系邮箱:773537036@qq.com