PE文件(一)PE结构概述

news2024/12/24 0:36:05

PE结构简述

Windows操作系统是只能运行以内存4D 5A开头,翻译是MZ的可执行文件,也叫做PE结构文件,是以exe,.sys,.dll等等作为后缀的文件。而不同的操作系统能运行的可执行文件都是各自特有的,比如Linux可运行的可执行文件叫做elf结构文件

在以后pe结构文件的知识中,我们均以exe文件进行pe文件的演示与讲解

32位计算机中任何一个exe文件在运行时所在的内存叫做虚拟内存,这些文件都有自己独立的4GB的虚拟内存,其中2GB用于应用程序,其余2GB用于操作系统

exe文件有两种状态:

一种是在硬盘(未运行时)的状态:在硬盘上的exe文件打开后内存首地址是从0开始的(逻辑地址)

另一种是在内存中(运行时)的状态:正在运行时的exe文件在内存(即虚拟内存)中的内存首地址是从0x1000000开始的(物理地址),该内存首地址根据不同的文件有不同的地址

我们常见的txt文件是可以打开运行它,但它并不是可执行文件,这是因为该文件实际是在exe程序中(比如word)打开的

pe结构分节

PE结构文件内容是分节的,每一节之间以0作为空白区域,其分节有以下几种原因:

1.节省硬盘空间

在此之前我们应该了解到在早期的编译器编译的文件,其在硬盘对齐是200h字节,这是因为早期硬盘很昂贵,而在内存对齐是1000h字节。但在现代的编译器中编译器编译的文件在硬盘和内存中的对齐都是1000h字节

我们首先观察一下早期编译器生成的exe文件(如notepad.exe在硬盘和内存中的存储分布

如图可知,一个pe文件在存储中分为了多个段,这些段也叫做节。图中左侧是pe文件在硬盘中的存储分布,分布空间紧密。右侧是pe文件在内存中的存储分布,其分布空间稀疏,这是由于pe文件从硬盘到内存有一个拉伸的过程。由图对比可知在硬盘上存储空间更小。如此看来是符合节省硬盘空间的原因的

现在我们观察一下现代编译器生成的pe文件在硬盘和内存中的存储分布:

同一份文件在内存中和在硬盘中的内容是一样的,但是他们文件内存起始位置是不一样的,它们分节之间的空白区域大小是一样的,这似乎与我们之前所作的文件硬盘与内存分布图有所不同,这是由于对齐的机制。对齐是为了提高读写速度,比如一本书一章的结尾可能会在新的一页留一个‘完’字,这个字会单独占有一页,而不是在下一章的内容一起占据同一页,这样的设置让我们更容易的去查找我们想要的内容

到目前为止看来,pe结构分节可能并不只是因为节省硬盘空间

2.多开,比如我们挂几个qq

假设我们现在有一个exe文件(比如qq),它的文件结构分为只读数据和可读可写数据,如下图表示,其每个部分都占有100兆的内存

此时我们多开一个该文件,内存分布是这样的:系统只会为我们多创建一个可读可写的数据的内存

这是我们可以发现,正是因为pe文件结构分节,我们才能占用更少的内存,发挥更大的作用

PE文件信息

如下是早期编译器编译的pe文件在硬盘和内存中的结构图

无论哪个块的内容有多大,它所被分配的大小在硬盘中都只有200h,内存中1000h,而块中的数据不论在硬盘还是内存中都是一样的,只是因为内存1000h对齐的原因,所以需要用0填充空出来的区域

如图可知:文件中我们能看到的数据(即.data,.text,.rdata)都被存储在块中,每个块在硬盘和内存中都被分配了200h和1000h的大小。

每一个节,都有一个对应的节表(图中块表)用于记录节的相关信息,如每一个节的概要性信息。这些节表是挨着存放在一个指定的区域的,所以广义上我们称这片区域为节表。

除此以外,pe文件还有两个结构:PE文件头和DOS头,这两个结构记录了该pe文件的概要性信息和特征:比如在内存中拉伸后占多大空间,或此程序启动后要分多大的堆、堆栈等

手动解析pe文件

该部分内容我们学习如何手动查找DOS头,NT头

DOS头和NT头中指定位置和宽度的数据都规定了不同含义,图中左边一列地址是相对于DOS头或PE文件头起始地址的的地址,如图所示,该图也是完整的pe结构图

DOS头

一.DOS头的作用

1.我们解析一个文件时会看最开始的两个字节(e_magic)是不是4D 5A(MZ),用于判断该文件是不是pe文件

2. 找到DOS头的最后4字节数据(e_Ifanew),它指向真正的PE文件开始的地址

3.其他DOS头中的数据可以不用理会,这是因为DOS头最初是给16位操作系统使用的,对于32位系统,DOS的作用就是上述两个

从DOS头结尾到PE签名(即NT头开始)之间,有一些空出来的空间。这个空间用于存放不同的编译器存放不同的数据,对于我们来说其实就是一些垃圾数据,而且程序本身也不会使用到这块空间。所以我们可以在这个空间加入我们自己的数据,并且该数据随着文件一起装入内存中,并分配了内存地址。因此虽然程序本身运行时不会使用这块空间,但我们可以利用一些方法访问该内存,如指针

二.手动解析DOS头

我们将ipmsg.exe程序用winhex打开,根据DOS头的结构来分析数据,找出DOS头对应的字节代码(DOS头大小为64字节,十六进制为0x40)

注意:winhex显示的文件数据是按不同含义的字段宽度顺序存的,并且其数据以小端序排列

如图便是我们打开程序所显示的文件的内存数据,接下来我们将按照DOS的结构,依次查找每个成员所对应的数据代码

struct _IMAGE_DOS_HEADER {

    0x00 WORD e_magic;   *   //0x5A4D    MZ,即表示此文件是可执行文件

    0x02 WORD e_cblp;        //0x0090

    0x04 WORD e_cp;          //0x0003

    0x06 WORD e_crlc;        //0x0000

    0x08 WORD e_cparhdr;     //0x0040

    0x0a WORD e_minalloc;    //0x0000

    0x0c WORD e_maxalloc;    //0xffff

    0x0e WORD e_ss;          //0x0000

    0x10 WORD e_sp;          //0x00B8

    0x12 WORD e_csum;        //0x0000

    0x14 WORD e_ip;          //0x0000

    0x16 WORD e_cs;          //0x0000

    0x18 WORD e_lfarlc;      //0x0040

    0x1a WORD e_ovno;        //0x0000

    0x1c WORD e_res[4];      //0x0000000000000000,此处是4个字节数组

    0x24 WORD e_oemid;       //0x0000

    0x26 WORD e_oeminfo;     //0x0000

    0x28WORDe_res2[10]; //0x0000000000000000000000000000000000000000

    0x3c DWORD e_lfanew;  *  //0x000000e0    表示真正的PE文件开始地址为0xe0,即PE签名所在地址

};

NT头

紧接着DOS头便是NT头,现在我们开始讲解NT头

NT头是由三部分组成:PE签名,标准PE头,可选PE头。在NT头中,首先是PE签名字段然后是标准PE头,最后紧跟着就是可选PE头

现在我们开始寻找NT头:

1.找PE签名

我们之前在找DOS头时,DOS头以0x000000e0结尾, 指向了左侧地址e0的地方,从图中可知,e0的地方有5045,在最右侧有PE文字,这也正好说明了此处是pe文件真正开始的地方,即NP头开始,但这个e0并不是一直固定的。

指向e0地址,而不是直接接着NT头的原因是,从DOS头结尾到NT头开始(即PE签名字段)之间,不同的编译器会存放不同的数据,但是对于我们来说就是一些垃圾数据,而且程序本身也不会使用到这块空间。所以我们可以在这个空间加入我们自己的数据,并且该数据随着文件一起装入内存中,并分配了内存地址。因此虽然程序本身运行时不会使用这块空间,但我们可以利用一些方法访问该内存,如指针

如下我们将对应PE签名结构体在上图所对应的内存地址进行展示

struct _IMAGE_NT_HEADERS {

0x00 DWORD Signature;   //0x00004550 即PE的签名占4字节

0x04 _IMAGE_FILE_HEADER FileHeader;   //结构体中存在结构体类型的数据,此处是标准pe头

0x18 _IMAGE_OPTIONAL_HEADER OptionalHeader; //此处可选pe头

};

2)找标准PE头

PE文件头(大小为20字节,0x12)

struct _IMAGE_FILE_HEADER {

    0x00 WORD Machine;  *                //0x014c

    0x02 WORD NumberOfSections;  *       //0x0004

    0x04 DWORD TimeDateStamp;  *         //0x4d74bc7e

    0x08 DWORD PointerToSymbolTable;     //0x00000000

    0x0c DWORD NumberOfSymbols;          //0x00000000

    0x10 WORD SizeOfOptionalHeader;  *   //0x00e0

    0x12 WORD Characteristics;  *        //0x010f

};

3)找可选PE头

PE可选头(大小不确定,需要根据标准PE头中的SizeOfOptionalHeader的值来判断),如下便是可选pe头对应上图的地址

struct _IMAGE_OPTIONAL_HEADER {

    0x00 WORD Magic;  *                    //0x010b

    0x02 BYTE MajorLinkerVersion;          //0x06

    0x03 BYTE MinorLinkerVersion;          //0x00

    0x04 DWORD SizeOfCode;  *              //0x00021000

    0x08 DWORD SizeOfInitializedData;  *   //0x0001b000

    0x0c DWORD SizeOfUninitializedData;  * //0x00000000

    0x10 DWORD AddressOfEntryPoint;  *     //0x0001d26f

    0x14 DWORD BaseOfCode;  *              //0x00001000

    0x18 DWORD BaseOfData;  *              //0x00022000

    0x1c DWORD ImageBase;  *               //0x00400000

    0x20 DWORD SectionAlignment;  *        //0x00001000

    0x24 DWORD FileAlignment;  *           //0x00001000

    0x28 WORD MajorOperatingSystemVersion; //0x0004

    0x2a WORD MinorOperatingSystemVersion; //0x0000

    0x2c WORD MajorImageVersion;           //0x0000

    0x2e WORD MinorImageVersion;           //0x0000

    0x30 WORD MajorSubsystemVersion;       //0x0004

    0x32 WORD MinorSubsystemVersion;       //0x0000

    0x34 DWORD Win32VersionValue;          //0x00000000

    0x38 DWORD SizeOfImage;  *             //0x0003d000

    0x3c DWORD SizeOfHeaders;  *           //0x00001000

    0x40 DWORD CheckSum;  *                //0x00000000

    0x44 WORD Subsystem;                   //0x0002

    0x46 WORD DllCharacteristics;          //0x0000

    0x48 DWORD SizeOfStackReserve;  *      //0x00100000

    0x4c DWORD SizeOfStackCommit;  *       //0x00001000

    0x50 DWORD SizeOfHeapReserve;  *       //0x00100000

    0x54 DWORD SizeOfHeapCommit;  *        //0x00001000

    0x58 DWORD LoaderFlags;                //0x00000000

    0x5c DWORD NumberOfRvaAndSizes;        //0x00000010

    0x60 _IMAGE_DATA_DIRECTORY DataDirectory[16]; //这个先不分析

};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1605861.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

zabbix 自定义模板,邮件报警,代理服务器,自动发现与自动添加及snmp

目录 一. 自定义监控内容 1. 在客户端创建自定义 key 2. 在 web 页面创建自定义监控项模块 2.1 创建模板 2.2 创建应用集 2.3 创建监控项 2.4 创建触发器 2.5 创建图形 2.6 将主机与模板关联起来 登录测试 2.7 设置邮件报警 测试邮件报警 3. nginx 服务状况的检测…

2024年腾讯云服务器价格一览表

随着云计算技术的快速发展,越来越多的企业和个人开始选择使用云服务器来满足他们的数据存储和计算需求。腾讯云作为国内领先的云服务提供商,其服务器产品因性能稳定、安全可靠而备受用户青睐。那么,2024年腾讯云服务器的价格情况如何呢&#…

状态压缩DP题单

P1433 吃奶酪&#xff08;最短路&#xff09; dp(i, s) 表示从 i 出发经过的点的记录为 s 的路线距离最小值 #include<bits/stdc.h> #define int long long using namespace std; const int N 20; signed main() { int n; cin >> n;vector<double>x(n 1),…

matplotlib plt.gca()学习

之前一直在这个代码里看到plt.gca()的使用&#xff0c;但是一直没搞明白这个怎么用&#xff0c;今天总结一下 gca是get current axis的首字母的缩写&#xff0c;就是控制坐标轴的&#xff0c;也是比较简单的&#xff0c;并不是一个很复杂的函数 移动坐标轴 import matplotlib…

Bacnet 入门参考资料 (一)

[OTC] 在网上整理的一些BACnet的相关资料&#xff0c;在这里作一个整理&#xff0c;方便自己食用。 参考博文 ① BACnet基础入门&#xff1a;https://blog.csdn.net/li1197538342/article/details/128341198 ② BACnet网络讲义 &#xff08;建议先看这个&#xff0c;第1章&a…

LeetCode 1.两数之和(HashMap.containsKey()、.get、.put操作)

给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是&#xff0c;数组中同一个元素在答案里不能重复出现。 你可以按任意顺序返回…

代码随想录第39天|62.不同路径 63. 不同路径 II

62.不同路径 62. 不同路径 - 力扣&#xff08;LeetCode&#xff09; 代码随想录 (programmercarl.com) 动态规划中如何初始化很重要&#xff01;| LeetCode&#xff1a;62.不同路径_哔哩哔哩_bilibili 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标…

spring-数据处理及跳转

结果跳转方式 ModelAndView 设置ModelAndView对象 , 根据view的名称 , 和视图解析器跳到指定的页面 . 页面 : {视图解析器前缀} viewName {视图解析器后缀} <!-- 视图解析器 --> <bean class"org.springframework.web.servlet.view.InternalResourceViewRes…

flink network buffer

Flink 的网络协议栈是组成 flink-runtime 模块的核心组件之一&#xff0c;是每个 Flink 作业的核心。它连接所有 TaskManager 的各个子任务(Subtask)&#xff0c;因此&#xff0c;对于 Flink 作业的性能包括吞吐与延迟都至关重要。与 TaskManager 和 JobManager 之间通过基于 A…

一些重新开始面试之后的八股文汇总

一、内存中各项名词说明 1、机器内存概念说明 linux中的free命令可以查看机器的内存使用情况&#xff0c;vmstat命令也可以 其中不容易被理解的是&#xff1a; 内存缓冲/存数&#xff08;buffer/cached&#xff09; 1.buffers和cache也是RAM划分出来的一部分地址空间 2.buff…

鸿蒙入门05-真机运行“遥遥领先”

如果你有一台真的 "遥遥领先"那么是可以直接在手机上真机运行你的项目的我们也来尝试一下运行 一、手机设置开发者模式 打开手机设置 打开手机设置界面 向下滑动到关于手机位置 快速连续点击版本号位置 下图所示位置快速连续点击 打开 3 - 5 次即可 会提示您已经进…

CoFSM基于共现尺度空间的多模态遥感图像匹配方法--论文阅读记录

目录 论文 Multi-Modal Remote Sensing Image Matching Considering Co-Occurrence Filter 参考论文&#xff1a;SIFT系列论文&#xff0c; SIFT Distinctive Image Features from Scale-Invariant Keypoints&#xff0c;作者&#xff1a;David G. Lowe 快速样本共识算法…

H264标准协议基础3

参考博文 上一篇H264标准协议基础2 1.解码视频帧的poc计算 2.残差4x4 矩阵中的trailingones & numcoeff 2.1查表 trailingones 表达出尾部one(1,-1)系数的个数,按照zigzag扫描出(1,-1)个数,trailingones的最大为3; numcoeff 表达非零值系数的个数,最多为16个…

初识ansible核心模块

目录 1、ansible模块 1.1 ansible常用模块 1.2 ansible-doc -l 列出当前anisble服务所支持的所有模块信息&#xff0c;按q退出 1.3 ansible-doc 模块名称 随机查看一个模块信息 2、运行临时命令 2.1 ansible命令常用的语法格式 3、常用模块详解与配置实例 3.1命令与…

内部类

一.概念 当一个事物内部&#xff0c;还有一个部分需要一个完整的结构进行描述&#xff0c;而这个内部的完整的结构又只为外部事物提供服务&#xff0c;那么将这个内部的完整结构最好使用内部类。在Java中&#xff0c;可以将一个类定义在另一个类或者一个方法内部&#xff0c;前…

如何进行开关电源温升极限测试?

开关电源温升极限测试是指开关电源在没有过温保护的条件下&#xff0c;逐渐升高电源的测试温度&#xff0c;直到开关电源损坏。温升极限测试是为了研究开关电源能够承受的最高环境温度&#xff0c;从而评估开关电源的性能&#xff0c;优化提升开关电源的工艺设计。 温升极限测试…

【Canvas技法】六种环状花纹荟萃

【图例】 【代码】 <!DOCTYPE html> <html lang"utf-8"> <meta http-equiv"Content-Type" content"text/html; charsetutf-8"/> <head><title>使用HTML5/Canvas绘制六种环状花纹</title><style type&quo…

SimCLR v2(NeurIPS 2020)论文解读

paper&#xff1a;Big Self-Supervised Models are Strong Semi-Supervised Learners official implementation&#xff1a;https://github.com/google-research/simclr 本文的创新点 本文在SimCLR的基础上做了一些改进&#xff0c;提出了SimCLR v2&#xff0c;进一步提升了…

车机系统与 Android 的关系概述

前言&#xff1a;搞懂 Android 系统和汽车到底有什么关系。 文章目录 一、基本概念1、Android Auto1&#xff09;是什么2&#xff09;功能 2、Google Assistant3、Android Automotive1、Android Auto 和 Android Automotive 的区别 4、App1&#xff09;App 的开发2&#xff09;…

JavaWeb开发02-MYSQL-DDL-DML-DQL-多表设计-多表查询-事务-索引

一、MySQL概述 通过SQL语句可以操作数据库 关系型数据库&#xff1a; 只要是关系型数据库就可以用SQL语句这一统一标准进行操作数据库 1.MYSQL数据模型 客户端通过SQL语句交给了数据库管理系统DBMS&#xff0c;进行相应操作&#xff0c;创建一个一个数据库&#xff0c;体现为一…