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 结构的RVA。OriginalFirstThunk 和 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的原理和思想仍值得进一步学习。
原文地址
http://www.cnblogs.com/71dao/archive/2008/12/23/1360807.html