PE (Portable Executable):微软参考COFF(Common Object File Format)规范,在Windows NT系统上制定的一种标准,

用于exe可执行文件、obj目标文件和dll动态链接库等文件格式。PE32+是PE的64位扩展,其并未添加额外结构,只是把原来32位的字段变成了64位。

与COFF一样,PE也是基于段(Segment,注:有时也被叫节Section)的结构,

按照不同属性将信息分段存放,常见的段有:代码段(.text)、数据段(.data)、只读数据段(.rdata)、资源表(.rsrc)、重定位表(.reloc)等。

因为PE文件在装载时被直接映射到进程的虚拟空间中运行,它是进程的虚拟空间的映像。所以PE文件很多时侯被叫做映像文件(Image File)。

RVA (Relative Virtual Address):相对于PE文件装载基地址(Base Address)的一个地址偏移。

③ windows系统使用的x86的处理器,为小端序;WORD的二进制1A实际的数值为:5402(数学写法为:1A);

DWORD的二进制B26A实际的数值为:409613746(数学写法为:6AB2)

PE文件结构:

注1:IMAGE_DOS_HEADER、IMAGE_NT_HEADER、IMAGE_SECTION_HEADER等都定义在winNT.h中
注2:IMAGE_DOS_HEADER和DOS Stub是兼容DOS应用而存在的结构,PE文件的内容从IMAGE_NT_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[]; // Reserved words
WORD e_oemid; // OEM identifier (for e_oeminfo)
WORD e_oeminfo; // OEM information; e_oemid specific
WORD e_res2[]; // Reserved words
LONG e_lfanew; // File address of new exe header
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER; ///////////////////////////////////////////////////////////////////////////
typedef struct _IMAGE_FILE_HEADER {
WORD Machine;
WORD NumberOfSections;
DWORD TimeDateStamp;
DWORD PointerToSymbolTable;
DWORD NumberOfSymbols;
WORD SizeOfOptionalHeader;
WORD Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER; typedef struct _IMAGE_DATA_DIRECTORY {
DWORD VirtualAddress;
DWORD Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY; #define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16 typedef struct _IMAGE_OPTIONAL_HEADER64 {
WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
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;
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONAL_HEADER64; typedef struct _IMAGE_OPTIONAL_HEADER {
//
// Standard fields.
// WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
DWORD BaseOfData; //
// NT additional fields.
// DWORD 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;
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; typedef struct _IMAGE_NT_HEADERS64 {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER64 OptionalHeader;
} IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64; typedef struct _IMAGE_NT_HEADERS {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32; typedef struct _IMAGE_ROM_HEADERS {
IMAGE_FILE_HEADER FileHeader;
IMAGE_ROM_OPTIONAL_HEADER OptionalHeader;
} IMAGE_ROM_HEADERS, *PIMAGE_ROM_HEADERS; #ifdef _WIN64
typedef IMAGE_NT_HEADERS64 IMAGE_NT_HEADERS;
typedef PIMAGE_NT_HEADERS64 PIMAGE_NT_HEADERS;
#else
typedef IMAGE_NT_HEADERS32 IMAGE_NT_HEADERS;
typedef PIMAGE_NT_HEADERS32 PIMAGE_NT_HEADERS;
#endif ///////////////////////////////////////////////////////////////////////////
#define IMAGE_SIZEOF_SHORT_NAME 8 typedef struct _IMAGE_SECTION_HEADER {
BYTE Name[IMAGE_SIZEOF_SHORT_NAME];
union {
DWORD PhysicalAddress;
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;

程序编译后,变量与函数存储在PE文件中的位置如下:

注:BSS段存放的是未初始化的全局变量与静态变量(含全局和局部);以上代码,vc编译器会去除BSS段,将这部分内容直接放到数据段中

主要的原因是段的最小单元为4KB,所以为了节省PE文件、内存占用及加快PE文件的映射过程,vc编译器会尽量将属性一致的段进行合并

数据目录

① 一般来说,导出表、导入表、导入地址表存放在.rdata段(只读数据段)、重定位表存放在独立的.reloc段(重定位表段)

② 在PE文件中,将段表中各段的VirtualAddress(段的RVA)及VirtualSize(段的实际大小)与所求RVA比较,就可知该RVA落在哪个段中

假设落在段A中,则所求RVA在PE文件中offset为:段A的PointerToRawData + (所求RVA - 段A的VirtualAddress)

(1)导出表

导出符号:当前模块(DLL)导出供其他模块使用的函数和变量。

注1:Base一般为1,AddressOfNames、AddressOfNameOrdinals指向的表的size一致,且里面元素一一对应

注2:AddressOfFunctions指向的表的size >= AddressOfNames指向的表的size,主要有以下两点原因导致:

① 当在def文件中指定的符号Ordinal不连续时,会导致一些空地址(即:0地址)元素

如:导出2个Orinal为1,3的符号,那么AddressOfFunctions指向的表的size为3(第2个元素为空地址),AddressOfNames指向的表的size为2

def文件符号导出如下:

DllAdd @ 1

g_nDll @ 3

② 在def中只导出Ordinal而不导出符号名称  如:DllAdd @ 1 NONAME

注3:符号在导出地址表(EAT)中的索引号为:名字序号对应表中的Ordinal - Base

导出方法:

① 在代码中使用__declspec(dllexport)关键字

② def文件

③ 链接器/export参数

(2)导入表:

导入符号:在当前模块使用,但在其他模块(DLL)中定义实现的函数和变量。

导入方法:

① 在dll的头文件中,对导出的函数和变量使用__declspec(dllimport)

② 链接dll的导入库

每个导入模块(dll)使用一个IMAGE_IMPORT_DESCRIPTOR结构体存放,并以全0内容的IMAGE_IMPORT_DESCRIPTOR作为结尾。

IAT、INT中的元素使用IMAGE_THUNK_DATA结构体存放, 并以全0内容的IMAGE_THUNK_DATA作为结尾。

IAT中IMAGE_THUNK_DATA最高位为1,则低31位为导入符号的序号值;否则IMAGE_THUNK_DATA为指向IMAGE_IMPORT_BY_NAME结构的RVA。

动态连接器使用IMAGE_IMPORT_BY_NAME的Hint值去定位符号在目标导出表中的位置,若刚好找到则命中,如果没命中就按照二分查找方法进行符号查找。

INT未绑定时,内容与IAT一样;绑定时,IMAGE_THUNK_DATA中存放的是导入符号运行时的虚拟内存地址,如果外部模块的时间戳与TimeDataStamp一致,

且在载入外部模块未发生重定基址,则直接使用该地址来访问该符号;否则,使用IAT进行符号解析与查找。

注1:在动态链接器刚完成映射还没有开始重定位和符号解析时,IAT中元素值表示相对应的导入符号的序号或者是符号名;当完成该模块链接时,元素值会被动态链接器改成该符号的真正地址。

注2:程序每次运行时,所有被依赖的dll都会被装载,然后一系列的导入导出符号依赖关系都会被重新解析。然而在大多数情况下,这些dll都会以同样的顺序被装载到同样的内存地址,所以它们的导出符号的地址都是不变的。

若将这些导出函数的地址保存到模块的导入表中,就可以省去每次启动时符号解析过程,这种DLL性能优化方式被叫做DLL绑定(DLLBinding)。

(3)导入地址表

注:导入地址表(IAT)存放着所有导入模块的地址信息(外部地址),各个模块之间用一个4字节的0隔开。模块的先后顺序不保证与导入表模块的顺序一致。

(4)重定位表

按照基址生成出的绝对地址,如果目标地址被占用或基于安全考虑,则这些地址就需要重定位。

类型(高4位) 含义
0 无意义,仅用来做对齐用
1 高16位需要修正
2 低16位需要修正
3 32位都需要修正

PE文件工具

https://down.52pojie.cn/Tools/PEtools/

 (1)PE-Explorer

(2)LordPE

(3)ExeinfoPe

PE文件基础的更多相关文章

  1. 深入学习PE文件(转)

    PE文件是Win32的原生文件格式.每一个Win32可执行文件都遵循PE文件格式.对PE文件格式的了解可以加深你对Win32系统的深入理解. 一. 基本结构. 上图便是PE文件的基本结构.(注意:DO ...

  2. PE文件解析 基础篇

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

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

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

  4. PE文件学习系列笔记四-C++实现PE文件的分析

    合肥程序员群:49313181.    合肥实名程序员群:128131462 (不愿透露姓名和信息者勿加入) Q  Q:408365330     E-Mail:egojit@qq.com 综述: 首 ...

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

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

  6. Java:IO流与文件基础

    Java:IO流与文件基础 说明: 本章内容将会持续更新,大家可以关注一下并给我提供建议,谢谢啦. 走进流 什么是流 流:从源到目的地的字节的有序序列. 在Java中,可以从其中读取一个字节序列的对象 ...

  7. 获取pe文件的文件类型

    工程文件petype.cpp通过调用pefile类中的函数获取文件类型. 文件类型的判断通过5个监测点完成. 监测点1:dos头的e_magic 监测点2:nt头的Signature 监测点3:文件头 ...

  8. 浅析MSIL中间语言——PE文件结构篇

    一.开篇 开篇我想讲一下于本文无关的话题,其实我很想美化一下自己博客园一直没时间弄,无意间找了博客园李宝亨的博客园里面有一篇分享自己主题的文章,我就将这个模板暂时用作我的blog主题,我要讲述一个关于 ...

  9. PE文件学习系列三-PE头详解

    合肥程序员群:49313181.    合肥实名程序员群:128131462 (不愿透露姓名和信息者勿加入) Q  Q:408365330     E-Mail:egojit@qq.com 最近比较忙 ...

随机推荐

  1. IdentityServer4(7)- 使用客户端认证控制API访问(客户端授权模式)

    一.前言 本文已更新到 .NET Core 2.2 本文包括后续的Demo都会放在github:https://github.com/stulzq/IdentityServer4.Samples (Q ...

  2. 从Java小白到收获BAT等offer,分享我这两年的经验和感悟

    微信公众号[程序员江湖] 作者黄小斜,斜杠青年,某985硕士,阿里 Java 研发工程师,于 2018 年秋招拿到 BAT 头条.网易.滴滴等 8 个大厂 offer,目前致力于分享这几年的学习经验. ...

  3. 人工智能(AI)库TensorFlow 踩坑日记之一

    上次写完粗浅的BP算法 介绍 本来应该继续把 卷积神经网络算法写一下的 但是最近一直在踩 TensorFlow的坑.所以就先跳过算法介绍直接来应用场景,原谅我吧. TensorFlow 介绍 TF是g ...

  4. CPU上下文切换

    CPU上下文切换包括进程上下文切换.线程上下文切换及中断上下文切换,当任务进行io或发生时间片事件及发生中断(如硬件读取完成)时,就会进入内核态,发生CPU上下文切换. 进程上下文切换,进程的上下文信 ...

  5. 以 SPI 方式获取 SD 卡容量(V2.0)

    下面是 SD 卡 V2.0 协议的 CSD 寄存器内容,来自官方手册: 单片机如何确定当前的 SD 卡遵循 V2.0 协议 CSD 寄存器为 128 个位,即 16 个字节.通过检测 CSD 寄存器的 ...

  6. [转]Ble蓝牙的使用手册

    本文转自:https://blog.csdn.net/dodan/article/details/52060446 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.cs ...

  7. Linq To Xml操作XML增删改查

    对XML文件的操作在平时项目中经常要运用到,比如用于存放一些配置相关的内容:本文将简单运用Linq TO Xml对XML进行操作,主要讲解对XML的创建.加载.增加.查询.修改以及删除:重点在于类XD ...

  8. 数据库部分(MySql)_4

    约束 约束:给表的字段名添加限制条件; 非空约束(not null):添加非空约束后,字段值不能为null: 唯一约束(unique):添加唯一约束后,字段值不能重复: 主键约束(primary ke ...

  9. JQuery官方学习资料(译):选择元素

    选择元素     JQuery最基本的概念是“选择一些元素并让它们做些什么”.JQuery支持大部分的CSS3的选择器,以及一些非标准的选择器. 通过ID选择元素 $( "#myId&quo ...

  10. async,await,Task 的一些用法

    async,await,Task 的一些用法 private void Form1_Load(object sender, EventArgs e) { Display(); } public asy ...