PE文件加载解疑
疑点:为什么脱壳要重构导入表,脱壳存根不是已经进行iat表的恢复了吗?
解决方法:理解PE文件正常加载的流程与脱壳存根加载的流程,比较不同。同时针对性找文章。
输入表的作用与流程
在分析为什么脱壳要重构IAT之前,我们需要先了解导入表的基本知识,这很重要。
具体的导入表结构就不多说,PE文件详解的blog里已经有过详细介绍,所以下面主要从解决问题的方向出发。
首先区分几个概念:IT、INT、IAT。细分说来就是导入表、导入名称表、导入地址表。
上三者中,导入表包含导入名称表和导入地址表,及导入表中有两个字段指向INT与IAT。而INT的主要作用是告诉加载器该DLL都加载了哪些函数,通过具体的函数名称或者序号实现;IAT的作用是存储导入函数的实际内存地址(程序调用函数就是通过指向IAT的具体表项来实现的),当然在程序没有加载之前该表与INT表指向相同的数组。
晓得了导入表的结构,下面简单捋一遍PE文件加载时的流程:
PE文件的加载是由PE加载器完成的,PE加载器会先搜索OriginalFirstThunk,之后会迭代搜索数组的指针,也就是最终获取到具体函数的名称,然后会通过函数名称(或者序号)获取到该函数在内存中的实际地址(只有程序加载后才可以知晓),最终将该地址填写到IAT对应表项中,实现地址的重写保证程序的正常运行。
小结:在没有加壳情况下,PE文件的加载是通过PE加载器完成的,这个过程中需要根据导入表完成IAT表的重写,保证该表项对应的数据是函数的实际内存地址。
脱壳与输入表
学习了前面的知识,其实答案就已经明了了:为什么脱壳时要重建导入表呢,因为一个正常的PE文件的加载需要导入表来完成IAT的重写。
但是脱壳存根在进行文件加载时并不需要完整的导入表,因为他是通过显式装载调用DLL的方式获取各个函数的地址并重写IAT。(例如getprocaddress函数),所以一个加壳程序在执行时可能不会在内存中出现导入表(多是一个个函数名称的字符串),这时候就要通过IAT表重建导入表了。
导入表的重建具体可以看《加密与解密》-脱壳篇,这里不再赘述。