写在前面

  此系列是本人一个字一个字码出来的,包括代码实现和效果截图。 如有好的建议,欢迎反馈。码字不易,如果本篇文章有帮助你的,如有闲钱,可以打赏支持我的创作。如想转载,请把我的转载信息附在文章后面,并声明我的个人信息和本人博客地址即可,但必须事先通知我

你如果是从中间插过来看的,请仔细阅读 羽夏壳世界——序 ,方便学习本教程。

概述

  导入表和重定位表是一个比较复杂的结构,导入表相对复杂,当然还有更复杂的资源表,不过这里并不会介绍它。

  导入表是用来做什么的?比如你在写代码的时候,总会调用一些WinAPI,而这些函数都是通过DLL导出的。导入表的作用就是告诉操作系统加载该PE文件的时候需要找哪些DLL,使用了哪些DLL所提供的函数。

  重定位表可能稍微难理解一些,不过如果学习上篇应该就不太难了。我们在介绍ImageBase这个成员的时候,曾说它是PE文件倾向于要加载的地址,但是如果开了基址随机或者是DLL的话通常不会使用该地址,而这个成员就是用来进行重定位的,我们可以看看为什么需要重定位:

  如果执行上述代码,这个push是所谓的死地址,也就是要puts的字符串,如果加载的基址并不是我们所谓的ImageBase,而这个是作为硬编码的一部分的,如果不改变的话,这地址是错误的,如果不进行重定位,就会导致打印的字符串不对甚至报0xC0000005错误,这个就是重定位的意义,我们来看示意图:

  下面我们来介绍导入表和重定位表的结构:

导入表

  在介绍导入表之前,我们先放个示意图:

  导入表相关信息是放到IMAGE_IMPORT_DESCRIPTOR结构体中的,它的结构如下:

typedef struct _IMAGE_IMPORT_DESCRIPTOR {
union {
DWORD Characteristics; // 0 for terminating null import descriptor
DWORD OriginalFirstThunk; // RVA to original unbound IAT (PIMAGE_THUNK_DATA)
} DUMMYUNIONNAME;
DWORD TimeDateStamp; //时间戳
DWORD ForwarderChain; //不使用
DWORD Name; //指向Ascii字符串
DWORD FirstThunk; // RVA to IAT (if bound this IAT has actual addresses)
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;

  这结构体一个挨着一个,以空结构体为结尾(内容全为0的IMAGE_IMPORT_DESCRIPTOR)。用一句话概括就是一个IMAGE_IMPORT_DESCRIPTOR不定长数组,通过最后一个是空表示结束。

  OriginalFirstThunk包含指向输入名称表INTRVAINT是一个IMAGE_THUNK_DATA结构的数组,数组中的每个IMAGE_THUNK_DATA结构都指向IMAGE_IMPORT_BY_NAME结构,数组以一个内容为0的IMAGE_THUNK_DATA结构结束。

  TimeDateStamp是一个32位的时间标志,可以忽略。

  ForwarderChain是第1个被转向的API的索引,一般为0,在程序引用一个DLL中的API,而这个API又在引用其他DLLAPI时使用,但这样的情况很少出现。

  Name是DLL名字的指针。它是一个以\0结尾的ASCII字符的RVA地址,该字符串包含输入的DLL名,例如KERNEL32.DLL

  FirstThunk包含指向输入地址表IATRVAIAT是一个IMAGE_THUNK_DATA结构的数组。

  OriginalFirstThunkFirstThunk指向IMAGE_THUNK_DATA数组结构,结构是相似的,它的结构体如下所示:

typedef struct _IMAGE_THUNK_DATA32 {
union {
DWORD ForwarderString; // PBYTE
DWORD Function; // PDWORD
DWORD Ordinal;
DWORD AddressOfData; // PIMAGE_IMPORT_BY_NAME
} u1;
} IMAGE_THUNK_DATA32;
typedef IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32;
typedef struct _IMAGE_THUNK_DATA64 {
union {
ULONGLONG ForwarderString; // PBYTE
ULONGLONG Function; // PDWORD
ULONGLONG Ordinal;
ULONGLONG AddressOfData; // PIMAGE_IMPORT_BY_NAME
} u1;
} IMAGE_THUNK_DATA64;
typedef IMAGE_THUNK_DATA64 * PIMAGE_THUNK_DATA64;

  上面的结构体32位和64位的区别不大,就是成员大小的问题。它是一个共用体的结构体,这个比较复杂,在不同的时刻具有不同的含义。

  当IMAGE_THUNK_DATA值的最高位为1时,表示函数以序号方式输人,这时低31位,或者一个64位可执行文件的低63位,被看成一个函数序号。当双字的最高位为0时,表示函数以字符串类型的函数名方式输入,这时的值是一个RVA,指向一个IMACE_IMPORT_BY_NAME结构。

  下面我们继续介绍IMAGE_IMPORT_BY_NAME,它的结构如下:

typedef struct _IMAGE_IMPORT_BY_NAME {
WORD Hint;
CHAR Name[1];
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;

  Hint是本函数在其所驻留DLL的输出表中的序号。该域被PE装载器用来在DLL的输出表里快速查询函数。该值不是必需的,一些链接器将它设为·。

  Name含有输入函数的函数名。函数名是一个ASCII字符串,以'\0'结尾。注意,这里虽然将Name的大小以字节为单位进行定义,但其实它是一个可变尺寸域,是不定长的。

  为了更好的理解,我们来看一个OriginalFirstThunk指向的结构示意,FirstThunk也是一样的。

  那么IATINT到底有啥区别,我们来看个图:

PE加载前

PE加载后

  可以看出,当PE文件在磁盘的时候,这两个存储的东西是一模一样的,但是被加载后,IAT变为了地址表,而INT被废弃掉,不再使用。

  由于导入表的结构十分复杂,可能你看第一遍该博文的时候可能会犯糊涂,建议使用自己熟悉的编程语言手动解析PE的导入表,当你能够比较轻松的解析它的时候,你就会明白导入表的设计。

  下面我们来看一下在二进制文件中相应的内容:

重定位表

  重定位表的结构相对比较简单,在学习之前请看下面的示意图:

  导入表也是一个不定长数组,用与导入表相同的方式表示结束位置,每一个成员开头描述都是一个IMAGE_BASE_RELOCATION结构:

typedef struct _IMAGE_BASE_RELOCATION {
DWORD VirtualAddress;
DWORD SizeOfBlock;
// WORD TypeOffset[1];
} IMAGE_BASE_RELOCATION;
typedef IMAGE_BASE_RELOCATION UNALIGNED * PIMAGE_BASE_RELOCATION;

  VirtualAddress是指这组重定位数据的开始RVA地址。各重定位项的地址加这个值才是该重定位项的完整RVA地址。

  SizeOFBlock是当前重定位结构的大小。因为VirtualAddressSizeOfBlock的大小都是固定的4字节,所以这个值减8就是TypeOffset数组的大小。

  TypeOffset是一个数组。数组每项大小为2字节,共16位。这16位分为高4位和低12位。高4位代表重定位类型,低12位是重定位地址,它与VirtualAddress相加就是指向PE映像中需要修改的地址数据的指针。

  对于常见的重定位类型,如下所示:

类型 含义
IMAGE_REL_BASED_ABSOLUTE 没有具体含义,只是为了让每个段4字节对齐
IMACE_REL_BASED_HIGHLOW 重定位指向的整个地址都需要修正,实际上大部分情况下都是这样的
IMAGE_REL_BASED_DIR64 出现在64位 PE 文件中,对指向的整个地址进行修正

  基址重定位数据采用类似按页分割的方法组织,是由许多重定位块串接成的,每个块中存放4KB的重定位信息,每个重定位数据块的大小必须以4字节对齐。

  下面我们来看一下一个64位程序的重定位表:

  在二进制文件的位置和内容:

地址转化

  如果想要通过VA得到RVA,这个十分简单:内存地址 – ImageBase

  如果我们向通过RVA得到FOA,那么怎么样呢?首先我们得判断RVA是否位于PE头中,如果是FOA == RVA,因为此时并没有进行内存展开。如果RVA不在的话,我们就得判断RVA位于哪个节。若RVA >= 节.VirtualAddress && RVA <= 节.VirtualAddress +当前节内存对齐后的大小,那么差值 = RVA - 节.VirtualAddress,再加上相应的该节在文件中的FOA,就可以得到了真正的FOA

  如何通过FOA得到RVA呢?这里我就不多说了,原理是一样的,只是判断的东西不太一样,正确答案将会在实现篇进行揭晓。

下一篇

  羽夏壳世界——基础篇小结

羽夏壳世界—— PE 结构(下)的更多相关文章

  1. 羽夏壳世界—— PE 结构(上)

    羽夏壳世界之 PE 结构(上),介绍难度较低的基本 PE 相关结构体.

  2. (五)羽夏看C语言——结构体与类

    写在前面   由于此系列是本人一个字一个字码出来的,包括示例和实验截图.本人非计算机专业,可能对本教程涉及的事物没有了解的足够深入,如有错误,欢迎批评指正. 如有好的建议,欢迎反馈.码字不易,如果本篇 ...

  3. 羽夏 Bash 简明教程(下)

    写在前面   该文章根据 the unix workbench 中的 Bash Programming 进行汉化处理并作出自己的整理,并参考 Bash 脚本教程 和 BashPitfalls 相关内容 ...

  4. (四)羽夏看C语言——循环与跳转

    写在前面   由于此系列是本人一个字一个字码出来的,包括示例和实验截图.本人非计算机专业,可能对本教程涉及的事物没有了解的足够深入,如有错误,欢迎批评指正. 如有好的建议,欢迎反馈.码字不易,如果本篇 ...

  5. 羽夏笔记——PE结构(不包含.Net)

    写在前面   本笔记是由本人独自整理出来的,图片来源于网络.本人非计算机专业,可能对本教程涉及的事物没有了解的足够深入,如有错误,欢迎批评指正. 如有好的建议,欢迎反馈.码字不易,如果本篇文章有帮助你 ...

  6. 羽夏看Linux内核——引导启动(下)

    写在前面   此系列是本人一个字一个字码出来的,包括示例和实验截图.如有好的建议,欢迎反馈.码字不易,如果本篇文章有帮助你的,如有闲钱,可以打赏支持我的创作.如想转载,请把我的转载信息附在文章后面,并 ...

  7. 羽夏逆向指引—— Hook

    写在前面   此系列是本人一个字一个字码出来的,包括示例和实验截图.可能有错误或者不全面的地方,如有错误,欢迎批评指正,本教程将会长期更新. 如有好的建议,欢迎反馈.码字不易,如果本篇文章有帮助你的, ...

  8. 羽夏看Linux内核——中断与分页相关入门知识

    写在前面   此系列是本人一个字一个字码出来的,包括示例和实验截图.如有好的建议,欢迎反馈.码字不易,如果本篇文章有帮助你的,如有闲钱,可以打赏支持我的创作.如想转载,请把我的转载信息附在文章后面,并 ...

  9. 跟羽夏学 Ghidra ——窗口

    写在前面   此系列是本人一个字一个字码出来的,包括示例和实验截图.本人非计算机专业,可能对本教程涉及的事物没有了解的足够深入,如有错误,欢迎批评指正. 如有好的建议,欢迎反馈.码字不易,如果本篇文章 ...

  10. (三)羽夏看C语言——进制

    写在前面   由于此系列是本人一个字一个字码出来的,包括示例和实验截图.本人非计算机专业,可能对本教程涉及的事物没有了解的足够深入,如有错误,欢迎批评指正. 如有好的建议,欢迎反馈.码字不易,如果本篇 ...

随机推荐

  1. C# AES CBC模式 加密和解密

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.S ...

  2. verilog之readmemb

    verilog之readmemb 1.基本作用 用于读取存储器的值的系统函数.这里首先要知道什么是存储器.在verilog中,有一些比较大的数据是需要存储的,一般需要使用存储器,语法结构类似二维数组. ...

  3. KingbaseES V8R6 索引膨胀

    索引膨胀 对于索引,随着业务不断的增删改,会造成膨胀,尤其Btree索引,也会涉及索引分裂.合并等,导致索引访问效率降低.维护成本增加.另外,索引页的复用与HEAP PAGE不一样,因为索引的内容是有 ...

  4. arch xfce启用自动挂载usb设备,自动访问usb设备,自动连接usb设备

    1.安装gvfs sudo pacman -S gvfs GVFS(Gnome Virtual File System)是一个用于 GNOME 桌面环境的虚拟文件系统,它提供了一种统一的方式来访问和管 ...

  5. 世界疫情div界面搭建初步

    1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="U ...

  6. 禅道16.2版本发布!主要新增专业研发看板,修复Bug

    大家好,禅道16.2发布了,本次发布在项目管理模型层面新增了专业研发看板功能,今后禅道全面支持Scrum.瀑布.看板3种项目管理模型,功能更加健全,用户选择更加灵活. 视频地址:https://www ...

  7. 电科院密码保密与信息安全竞赛网络攻防宣传赛 Writeup

    一. 战队信息 战队名称:20221214 战队排名:1 二. 解题过程 ctf1 用Winhex打开,最后有一串编码字符,拿去一把梭即可. ctf2 目录穿越 GET /icons/.%2e/%2e ...

  8. 3 CSS组合选择器

    3 组合选择器 页面元素比较复杂,存在多个嵌套.为了更加灵活选择页面中的元素,CSS中还提供了组合选择器.组合选择器就是将多个基本选择器通过一定的规则连接起来组成一个复杂选择器. 后代子代选择器 &l ...

  9. #拓扑排序#洛谷 5157 [USACO18DEC]The Cow Gathering P

    题目 给出一棵树和一些限制关系 \((a_i,b_i)\), 一种合法的删点序列当且仅当删除一个点之后树的大小不超过 1 或不存在孤立点, 并且 \(a_i\) 要比 \(b_i\) 先删除,问 \( ...

  10. C++ 中的可移植性和跨平台开发

    在当今软件开发行业中,跨平台开发已经成为了一种非常流行的方式.C++作为一门强大的编程语言,也被广泛应用于跨平台开发中.然而,由于不同操作系统的差异和限制,C++在不同的平台上的表现可能会有所不同.为 ...