从DOS头到节区头是PE头部分,其下的节区合称PE体。文件中使用偏移(offset),内存中使用VA(Virtual Address,虚拟地址)来表示位置。文件加载到内存时,情况就会发生变化(节区的大小、位置等)。文件的内容一般可分为代码(.text)、数据(.data)、资源(,rsrc)节,分别保存。

VA指的是进程虚拟内存的绝对地址,RVA(Relative Virtuall Address,相对虚拟地址)指从某个基准位置(ImageBase)开始的相对地址。

VA与RVA满足:RVA+ImageBase=VA         PE头内部信息大多以RVA形式存在。

DOS头

  IMAGE_DOS_HEADER结构体大小为64字节。

  其中有2个重要成员:e_magic与e_lfanew。(分别在结构体的开头和结尾)

  e_magic: DOS签名(signature, 4D5A=>ASCII值“MZ”)

  e_lfanew: 指示NT头的偏移(根据不同文件拥有可变值)。

NT头

  IMAGE_NT_HEADERS结构体由3个成员组成,第一个成员为签名(Signature)结构体,其值为50450000h(“PE”00)。另外两个成员分别为文件头(File Header)与可选头(Optional Header)结构体。IMAGE_NT_HEADERS结构体的大小为F8。

 文件头

  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;

  IMAGE_FILE_HEADER结构体有4种重要成员。

  1.Machine,每个cpu都有唯一的Machine码。

  2.NumberOfSections  用来指出文件中存在的节区数量,要与实际节区数相同。

  3.SizeOfOptionalHeader  用来指出IMAGE_OPTIONAL_HEADER32结构体的长度。(32位和64位是不同的)

  4.Characteristics     用于标识文件的属性,文件是否是可运行的形态、是否为DLL文件等信息,以bit OR形式组合起来。 0x0002代表可执行文件,0x2000代表DLL文件。

 可选头

  typedef struct _IMAGE_OPTIONAL_HEADER {

  WORD Magic; 10B 32位PE 20B 64位PE 107 ROM映像
  BYTE MajorLinkerVersion; 链接器版本号
  BYTE MinorLinkerVersion; 链接器副版本号
  DWORD SizeOfCode; 所有代码节的总和 该大小是基于文件对齐后的大小
  DWORD SizeOfInitializedData; 所有含已初始化数据的节的总大小
  DWORD SizeOfUninitializedData; 所有含未初始化数据的节的大小
  DWORD AddressOfEntryPoint; 程序执行入口RVA
  DWORD BaseOfCode; 代码节的起始RVA
  DWORD BaseOfData; 数据节的起始RVA
  DWORD ImageBase; 程序的优先装载地址
  DWORD SectionAlignment; 内存中节的对齐粒度
  DWORD FileAlignment; 文件中节的对齐粒度
  WORD MajorOperatingSystemVersion; 操作系统主版本号
  WORD MinorOperatingSystemVersion; 操作系统副版本号
  WORD MajorImageVersion; PE文件映像的版本号
  WORD MinorImageVersion;
  WORD MajorSubsystemVersion; 子系统的版本号
  WORD MinorSubsystemVersion;
  DWORD Win32VersionValue; 未用 必须设置0
  DWORD SizeOfImage; 内存中整个PE文件的映像尺寸
  DWORD SizeOfHeaders; 所有节表按照文件对齐粒度后的大小
  DWORD CheckSum; 校验和
  WORD Subsystem; 指定使用界面的子系统
  WORD DllCharacteristics; DALL文件属性
  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;
  重要成员

  1.Magic  32位时,值为10B,64位时,值为20B。

  2.AddressOfEntryPoint  持有EP的RVA值,指出程序最先执行的代码起始地址。

  3.ImageBase  进程虚拟内存的范围是0~FFFFFFFF(32位)。ImageBase指出文件的优先装入地址。EXE、DLL文件被装载到用户内存的0~7FFFFFFF中,SYS加载到80000000~FFFFFFFF。一般而言,EXE的ImageBase的值为00400000,DLL文件的ImageBase的值为10000000.文件载入后的地址为ImageBase+AddressOfEntryPoint。

  4.SectionAlignment,FileAlignment   FileAlignment指定了节区在磁盘文件中的最小单位,SectionAlignment指定了节区在内存中的最小单位。磁盘文件或内存的节区大小必定为他们的值的整数倍。

  5.SizeOfImage  指定了PE Image在虚拟内存中所占空间的大小。一般而言,文件的大小与加载到内存中的大小是不同的。

  6.SizeOfHeader  指出整个PE头的大小。该值必须是FileAlignment的整数倍。第一节区所在位置与SizeHeaders距文件开始偏移的量相同。

  7.Subsystem  区分系统驱动文件和普通的可执行文件。

  8.NumberOfRvaAndSizes  用来指定DataDirectory数组的个数。

  9.DataDirectory  是由IMAGE_DATA_DIRECTORY结构体组成的数组。 

    #define IMAGE_DIRECTORY_ENTRY_EXPORT 0 导出表
    #define IMAGE_DIRECTORY_ENTRY_IMPORT 1 导入表
    #define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 资源目录
    #define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 异常目录
    #define IMAGE_DIRECTORY_ENTRY_SECURITY 4 安全目录
    #define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 重定位基本表
    #define IMAGE_DIRECTORY_ENTRY_DEBUG 6 调试目录
    #define IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7 描术字串
    #define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 机器值
    #define IMAGE_DIRECTORY_ENTRY_TLS 9 TLS目录
    #define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 载入配值目录
    #define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 绑定输入表
    #define IMAGE_DIRECTORY_ENTRY_IAT 12 导入地址表
    #define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 延迟载入描述
    #define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14 COM信息

节区头

  

  VirtualSize  内存中节区所占大小

  VirtualAddress  内存中节区起始地址(RVA)  不带有任何值,由SectionAlignment确定

  SizeOfRawData  磁盘文件中节区所占大小

  PointerToRawData  磁盘文件中节区起始位置  不带有任何值,由FileAlignment确定

  Characteristics  节区属性(bit OR)

RVA to RAW

  RAW = RVA - VirtualAddress + PointerToRawData

IAT(Import Address Table,导入地址表)

  IMAGE_IMPORT_DESCRIPTOR

    记录着PE文件要导入哪些库文件。

  typedef struct _IMAGE_IMPORT_DESCRIPTOR {

  union {
  DWORD Characteristics;
  DWORD OriginalFirstThunk; //INT(Import Name Table) address (RVA)
  };
  DWORD TimeDateStamp;
  DWORD ForwarderChain;
  DWORD Name; //library name string address (RVA)
  DWORD FirstThunk; //IAT(Import Address Table) address (RVA)
  } IMAGE_IMPORT_DESCRIPTOR;

  typedef struct _IMAGE_IMPORT_BY_NAME {
  WORD Hint; //ordinal
  BYTE Name[1]; //function name string
  } IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;

    重要成员:

      OriginalFirstThunk  INT的地址(RVA)

      Name  库名称字符串的地址(RVA)

      FirstThunk  IAT的地址(RVA)

    导入函数输入至IAT的顺序:

      1.读取IID的Name成员,获取库名称字符串("kernel32.dll")

      2.装载相应库——————> LoadLibrary("kernel32.dll")

      3.读取IID的OriginalFirstThunk成员,获取INT地址

      4.逐一读取INT中数组的值,获取相应IMAGE_IMPORT_BY_NAME地址(RVA)

      5.使用IMAGE_IMPORT_BY_NAME的Hint(ordinal) 或Name项,获取相应函数的起始地址。 GetProcAddress("GetCurrentThreadId")

      6.读取IID的FirstThunk(IAT)成员,获得IAT地址

      7.将上面获得的函数地址输入相应IAT数组值。

      8.重复以上补助4~7,直到INT结束(遇到NULL时)

EAT

  IMAGE_EXPORT_DIRECTORY

    typedef struct _IMAGE_EXPORT_DIRECTORY {
    DWORD Characteristics; // 未使用,总为0 DWORD TimeDateStamp; // 文件创建时间戳
    WORD MajorVersion; // 未使用,总为0 WORD MinorVersion; // 未使用,总为0
    DWORD Name; // 指向一个代表此 DLL名字的 ASCII字符串的 RVA
    DWORD Base; // 函数的起始序号
    DWORD NumberOfFunctions; // 导出函数的总数 DWORD NumberOfNames; // 以名称方式导出的函数的总数 DWORD AddressOfFunctions; // 指向输出函数地址的RVA
    DWORD AddressOfNames; // 指向输出函数名字的RVA
    DWORD AddressOfNameOrdinals; // 指向输出函数序号的RVA} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;

    重要成员:

      NumberOfFunctions  实际Export函数的个数

      NumberOfNames    Export中具名的函数个数

      AddressOfFunctions  Export函数地址数组

      AddressOfName    函数名称地址数组

      AddressOfNameOrdinals  Ordinal地址数组

    GetProcAddress()操作原理:

      (1)利用AddressOfNames成员转到“函数名称数组”。

      (2)“函数名称数组”中存储着字符串地址。通过比较字符串,查找指定的函数名称(此时数组的索引称为name_index)。

      (3)利用AddressOfNameOrdinals成员,转到ordinal数组。

      (4)在ordinal数组中通过name_index查找相应ordinal值。

      (5)利用AddressOfFunctions成员转到“函数地址数字”(EAT)

      (6)在“函数地址数组”中将刚刚求得的ordinal用作数组索引,获得指定函数的起始地址。

逆向学习-PE文件格式的更多相关文章

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

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

  2. PE文件格式详解,第二讲,NT头文件格式,以及文件头格式

    PE文件格式详解,第二讲,NT头文件格式,以及文件头格式 作者:IBinary出处:http://www.cnblogs.com/iBinary/版权所有,欢迎保留原文链接进行转载:) PS:本篇博客 ...

  3. Reverse Core 第二部分 - 13章 - PE文件格式

    @date: 2016/11/24 @author: dlive ​ PE (portable executable) ,它是微软在Unix平台的COFF(Common Object File For ...

  4. PE文件格式偏移参考

    在进行PE文件格式病毒分析的时候,经常要使用到PE文件格式的解析,尤其是对LoadPE形式的病毒的分析,经常要查看PE文件格式的偏移,特地从博客<PE文件格式的偏移参考>中转载收录一份,之 ...

  5. 深入理解 Win32 PE 文件格式

    深入理解 Win32 PE 文件格式 Matt Pietrek 这篇文章假定你熟悉C++和Win32. 概述 理解可移植可执行文件格式(PE)可以更好地了解操作系统.如果你知道DLL和EXE中都有些什 ...

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

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

  7. PE文件格式分析

    PE文件格式分析 PE 的意思是 Portable Executable(可移植的执行体).它是 Win32环境自身所带的执行文件格式.它的一些特性继承自Unix的Coff(common object ...

  8. 深入理解 Win32 PE 文件格式 Matt Pietrek(慢慢体会)

    这篇文章假定你熟悉C++和Win32. 概述 理解可移植可执行文件格式(PE)可以更好地了解操作系统.如果你知道DLL和EXE中都有些什么东西,那么你就是一个知识渊博的程序员.这一系列文章的第一部分, ...

  9. PE文件格式详解(六)

    0x00 前言 前面两篇讲到了输出表的内容以及涉及如何在hexWorkShop中找到输出表及输入DLL,感觉有几个地方还是没有理解好,比如由数据目录表DataDirectory[16]找到输出表表后以 ...

随机推荐

  1. 基于python语言的tensorflow的‘端到端’的字符型验证码识别源码整理(github源码分享)

    基于python语言的tensorflow的‘端到端’的字符型验证码识别 1   Abstract 验证码(CAPTCHA)的诞生本身是为了自动区分 自然人 和 机器人 的一套公开方法, 但是近几年的 ...

  2. Javascript高级编程学习笔记(94)—— Canvas(11) 合成

    合成 除了之前介绍的属性之外,还有两个属性会应用到整个2d上下文中; globalAlpha 用于指定所有绘制的透明度 globalComposition 用于表示后绘制的图形怎样与先绘制的图形进行结 ...

  3. Java数据结构和算法 - 高级排序

    希尔排序 Q: 什么是希尔排序? A: 希尔排序因计算机科学家Donald L.Shell而得名,他在1959年发现了希尔排序算法. A: 希尔排序基于插入排序,但是增加了一个新的特性,大大地提高了插 ...

  4. docker+es+kibana和springboot中使用es

    本次和大家分享的主要是docker搭建es和springboot操作es的内容,也便于工作中或将来使用方便,因此从搭建es环境开始到代码插入信息到es中:主要节点如下: elasticsearch启动 ...

  5. 从壹开始前后端分离【 .NET Core2.0 +Vue2.0 】框架之九 || 依赖注入IoC学习 + AOP界面编程初探

    更新 1.如果看不懂本文,或者比较困难,先别着急问问题,我单写了一个关于依赖注入的小Demo,可以下载看看,多思考思考注入的原理: https://github.com/anjoy8/BlogArti ...

  6. 从ASP.NET Core2.2到3.0你可能会遇到这些问题

    趁着假期的时间所以想重新学习下微软的官方文档来巩固下基础知识.我们都知道微软目前已经发布了.NET Core3.0的第三个预览版,同时我家里的电脑也安装了vs2019.So,就用vs2019+.NET ...

  7. [区块链] 带你进入Bitcoin开发 - 环境搭建

    学习完区块链枯燥乏味的.高深的.必备的基础知识后,终于可以走上开发之路了!真是迫不及待啦!之后博客更新主要放在区块链的开发上,相信这才是大多数同学更加感兴趣的地方!学习过程从最经典的区块链鼻祖-比特币 ...

  8. 【Android Studio安装部署系列】二十、Android studio如何将so文件添加到svn中

    版权声明:本文为HaiyuKing原创文章,转载请注明出处! 概述 在使用android studio的过程中,常常会遇到一个头疼的问题,添加的so,居然无法被svn添加. 选项都是灰的: 那这种问题 ...

  9. 前端笔记之移动端&响应式(中)视口&百分比布局&弹性盒模型&rem&fillpage

    一.viewport视口 1.1什么是屏幕尺寸.屏幕分辨率.屏幕像素密度? 屏幕尺寸:指屏幕的对角线的长度,单位是英寸,常见的屏幕尺寸有3.5.3.7.4.2.4.7.5.0.5.5.6.0等. 屏幕 ...

  10. 基于.NetCore的Redis5.0.3(最新版)快速入门、源码解析、集群搭建与SDK使用【原创】

    1.[基础]redis能带给我们什么福利 Redis(Remote Dictionary Server)官网:https://redis.io/ Redis命令:https://redis.io/co ...