深入理解PE结构中的导入表3_一些奇怪的导入表

struct IMAGE_IMPORT_TABLE{
  union {
  dword Characteristics;
  dword OriginalFirstThunk; //RVA 指向INT结构体数组
} INTName;

  dword TimeDateStamp;    // 时间戳 0 if not bound
  dword ForwarderChain;    // -1 if no forwarders
  dword Name;        //RVA 指向导入的dll文件名称 
  dword FirstThunk;     // RVA 指向IAT结构体数组
}

一、 FirstThunk指向的Thun_DATA(IAT)为0

如果FirstThunk指向的Thunk_DATA数据为0,那么操作系统判定为无效的dll,就不会为程序加载这个dll,并且OS操作系统将会解析下一个导入表结构。

OS加载dll的过程:

  先找到导入表结构的位置,然后开始循环遍历导入表,先判断导入表的FirstThunk指向的IAT是否为0,如果不为0,则通过Name字段获取dll的名字,然后加载dll,然后通过INT获取函数名称,

        然后通过LoadLibrary加载完dll,通过函数名称获取函数地址,然后填到FirstThunk指向的IAT中。

          先判断导入表的FirstThunk指向的IAT是否为0,如果不0,则认为该dll无效,就直接循环读取下一个dll的导入表结构。

但是原版的OD、CFF等一些其他的编译、PE工具,不这样判断,那么这样就产生了和操作系统不一样的判断方式。

当FirstThunk指向的Thunk_DATA数据为0时候,一些工具会仍然对读取这个dll,按照原来的方式读取函数名称,获取函数的地址。

原版的OD就有这个问题,原版的od会继续导入无效的dll,并且读取函数名称,获取函数地址。同时会把函数名称显示出来,原版的od显示函数名称,设置读取的长度为260,因此当故意设置的函数名称超长的时候,

这个时候就会出现一种现象,OS不处理,因为OS对于无效的导入表(Thunk_DATA数据)不处理,OS对于超长的dll名称和超长函数名称也不处理。

但是原版的OD和CFF等工具还会处理,会分析显示无效的dll的函数名称等,这样就出现操作系统和工具行为上的不一致,这样就产生反调试的情况。

二、 FirstThunk指向的Thun_DATA(IAT)为非0,瞎填的不正确的其他值

 这里我们把IAT表里填的内容,胡乱填,这个时候还是有效

 因为,操作系统加载的时候,先检查当OriginalFirstThunk指向的INT表有效的时候,会优先使用INT表,不会检查IAT里面的具体值。

 FirstThunk指向的ITA不为0(只要不是0,胡乱填都行),就从Name获取dll名称,LoadLibrary加载dll。然后从INT表获取函数名称,然后在获取函数地址,填进IAT表中。

 三、 OriginalFirstThunk的值为0,注意和上边的区别,这里是OriginalFirstThunk里的RVA值为0,不是指向的THUNK_DATA.

这个时候,会认为是一个有效的dll。加载dll的过程,首先判断FirstThunk指向的值不为0,然后读取Name字段中的dll名称,然后LoadLibrary加载dll,准备读取函数名称,发现OriginalFirstThunk的RVA值为0,那么就读取FirstThunk中的值,把IAT表指向的位置作为函数的名称或者序号,然后使用LoadLibrary获取函数指针,然后原地把这个函数地址填充到IAT表中,这个时候导入表又有效果了。

利用od查看IAT表中的地址确实已被修改为函数地址。

 因此只要记住三种情况:

  1、导入表指向名称的RVA为0,这个时候IAT表里面的内容不能乱填,要以IAT表为准加载dll

       2、当导入表指向的IAT表内容为0,那么这个dll无效,无法加载。

       3、当导入表指向的IAT表内容为非0,那么这个以INT表为准加载dll。

posted @ 2023-08-17 16:45  一日学一日功  阅读(42)  评论(0)    收藏  举报