IMAGE_IMPORT_DESCRIPTOR 结构是导入段的基本结构,导入段是由此类型组成的数组构成,每个DLL对应数组中的一项。数组的后一项为NULL。每一项中有两个关键成员,指出IMAGEA_THUNK_DATA类型的数组的偏移量,这两个数组分别列出了从此DLL导入的函数名称,以及它它们在本进程地址空间的RVA。

  以下是其定义:

typedef struct _IMAGE_IMPORT_DESCRIPTOR {
   union {
          DWORD Characteristics;
          DWORD OriginalFirstThunk;// 指向一个 IMAGE_THUNK_DATA 结构数组的RVA
         }
   DWORD TimeDateStamp;// 文件生成的时间
    DWORD ForwarderChain;// 这个数据一般为0,可以不关心
    DWORD Name;   // RVA,指向DLL名字的指针,ASCII字符串
    DWORD FirstThunk; //指向一个 IMAGE_THUNK_DATA 结构数组的RVA,这个数据与IAT所指向的地址一致
}IMAGE_IMPORT_DESCRIPTOR,*PIMAGE_IMPORT_DESCRIPTOR


  OriginalFirstThunk 和FirstThunk是此结构中的关键成员。它们指出这两个IMAGE_THUNK_DATA类型的数组。

  Name 是指向DLL名称的指针。

  pImport指向模块的导入段,可以使用它来访问IMAGE_IMPORT_DESCRIPTOR结构的每一个成员。

  注意:以上所有偏移量都是相对于模块基地址的,因此在实际使用中一定要加上模块的地址。

  OriginalFirstThunk指向NAT,NAT列出了从该DLL导入的所有导入函数的名称。

  FirstThunk 指向IAT,IAT列出了与NAT相对应的导入函数的地址。

  看此结构定义:

typedef struct _IMAGE_THUNK_DATA32 {    

     union {        

               PBYTE  ForwarderString;  

               PDWORD Function;        

               DWORD Ordinal;  

               PIMAGE_IMPORT_BY_NAME  AddressOfData;    

          } u1;

} IMAGE_THUNK_DATA32;
 


  上述代码中,外层循环while(pImport->FirstThunk)用于遍历所有DLL,内层循环用于遍历查找每个DLL中的导入函数名称。我们以查找MessageBoxA函数为例来做介绍。此处我们通过对NAT进行扫描,将其与我们给出的字符串类型的函数名MessageBoxA进行比较。注意要使用大小写不敏感的函数:

int WINAPI lstrcmpi(
  __in  LPCTSTR lpString1,
  __in  LPCTSTR lpString2
);


  这是一种方法,另一种方法是比较函数地址。我们可以对IAT进行扫描,将得到的地址与我们给出的函数地址进行比较。这两种方法是经常使用的方法。