IAT Hook 导入表

每一个模块都会有导出表,导入表。IAT HOOK就是修改导入表内的函数,使得模块中的 CALL [XXXXX] call到自己的函数里面去。执行自己的功能

 

模块开始是 IMAGE_DOS_HEADER,这个结构内有一个e_flew成员,这是个偏移,h_module+e_flew可以得到 IMAGE_NT_HEADER,Nt头内包含了很多信息,导入表也在这里面。

 PIMAGE_DOS_HEADER pDosHeader=(PIMAGE_DOS_HEADER)h_module;
 PIMAGE_NT_HEADERS pNtHeader=(PIMAGE_NT_HEADERS)(LONG(h_module)+pDosHeader->e_lfanew);

pNtHeader->OptionHeader->DataDirectory[] ,这个DataDirectory包含的IMAGE_DATA_DIRECTORY结构共有16个。其中都有定义,每个结构分别负责什么,IMAGE_DIRECTORY_ENTRY_IMPORT(定义为1)负责导入表,就是第二个IMAGE_DATA_DIRECTORY结构.这个结构内包含了size以及VirtualAddress,也就是大小,以及导入表结构的偏移,

因此.模块基址+结构内的偏移就可以得到指向导入表结构的指针了。


 PIMAGE_IMPORT_DESCRIPTOR pId=(PIMAGE_IMPORT_DESCRIPTOR)((BYTE*)h_module+pNtHeader->OptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);

IMAGE_IMPORT_DESCRIPTOR 结构为

typedef struct _IMAGE_IMPORT_DESCRIPTOR {
    union {
        DWORD   Characteristics;            // 0 for terminating null import descriptor
        DWORD   OriginalFirstThunk;  //       (PIMAGE_THUNK_DATA)结构,也就是hint保存的地址 以及函数名字的地址
    } DUMMYUNIONNAME;
    DWORD   TimeDateStamp;                  // 0 if not bound,
                                            // -1 if bound, and real date\time stamp
                                            //     in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
                                            // O.W. date/time stamp of DLL bound to (Old BIND)

    DWORD   ForwarderChain;                 // -1 if no forwarders
    DWORD   Name;                          //导入模块的名称的偏移,比如"kernel32.dll"
    DWORD   FirstThunk;                //   导入函数的地址的偏移

} IMAGE_IMPORT_DESCRIPTOR;

 

导入了多少个模块的函数就有多少个 IMAGE_IMPORT_DESCRIPTOR结构,因此获取的时候可以通过一个 PIMAGE_IMPORT_DESCRIPTOR指针循环枚举

知道某个结构的FirstThunk值为0 就停止。

FirstThunk 还有OriginalFirstThunk 偏移指向的都是IMAGE_THUNK_DATA结构数组(其实就是一个DWORD数组)。这个union结构在不同的时候有不同的意思,由于IAT HOOK肯定是程序运行了。因此我说的是这个模块被加载到内存以后的状态。

typedef struct _IMAGE_THUNK_DATA32 {
    union {
        DWORD ForwarderString;      // PBYTE
        DWORD Function;             // PDWORD
        DWORD Ordinal;
        DWORD AddressOfData;        // PIMAGE_IMPORT_BY_NAME
    } u1;
} IMAGE_THUNK_DATA32;

当用FirstThunk的时候 要用的就是Function,此时FUNCTION里面的内容已经不再是偏移了,如下中p->u1.Function就已经是某一个导入函数的地址。而 p->u1.Function的地址就是我们需要的东西了,我们修改地址内的值就可以让CALL 到我们的函数里去。

p->u1.Function=我们的函数地址;

PIMAGE_THUNK_DATA p=(PIMAGE_THUNK_DATA)((BYTE*)h_module+pId->FirstThunk);
然后可以用p++循环得到所有导入函数的实际地址,注意是导入函数的地址,不是保存导入函数的地址。

例如 是 p->u1.Function=0x77f14840就是函数地址(假设为 GetModuleHandleA)。若要取得保存它的地址,只要& p->u1.Function 就行了,例如得到& p->u1.Function=0x42204 (假设);

所有 该模块中调用ACLL GetModuleHandleA的地方会编译为  CALL [42204],我们修改42202里面的值就可以CALL到我们的函数了。

 

 

 

用OriginalFirstThunk的时候需要用的是AddressOfData ,AddressOfData还是个偏移( PIMAGE_IMPORT_BY_NAME),这个偏移指向了一个PIMAGE_IMPORT_BY_NAME,然后这指针这里的这个结构有两部分,前一部分的WORD是函数的序号(hint),后一部分就是函数的字符串ASCII

 PIMAGE_THUNK_DATA p2=(PIMAGE_THUNK_DATA)((BYTE*)h_module+pId->OriginalFirstThunk);

(BYTE*)h_module+p2->u1.AddressOfData就是 HINT

(char*)((BYTE*)h_module+p2->u1.AddressOfData+2);这就是函数名;

 

然后p2++;循环

这里的 p p2的数据是一一对应的,

所以同时p++;p2++

就可以通过函数名字获得函数的地址以及保存它的地址了。

传入模块句柄,需要IAT HOOK的函数名 ,这个函数所在的模块名。成功返回保存函数地址的地址。否则返回0;

 

DWORD Addr=ReadIATTable(GetModuleHandle(NULL),"GetCurrentThreadId","Kernel32.dll");
 printf("\n\n0x%08x\n\n",Addr);

最后输出 0XD82008

而0XD82008里面保存的是 7712F1B2 也就是GetCurrentThreadId的地址,修改0XD82008里面的值就可以达到IAT hook的效果

 注意,如果函数不是用名字导入的。而是用序号导入的,那么p2->u1.AddressOfData 以及p->u1.Function里面的DWORD数据的最高位就是1 后一个WORD就是导入序号

比如p2->u1.AddressOfData ==0X80000010==p->u1.Function这就表示该函数是用序号导入的

 

DWORD ReadIATTable(HMODULE h,char * funcname,char *modulename)
{
 if(h==NULL)
  return 0;
 PIMAGE_DOS_HEADER pDosHeader=(PIMAGE_DOS_HEADER)h;
 PIMAGE_NT_HEADERS pNtHeader=(PIMAGE_NT_HEADERS)\
  (LONG(h)+pDosHeader->e_lfanew);
 PIMAGE_OPTIONAL_HEADER pOpt=&pNtHeader->OptionalHeader;
 PIMAGE_IMPORT_DESCRIPTOR pId=(PIMAGE_IMPORT_DESCRIPTOR)((BYTE*)h+pOpt->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
 
 while(pId->FirstThunk)
 {
  PIMAGE_THUNK_DATA p=(PIMAGE_THUNK_DATA)((BYTE*)h+pId->FirstThunk);
  PIMAGE_THUNK_DATA p2=(PIMAGE_THUNK_DATA)((BYTE*)h+pId->OriginalFirstThunk);

  if(strcmpi(modulename,(char*)((BYTE*)h+pId->Name))==0)
  {
   while(p->u1.Function)
   {
    if(strcmpi(funcname,\
     (char*)((BYTE*)h+p2->u1.AddressOfData+2))==0)
    {
    return (DWORD)&p->u1.Function;
    }
    p2++;

    p++;
   }
  }
  pId++;
 }
 return 0;
}

 

 

posted @ 2012-11-01 22:44  IamHuskar  阅读(556)  评论(0编辑  收藏  举报