PE文件的导入表,动态链接库中的函数应该如何导入

阅读须知:

探索者安全团队技术文章仅供参考,未经授权请勿利用文章中的技术资料对任何计算机系统进行入侵操作,由于传播、利用本公众号所提供的技术和信息而造成的任何直接或者间接的后果及损失,均由使用者
本人负责,作者不为此承担任何责任,如有侵权烦请告知,我们会立即删除并致歉,创作不易转载请标明出处.感谢!

导入地址表IAT

  1. IAT保存的内容与windos操作系统的核心进程、内存、DLL结构有关。IAT是一种表格,用来记录程序正在使用哪些库中的哪些函数。

动态链接库(DLL)

  1. 常见的kernel.dll就是一个非常重要的动态链接库,其中包含了运行程序时需要使用到的函数,后续结合导入描述符(IMAGE_IMPORT_DESCRIPTOR)中的内容,观察PE装载器时如何将kernel.dll中需要用到的函数装载进入到IAT中的。

导入函数

  1. 一个程序往往需要使用到多个动态链接库,其以结构体的形式存在于IMPORT Directory Table(导入表)中。

  2. 这里依旧以上一篇的hello.exe文件为例, 首先要找到IMPORT Directory Table(导入表)的位置,这在上一篇文章中有详细介绍(PE文件格式),定位到00003000:

  3. 这里可以看到整个导入表,导入表是一个数组,其最后一个元素为NULL(权全为00),当前数组共有两个元素,即两个DLL需要导入,其中的每一个元素结构体为IMAGE_IMPORT_DESCRIPTOR(导入描述符):

  4. 前四个字节为INT,其指向了该动态链接库要导入哪些函数,最后一个 FirstThunk为“”IAT“”,即PE文件加载到内存中时函数地址需要填入的位置,倒数第二个name字段则指向了该动态链接库的名称的地址(这里的地址都是RVA(在内存中的偏移),要变成在文件中的偏移FOA需要里面前面那片文章中的公式进行转换)。

  5. 86E8->36E8,动态链接库名称的位置:在这里插入图片描述

  6. 从动态链接库中导入函数时,首先需要在IID中拿到OriginalFirstThunk(INT),这里存储了所有需要导入的函数信息,是一个指针数组(其中存储都是以RVA的形式),数组中的每一个元素均指向结构体为IMAGE_IMPORT_BY_NAME的元素,IMAGE_IMPORT_BY_NAME第一个元素为函数的编号(hint),第二个元素时需要导入的函数的名字,下面以kernel.dll来为实例观察需要导入哪些函数:

  7. 根据kernel.dll的IMAGE_IMPORT_DESCRIPTOR的第一个元素定位到INT,其中的RVA需要转化为FOA在文件中的偏移,兄弟们看上一篇后自行转化过来:在这里插入图片描述

  8. 观察其中的第一个元素0000836C,它指明了第一个函数IMAGE_IMPORT_BY_NAME结构体的RVA,转化为FOA 0000336C,观察在文件中的偏移:

  9. 首位的010D是函数的编号,后面是函数的名称DeleteCriticalSection,那他要被装载到内存中的什么地方呢,就需要根据前面的IMAGE_IMPORT_DESCRIPTOR(导入描述符)中的IAT指明的位置RVA 000081D4,下面进入到内存中观察函数是否被装载到此处。
    10.使用x64dbg打开hello.exe文件 :

  10. 可以看到文件被加载到内存中的基地址是400000(ImageBase),后续要根据ImageBase基地址和RVA偏移地址相加求出VA绝对地址:000081D4+400000=4081D4,在内存中跳转到该位置,查看其内容是否为DeleteCriticalSection函数的地址:在这里插入图片描述

  11. 根据内存中4081D4内存地址处的注释,可以确定该处的值00007FF99FD2A710是函数DeleteCriticalSection的起始地址,后续共kermel.dll需要导入的23个函数全部导入到内存中。

  12. 在内存中可以直接使用RVA来查看一下动态链接库的名字是不是在ImageBase+RVA处,根据前面的RVA86E8,在内存中跳转到4086E8

  13. 接着在INT中看下一个函数,RVA是00008384->00003384,可以看出编号是0131,名字是EnterCriticalSection

  14. 下面再内存中找到RVA为000081D4,观察该处后面第二个位置函数是不是EnterCriticalSection(第一个位置的函数已经被导入),结合内存中的值可以说是一模一样,后面的函数一次再INT中取值,找到对应的函数名,通过GetProcAddress方法拿到对应函数的地址,再将其填入IAT,一致循环知道INT被拿完,则所需的kernel.dll动态链接库中的全部函数均导入完毕。后续第二个动态链接库msvcrt.dll也依照上面的方法进行导入:

  15. 另外,再代码中我们可以看到,调用动态链接库导入的函数,都是以取一个地址后,call该地址处的值来进行调用,而不是直接call函数的地址,为什么要进行一次中转呢?:

  16. 第一点:hello.exe等可执行文件,并不知道自己要工作再哪总平台上,哪种环境中,再各种不同的环境下kernel.dll的版本都不相同,而相应库中的函数位置也不相同,所以不能直接再call后面跟上函数的具体地址(这样就只能保证在当前一个平台下使用),需要给定一个内存单元(如4081D4),程序在具体环境下运行时,动态的将需要的库函数地址放在此处。

  17. 第二点:DLL的重定位DLL文件的imageBase不会时一个固定的值,如果内存中该位置被占了,那么PE装载器就需要重新定位基地址,所以无法直接将函数的实际地址直接写在指令中,并且PE文件中也指定kernel.dll中导入的函数应该在内存中的RVA()。

  • 26
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值