当前位置: 首页 > news >正文

PE 文件格式 详解 二

内容引用自:看雪《逆向工程原理》。如有错误,欢迎留言。

1、 区块表(节表)

  区块表紧跟在PE头后面,所有区块的属性都被定义在区块表中。区块表中的数据仅仅是因为属性相同被放到一起,对程序的各种方法、数据的追溯还是要用到DataDirectory。

  区块表是由一组IMAGE_SETION_HEADER结构组成,每个结构描述一个区块,各结构的排列顺序与其所描述的区块在文件中的排列顺序是一致的。

  区块表最后以一个空的IMAGE_SETION_HEADER结构作为结尾。IMAGE_SETION_HEADER (长度为28h)定义如下:

  #define IMAGE_SIZEOF_SHORT_NAME  8

  typedef strutct _IMAGE_SETION_HEADER {

    BYTE  Name[IMAGE_SIZEOF_SHORT_NAME];      //注意   此处是 8 个字节

    union {

      DWORD PhysicalAddress;

      DWORD VirtualSize;      // 区块的大小

    }

    DWORD VirtualAddress;         // 节区的 RVA 地址
    DWORD SizeOfRawData;            // 在文件中对齐后的尺寸
    DWORD PointerToRawData;        // 在文件中的偏移量
    DWORD PointerToRelocations;     // 在OBJ文件中使用,重定位的偏移,无用
    DWORD PointerToLinenumbers;   // 行号表的偏移(供调试使用地),无用
    WORD NumberOfRelocations;      // 在OBJ文件中使用,重定位项数目,无用
    WORD NumberOfLinenumbers;    // 行号表中行号的数目,无用
    DWORD Characteristics;       // 节属性如可读,可写,可执行等  

  } IMAGE_SETION_HEADER

  需要注意的点:

  Name:区块名   实际上没有任何意义,只要不重复可以任意命名,设置为特定的名字仅仅是正规编程方便查看。

  VirtualSize:对应区块的实际大小,未进行对齐处理前的大小

  VirtualAddress:对应区块装入内存中的RVA地址

  SizeOfRawData:对应区块在磁盘中的大小,在可执行文件中,该值是已经被FileAligment处理过的长度。

  PointerToRawData:对应区块在磁盘中的偏移,从文件头开始算起

  Characteristics:按位指出对应区块的属性 (bit OR),常见值如下:

  

  此处涉及到RVA to RAW,即 相对虚拟地址 到 文件物理偏移地址的转换。比如在DataDirectory中存放的为RVA地址,就需要转换为物理偏移。

  其转换方法为:RVA  - VisualAddress + PointerToRawData

 2、 导入表

  导入表是要提供程序执行时需要调用的导入函数的所属DLL、函数名、内存地址等。该表在区块中,由DataDirectory第二个数组项指向。

  导入表是由一组IMAGE_IMPORT_DESCRIPTOR结构组成,结构的数量取决于程序要使用的DLL文件的数量,每一个结构对应一个DLL文件,以一个内容全为 0 的IMAGE_IMPORT_DESCRIPTOR作为结束。

  IMAGE_IMPORT_DESCRIPTOR定义:

  typedef  struct _IMAGE_IMPORT_DESCRIPTOR {

    union {

      DWORD  Characteristics;          

      DWORD  OriginalFirstThunk;    #一般定义该值,指向 INT (Import Name Table)  RVA

    };

    DWORD  TimeDateStamp;

    DWORD  ForwarderChain;

    DWORD  Name;            #指向 DLL 名称 的地址   RVA

    DWORD  FirstThunk;          #指向 IAT (Import Address Table)    RVA

  } IMAGE_IMPORT_DESCRIPTOR;

  需要注意的点:

  Name :  不会直接给出DLL的名字   此处是一个RVA 地址。   该地址指向了 DLL 名称。

  OriginalFirstThunk 和 FirstThunk :  虽然一个指向 INT  一个指向 IAT ,但实际这两个表均是由一系列 IMAGE_THUNK_DATA结构组成的数组(静态存储时一般指向相同的内容)。数组最后以一个全为0 的结构作为结束。OriginalFirstThunk指向的INT不会变化,当PE文件加载进内存后,FirstThunk指向的IAT会存储导入函数真实的内存地址。

  IMAGE_THUNK_DATA 定义如下:

  typedef  _IMAGE_THUNK_DATA {

    union {

      DWORD  ForwarderString;      #RVA  指向forwarder string

      DWORD  Function;        #被导入函数的入口地址

      DWORD  Ordinal;          #该函数的序数

      DWORD  AddressOfData;      #指向 IMAGE_IMPORT_BY_NAME 结构体

    };

  } IMAGE_THUNK_DATA32

  对于可执行文件,IMAGE_THUNK_DATA 中存储的要么是 Ordinal(最高位为1,其余31位为序号)  要么是AddressOfData。在DLL中对每个函数都进行了编号(导出表中的导出序号),访问函数即可以通过名称访问,也可以通过编号访问。

  当IMAGE_THUNK_DATA存放的为 AddressOfData 时,该地址指向一个 IMAGE_IMPORT_BY_NAME 结构体

  IMAGE_IMPORT_BY_NAME 定义如下:

  typedef struct _IMAGE_IMPORT_BY_NAME {

    WORD  Hint;        #Ordinal    函数的序号

    BYTE  Name[1];        #此处为函数名字,实际为变长数组,以00结尾,即字符串结束符

  } IMAGE_IMPORT_BY_NAME

 

  常见寻找导入函数的方法(静态存储):

  根据 DataDirectory 中RVA 找到 导入表的文件偏移地址, 根据 OriginalFirstThunk 找到 INT (Import Name Table), 根据 AddressOfData 找到 IMAGE_IMPORT_BY_NAME 即可找到函数名。

 3、导出表

  为了让其他程序调用本程序提供的导出函数,需要定义导出表。导出表定义如下:

  typedef  struct _IMAGE_EXPORT_DIRECTORY {

    DWORD  Characteristics;        # 通常为0

    DWORD  TimeDateStamp;        # 时间戳

    WORD  MajorVersion;          # 通常为0

    WORD  MinorVersion;          # 通常为0

    DWORD  Name;            # 指向真实的DLL名字的RVA,PE装载器用的是这个名字

    DWORD  Base;            # 序号基数,等于所有函数导出序号最小的值,该值也是导出地址表中第一个地址对应的导出函数的导出序号

    DWORD  NumberOfFunctions;      # 导出函数的总数,不一定是真实导出函数的个数。等于导出序号的最小值依次递增一到最大值的个数。导出函数的序号可以自定义,所以可能是不连续的不规则的。

    DWORD  NumberOfNames;      # 通过函数名导出的函数的总数。

    DWORD  AddressOfFunctions;      # 指向导出函数地址表的RVA。 导出函数地址表(可以当成数组)存放的是所有导出函数所在地址的RVA。  导出函数地址表个数和NumberOfFunctions 相等。 该地址表对应函数的导出序号从 Base 开始,依次递增一。

    DWORD  AddressOfNames;      # 指向导出函数名称地址表的RVA。 导出函数名称地址表存放的是指向按名称导出函数的函数名的RVA。 导出函数名称地址表的大小和 NumberOfNames 相等。

    DWORD  AddressOfNameOrdinals;     # 指向导出序号表的RVA。 导出序号表存放的是以函数名称导出的函数的导出序号,每个成员都是WORD类型,其表的大小和 NumberOfNames 相等。

  } IMAGE_EXPORT_DIRECTORY

  查找函数的入口地址

  依照导出序号(ordinal)查找:  

    1、根据 序号 减去 IMAGE_EXPORT_DIRECTORY中的 Base,得到索引(比如index),如果 index 大于NumberOfFunctions 则放弃 出错

    2、找到 IMAGE_EXPORT_DIRECTORY中的 AddressOfFunctions,在导出地址表中从 0 开始找到 index 处的地址,即为函数的入口偏移地址

  依照 导出函数名(比如名字为MyFunction) 查找:

    1、以 NumberOfNames 为次数开始对 AddressOfNames 遍历,可以从 0 ,可以从 1,只要前后遵循一致即可。 找到指向对应 MyFunction 的函数的地址所在的相对函数名称地址开始的偏移(比如index)

    2、根据 index 找到 AddressOfNameOrdinals 在该偏移处的值(比如为index_Ordinal),该值 + Base 其实就是 导出序号,不过找入口偏移地址不需要 + Base。

    3、找到AddressOfFunctions ,在导出地址表中从0 开始找到 index_Ordinal 处的地址,即为函数的入口偏移地址。

 

转载于:https://www.cnblogs.com/lwhat/p/7775184.html

相关文章:

  • Linux Mint 教程
  • Jenkins-权限控制
  • 阿里 Linux服务器外网无法连接MySQL解决方法
  • C语言博客作业--嵌套循环
  • java获取当前上一周、上一月、上一年的时间
  • mysql-proxy实现读写分离
  • Linux基本命令—权限管理、文件搜索、帮助、压缩解压、网络通信
  • 中英文金额大写转换器
  • 计算机基础作业
  • 应急响应小总结
  • 课后作业-阅读任务-阅读提问-3
  • HttpClient实现POST参数提交
  • 代码式书写404页面
  • idHttp 中GET POST应用
  • 超级小的web手势库AlloyFinger
  • 【编码】-360实习笔试编程题(二)-2016.03.29
  • 【跃迁之路】【733天】程序员高效学习方法论探索系列(实验阶段490-2019.2.23)...
  • Docker 笔记(1):介绍、镜像、容器及其基本操作
  • iOS筛选菜单、分段选择器、导航栏、悬浮窗、转场动画、启动视频等源码
  • Odoo domain写法及运用
  • Vue实战(四)登录/注册页的实现
  • 从PHP迁移至Golang - 基础篇
  • 二维平面内的碰撞检测【一】
  • 类orAPI - 收藏集 - 掘金
  • 如何学习JavaEE,项目又该如何做?
  • 入口文件开始,分析Vue源码实现
  • 使用putty远程连接linux
  • 手机app有了短信验证码还有没必要有图片验证码?
  • 在GitHub多个账号上使用不同的SSH的配置方法
  • 不要一棍子打翻所有黑盒模型,其实可以让它们发挥作用 ...
  • ​520就是要宠粉,你的心头书我买单
  • ​LeetCode解法汇总2696. 删除子串后的字符串最小长度
  • # 20155222 2016-2017-2 《Java程序设计》第5周学习总结
  • #Ubuntu(修改root信息)
  • $var=htmlencode(“‘);alert(‘2“); 的个人理解
  • (2.2w字)前端单元测试之Jest详解篇
  • (ZT)薛涌:谈贫说富
  • (zt)最盛行的警世狂言(爆笑)
  • (八)Docker网络跨主机通讯vxlan和vlan
  • (二)fiber的基本认识
  • (附源码)spring boot基于小程序酒店疫情系统 毕业设计 091931
  • (附源码)SSM环卫人员管理平台 计算机毕设36412
  • (含react-draggable库以及相关BUG如何解决)固定在左上方某盒子内(如按钮)添加可拖动功能,使用react hook语法实现
  • (免费领源码)Java#Springboot#mysql农产品销售管理系统47627-计算机毕业设计项目选题推荐
  • (亲测成功)在centos7.5上安装kvm,通过VNC远程连接并创建多台ubuntu虚拟机(ubuntu server版本)...
  • (算法)Game
  • (转) SpringBoot:使用spring-boot-devtools进行热部署以及不生效的问题解决
  • (转载)在C#用WM_COPYDATA消息来实现两个进程之间传递数据
  • **Java有哪些悲观锁的实现_乐观锁、悲观锁、Redis分布式锁和Zookeeper分布式锁的实现以及流程原理...
  • .net core webapi 大文件上传到wwwroot文件夹
  • .Net IOC框架入门之一 Unity
  • .net安装_还在用第三方安装.NET?Win10自带.NET3.5安装
  • .Net下C#针对Excel开发控件汇总(ClosedXML,EPPlus,NPOI)
  • .NET与java的MVC模式(2):struts2核心工作流程与原理
  • .Net组件程序设计之线程、并发管理(一)