欢迎大家一起来Hacking水友攻防实验室学习,渗透测试,代码审计,免杀逆向,实战分享,靶场靶机,求关注

PE文件学习

推荐工具:lord PE、stud PE 《PE权威指南》,了解格式,看雪,吾爱破解 ,EXE是如何组成的,如何逆向一个EXE文件和安卓文件。

  • 这些东西不能不知道

EXE文件和DLL文件实际上是一样的,唯一的区别就是用一个字段标示出了这个文件是EXE文件还是DLL文件

64位不过就是把32位的字段拓展成64位,也叫PE32+文件,没本质区别。

PE文件的定义基本都在“我的电脑”——下的“winnt.h”头文件中。

而在这个“winnt.h”文件中的image format下就是系统定义的PE结构:

我们需要能看懂这张图片:(桌面)

  • PE文件基础知识

Pe文件是一个平面地址结构,所有代码和数据都被合并在一起,组成一个很大的结构。

文件的内容被分为不同的区块(堆、栈……),各个区块按照页边界来对齐,区块没有大小限制,是一个连续的结构(区块 = 很多连续的页合起来)。

PE文件被放在硬盘中的时候,按照那张图一样放着,但是使用pe文件的时候要映射到内存中,注意映射过来之后不是100%格式没变,在硬盘中啥样在内存中还是啥样。而是由一个叫做PE装载器的东西决定哪里要映射哪里不要映射。一般文件较高偏移位置也要映射到较高的内存地址。

映射关系如下:

我们在使用(调用)一个pe文件的时候必须抓住它的基地址,一般使用函数:

HMODULE GETModuleHandle (LPCTSTR IpModuleName)

返回一个句柄指针(基地址编号,可以理解为基地址,指向这个基地址的指针) , 一个pe文件可以视为一个模块,参数是指向这个pe文件(模块)名称的指针。

  • DOS头部分

小甲鱼PE详解之IMAGE_DOS_HEADER结构定义即各个属性的作用(PE详解01)_fish_c的博客-CSDN博客

  • PE头(NT)

小甲鱼PE详解之IMAGE_NT_HEADERS结构定义即各个属性的作用(PE详解02)_fish_c的博客-CSDN博客

PE文件头(Image_NT_Header)挨着DOS stub,在执行pe文件的时候,pe装载器将从IMAge_DOS_Header结构体中的elfanew中找到pe头的起始偏移量,加上基地址就可以得到pe头文件的指针。

就拿上图来说:00h + D0h = D0h   pe头指针 = IMage 基址 + dos_header ->elfanew

从这里开始看到的是NT部分(pe头部分):

一开始是signature 5045表示是pe头标记(P/E的anscii码)

然后是文件头file_header:这个结构体包括:

机器平台(CPU类型):014c说明这是x86平台的CPU

这个EXE文件拥有的区块表数目:4块

文件何时被创建:

可选文件头大小:一般32位是00 E0,64位是00F0,决定了后面的可选文件头大小多大

文件信息标志,文件属性:012F , 这个需要或运算结合微软的表格来。

接下来是可选映像头部,

小甲鱼PE详解之IMAGE_OPTIONAL_HEADER32 结构定义即各个属性的作用(PE详解03)_fish_c的博客-CSDN博客

特别大一块,

这个地方记着不可以背,只是工具书!

Address of entry point,

Image base :

对于EXE文件来说,由于每个文件总是使用独立的虚拟地址空间,优先装入地址不可能被**模块占据,所以EXE总是能够按照这个地址装入,这也意味着EXE 文件不再需要重定位信息。对于DLL文件来说,由于多个DLL文件全部使用宿主EXE文件的地址空间,不能保证优先装入地址没有被**的DLL使用,所以 DLL文件中必须包含重定位信息以防万一。因此,在前面介绍的 IMAGE_FILE_HEADER 结构的 Characteristics 字段中,DLL 文件对应的 IMAGE_FILE_RELOCS_STRIPPED 位总是为0,而EXE文件的这个标志位总是为1。

在链接的时候,可以通过对link.exe指定/base:address选项来自定义优先装入地址,如果不指定这个选项的话,一般EXE文件的默认优先装入地址被定为00400000h,而DLL文件的默认优先装入地址被定为10000000h。

数据块如果可以一致,那就是“兼容”。

  • 块表(节表)

小甲鱼PE详解之区块表(节表)和区块(节)续(PE详解05)_fish_c的博客-CSDN博客

一般被感染之后会有个标志位记为1.

内存中以1000h对齐,磁盘中以200h对齐。

块名:

内存中块大小:

文件中(磁盘中)块的大小:3000

文件中的偏移:1000

重要的就只有前5个

还有块属性

  • 区块具体描述、对齐值到底是什么?RVA

小甲鱼PE详解之区块描述、对齐值以及RVA详解(PE详解06)_fish_c的博客-CSDN博客

有一个一定要会:

假如我们现在要找xmind这个EXE文件的输入表的RVA对应的真实磁盘地址:

rva地址是410C。

找到在rdata这个区块里面。算出相对于这个区块的RVA2是:410C -  4000 = 10C,再找出文件块中偏移地址:

加起来:410c 。

  • 导入表import详解

小甲鱼PE详解之输入表(导入表)详解(PE详解07)_fish_c的博客-CSDN博客

我们跟着小甲鱼做了一个实验,我认为很有必要做这个实验。。。

我们一开始无脑找到了massgebox函数(这是一个导入函数/输入函数)的来自的dll(它来自user。32.dll)的名称。用的是IDA静态分析。

PE装载器的核心操作是重写输入函数的真实地址

我们实验第一步:先根据“数据目录表”找到我们需要查找的EXE文件(hello.exe)文件的导入表(输入表)的磁盘位置:

导入表的相对虚拟地址是2A000,大小003C,我们对比块(节section)段可以得知导入表处在内存的相对虚拟地址的。Data节中,偏差为2A000-2A000 = 0.对应的磁盘地址(文件地址)是28000+0=28000。

接下来我们要去输入表的起始位置找5个双字,对应上图的结构。

记住,我们在没有运行这个程序(hello.exe)之前,它所需要的输入函数只是一个名称而已,不具备真正的函数入口,换言之他并没有成功地动态链接所需要的user32.dll进入内存。所以这个时候我们看这幅图:

无论是从INT还是IAT,我们都可以找到需要的函数:

也可以反推出来我们用IDA看到的地址其实是:

内存中,相对于我们要调用的user32.dll函数的IMAGE_THUNK_DATA的虚拟地址

如果我们需要查看程序执行之后的EXE文件(已经完成user32.dll的动态链接)的内存情况:

这幅图:

我们需要用loardPE dump一下(任何一款动态分析软件都可以):

右键完整转存。

同样的方式找到IAT中真正的massagebox地址:

7710002E就是真正的函数地址。但是这个地址不在内存里面,也不在我们的hello。Exe里面,而是在磁盘里面。所以我们用一般软件看不到。

  • 导出表详解

当PE 文件被执行的时候,Windows 加载器将文件装入内存并将导入表(Export Table) 登记的动态链接库(一般是DLL 格式)文件一并装入地址空间,再根据DLL 文件中的函数导出信息对被执行文件的IAT 进行修正。

那导出表是干啥用的呢? 导出表就是记载着动态链接库的一些导出信息。通过导出表,DLL 文件可以向系统提供导出函数的名称、序号和入口地址等信息,比便Windows 加载器通过这些信息来完成动态连接的整个过程。

小甲鱼PE详解之输入表(导出表)详解(PE详解09)_fish_c的博客-CSDN博客

我现在觉得是:

加载过程重定位——需要某个函数的时候执行的时候,从输入表中可以看到真正需要这个函数(比如我们在hello.exe中需要messagebox函数,)的入口地址是什么,但是这个函数被我们封装在user32.dll下。——我们是先从磁盘中把整个user32.dll动态链接到hello。Exe程序的内存空间中去——然后再根据user32.dll的导出表export去找到具体需要的函数messagebox的入口地址(有两种方法:1、从序号找,2、从函数名称找)

例题分析:count。Dll文件中的export表中的函数

  1. 找到count。Dll的export表格
  2. 找到export对应的文件地址:

2060 - 2000 = 60 ;60+600=660

  1. 在010里面查看660H的内容(export内容)

我们可以对照这个结构来看:

IMAGE_EXPORT_DIRECTORY STRUCT
Characteristics                       DWORD ? ; 未使用,总是定义为0 
TimeDateStamp                      DWORD ?      ; 文件生成时间 (这个例子里面是3CAC7619)
MajorVersion                         WORD ? ; 未使用,总是定义为0 
MinorVersion                          WORD ? ; 未使用,总是定义为0
Name                                      DWORD ? ; 模块的真实名称  (动态链接库的名称在209C)
Base                                         DWORD ? ; 基数,加上序数就是函数地址数组的索引值 base=1
NumberOfFunctions            DWORD ? ; 导出函数的总数    这个dll有2个函数
NumberOfNames                                    DWORD ? ; 以名称方式导出的函数的总数    同上行
AddressOfFunctions                               DWORD ? ; 指向输出函数地址的RVA  2088
AddressOfNames                                 DWORD ? ; 指向输出函数名字的RVA   2090
AddressOfNameOrdinals                       DWORD ? ; 指向输出函数序号的RVA   2098









Count.dll
 1

1046,F...

2


1023,#...

2

2088

10000

DecCount

20A8

2090


6E756F43

IncCount

20B2

2098


1. 从序号查找函数入口地址

下边小甲鱼带大家来模拟一下Windows 装载器查找导出函数入口地址的整个过程。如果已知函数的导出序号,如何得到函数的入口地址呢 ?

Windows 装载器的工作步骤如下:

  1. 定位到PE 文件头
  2. 从PE 文件头中的 IMAGE_OPTIONAL_HEADER32 结构中取出数据目录表,并从第一个数据目录中得到导出表的RVA
  3. 从导出表的 Base 字段得到起始序号
  4. 将需要查找的导出序号减去起始序号,得到函数在入口地址表中的索引
  5. 检测索引值是否大于导出表的 NumberOfFunctions 字段的值,如果大于后者的话,说明输入的序号是无效的
  6. 用这个索引值在 AddressOfFunctions 字段指向的导出函数入口地址表中取出相应的项目,这就是函数入口地址的RVA 值,当函数被装入内存的时候,这个RVA 值加上模块实际装入的基地址,就得到了函数真正的入口地址

2. 从函数名称查找入口地址

如果已知函数的名称,如何得到函数的入口地址呢?与使用序号来获取入口地址相比,这个过程要相对复杂一点!

Windows 装载器的工作步骤如下:

  1. 最初的步骤是一样的,那就是首先得到导出表的地址
  2. 从导出表的 NumberOfNames 字段得到已命名函数的总数,并以这个数字作为循环的次数来构造一个循环
  3. 从 AddressOfNames 字段指向得到的函数名称地址表的第一项开始,在循环中将每一项定义的函数名与要查找的函数名相比较,如果没有任何一个函数名是符合的,表示文件中没有指定名称的函数
  4. 如果某一项定义的函数名与要查找的函数名符合,那么记下这个函数名在字符串地址表中的索引值,然后在 AddressOfNamesOrdinals 指向的数组中以同样的索引值取出数组项的值,我们这里假设这个值是x
  5. 最后,以 x 值作为索引值,在 AddressOfFunctions 字段指向的函数入口地址表中获取的 RVA 就是函数的入口地址

一帮情况下病毒程序就是通过函数名称查找入口地址的,因为病毒程序作为一段额外的代码被附加到可执行文件中的,如果病毒代码中用到某些 API 的话,这些 API 的地址不可能在宿主文件的导出表中为病毒代码准备好。因此只能通过在内存中动态查找的方法来实现获取API 的地址。关于病毒代码具体的实现分析,小甲鱼在今后将跟大家共同研究讨论这个话题~

摘自《小甲鱼——-——————PE详解之输入表》

九、基址重定位

重新定义位置

但凡直接寻址,必然需要重定位:

直接寻址方式:
指令的地址码部分直接给出的不是操作数,而是操作数的存储器地址,这种方式称为直接寻址方式。

换句话说,机器字节码和偏移地址相同的,就是直接寻址。

我们还是拿上边那张图片来说事儿,我们说了上边的那些指令需要重定位,现在就假设重定位后的基地址由原来的10000000h 变为 20000000h了,那么类似这样的语句:inc dword ptr [10003000] 应该改成 inc dword ptr [20003000] 。
注意,重定位的算法我们可以总结为:将直接寻址指令中的双字地址加上模块的实际装入地址与模块建议装入地址之差。

我们建议装入的地址,也就是我们原先没有重定位过的基地址:

在PE头中已经定义过:

实际装入的地址我们不知道,但是可以从base relocation table中寻找。

那么重定位表格中到底是什么呢?

也就是存放着————“需要被修改的那些汇编代码的地址”

重定位表的结构如下:base relocation table

Virtual address     1000

Sizeofblock                     18

Typeoffset

VirtualAddress 是 Base Relocation Table 的位置它是一个 RVA 值;
SizeOfBlock 是 Base Relocation Table 的大小;
TypeOffset 是一个数组,数组每项大小为两个字节(16位),它由高 4位和低 12位组成,高 4位代表重定位类型,低 12位是重定位地址,它与 VirtualAddress 相加即是指向PE 映像中需要修改的那个代码的地址。

从sizeofblock就可以算出来有多少个typeoffset(有多少个需要被重定向的语句):18-8(virtual  address占8个字节) = 10H /2 (typeoffset每一项是2个字节) = 8H (可是只有7项,为何?因为最后的00000是一项结尾项,用来诠释typeoffset终结了)

我现在觉得是:

  1. 需要某个函数的时候执行的时候,从输入表中可以看到真正需要这个函数(比如我们在hello.exe中需要messagebox函数,)的入口地址是什么,但是这个函数被我们封装在user32.dll下。
  2. 我们是先从磁盘中把整个user3dll动态链接到hello。Exe程序的内存空间中去
  3. 然后再根据user32.dll的导出表export去找到具体需要的函数messagebox的入口地址(有两种方法:1、从序号找,2、从函数名称找)
  4. 执行这个messagebox函数的每一条汇编指令,可是问题来了,代码我们原先写在磁盘里面,相对地址偏移都是磁盘里面的,但凡涉及直接寻址的指令(比如:inc dword ptr【10003000】,这个时候的10003000并不是程序被加载(或者说映射)到内存之后的偏移了,相对寻址不用改是因为相对偏移在映射之后不变)都要重定位。

我们回到上图:

就拿第一个typeoffset 3028来说,3是最高位表示类型,后面的028加上virtual address1000 =1028 ,这个1028就是pe映像中(即程序被加载进内存中后)要修改的那个代码 Inc dword ptr【10003000】的地址,可以用IDA验证结论:

从1028开始就是要改的东西了(要改的其实只有10003000,inc不用改)

十、资源(汉化、改图标等)

资源一般指的是加速器图标等、窗口、界面、菜单、……………………

资源是“树”结构。

接下来以QQ为例子分析一下这个资源:

文件对齐值和内存对齐值一样的话,都是以1000为一页,这样的话就不会有额外的偏移了,找到资源表入口:6000

IMAGE_RESOURCE_DIRECTORY STRUCT

Characteristics        DWORD      ?  0 ;理论上为资源的属性,不过事实上总是0
TimeDateStamp      DWORD      ?   ;资源的产生时刻0000 0000
MajorVersion          WORD        ?  0000 ;理论上为资源的版本,不过事实上总是0
MinorVersion          WORD        ? 0000
NumberOfNamedEntries  WORD     ?   ;以名称(字符串)命名的入口数量0000
NumberOfIdEntries         WORD     ?   ;以ID(整型数字)命名的入口数量0003

IMAGE_RESOURCE_DIRECTORY ENDS

资源属性为0000  0000

产生时刻0000 0000         版本 0000 和0000

0000+0003 = 0003 所以一共有3个“小跟班”:然后我们接下来看“入口”的结构继续分析:

IMAGE_RESOURCE_DIRECTORY_ENTRY STRUCT1

Name                  DWORD   0000 0003(图标资源类型) ?   ;目录项的名称字符串指针或ID
OffsetToData        DWORD     8000 0028?   ;目录项指针

IMAGE_RESOURCE_DIRECTORY_ENTRY ENDS

IMAGE_RESOURCE_DIRECTORY_ENTRY STRUCT2

Name                  DWORD  0000 000E(版本信息)  ?   ;目录项的名称字符串指针或ID
OffsetToData        DWORD   8000 0070 ?   ;目录项指针

IMAGE_RESOURCE_DIRECTORY_ENTRY ENDS

IMAGE_RESOURCE_DIRECTORY_ENTRY STRUCT3

Name                  DWORD    ? 0000 0018(自定义类型)  ;目录项的名称字符串指针或ID
OffsetToData        DWORD  8000 0088   ?   ;目录项指针

IMAGE_RESOURCE_DIRECTORY_ENTRY ENDS

资源directory

资源directory_entry1图标80000028

Directory—entry2

版本信息70

Directory——entry3

自定义类型88

2021-10-02PE文件学习相关推荐

  1. c语言程序设计第四版乌云高娃,C语言程序设计教学课件作者第3版乌云高娃学习手册C语言程序设计教学课件作者第3版乌云高娃学习手册学习手册第10章文件及其应用课件.docx...

    C语言程序设计教学课件作者第3版乌云高娃学习手册C语言程序设计教学课件作者第3版乌云高娃学习手册学习手册第10章文件及其应用课件.docx 学习手册(1):文本文件的操作学习内容文本文件的操作学习目标 ...

  2. 2021年7月份学习总结,多套WebFuture的系统部署(简易版)

    本文摘录2021年7月份学习总结,创建日期:2021年08月03日 15:37:15,有修改. 在Linux(中标麒麟)+达梦数据库+WebFuture搭配下部署. 「Linux(中标麒麟)+达梦数据 ...

  3. 2021.1.17-Robocup 2D学习日志

    2021.1.17-Robocup 2D学习日志 环境平台 比赛平台server和monitor 球员客户端client 比赛规则 自动裁判 人为干预 基本的资料 环境平台 比赛平台server和mo ...

  4. Spring Boot安装及使用(2021.10.28)

    Spring Boot安装及使用 2021.10.28 1.Spring Boot 简介 1.1 为何选择Spring? 1.2 Spring的功能 1.3 Spring项目的依赖包管理工具(Mave ...

  5. Python 最近两条好消息:①TIOBE排名超过C和Java②新版本发布3.10.0,还有今天刚发布的《What’s New in Python(2021.10.15)》

    来自TIOBE的最新10月份统计数据显示,Python首次超越Java.JavaScript.C语言等,成为最受欢迎的编程语言.TIOBE过去20年一直在追踪编程语言的受欢迎程度,其数据来自于对25个 ...

  6. 10个深度学习软件的安装指南(附代码)

    来源:AI前线 本文长度为2385字,建议阅读4分钟 本文为你介绍10个深度学习软件安装指南. 由于近期论文的需要,我搭建了一个基于 Ubuntu 和英伟达的深度学习环境.尽管已经有很多非常棒的关于英 ...

  7. C++ 落选,2021 年最想学习的五大编程语言

    作者 | Ashutosh Kumar 译者 | 火火酱,责编 | Carol 出品 | CSDN(ID:CSDNnews) 如今,市面上有非常多编程语言,选起来真是令人眼花缭乱. 选择第一门语言是非 ...

  8. C#内存映射文件学习总结

    C#内存映射文件学习 http://www.cnblogs.com/flyant/p/4443187.html 内存映射文件是由一个文件到进程地址空间的映射. C#提供了允许应用程序把文件映射到一个进 ...

  9. 2021年最想学习的五大编程语言

    如今,市面上有非常多编程语言,选起来真是令人眼花缭乱. 选择第一门语言是非常重要的,因为这是搭建基础的开始,自此以后我们会逐渐走进并了解编程世界.但老实说,选择哪一种编程语言并不十分重要,重要的是我们 ...

  10. 2021.10.25-10.31 AI行业周刊(第69期):AI进化之路

    本周<Opencv基础及AI项目实战>以及<Pytorch模型推理及多任务通用范式>两门课程已经完结. 两门课程中,针对所有完成作业的同学,都颁发了毕业证书. 并且对于完成比较 ...

最新文章

  1. 新手关于import/export的理解
  2. 【干货】实用案例|产品设计中的恰到好处
  3. Eclipse 常用快捷键-java
  4. tibco汉化包6.3.0_TIBCO BusinessWorks 6和Container Edition与BW5的比较
  5. 如何为Kubernetes实现原地升级
  6. spark streaming性能优化
  7. aboboo 上一句 快捷键_电脑软件推荐|这几个快捷键你一定能用得上
  8. 注解形式控制器 数据验证,类型转换(2)
  9. mipi的dsi全称_MIPI扫盲——DSI介绍(二)
  10. python3.7读取csv文件_Python3 读取csv文件
  11. slickedit自定义代码片段
  12. linux上的ds命令,使用DS-5 进行Linux应用开发
  13. 推荐阅读《未来世界的幸存者》
  14. Android Studio入门到精通
  15. 3-8 查询水果价格 (15 分)
  16. Xcode8兼容iOS7的解决方法
  17. python打印一年的日历_python一年月份_Python程序可打印任何一年的日历
  18. 汇编语言lcall d200c,有没有哪位大佬能帮我把汇编语言帮我转为c或者c++的 求帮助...
  19. 服务器监控管理工具大全
  20. java自动违例设计,如何在Java中创建自己的违例

热门文章

  1. 山东大学为什么火了_“火得一塌糊涂”的山东大学:号称巨无霸、全国排名前三十...
  2. MVP+okhttp请求网络接口
  3. PowerBI指标拆解分析可视化
  4. 使用vivado封装IP
  5. ssd固态硬盘怎么分区
  6. 网盘和同步盘有什么不同?
  7. 中科院昆明植物所李爱荣组诚聘博士后—根部半寄生植物的根际过程及调控机理...
  8. flurry+atos crash代码定位
  9. 企业带宽管理解决方案
  10. 安装widows和linux双系统,启动项问题