下面我们来讲导出表。
首先还是查找导出表的数据目录所在地,还有导出表的节区起始
首先基本信息贴一张
之前我也发过相关的结构信息了,下面我就直接贴出数据目录导出表的各项信息
这里可以看到RVA是 00099740
然后我们在查看节区信息
然后这个时候我们发现了一个问题 节区.edata是导出表节区。但是我们在PE文件中没有发现.edata节区 该怎么转换成FOA(文件偏移)呢?
很简单,首先我们要根据得到的RVA来判断在那个节区范围以内
我们可以看见 节区.text
内存中大小为 0009F9C7
RVA为 00001000
而我们之前拿到的导出表RVA是 00099740 那么我们就可以判断00099740 >= 00001000 && 00099740 <= 9F9C7 导出表就在节区.text里面
如果遇到这种情况我们就可以根据如下公式将其转换成FOA(文件偏移)
(导出表RVA) - (节区起始RVA)+ (文件偏移) = FOA
00099740 - 00001000 + 00000400 = 00098B40
这是导出表的详细结构 ,不用太在意里面的数值,因为这个查看软件 可能有问题 我们主要看PE文件中的数值这个PE查看器是我用来看结构的。。。。。。
这里重点说几个成员
Number of Functions 函数的实际个数
Names of Names 有名函数的实际个数。(这里解释一下什么叫有名函数个数,就是可以通过函数名查找到的函数个数)
Address Table RVA 这个成员是 函数地址表 是一个数组
Name Pointer Table RVA 这个成员是 函数名表 也是一个数组
Ordinal Table RVA 这个成员是 前面两个数组 的索引 什么意思呢。 打个比方你可以通过Ordinal Table RVA 这个数组里面的值 来 访问对于的函数地址还有函数名 在后面就可以看到。
红线标注的就是比较重要的成员了 当然上面我只介绍了 最重要的成员 同时我们也可以发现上面那个PE文件查看器里面的有一些值还是是对的。
那么下面我们开始 实战
第一个查找到是 这个导出模块的名字
如上面的图所示 模块名的RVA 是 0009C1AA 转换成 FOA = 9B5AA
可以发现 导出模块的名字 是USER32.dll
这里需要说一下 查找的流程
第一 我们不能通过函数地址 的 索引查找对应索引的 函数名 和 索引表,什么意思呢 比如数组 a[0] 可以访问到正确的成员 ,但是我们能通过这个[0]来访问到其他数组对应的正确成员。
第二 我们要通过函数名 来获得实际的正确的数组下标,然后通过数组下标 访问到正确的索引号。
篇幅有限就不详细的 介绍转换 过程了 直接 上数据
这里查看的成员是 Address Table RVA 导出函数的RVA
下面是成员 Name Pointer Table RVA
下面是成员Ordinal Table RVA
在这里我们可以发现 明明RVA的地址对不上 为什么 Value 确实相同的呢 ,这是因为 PEview 的原因如果通过硬编码查看的话 会发现 并没有什么关联
这里我们来看一下PE文件就知道了
0009AA64 这是函数名表 转换成FOA 99E64
可以发现 99E64里面保存的 是第一个成员
09C1E1 转换一下 FOA = 9B5E1
查看一下Ordinal 成员
同样的 0009B9E8 转换成 9ADE8
在这里说一下 Ordinal成员 指向的表 是一个2字节的数组 0003保存 就是函数地址的索引,下面我们就可以通过这个索引来查看相应的函数地址了。
00099768 函数地址RVA 同时转换成 FOA 98b68
选中的就是函数地址数组的 第一个成员 那么我们通过 从Ordinal成员 获得的值 来访问 函数地址数组的成员 数组第一个成员 也就是0 依次往后推,在我用红箭头的地方就是我们要找的函数地址了,RVA = 44970
下面我们通过OD来查看函数地址
这里先查看 模块被加载进内存之后的 新基址
RVA 772d0000 + 44970 = 77314970
我们可以发现 这个位置 是一个JMP 继续跟
可以看到这里的JMP 然后我们想继续跟结果发现跟不下去了
我们查一下下一个 地址看看是什么情况
772d0000 + 448a0 = 773148A0
可以发现这里就是函数入口地址
导出表到这里就结束了 ,讲得不好,大佬们见笑了。
但是我有一个疑惑,为什么导出表 有一些是定位到一个JMP 位置处 还有一些是直接定位到函数入口点呢
2020安全开发者峰会(2020 SDC)议题征集 中国.北京 7月!
最后于 3天前 被kanxue编辑 ,原因: