DOS头

在之前,我们已经了解过PE文件的整体结构了,并且我们进行了静动态差异的文件分析,其开头部分就是DOS

部分,包含了DOS MZ文件头和DOS块,那么我们来了解一些DOS部分的结构和其相关意义。

DOS MZ文件头

DOS MZ文件头就是一个结构体IMAGE_DOS_HEADER,其定义如下所示:

 typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header
WORD e_magic; // Magic number
WORD e_cblp; // Bytes on last page of file
WORD e_cp; // Pages in file
WORD e_crlc; // Relocations
WORD e_cparhdr; // Size of header in paragraphs
WORD e_minalloc; // Minimum extra paragraphs needed
WORD e_maxalloc; // Maximum extra paragraphs needed
WORD e_ss; // Initial (relative) SS value
WORD e_sp; // Initial SP value
WORD e_csum; // Checksum
WORD e_ip; // Initial IP value
WORD e_cs; // Initial (relative) CS value
WORD e_lfarlc; // File address of relocation table
WORD e_ovno; // Overlay number
WORD e_res[4]; // Reserved words
WORD e_oemid; // OEM identifier (for e_oeminfo)
WORD e_oeminfo; // OEM information; e_oemid specific
WORD e_res2[10]; // Reserved words
LONG e_lfanew; // File address of new exe header
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

它有很多成员,但我们并不需要去深入的理解每个成员的含义和作用,这是因为这个结构体是给16位平台看

的,而我们现在的环境大部分都是32位和64位的,所以现在的平台不再需要这个完整的结构体了,只需要其中

的两个成员e_magice_lfanew.

你可以尝试在16进制的编辑器中去编辑某个EXE文件保留两个成员e_magic和e_lfanew,其他的以0x00填充,然

后保存文件,你会发现修改后的文件还是可以正常运行的:



保留这两个成员的原因是因为它们代表着我们之前所说的PE指纹,操作系统也是根据这个来识别是否是PE文件

的,所以不能够更改、删除(e_magic是一种标识,e_lfanew则表示PE文件头的位置)。

DOS块

DOS块就是夹在DOS MZ文件头和PE文件头之间的内容,这里面的内容可以根据自己的需要随意的修改和添加,

并不会影响文件的正常运行。

PE头

PE头整体就是如下这个结构体:

 typedef struct _IMAGE_NT_HEADERS {
DWORD Signature; // PE标识
IMAGE_FILE_HEADER FileHeader; // 标准PE头
IMAGE_OPTIONAL_HEADER32 OptionalHeader; // 扩展PE头
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

第一个成员就是PE标识,该标识不能破坏,因为操作系统在启动一个程序的时候会检测这个标识。

标准PE头

标准PE头是PE头的第二个成员,它是如下所示的结构体:

typedef struct _IMAGE_FILE_HEADER {
WORD Machine; // 可以运行在什么样的CPU上
WORD NumberOfSections; // 表示节的数量
DWORD TimeDateStamp; // 编译器填写的时间戳
DWORD PointerToSymbolTable; // 调试相关
DWORD NumberOfSymbols; // 调试相关
WORD SizeOfOptionalHeader; // 扩展PE头的大小
WORD Characteristics; // 文件属性
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

其第一个成员Machine表示可以运行在什么样的CPU上,如果它的值为0x0则表示可以运行在任意的CPU上,支

持在Intel 386以及后续的型号CPU运行则值为0x14c,支持64位的CPU型号则值为0x8664。

我们可以分别在32位、64位系统上提取notepad.exe进行对比来看看这个成员(010 Editor → Tools → Compare

Files...):





第二个成员NumberOfSections表示当前PE文件中节的数量,也就是节表中有几个结构体;第三个成员

TimeDateStamp表示编译器编译的时候插入的时间戳,与文件属性里面的创建时间和修改时间是无关的。

第四、第五个成员是调试相关的,我们暂时不用去了解;第六个成员SizeOfOptionalHeader表示扩展PE头的大

小,默认情况下32位PE文件对应值位0xE0,64位PE文件对应值为0xF0。

第七个成员Characteristics用来记录当前PE文件的一些属性,该成员是16位(2字节)大小,其每一数据位对

应的属性如下所示:

扩展PE头

扩展PE头在32位和64位环境下是不一样的,在本章节中只介绍32位扩展PE头。如下结构体就是32位的扩展PE

头:

typedef struct _IMAGE_OPTIONAL_HEADER {
WORD Magic; // PE32:10B PE32+:20B
BYTE MajorLinkerVersion; // 链接器版本号
BYTE MinorLinkerVersion; // 链接器版本号
DWORD SizeOfCode; // 所有代码节的总和(文件对齐后的大小),编译器填的(没用)
DWORD SizeOfInitializedData; // 包含所有已经初始化数据的节的总大小(文件对齐后的大小),编译器填的(没
用) DWORD SizeOfUninitializedData; // 包含未初始化数据的节的总大小(文件对齐后的大小),编译器填的(没用)
DWORD AddressOfEntryPoint; // 程序入口
DWORD BaseOfCode; // 代码开始的基址,编译器填的(没用)
DWORD BaseOfData; // 数据开始的基址,编译器填的(没用)
DWORD ImageBase; // 内存镜像基址
DWORD SectionAlignment; // 内存对齐
DWORD FileAlignment; // 文件对齐
WORD MajorOperatingSystemVersion; // 标识操作系统版本号,主版本号
WORD MinorOperatingSystemVersion; // 标识操作系统版本号,次版本号
WORD MajorImageVersion; // PE文件自身的版本号
WORD MinorImageVersion; // PE文件自身的版本号
WORD MajorSubsystemVersion; // 运行所需子系统版本号
WORD MinorSubsystemVersion; // 运行所需子系统版本号
DWORD Win32VersionValue; // 子系统版本的值,必须为0
DWORD SizeOfImage; // 内存中整个PE文件的映射的尺寸
DWORD SizeOfHeaders; // 所有头加节表按照文件对齐后的大小,否则加载会出错
DWORD CheckSum; // 校验和
WORD Subsystem; // 子系统,驱动程序(1)、图形界面(2) 、控制台/DLL(3)
WORD DllCharacteristics; // 文件特性
DWORD SizeOfStackReserve; // 初始化时保留的栈大小
DWORD SizeOfStackCommit; // 初始化时实际提交的大小
DWORD SizeOfHeapReserve; // 初始化时保留的堆大小
DWORD SizeOfHeapCommit; // 初始化时实践提交的大小
DWORD LoaderFlags; // 调试相关
DWORD NumberOfRvaAndSizes; // 目录项数目
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; // 表,结构体数组
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

扩展PE头的成员有很多,但我们不需要每个都记住,大概的了解一下即可,重点关注如下这几个成员:

成员Magic表示当前PE文件是32位还是64位,32位时该值对应0x10B,64位时该值对应0x20B。

成员AddressOfEntryPoint表示当前程序入口的地址,这个成员要与成员ImageBase相加才能得出真正的入口地

址,成员ImageBase用来表示内存镜像基址,也就是PE文件在内存中按内存对齐展开后的首地址,我们可以在

实际PE文件中看下,如下图所示就是PE文件静态状态下的两个成员值,AddressOfEntryPoint为0x739D,

ImageBase为0x1000000,那么最终的程序在内存中的入口地址就是0x100739D:

那么如何证实推断的结果是正确的呢,我们可以直接使用DTDebug之类的调试器打开这个PE文件,调试器会自

动在程序入口断点,如下图所示则表示我们的推测是正确的:



成员FileAlignment、SectionAlignment和SizeOfHeader在之前的章节中已经了解过了,这里不再赘述。

成员SizeOfImage表示在内存中整个PE文件映射的大小,可比实际的值大(内存对齐之后的大小,也就表示必须是SectionAlignment的整数倍)。

扩展PE头的成员有很多,但我们不需要每个都记住,大概的了解一下即可,重点关注如下这几个成员:

成员Magic表示当前PE文件是32位还是64位,32位时该值对应0x10B,64位时该值对应0x20B。

成员AddressOfEntryPoint表示当前程序入口的地址,这个成员要与成员ImageBase相加才能得出真正的入口地

址,成员ImageBase用来表示内存镜像基址,也就是PE文件在内存中按内存对齐展开后的首地址,我们可以在

实际PE文件中看下,如下图所示就是PE文件静态状态下的两个成员值,AddressOfEntryPoint为0x739D,

ImageBase为0x1000000,那么最终的程序在内存中的入口地址就是0x100739D:

那么如何证实推断的结果是正确的呢,我们可以直接使用DTDebug之类的调试器打开这个PE文件,调试器会自

动在程序入口断点,如下图所示则表示我们的推测是正确的:



成员FileAlignment、SectionAlignment和SizeOfHeader在之前的章节中已经了解过了,这里不再赘述。

成员SizeOfImage表示在内存中整个PE文件映射的大小,可比实际的值大(内存对齐之后的大小,也就表示必须是SectionAlignment的整数倍)。

成员CheckSum表示校验和,是用来判断文件是否被修改的,它的计算方法就是文件的两个字节与两个字节相加,最终的值(不考虑溢出情况)就是校验和。

最后一个需要我们了解的成员是DllCharacteristics,它用来表示PE文件的特性,但不要被名字所迷惑,它不是针对DLL文件的;它的数据宽度是16位(4字节),其每一数据位对应的属性如下所示:

PE节表

在PE中,节数据有几个,分别对应着什么类型以及其他相关的属性都是由PE节表来决定的,PE节表是一个结构体数组,结构体的定义如下所示:

#define IMAGE_SIZEOF_SHORT_NAME 8
typedef struct _IMAGE_SECTION_HEADER {
BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; // ASCII字符串(节名),可自己义,只截取8个字节,可以8个字节都是
名字
union { // Misc,双字,是该节在没有对齐前的真实尺寸,该值可以不准确
DWORD PhysicalAddress; // 真实宽度,这两个值是一个联合结构,可以使用其中的任何一个
DWORD VirtualSize; // 一般是取后一个
} Misc;
DWORD VirtualAddress; // 在内存中的偏移地址,加上ImageBase才是在内存中的真正地址
DWORD SizeOfRawData; // 节在文件中对齐后的尺寸
DWORD PointerToRawData; // 节区在文件中的偏移
DWORD PointerToRelocations; // 调试相关
DWORD PointerToLinenumbers; // 调试相关
WORD NumberOfRelocations; // 调试相关
WORD NumberOfLinenumbers; // 调试相关
DWORD Characteristics; // 节的属性
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

代码中的注释可以大致了解到每个成员的作用,其中有2个成员来描述节的大小,分别是没有对齐前的真实尺寸和对齐后的宽度,这时候会出现一种情况就是对齐前的真实尺寸大于对齐后的宽度,这就是存在全局变量没有赋予初始值导致的,在文件存储中全局变量没有赋予初始值也就不占空间,但是在内存中是必须要赋予初始

值的,这时候宽度就大了一些,所以在内存中节是谁大就按照谁去展开。



与其他结构体一样,PE节也有属性,这就是成员Characteristics,其数据宽度是16位(4字节),其每一数据位

对应的属性如下所示:

PE文件整体结构解析的更多相关文章

  1. PE文件结构部分解析以及输入的定位

    原文链接地址:http://www.cnblogs.com/shadow-lei/p/3554670.html PE文件定义 PE 文件("Portable executable" ...

  2. 手写PE文件(一)

    DOS Header(IMAGE_DOS_HEADER)->64 Byte DOS头部 DOS Stub 112字节 "PE"00(Signature) 4个字节 IMAGE ...

  3. PE文件加节感染之Win32.Loader.bx.V病毒分析

    一.病毒名称:Win32.Loader.bx.V 二.分析工具:IDA 5.5.OllyDebug.StudPE 三.PE文件加节感染病毒简介 PE病毒感染的方式比较多,也比较复杂也比较难分析,下面就 ...

  4. PE文件解析器的编写(二)——PE文件头的解析

    之前在学习PE文件格式的时候,是通过自己查看各个结构,自己一步步计算各个成员在结构中的偏移,然后在计算出其在文件中的偏移,从而找到各个结构的值,但是在使用C语言编写这个工具的时候,就比这个方便的多,只 ...

  5. PE格式文件的解析代码

    #include "Global.h" ; //标志,用于表示是否为pe32+文件 ; //标志,用于表示读入的模式,若为0代表是内存读入,不为0,代表是文件打开,此时mode是文 ...

  6. PE文件解析 基础篇

    PE文件解析 基础篇 来源 https://bbs.pediy.com/thread-247114.htm 前言 之前学习了PE格式,为了更好的理解,决定写一个类似LoadPE的小工具. 编译器是VS ...

  7. C++PE文件格式解析类(轻松制作自己的PE文件解析器)

    PE是Portable Executable File Format(可移植的运行体)简写,它是眼下Windows平台上的主流可运行文件格式. PE文件里包括的内容非常多,详细我就不在这解释了,有兴趣 ...

  8. 解析PE文件的附加数据

    解析程序自己的附加数据,将附加数据写入文件里. 主要是解析PE文件头.定位到overlay的地方.写入文件. 常应用的场景是在crackme中,crackme自身有一段加密过的附加数据.在crackm ...

  9. 解析PE文件

    最近在自学解析PE文件,根据小辣椒(CFF Explorer)以及各论坛上大佬的帖子,做了个黑屏打印PE文件的,历时7天完成,在此想跟有相关需要的同学们分享下思路,有不足之处也希望大家不吝赐教,指点出 ...

  10. [系统安全] 十六.PE文件逆向基础知识(PE解析、PE编辑工具和PE修改)

    [系统安全] 十六.PE文件逆向基础知识(PE解析.PE编辑工具和PE修改) 文章来源:https://masterxsec.github.io/2017/05/02/PE%E6%96%87%E4%B ...

随机推荐

  1. 可视化大屏的终极解决方案居然这么简单,vue-autofit一行全搞定!

    可视化大屏适配/自适应现状 可视化大屏的适配是一个老生常谈的话题了,现在其实不乏一些大佬开源的自适应插件.工具但是我为什么还要重复造轮子呢?因为目前市面上适配工具每一个都无法做到完美的效果,做出来的东 ...

  2. [C++提高编程] 3.4 案例-评委打分

    文章目录 3.4 案例-评委打分 3.4.1 案例描述 3.4.2 实现步骤 3.4 案例-评委打分 3.4.1 案例描述 有5名选手:选手ABCDE,10个评委分别对每一名选手打分,去除最高分,去除 ...

  3. Vulhub activewq_漏洞复现——源码分析

    Vulhub activewq_漏洞复现--源码分析 漏洞简介 Apache ActiveMQ是由美国阿帕奇(Apache)软件基金会开发的开源消息中间件,支持Java消息服务.集群.Spring框架 ...

  4. pytest测试实战和练习

    开头 经过前面几章的学习,这时候要来个测试实战会比较好巩固一下学过的知识 任务要求 1.实现计算器(加法,除法)的测试用例 2.使用数据驱动完成测试用例的自动生成 3.在调用测试方法之前打印[开始计算 ...

  5. 2022-11-10:写一个 bash 脚本以统计一个文本文件 words.txt 中每个单词出现的频率。 为了简单起见,你可以假设: words.txt只包括小写字母和 ‘ ‘ 。 每个单词只由小写

    2022-11-10:写一个 bash 脚本以统计一个文本文件 words.txt 中每个单词出现的频率. 为了简单起见,你可以假设: words.txt只包括小写字母和 ' ' . 每个单词只由小写 ...

  6. 2021-08-18:扰乱字符串。使用下面描述的算法可以扰乱字符串 s 得到字符串 t :1.如果字符串的长度为 1 ,算法停止。2.如果字符串的长度 > 1 ,执行下述步骤:在一个随机下标处将字符串

    2021-08-18:扰乱字符串.使用下面描述的算法可以扰乱字符串 s 得到字符串 t :1.如果字符串的长度为 1 ,算法停止.2.如果字符串的长度 > 1 ,执行下述步骤:在一个随机下标处将 ...

  7. 2021-09-29:不同路径。一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为

    2021-09-29:不同路径.一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 "Start" ).机器人每次只能向下或者向右移动一步.机器人试图达到网格的右 ...

  8. 记一次 .NET 某医院门诊软件 卡死分析

    一:背景 1. 讲故事 前几天有位朋友找到我,说他们的软件在客户那边卡死了,让我帮忙看下是怎么回事?我就让朋友在程序卡死的时候通过 任务管理器 抓一个 dump 下来,虽然默认抓的是 wow64 ,不 ...

  9. 火山引擎DataTester:A/B实验平台数据集成技术分享

    DataTester的数据集成系统,可大幅降低企业接入A/B实验平台门槛.   当企业想要接入一套A/B实验平台的时候,常常会遇到这样的问题: 企业已经有一套埋点系统了,增加A/B实验平台的话需要重复 ...

  10. SQL Server修改列的数据类型

    ALTER TABLE LJPA001H DROP CONSTRAINT DF_LJPA001H_pa_sex_1 ALTER TABLE LJPA001H ALTER COLUMN pa_sex V ...