引言

PE文件格式是Windows操作系统下的可执行文件的格式,包括.exe文件和.dll文件,通过PE文件格式的学习,可以帮助我们更加熟悉有关Windows系统下的逆向分析和PC端病毒的学习,同时PE文件格式也是HOOK,加壳等知识的基础,在这里分享一下自己的有关PE文件格式学习的收获和如何编写一个PE文件解析器。

PE文件格式

PE文件由DOS头部,PE文件头,块表以及数据段,代码段等区块构成。



由于PE文件在磁盘和在内存当中的对齐值的不同,造成偏移量的不同,我们将其分为相对虚拟地址和文件偏移地址。现对这些名词进行一些解释。

1. 基地址(ImageBase):映射文件的起始地址称为模块句柄,通过该句柄可访问其他数据结构。这个起始地址即为基地址。基地址的值由文件本身决定,默认EXE文件的基地址为400000h,DLL文件基地址为10000000h.但是这样显然会产生冲突,所以后续用基地址重定位解决这个问题。

2. 虚拟地址(VA):载入内存后的地址。

3. 相对虚拟地址(RVA):载入内存后相对基地址的偏移量。RVA=VA-ImageBase。

4. 文件偏移地址:在磁盘当中相对基地址的偏移量。

DOS首部

typedef struct  _IMAGE_DOS_HEADER{
uint16_t signature; // Magic number: 0x4D5A ("MZ" in ASCII)
uint16_t last_page_size; // Size of the last page in bytes
uint16_t num_pages; // Total number of pages in the file
uint16_t num_relocations; // Number of relocation entries
uint16_t header_size; // Size of the header in paragraphs
uint16_t min_alloc; // Minimum number of paragraphs to allocate
uint16_t max_alloc; // Maximum number of paragraphs to allocate
uint16_t init_ss; // Initial SS value (relative to the start of the executable)
uint16_t init_sp; // Initial SP value
uint16_t checksum; // Checksum of the executable
uint16_t init_ip; // Initial IP value
uint16_t init_cs; // Initial CS value (relative to the start of the executable)
uint16_t reloc_table_offset; // Relative offset of the relocation table
uint16_t overlay_number; // Overlay number
uint16_t reserved[4]; // Reserved fields
uint16_t oem_id; // OEM identifier (used by OEM-specific programs)
uint16_t oem_info; // OEM information
uint16_t reserved2[10]; // Reserved fields
uint32_t pe_header_offset; // Offset of the PE header from the start of the executable
} DOSHeader;

这里我们需要关注的地方有两个。

1. e_magic即signature:位于起始位置,大小为word,作为标识符,值为5A4Dh,ASCII表示为MZ。

2. e_lfanew:偏移量为3ch,大小为double word,用于表示PE文件头的偏移量。

PE文件首部

typedef struct _IMAGE_NT_HEADERS
{
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER32 OptionalHeader;
}
typedef struct _IMAGE_FILE_HEADER {
WORD Machine; // 目标机器类型
WORD NumberOfSections; // 节表中的节的数量
DWORD TimeDateStamp; // 文件创建时间戳
DWORD PointerToSymbolTable; // COFF符号表的文件偏移
DWORD NumberOfSymbols; // COFF符号表中符号的数量
WORD SizeOfOptionalHeader; // 可选头的大小
WORD Characteristics; // 文件属性标志
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

FILE_HEADERS当中需要关注的是NumnerOfSections表示节的数目,即区块表的数目。

这其中我们重点关注的是OptionalHeader

typedef struct _IMAGE_OPTIONAL_HEADER {
// 标识PE文件的格式,可以是32位或64位
WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
DWORD BaseOfData; // 仅在32位PE文件中存在
// ImageBase是PE文件在内存中的基地址
ULONGLONG ImageBase;
DWORD SectionAlignment;
DWORD FileAlignment;
WORD MajorOperatingSystemVersion;
WORD MinorOperatingSystemVersion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsystemVersion;
WORD MinorSubsystemVersion;
DWORD Win32VersionValue;
DWORD SizeOfImage;
DWORD SizeOfHeaders;
DWORD CheckSum;
WORD Subsystem;
WORD DllCharacteristics;
ULONGLONG SizeOfStackReserve;
ULONGLONG SizeOfStackCommit;
ULONGLONG SizeOfHeapReserve;
ULONGLONG SizeOfHeapCommit;
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
// 数据目录表的数组,包含了PE文件中各个数据目录的位置和大小
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER, *PIMAGE_OPTIONAL_HEADER;

其中,IMAGE_DATA_DIRECTORY是另一个结构体,用于描述PE文件的数据目录。IMAGE_NUMBEROF_DIRECTORY_ENTRIES是一个常量,表示数据目录表的数量。

typedef struct _IMAGE_DATA_DIRECTORY {
DWORD VirtualAddress; // 数据目录在内存中的虚拟地址
DWORD Size; // 数据目录的大小(字节)
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

很多我们需要去分析的参数都可以在这个这个里面去找相应的数据,同时数据目录表当中包含了导入表,导出表和资源表等内容,这是我们需要关注的重点。

区块表

每一个区块都有一个对应的区块表,区块的数目由FILE_HEADERS当中的NumberOfSections决定。

typedef struct _IMAGE_SECTION_HEADER {
BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; // 区块名称
union {
DWORD PhysicalAddress; // 仅在OBJ文件中有效
DWORD VirtualSize; // 区块在内存中的虚拟大小
} Misc;
DWORD VirtualAddress; // 区块在内存中的虚拟地址
DWORD SizeOfRawData; // 区块在文件中的大小
DWORD PointerToRawData; // 区块在文件中的偏移地址
DWORD PointerToRelocations; // 重定位表的文件偏移地址
DWORD PointerToLinenumbers; // 行号表的文件偏移地址
WORD NumberOfRelocations; // 重定位表中的项数
WORD NumberOfLinenumbers; // 行号表中的项数
DWORD Characteristics; // 区块的属性
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

区块

区块的对齐值:

FileAlignment表示磁盘区块的对齐值

SectionAlignment表示内存当中区块的对齐值

文件偏移地址和虚拟偏移地址:文件偏移地址是在磁盘当中,虚拟偏移地址是在内存当中,由对齐值影响其大小。

导入表

导入表和导出表本质其实是一样的,但是导入表相对而言更加复杂,而且导出表只在.dll文件当中存在,所以在这里我们对导入表进行分析。

1. IMAGE_OPTIONAL_HEADERS当中存储导入表的RVA和SIZE

2. 在对应的RVA即IMPORT Directory Table处是每一个模块对应的一个结构体。

分别为Import Name Table RVA,Name RVA,Import Address Table RVA,Forwarder Chain和Time Date Stamp。

Name RVA可以确定依赖的模块名称



IAT和INT在载入之前都是指向函数名称但是在载入内存后,IAT将指向对应函数的地址。



PE文件结构1的更多相关文章

  1. 再探.NET的PE文件结构(安全篇)

    一.开篇 首先写在前面,这篇文章源于个人的研究和探索,由于.NET有自己的反射机制,可以清楚的将源码反射出来,这样你的软件就很容易被破解,当然这篇文章不会说怎么样保护你的软件不被破解,相反是借用一个软 ...

  2. PE文件结构详解(六)重定位

    前面两篇 PE文件结构详解(四)PE导入表 和 PE文件结构详解(五)延迟导入表 介绍了PE文件中比较常用的两种导入方式,不知道大家有没有注意到,在调用导入函数时系统生成的代码是像下面这样的: 在这里 ...

  3. PE文件结构详解(五)延迟导入表

    PE文件结构详解(四)PE导入表讲 了一般的PE导入表,这次我们来看一下另外一种导入表:延迟导入(Delay Import).看名字就知道,这种导入机制导入其他DLL的时机比较“迟”,为什么要迟呢?因 ...

  4. PE文件结构详解(四)PE导入表

    PE文件结构详解(二)可执行文件头的最后展示了一个数组,PE文件结构详解(三)PE导出表中解释了其中第一项的格式,本篇文章来揭示这个数组中的第二项:IMAGE_DIRECTORY_ENTRY_IMPO ...

  5. PE文件结构详解(三)PE导出表

    上篇文章 PE文件结构详解(二)可执行文件头 的结尾出现了一个大数组,这个数组中的每一项都是一个特定的结构,通过函数获取数组中的项可以用RtlImageDirectoryEntryToData函数,D ...

  6. PE文件结构详解(二)可执行文件头

    在PE文件结构详解(一)基本概念里,解释了一些PE文件的一些基本概念,从这篇开始,将详细讲解PE文件中的重要结构. 了解一个文件的格式,最应该首先了解的就是这个文件的文件头的含义,因为几乎所有的文件格 ...

  7. PE文件结构详解(一)基本概念

    PE(Portable Execute) 文件是Windows下可执行文件的总称,常见的有DLL,EXE,OCX,SYS等,事实上,一个文件是否是PE文件与其扩展名无关,PE文件可以是任 何扩展名.那 ...

  8. PE文件结构(四) 输出表

    PE文件结构(四) 參考 书:<加密与解密> 视频:小甲鱼 解密系列 视频 输出表 一般来说输出表存在于dll中.输出表提供了 文件里函数的名字跟这些函数的地址, PE装载器通过输出表来改 ...

  9. PE文件结构(五岁以下儿童)基地搬迁

    PE文件结构(五岁以下儿童) 參考 书:<加密与解密> 视频:小甲鱼 解密系列 视频 基址重定位 链接器生成一个PE文件时,它会如果程序被装入时使用的默认ImageBase基地址(VC默认 ...

  10. COFF/PE文件结构

    COFF/PE文件结构 原创 C++应用程序在Windows下的编译.链接(二)COFF/PE文件结构 2.1概述 在windows操作系统下,可执行文件的存储格式是PE格式:在Linux操作系统下, ...

随机推荐

  1. 2021-07-12:缺失的第一个正数。给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。比如[3,4,5

    2021-07-12:缺失的第一个正数.给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数.请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案.比如[3,4,5 ...

  2. 2021-06-30:给定长度为m的字符串aim,以及一个长度为n的字符串str ,问能否在str中找到一个长度为m的连续子串, 使得这个子串刚好由aim的m个字符组成,顺序无所谓, 返回任意满足条件

    2021-06-30:给定长度为m的字符串aim,以及一个长度为n的字符串str ,问能否在str中找到一个长度为m的连续子串, 使得这个子串刚好由aim的m个字符组成,顺序无所谓, 返回任意满足条件 ...

  3. blender 3D 建模仿真摄像头视角

    前言 摄像头的视角模拟,可以在产品概念设计阶段提供比较直观的视觉效果,通过将模型与实际环境进行校准,基本上可以通过模型来确定摄像头需要FOV,焦距,景深和安装位置及角度等参数. 由于工作需要,第一次自 ...

  4. Java 网络编程 —— 实现非阻塞式的服务器

    创建阻塞的服务器 当 ServerSocketChannel 与 SockelChannel 采用默认的阻塞模式时,为了同时处理多个客户的连接,必须使用多线程 public class EchoSer ...

  5. 2014年蓝桥杯C/C++大学B组省赛真题(地宫寻宝)

    题目描述: X 国王有一个地宫宝库.是 n x m 个格子的矩阵.每个格子放一件宝贝.每个宝贝贴着价值标签. 地宫的入口在左上角,出口在右下角. 小明被带到地宫的入口,国王要求他只能向右或向下行走. ...

  6. TypeError: Cannot read property ‘make‘ of undefined

    这搞个html-webpack-plugin插件进来运行就一大篇报错尴尬 看了一圈又是版本兼容的问题,做下修改.... OK 运行成功

  7. nginx: [emerg] https protocol requires SSL support in /usr/local/nginx/conf/nginx.conf:50

    最近在nginx中配置一个443端口 一.安装nginx 首先得先安装个nginx 1.安装依赖包 # 一键安装上面四个依赖 [root@dex ~]# yum -y install gcc zlib ...

  8. R 绘制 GWAS 研究的 Manhattan 图

    曼哈顿图本质上是一个散点图,用于显示大量非零大范围波动数值,最早应用于全基因组关联分析(GWAS)研究展示高度相关位点.它得名源于样式与曼哈顿天际线相似(如下图). 近几年,在宏基因组领域,尤其是差异 ...

  9. tomghost

    思路: 先使用端口扫描,会发现22,8009,8080 8009的考察点:tomcat ajp协程属性设置导致的文件读取和文件执行. https://github.com/00theway/Ghost ...

  10. STL-array(ACM)

    1.C++ 11 出现的, C++98没有 2.可以作为数组元素 pair<array<int, 3>, int>pair<int a[3], int > 不能这样 ...