① 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. Linux编程 9 (shell类型,shell父子关系,子shell用法)

    一. shell类型 1.1  交互式 bin/ shell程序 当用户登录到某个虚拟控制台终端或是在GUI中启动终端仿真器时,默认的shell程序就会开始运行.系统启动什么样的shell程序取决于你 ...

  2. 【Android基础】Fragment 详解之Fragment生命周期

    上一篇文章简单介绍了一下Fragment,这一篇文章会详细的说一下Fragment的生命周期和创建一个用户界面. Fragment的主要功能就是创建一个View,并且有一个生命周期来管理这个View的 ...

  3. 非业务 Oracle SQL 语句备份

    1.创建一个将 Oracle 生成的 GUID 格式化为标准的 GUID 的函数 2.在 PLSQL 中测试并输出语句异常的语句块 3.在查询语句中日期的一种特殊表示方法 4.利用 ROWNUM 做分 ...

  4. Effective Java——(一)创建和销毁对象

    第一条 考虑用静态工厂方法代替构造器 什么叫静态工厂方法,就是通过在类中通过静态方法对对象初始化. 比如说 public class StaticFactory { private String na ...

  5. JS_正则表达式_使用字符串创建的正则表达式_反斜杠也需要添加转义符

    备注:   使用字符串创建的正则表达式:"\"也需要加转义符: var reg1=new RegExp("\\w+");       这和 直接使用:var r ...

  6. Thread类(线程)

    操作系统通过线程对程序的执行进行管理,当操作系统运行一个程序的时候,首先,操作系统将为这个准备运行的程序分配一个进程,以管理这个程序所需要的各种资源.在这些资源之中,会包含一个称为主线程的线程数据结构 ...

  7. VS2017 启动调试出现 无法启动程序“http://localhost:15613” 操作在当前状态中是非法的。 同时附加进程也是错误的解决方法

    第一次发表这样的博客,不会如何的排版,还有很多的不懂,大神勿喷哈! 同时是给自己做的一次记录,已方便后面可能会同样出现该问题后不用像无头苍蝇一样到处百度乱找 VS2017 启动调试出现  无法启动程序 ...

  8. [angularjs] angularjs系列笔记(一)简介

    Angularjs通过新的属性和表达式扩展了html Andularjs 可以构建一个单一页面的应用程序(SPAS SinglePageApplications) Angularjs通过指令扩展了ht ...

  9. js 点击文本框,预览选择图片

    点击文件选择框,选择图片文件,通过FileReader对象,读取图片文件中的内容,存放于result中,具体代码如下 <input type="file" onchange= ...

  10. 【Java每日一题】20170119

    20170118问题解析请点击今日问题下方的“[Java每日一题]20170119”查看(问题解析在公众号首发,公众号ID:weknow619) package Jan2017; import jav ...