我的学习笔记之二——修改导入表HOOK API(ring3

it2024-08-12  59

IAT即Import Address Table 是PE(可以理解为EXE)的输入地址表,我们知道一个程序运行时可以要调用多个模块,或都说要调用许多API函数,但这些函数不一定都在EXE本身中,例如你调用Messagebox来显示一个对话框时,你只需要调用它,你并没有编写Messagebox的函数的实现过程,Messagebox的函数的实现过程实际上是在user32.dll这个库文件中,当这个程序运行时会在user32.dll中找到Messagebox并调用它。

那么具体的调入过程是怎样的呢?下面来谈谈其中的重要环节即:IMAGE_IMPORT_BY_NAME,那么IMAGE_IMPORT_BY_NAME的结构到底是什么样子的呢:?下面是引入表图

OriginalFirstThunk IMAGE_IMPORT_BY_NAME FirstThunk

|

   | IMAGE_THUNK_DATA IMAGE_THUNK_DATA IMAGE_THUNK_DATA IMAGE_THUNK_DATA ... IMAGE_THUNK_DATA --->--->--->--->--->---> Function 1 Function 2 Function 3 Function 4 ... Function n <---<---<---<---<---<--- IMAGE_THUNK_DATA IMAGE_THUNK_DATA IMAGE_THUNK_DATA IMAGE_THUNK_DATA ... IMAGE_THUNK_DATA

首先不要被IMAGE_THUNK_DATA这个名字弄糊涂: 它仅是指向 IMAGE_IMPORT_BY_NAME 结构的RVAOriginalFirstThunk FirstThunk 所指向的这两个数组大小取决于PE文件从DLL中引入函数的数目。比如,如果PE文件从kernel32.dll中引入10个函数,那么IMAGE_IMPORT_DESCRIPTOR 结构的 Name1域包含指向字符串"kernel32.dll"RVA,同时每个IMAGE_THUNK_DATA 数组有10个元素。为什么我们需要两个完全相同的数组? 为了回答该问题,我们需要了解当PE文件被装载到内存时,PE装载器将查找IMAGE_THUNK_DATA IMAGE_IMPORT_BY_NAME 这些结构数组,以此决定引入函数的地址。然后用引入函数真实地址来替代由FirstThunk指向的 IMAGE_THUNK_DATA 数组里的元素值。因此当PE文件准备执行时,上图已转换成:

OriginalFirstThunk IMAGE_IMPORT_BY_NAME FirstThunk

|

   | IMAGE_THUNK_DATA IMAGE_THUNK_DATA IMAGE_THUNK_DATA IMAGE_THUNK_DATA ... IMAGE_THUNK_DATA --->--->--->--->--->---> Function 1 Function 2 Function 3 Function 4 ... Function n          Address of Function 1 Address of Function 2 Address of Function 3 Address of Function 4 ... Address of Function n

我们现在要做的是把后面的地址改成我们自己的函数地址,例如当EXE调用Messagebox时让它转入我们的函数,我们处理完后再转入真正的函数地址。

因此API HOOK和其它HOOK存在本质的区别,可以理解为API劫持,说一行道一万不如动手去实践,下面代码演示了如何HOOK本进程中的Messagebox,当本EXE调用Messagebox时会先转入到我们的函数中,请看清是怎样找到PE的引入表并修改的。

#include  < stdio.h > #include  < windows.h > #include  < Dbghelp.h > #pragma  comment(lib,"Dbghelp.lib") #pragma  comment(lib,"User32.lib") typedef  int  (__stdcall  * OLD_MessageBox)( HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption,UINT uType ); OLD_MessageBox g_procOldMessageBox  =  NULL; int  __stdcall HOOK_MessageBox( HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption,UINT uType) {  printf( " %s\t%d\r\n " ,__FUNCTION__,__LINE__);   if  (NULL  !=  g_procOldMessageBox)    return  g_procOldMessageBox(hWnd,lpText, " 不好意思,hook到了! " ,uType);    else    return  MessageBox(hWnd,lpText,lpCaption,uType); ; } int  replace_IAT( const   char   * pDllName, const   char   * pApiName, bool  bReplace) {  HANDLE hProcess  =  ::GetModuleHandle (NULL);  DWORD dwSize  =   0 ;  PIMAGE_IMPORT_DESCRIPTOR pImageImport  =  (PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryEntryToData(hProcess,TRUE,   IMAGE_DIRECTORY_ENTRY_IMPORT, & dwSize);   if  (NULL  ==  pImageImport)    return   1 ;  PIMAGE_IMPORT_BY_NAME pImageImportByName  =  NULL;  PIMAGE_THUNK_DATA  pImageThunkOriginal  =  NULL;  PIMAGE_THUNK_DATA  pImageThunkReal   =  NULL;   while  (pImageImport -> Name)  {    if  ( 0   ==  strcmpi(( char * )((PBYTE)hProcess + pImageImport -> Name),pDllName))   {     break ;   }    ++ pImageImport;  }   if  ( !  pImageImport -> Name)    return   2 ;  pImageThunkOriginal  =  (PIMAGE_THUNK_DATA)((PBYTE)hProcess + pImageImport -> OriginalFirstThunk  );  pImageThunkReal  =  (PIMAGE_THUNK_DATA)((PBYTE)hProcess + pImageImport -> FirstThunk   );   while  (pImageThunkOriginal -> u1.Function)  {    if  ((pImageThunkOriginal -> u1 .Ordinal  &  IMAGE_ORDINAL_FLAG)  !=  IMAGE_ORDINAL_FLAG)   {    pImageImportByName  =  (PIMAGE_IMPORT_BY_NAME)((PBYTE)hProcess + pImageThunkOriginal -> u1 .AddressOfData );     if  ( 0   ==  strcmpi(pApiName,( char * )pImageImportByName -> Name))    {     MEMORY_BASIC_INFORMATION mbi_thunk;     VirtualQuery(pImageThunkReal,  & mbi_thunk,  sizeof (MEMORY_BASIC_INFORMATION));      VirtualProtect(mbi_thunk.BaseAddress,mbi_thunk.RegionSize, PAGE_READWRITE,  & mbi_thunk.Protect);       if  ( true   ==  bReplace)     {      g_procOldMessageBox  = (OLD_MessageBox) pImageThunkReal -> u1.Function;       pImageThunkReal -> u1.Function  =  (DWORD)HOOK_MessageBox;     }      else      pImageThunkReal -> u1.Function  =  (DWORD)g_procOldMessageBox;     DWORD dwOldProtect;      VirtualProtect(mbi_thunk.BaseAddress, mbi_thunk.RegionSize, mbi_thunk.Protect,  & dwOldProtect);       break ;    }   }    ++ pImageThunkOriginal;    ++ pImageThunkReal;  }   return   0 ; } int  main() {  replace_IAT( " User32.dll " , " MessageBoxA " , true );  MessageBox(NULL, " EnumIAT User32.dll MessageBoxA true; " , " 没有Hook " ,MB_OK);  replace_IAT( " User32.dll " , " MessageBoxA " , false );  MessageBox(NULL, " EnumIAT User32.dll MessageBoxA false; " , " 没有Hook " ,MB_OK);   return  getchar(); }

上面的代码修改一下随可用于全局HOOK,但我不看好IAT HOOK,虽然WINDOWS核心编程看好这种做法。因为现在主流操作系统是XP和WINDOWS2003,都是数据执行保护(也可能某个补丁前没有这个功能),它们会保护多数进程在内在中不被修改,所以IAT全局HOOK会被系统拦截从而失败,但也同时发现它能保护进程却不保护模块,因此用INLINE HOOK可能会更可行些。但IAT的原理和思想仍值得进一步学习。  

转载于:https://www.cnblogs.com/71dao/archive/2008/12/23/1360807.html

相关资源:C++基于hook iat改变Messagebox实例
最新回复(0)