PE文件结构解析
说明:本文件中各种文件头格式截图基本都来自看雪的《加密与解密》;本文相当《加密与解密》的阅读笔记。
1.PE文件总体结构
PE文件框架结构,就是exe文件的排版结构。也就是说我们以十六进制打开一个.exe文件,开头的那些内容就是DOS头内容,下来是PE头内容,依次类推。
如果能认识到这样的内含,那么“exe开头的内容是不是就直接是我们编写的代码”(不是,开头是DOS头内容)以及“我们编写的代码被编排到了exe文件的哪里”(在.text段,.text具体地址由其相应的IMAGE_SECTION_HRADER指出)此类的问题答案就显而易见了。
exe文件从磁盘加载到内存,各部份的先后顺序是保持不变的,但由于磁盘(一般200H)和内存(一般1000H)区块的对齐大小不一样,所以同一内容在磁盘和在内存中的地址是不一样的。
换言之你在磁盘上看到一段内容一内容要到在内存中找到它--假设它是能映射到内容的部份--那么要做相应的地址转换。(比如你在Ultraedit中看到某几个字节而想在OllyDbg中找到这几个字节那么需要进行地址转换)
另外要注意,PE文件中存放的地址值都是内存中的地址,这些地址在OllyDbg中不需要转换到其指定的位置就能找到其指向的内容;这要根据这个地址找到内容在Ultraedit的地址,需要将此RVA址转换成文件偏移地址。
还要注意DOS头/PE头/块表,映射到内存时属同一区块而且是第一区块,所以此三者上的RVA和文件偏移地址是相等的。
2.DOS头部
2.1MS-DOS头部(IMAGE_DOS_HEADER)
最后的e_lfanew即是PE文件的RVA地址
我们在前边已经提过,对于DOS头/PE头/区块表三部分RVA和文件偏移地址是相等的,所以上边在十六进制文本编缉器中,直接转向e_lfanew指向的000000B0可以正好找到PE头。
2.2DOS stub
DOS stub是当操作系统不支持PE文件时执行的部分,一般由编译器自己生成内容是输出“This program cannot be run in MS-DOS mode”等提示。
PE文件头的位置由e_lfanew指出而不是在固定位置,所以DOS stub允许你改成自己想要执行的代码,想写多长写多长;但一般直接不理会。
3.PE头部
3.1 PE Signature
四个字节,内容“PE\0\0”,对应十六进制“50 45 00 00”
3.2 IMAGE_FILE_HEADER
SizeOfOptionalHeader指了OptionalHeader的大小,NumberOfSections指出了文件的区块数;没有指向OptionalHeader和区块表的指针,这暗示区块表紧接在OpthionalHeader后,OpthonalHeader紧接在FileHeader扣,紧接的意思是没有空格的。
3.3 IMAGE_OPTIONAL_HEADER
其中ImageBase指出程序装载的基地址
4.区块表
4.1 IMAGE_NT_HEADER
其中VirtualAddress指出了区块进入内存后的RVA地址(OD找区块用这个地址)PointerToRawDATA指出区块在磁盘文件中的地址(十六进制编缉器找区块用这个地址)
4.2 文件偏移地址和相对偏移地址的换算
1. 各区块自身不论多大,其自身差值都不会爱影响
2. 但如果其大小大于200h那么会影响下一区块的差值:比如当.text大小大于200h那么.rdata的文件偏移量将会后移;如果.text大小大于1000h那么.rdata的RVA也会后移
3.也就说表10-7中的差值只是说一般是这样子,但当程序很大时各区块的差值还是得重新计算;当然无论怎么样总是有:差值=区块RVA-区块文件偏移地址
5.数据目录表
数据目录表是IMAGE_OPTIONAL_HEADER结构的最后一个成员,类型为IMAGE_DATA_DIRECTORY * 16
数据目录表各成员位置由VirtualAddress指出,并且不是像PE头接在DOS头后不远处一样,一般都在很远的地址;所以一般都是跳过先讲完区块然后再回头讲,也因此初学者可能会感到有些混乱。
5.1输出表
数据目录表的第一个成员指向输出表的RVA,指向的地址是IMAGE_EXPORT_DIRECTORY结构。
其中AddressofFunctions指向输出函数的地址数组,AddressOfNames指向输出函数的名称数组,AddressOfNameOrdinale指向输出函数的输出序数数组。
AddressOfNames和AddressOfNameOrdinale的顺序是相同的,也就是说AddressOfNameOrdinale第一个元素的值就是就是AddressOfNames第一个函数的输出序数,依次类推。
使用输出序数做为数组下标到AddressofFunctions指向的地址数组即可找到函数对应的地址。PE重写IAT时使用GetProcAddress通过函数名获取函数地址基本也就是这个流程。
5.2输入表
数据目录表的第二个成员指向输入表的RVA;指向的地址是IMAGE_IMPORT_DESCRIPTOR(IID)结构,一个IID对应一个DLL,最后以一个全0的IID表示结束
OriginalFristThunk和FirstThunk都指向IMAGE_THUNK_DATA结构;IMAGE_THUNK_DATA都指向同一个IMAGE_IMPORT_BY_NAME
最重要的还是要理解为什么需要INT和IAT两个东西指向同一个东西;其流程是这样:
1.INT是不可写的,IAT是PE加载器可重写的
2.在编译的时候,编译器不懂IAT要填什么,就随便填成了和INT一样的内容(所以用十六进制编缉器查看时IAT和INT的内容是一样的)
3.PE装载器加载时根据INT找到IMAGE_IMPORT_BY_NAME中的函数名,然后使用GetProcAddress(HMODULE hModule,LPCSTR lpProcName)找到函数对应的地址(hModule是DLL的句柄,lpProcName是IMAGE_IMPORT_BY_NAME中的函数名)
4.PE装载器使用查找到地址重写IAT(所以用OllyDbg查看时IAT和INT的内容是不一样的)
5.所以可以直接这样理解:IAT初始是什吐槽内容并不要紧、INT就是为了重写IAT而存在的
5.3资源
windows系统中的各种可视元素叫做资源,包括快捷键(Accelerator)、位图(Bitmap)、光标(Cursor)、对话框(Dialog Box)、图标(Icon)、菜单(Menu)、字符串表(String Table)、工具栏(Toolbar)、版本信息(Version Information)等。
数据目录表的第三个成员指向资源结构的RVA,资源结构一般是相同的三层IMAGE_RESOURCE_DIRECTORY+n * IMAGE_RESOURCE_DIRECTORY_ENTRY加上指向最终资源代码的IMAGE_RESOURCE_DATA_ENTRY构成。
其中NumberOfNameEntries的值加上NumberOfIdEntries的值等于紧接在IMAGE_RESOURCE_DIRECTORY后边的IMAGE_RESOURCE_DIRECTORY_ENTRY的个数。
根据IMAGE_RESOURCE_DIRECTORY_ENTRY所在层级的不同,Name和OffsetToData的含义不相同。
Name:
不管在哪层,当最高位为1时低位值做指针使用;当最高位为0时低位值做编号使用。
更具体的,一般在第一层时高位为0低位用做编号表示资源类型比如是对话框还是菜单;第二高位为1低位为指向IMAGE_RESOURCE_DIR_STRING_U的指针该结构保存资源的名称;第三层时高位为0低位用做编号表示该资源中的语言比如是英语还是汉语。
OffsetToData:
不管在哪层,当最高位为1时指向下一层目录块的起始地址;当最高位为0时指向IMAGE_RESOURCE_DATA_ENTRY。
更具体的,一般在第一和第二层时最高位为1,指向下一层目录块的起始地址;在第三层时最高位为0,指向IMAGE_RESOURCE_DATA_ENTRY。
PE文件结构解析的更多相关文章
- PE文件结构详解(二)可执行文件头
在PE文件结构详解(一)基本概念里,解释了一些PE文件的一些基本概念,从这篇开始,将详细讲解PE文件中的重要结构. 了解一个文件的格式,最应该首先了解的就是这个文件的文件头的含义,因为几乎所有的文件格 ...
- COFF/PE文件结构
COFF/PE文件结构 原创 C++应用程序在Windows下的编译.链接(二)COFF/PE文件结构 2.1概述 在windows操作系统下,可执行文件的存储格式是PE格式:在Linux操作系统下, ...
- PE文件解析器的编写(二)——PE文件头的解析
之前在学习PE文件格式的时候,是通过自己查看各个结构,自己一步步计算各个成员在结构中的偏移,然后在计算出其在文件中的偏移,从而找到各个结构的值,但是在使用C语言编写这个工具的时候,就比这个方便的多,只 ...
- C++PE文件格式解析类(轻松制作自己的PE文件解析器)
PE是Portable Executable File Format(可移植的运行体)简写,它是眼下Windows平台上的主流可运行文件格式. PE文件里包括的内容非常多,详细我就不在这解释了,有兴趣 ...
- 再探.NET的PE文件结构(安全篇)
一.开篇 首先写在前面,这篇文章源于个人的研究和探索,由于.NET有自己的反射机制,可以清楚的将源码反射出来,这样你的软件就很容易被破解,当然这篇文章不会说怎么样保护你的软件不被破解,相反是借用一个软 ...
- PE文件结构详解(六)重定位
前面两篇 PE文件结构详解(四)PE导入表 和 PE文件结构详解(五)延迟导入表 介绍了PE文件中比较常用的两种导入方式,不知道大家有没有注意到,在调用导入函数时系统生成的代码是像下面这样的: 在这里 ...
- PE文件结构详解(五)延迟导入表
PE文件结构详解(四)PE导入表讲 了一般的PE导入表,这次我们来看一下另外一种导入表:延迟导入(Delay Import).看名字就知道,这种导入机制导入其他DLL的时机比较“迟”,为什么要迟呢?因 ...
- PE文件结构详解(四)PE导入表
PE文件结构详解(二)可执行文件头的最后展示了一个数组,PE文件结构详解(三)PE导出表中解释了其中第一项的格式,本篇文章来揭示这个数组中的第二项:IMAGE_DIRECTORY_ENTRY_IMPO ...
- PE文件结构详解(三)PE导出表
上篇文章 PE文件结构详解(二)可执行文件头 的结尾出现了一个大数组,这个数组中的每一项都是一个特定的结构,通过函数获取数组中的项可以用RtlImageDirectoryEntryToData函数,D ...
随机推荐
- 在ETH交易区块链里查看北大的那封信
本文仅限于科普编码知识使用,随便举的例子不代表本人立场. 欢迎在其他网站传播,但转载不得标注来源及作者. 1.随便打开一个ETH区块链浏览网站,比如:https://www.etherchain.or ...
- Codeforces 786 A. Berzerk
题目链接:http://codeforces.com/problemset/problem/786/A 这个题出做$DIV2$的$C$以及$DIV1$的A会不会难了一点啊... 做法和题解并不一样,只 ...
- 【BZOJ】3295: [Cqoi2011]动态逆序对
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3295 mamaya,弱鸡xrdog终于会写树套树啦.... 将树状数组中每一个节点看成一棵 ...
- JaveWeb 公司项目(4)----- Easyui的表单验证
前面三篇博文讲述的是界面的搭建和数据的传输,可以看出目前我做的这个小项目已经有了一个大体的雏形,剩下的就是细节部分的打磨和一些友好的人机交互设计,今天做的是表单的验证,作为初学者,着实花了一番功夫,所 ...
- 七牛云存储上传自有证书开启https访问
虽然七牛云存储也提供免费SSL证书申请,但我就喜欢用其他平台申请的,于是在腾讯云申请了免费SSL证书,正准备在七牛上传,弹出的界面却让我傻了眼,如下图所示: 腾讯免费SSL证书提供了不同服务器环境的版 ...
- 堆排序 java实现
import java.util.Arrays; /* * 思路: * 1.方法adjustDown:对于一个数组a[],针对第i个数进行向下(直到len-1)调整,使得该位置成为大顶堆 * 2.方法 ...
- ppython的移位操作
因为要将js的一个签名算法移植到python上,遇到一些麻烦. int无限宽度,不会溢出 算法中需要用到了32位int的溢出来参与运算,但是python的int是不会溢出的,达到界限后会自己转为lon ...
- 力扣(LeetCode)520. 检测大写字母
给定一个单词,你需要判断单词的大写使用是否正确. 我们定义,在以下情况时,单词的大写用法是正确的: 全部字母都是大写,比如"USA". 单词中所有字母都不是大写,比如"l ...
- nodejs导出excel
//导出Excel var nodeExcel = require("excel-export"); var fs = require("fs"); var c ...
- 实例对比 hibernate, spring data jpa, mybatis 选型参考
原文: 最近重构以前写的服务,最大的一个变动是将mybatis切换为spring data jpa,切换的原因很简单,有两点:第一.它是spring的子项目能够和spring boot很好的融合,没有 ...