Loading... [上篇文章](http://www.wublub.cn/index.php/archives/137/)中我们说到程序在加载到内存后,IAT会被写入导入函数的VA,来看看是如何获取到导入函数地址的VA,然后写入IAT表的。 源程序如下: ``` #include<windows.h> #include<tchar.h> int main(void) { MessageBox(NULL,_T("Hello World"),_T("对话框"),MB_OK); return 0; } ``` 编译为hw.exe,以此程序来学习pe在加载进内存时,如何获取导入函数的地址。 图中阴影部分为导入表。导入表的RVA=24000h,size=3Ch,FOA=23000h。 ![image.png](http://47.117.131.13/usr/uploads/2021/08/3095406164.png) 用OD加载hw.exe,在`call MessageBoxA`所在行右键,选择“数据窗口中跟随” | “内存地址”. ![image.png](http://47.117.131.13/usr/uploads/2021/08/3691481992.png) 先复习一下相关知识。函数调用前:1.push把参数压入栈;2.CALL进入函数执行:a.把返回地址压入栈;b.JMP函数入口。 ![image.png](http://47.117.131.13/usr/uploads/2021/08/2410932483.png) 这样,JMP到MessageBoxA的地址0x772CEA11,对比上图的导入表(阴影部分),发现该地址不在导入表的范围内,API的调用好像与导入表无关。其实不是这样的,该操作数虽然不在导入表的范围内,但导入表的数据结构描述中有一个字段是指向这个操作数所在的位置的。 在同样功能的位置(内存0x0042428C处,文件0x2328C处),内存0x0042428C对应的值:772CEA11h,而文件中此处的值为000242BCh。这意味着,文件被装载到内存后,这里的值发生了变化,真正标识MessageBoxA函数的地址应该是内存中此处的值。那么这两个值之间有什么联系呢? ![image.png](http://47.117.131.13/usr/uploads/2021/08/3788011326.png) 先来看文件中该位置的值,000242BCh,它是一个RVA。按照RVA2FOA的转换关系,可以计算出该值在文件中的位置。具体的计算过程如下: a.判断该RVA在哪一个节区;由下图可知在.idata节区 b.RVA2FOA:RVA - 节.virtualaddress + 节.pointofRawDate = FOA FOA = 000242BCh - 00024000h + 00023000h = 232BCh ![image.png](http://47.117.131.13/usr/uploads/2021/08/2138971376.png) 查看文件偏移232BCh处的值,发现是01BEh + 字符串“MessageBoxA”,前者是一个编号(hint),后者是调用的函数名(name)。 ![image.png](http://47.117.131.13/usr/uploads/2021/08/2365597548.png) 重新调整一下思路,在PE文件装载进内存时,windows加载器会根据一下指令中的地址查找到0x004242BC处。 `call 0042428C` 加载器继续查找对应位置0x0042428C得到值000242BCh,然后从文件的0x000242BC处获取函数的名字MessageBoxA和函数在动态链接库里的编号。加载器会根据函数的Hint / Name从内存地址空间中查找到函数的VA 772CEA11h,并将找到的函数地址重新覆盖内存的0x42428C这个位置。 当程序正真被装载进内存以后,0x42428C这个位置就已经被替换成为函数的正确的虚拟内存地址了。 回顾整个过程: ![image.png](http://47.117.131.13/usr/uploads/2021/08/2120150848.png) ![image.png](http://47.117.131.13/usr/uploads/2021/08/1011894959.png) ![image.png](http://47.117.131.13/usr/uploads/2021/08/1903046435.png) ![image.png](http://47.117.131.13/usr/uploads/2021/08/376378338.png) ![image.png](http://47.117.131.13/usr/uploads/2021/08/1479451317.png) ![image.png](http://47.117.131.13/usr/uploads/2021/08/1113405031.png) ![image.png](http://47.117.131.13/usr/uploads/2021/08/1593115579.png) 在找到导入函数的Hint/Name后就可以到对应的模块去遍历导出表,找到函数的VA。(在此程序中,当找到"0x01BE/MessageBox"后,就可以去遍历user32.dll的导出表,根据编号或者函数名,就能找到对应的函数地址)。 这样,当程序真正被装载进内存以后,0x0042428C这个位置就已经被替换成为函数的正确的虚拟内存地址了。 最后修改:2021 年 09 月 02 日 02 : 53 PM © 允许规范转载