我们知道当程序要调用系统dll时,会用到IAT表
具体是怎么实现的呢,
假设我们程序中某处要用到MessageBoxA,那么这里会有两种形式,一种是先call到一个地址,这个地址中是一个jmp [A],A中存放着数据,数据内容就是我们的MessageBox的入口地址。另一种情况是直接calll到MessageBoxA的入口地址即 [A],千万要注意是A中存放的数据,而不是A本身,A中存放的数据是MessageBox的入口地址,之所以叫做表也是这个原因,因为程序要调用很多系统dll,如果我们的dll地址发生改变,要对每一处调用修改其调用地址是很麻烦的,如果有这样的表,我们将所有的函数调用的入口地址集中在一个集中的地址中,然后通过解析这个表方便我们进行修改,这样我们只要修改这个表所指的函数入口地址。
好的前面说了两种方法,思考一个问题,这个表是怎么实现的,我们编程过程中并没有建这样的表啊?
实际上,这个表是windows方便我们所实现的,
在od中选择call了MessageBox的一行,用view 运行文件的方法查看,可以看到这里并不是函数入口地址。那么是什么使得加载之后它变成了入口地址呢?
其实这是windows在加载我们程序的时候自动帮我们做好的。
我们看到view运行文件的方法打开之后是是一个dword的数,这个十六进制数加上基址假设叫做B,我们跳过去看B,看到B中的数据是一个ASCII码的内容,即字符串MessageBox,也就是说,Windows在运行程序,即在加载前通过内部实现,把我们的字符串MessageBox换为了MessageBox的入口地址,这样我们在call 【A】的时候就是跳转到了MessageBox的入口地址。
让我们来整理一下思路,我们有一个这样的表,表中存放着所有系统函数的入口地址,如果我们要调用一个系统函数,那么正确的做法是call 【表地址】。
而表中的地址是怎么填充的呢,在文件还未加载进内存中的时候,这个表中实际上只有很多个指针,指针指向的地址是字符串。
再整理一下思路,我们有这样一个表,在还未加载进内存中之前,他是一个指针,指针指向一个数据段,数据段中的内容是函数名称的字符串。当程序加载进内存中时,操作系统根据所指数据段中的字符串把表中的指针转换成函数入口地址。
所以接下来就是我们的导入表的内容了,关于导入表可以看我的另一篇文章
http://blog.csdn.net/izzxacbbt/article/details/78806514
假设你已经知道了什么是导入表,并且数据窗口中找到了导入表的首地址,根据我那篇博客可以知道导入表是由IMage_import_descriptor(IID)组成的
每个IID有五个DWORD,我们这里只关心后面两个字段,即name和FirstThunk,name字段指向一个地址,该地址是dll的名称,FirstThunk指向我们的IAT的地址。
知道这些之后我们就可以给出操作系统是如何把IAT自动填充的了。
1,找到导入表起始地址
2,找到第一个IID
3,根据IID的第四个字段确定dll库名称
4,根据IID的第五个字段确定IAT表地址
5,通过IAT字符串指针定位到目标函数名称。
6,用目标函数地址修改IAT的字符串指针。
7,修改下一处IAT
8,IAT项为0,修改完毕
9,找到下一个IID从第3步开始重复上面的操作。
举个例子,如下,程序中401339处调用了MessageBox,call到4018dc
于是我们转到4018dc,可以看到他jmp到messageBox的目标地址75e5279e
,这个地址存放在IAT表中,地址在00405120,
我们在数据窗口中跳转到405120,可以看到,的确是75e5279e
接下来在该地址看文件未加载时候存放的是什么数据,
可以看到是一个地址,5226,加上基地址,就是405226,
我们跳转到存放字符串的地址405226,
可以看到的确是MEssageBox的名称,如果我们希望系统帮我们替换不同的函数,则只要把这里的MessageBox改为其他名字就行。