PE知识复习之PE的导入表

一丶简介

  上一讲讲解了导出表. 也就是一个PE文件给别人使用的时候.导出的函数  函数的地址 函数名称 序号 等等.

  一个进程是一组PE文件构成的.  PE文件需要依赖那些模块.以及依赖这些模块中的那些函数.这个就是导入表需要做的.

确定PE依赖那个模块. 确定PE依赖的那个函数.  以及确定函数地址.

总共分为三部分讲解.

  导入表定位位置: 在扩展头中有一个数据目录结构体. 第二项保存的就是导入表的 RVA 以及大小.

如下图所示:

EXE文件.没有导出表.有一个导入表. RVA 是 0x1A1C0  位于节Text中. 虚拟地址位 0x11000  文件偏移为 0x400

转换为 FOA =  1A1C0 - 11000 + 400 = 0x95c0

我们发现在文件中定位导入表的时候都是0,原因是程序加载到内存中.需要用到的时候.操作系统才会往这个地方填写数据.

二丶导入表结构

typedef struct _IMAGE_IMPORT_DESCRIPTOR {
union {
DWORD Characteristics; // 0 for terminating null import descriptor
DWORD OriginalFirstThunk; // RVA to original unbound IAT (PIMAGE_THUNK_DATA) 指向IAT结构注释表明了
} DUMMYUNIONNAME;
DWORD TimeDateStamp; // 时间戳.
// -1 if bound, and real date\time stamp
// in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
// O.W. date/time stamp of DLL bound to (Old BIND) DWORD ForwarderChain; // -1 if no forwarders
DWORD Name;                //指向DLL名字的 RVA
DWORD FirstThunk; // RVA to IAT (if bound this IAT has actual addresses)
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;

导入表大小为十进制的20个字节.  16进制的 0x14. 如果以16进制为一行. 则是 一行零4个字节

导入表跟导出表不同.导出表只有一个.里面有子表记录. 而导入表你依赖一个模块.则有一个导入表存在.

导入表结束位置是20个字节的连续为0的数据为结束位置. 也就是导入表最后一项都为0的时候.说明导入表结束了.

对于导入表来说.我们只需要关心三个成员.上面都标红了.

会一一进行讲解.首先从最简单的成员开始.

  2.1 Name成员. 确定依赖的模块的名字是什么

我们说过.一个PE文件.依赖模块. 那么这个成员就是记录了.我要依赖的模块的名字是什么.是一个RVA属性. RVA指向了一个ASCII码字符串.以0结尾.

因为在文件中导入表并没有.所以我们直接在内存中查看.

根据数据目录 导入表位置 0x1A1C0  + ImageBase(0x400000) == 0x41AC0

在内存中的0x41AC0位置.则是导入表的位置. 我们看一下.

导入表大小总共一行零4个字节. 倒数第二个成员则是 Name的 RVA  0x1A4A6

我们可以加上ImageBase 去内存中查看.

可以通过RVA 属性.看到导入表依赖的模块名字就是 VCRUNTIME140D.dll   带有D结尾的.dll说明是调试DLL. 140是编译器版本.说明是

VS2015编译的 .VCRuntime 是运行库 .  说明我们这个程序是一个 Debug版本编译的程序. 并且使用编译器 140版本编译的.

我们查看的这个Name属性.描述的就是 VCRUNTIME140D.dll 这个模块的信息了.如果想看其它依赖的模块就需要查看下一张导入表.

下一张导入表在第一章导入表的下面.最后一项的导入表全部为0.  我们下一张导入表的 依赖模块的模块名称的 RVA 属性是 0x1A75A

VA = Imagebase + RVA = 41A75A

依次查看即可.

   2.2 确定依赖的函数的名称

上面我们讲了Name成员.确定了导入表依赖的DLL的名字.那么我们导入表怎么确定依赖了那些函数那?

这个主要讲解导入表的第一个成员跟最后一个成员.

如下图所示:

第一个成员指向了一个INT 表.最后一个成员指向了一个 IAT表.

INT :: 导入名称表  Improt Name Table

IAT::  导入地址表  Improt Address Table

Name成员直接指向一个 ASC 结尾的字符串.

根据上图所示. 两张表是一样的. 但是所在位置是不一样的名字也不一样.一个叫做 INT 一个叫做IAT

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;

结构体大小:  4个字节. 他是一个联合体.找最大的.

里面有4个成员.为当前的4个字节起了四个名字.  真正有用的是下面两个. 也就是说有的时候需要用第三个成员.

有的时候需要用第四个成员. 而第四个成员是指向一个  IMAGE_IMPORT_BY_NAME的结构的RVA

typedef struct _IMAGE_IMPORT_BY_NAME {
WORD Hint; //编译器决定,不是空的话,就是函数在导出表中的 函数地址表的导出索引.
CHAR Name[]; //函数名称,0结尾.
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;

无论是第一个成员还是最后一个成员.都能确定 我一来的当前模块的那个函数.

为什么需要两个表. 这个下面会将. 首先讲解的就是无论使用那个表.都能找到依赖当前模块的函数.

第一个成员找:

  INT表  INT表是4个字节.最后0结尾.  INT表有多大.就是说依赖这个模块的多少个函数.

  IAT 同上. 0结尾.

那么我们怎么去寻找?

  看这个表的4个字节.  最高位为1那么就是函数的导出序号. 去掉最高位.就是函数的序号. 也就是说我们看的是序号.

  如果最高位不是1,那么找的就是一个 RVA ,一个指向 IMAGE_IMPROT_BY_NAME的结构.

例如下图:

INT 或者 IAT表. 都可以通过最高位判断. 是函数的序号.还是函数的名字.

INT或者IAT就是两种情况, 高位为1, 那么去掉高位就是依赖的函数序号. 不是1, 那么就是一个RVA. 指向了一个  IMAGE_IMPROT_BY_NAME 结构.

以一个导入表为例

INT的 RVA 为 1A2A8 VA = 41A2A8

41A2A8是INT表开始. 每一个是4个字节,以0结尾. 观看第一项. 高位为0,所以 0x1A48E 是一个RVA. 一个指向 IMAGE_IMPROT_BY_NAME 的结构

VA = 41A48E

高位两个字节,是函数在导出表中的导出索引.  后面就是以0结尾的函数名称了.

总结来说: 不管是INT表还是 IAT表. 主要看其高位值,高位为1,那么去掉高位,就是函数的序号. 高位为0.指向一个结构.这个结构保存了函数的导出序号.以及函数名称.

在IMAGE_IMPROT_BY_NAME 结构中的 HINT 如果不是空,那么这个序号(索引) 就是导出表的函数地址表的索引. 我们可以直接拿着这个索引去导出表中获取函数地址.

  

    2.3 确定函数地址

如果我们使用DLL的函数.那么在程序中.调用这个DLL的函数.那么就会生成一个间接Call

比如我们程序调用MessageBoxA

反汇编

跳转过去之后.会看到内存中有一个地址

这个地址才是真正的MessageBox的地址

在我们导入表中,最后一个成员  IAT表.就是上面所说的表,保存了函数地址表.

那么这和我们说的结构是不一样的. IAT不是说跟INT是一样的吗?

PE加载前加载后的区别.

一样是一样的.但是需要分清 PE加载前.还有PE加载后.如果加载前,那么IAT跟INT一样.都可以找到依赖的函数名称.

如果是加载后.也就是在内存中的话.那么IAT表保存的就是函数的地址.

PE加载后如下图:

IAT表保存的就是函数地址了.

从导入表中找到IAT表.

IAT表的RVA 偏移为 0x1A098  VA == 41A098

IAT表中存储了函数地址,4个字节为单位.0x6AD79CF0 就是函数 __Vcrt_loadlibraryExW . INT表中存储的就是 依赖的函数名称.上面我们也看到了.

三丶知识总结

导入表大小为20个字节. 十六进制 0x14 ,一行零4个字节.

  1.导入表重要成员有三个.  INT表. Name表.  IAT表.

    PE加载前.

        INT 表 IAT表相同. 根据INT或者IAT表的高位,高位为1.去掉高位就是函数序号. 高位为0. 那么是一个RVA偏移. 指向函数名称表.

          函数名称表

            HINT  当前函数在导出函数地址表中的索引

            Name  当前函数的名称.

    PE加载后INT 表同上. IAT表变成了存储函数地址的地址表了.

  2. Name 民称表. 直接指向DLL名称文件名. 是一个RVA .注意是直接指向.

  3.INT IAT表.的RVA 都是定位INT IAT表位置. 定位的位置是INT IAT表.这个表存储的才是数据

PE知识复习之PE的导入表的更多相关文章

  1. PE知识复习之PE的绑定导入表

    PE知识复习之PE的绑定导入表 一丶简介 根据前几讲,我们已经熟悉了导入表结构.但是如果大家尝试过打印导入表的结构. INT IAT的时候. 会出现问题. PE在加载前 INT IAT表都指向一个名称 ...

  2. PE知识复习之PE的重定位表

    PE知识复习之PE的重定位表 一丶何为重定位 重定位的意思就是修正偏移的意思.  如一个地址位 0x401234 ,Imagebase = 0x400000 . 那么RVA就是 1234.  如果Im ...

  3. PE知识复习之PE的节表

    PE知识复习之PE的节表 一丶节表信息,PE两种状态.以及重要两个成员解析. 确定节表位置: DOS + NT头下面就是节表. 确定节表数量: 节表数量在文件头中存放着.可以准确知道节表有多少个. 节 ...

  4. PE知识复习之PE的导出表

    PE知识复习之PE的导出表 一丶简介 在说明PE导出表之前.我们要理解.一个PE可执行程序.是由一个文件组成的吗. 答案: 不是.是由很多PE文件组成.DLL也是PE文件.如果我们PE文件运行.那么就 ...

  5. PE知识复习之PE的各种头属性解析

    PE知识复习之PE的各种头属性解析 一丶DOS头结构体 typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header WORD e_magic; // M ...

  6. PE知识复习之PE合并节

    PE知识复习之PE合并节 一丶简介 根据上一讲.我们为PE新增了一个节. 并且属性了各个成员中的相互配合. 例如文件头记录节个数.我们新增节就要修改这个个数. 那么现在我们要合并一个节.以上一讲我们例 ...

  7. PE知识复习之PE新增节

    PE知识复习之PE新增节 一丶为什么新增节.以及新增节的步骤 例如前几讲.我们的PE文件在空白区可以添加代码.但是这样是由一个弊端的.因为你的空白区节属性可能是只读的不能执行.如果你修改了属性.那么程 ...

  8. PE知识复习之PE扩大节

    PE知识复习之PE扩大节 一丶为什么扩大节 上面我们讲了,空白区添加我们的代码.但是有的时候.我们的空白区不够了怎么办.所以需要进行扩大节. 扩大节其实很简单.修改节数据对齐后的大小即可. 并且在PE ...

  9. PE知识复习之PE的RVA与FOA的转换

    PE知识复习之PE的RVA与FOA的转换 一丶简介PE的两种状态 首先我们知道PE有两种状态.一种是内存展开.一种是在文件中的状态.那么此时我们有一个需求. 我们想改变一个全局变量的初始值.此时应该怎 ...

随机推荐

  1. Zathura: 轻巧好用的 PDF 查看器]

    [Zathura: 轻巧好用的 PDF 查看器](https://linuxtoy.org/archives/zathura.html) 这个文件很轻巧,且支持VIM方式的 快捷键

  2. Do-Now—团队 冲刺博客六

    Do-Now-团队 冲刺博客六 作者:仇夏 前言 终于从四级的考试中解脱了(不过我觉得可能凉凉,呵呵),我们的APP制作也迎来了最后的两天. 自己觉得自己其实没有干成什么事情,代码什么的大都是队友们写 ...

  3. 关于 Mybatis 设置懒加载无效的问题

    看了 mybatis 的教程,讲到关于mybatis 的懒加载的设置: 只需要在 mybatis 的配置文件中设置两个属性就可以了: <settings> <!-- 打开延迟加载的开 ...

  4. JavaScript模板引擎Handlebars

    Handlebars模板库简单介绍 Handlebars是JavaScript一个语义模板库,通过对view(模板)和data(ajax请求的数据,一般是json)的分离来快速构建Web模板.它采用& ...

  5. Linux正则表达式练习

    练习一 1.生成30位的随机口令 [root@centos7 ~]#cat /dev/urandom | tr -dc "[:alnum:]" | head -c30 RJL5qc ...

  6. web项目如何使用Material Icons

    使用文档链接 图标库 最简单的使用方法 引入 <link href="https://fonts.googleapis.com/icon?family=Material+Icons&q ...

  7. ABP入门系列(5)——展现层实现增删改查

    ABP入门系列目录--学习Abp框架之实操演练 这一章节将通过完善Controller.View.ViewModel,来实现展现层的增删改查.最终实现效果如下图: 一.定义Controller ABP ...

  8. 【同余方程组】POJ1006 生理周期

    同余方程组: 先来看一道题目:有物不知其数,三三数之剩二:五五数之剩三:七七数之剩二.问物几何?  然后我们可以做如下变换,设x为所求的数. x%3=2              x ≡ a1(%m1 ...

  9. [Swift]LeetCode380. 常数时间插入、删除和获取随机元素 | Insert Delete GetRandom O(1)

    Design a data structure that supports all following operations in averageO(1) time. insert(val): Ins ...

  10. [Swift]LeetCode440. 字典序的第K小数字 | K-th Smallest in Lexicographical Order

    Given integers n and k, find the lexicographically k-th smallest integer in the range from 1 to n. N ...