原理
如果程序中使用了静态链接库文件,那么PE文件中就会有关于这些函数的导入表的存在,程序通过调用IAT表来定位函数地址从而实现调用。如果将这些已经确定好的函数地址给替换成为恶意函数地址,那么就会欺骗 程序运行恶意函数。
编码测试
- IDE: VS 2008
- APP:DLL TYPE
- OS: WINDOWS XP SP3
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "stdafx.h"
#include <windows.h>
//定义函数指针,和CreateFileW一致
typedef HANDLE ( WINAPI *CRETEFILEW)
(
LPCTSTR,
DWORD,
DWORD ,
LPSECURITY_ATTRIBUTES,
DWORD ,
DWORD ,
HANDLE
);
CRETEFILEW dwCreateFileWAddr = 0;
//msdn的搬运工T_T
// HANDLE WINAPI CreateFile(
// _In_ LPCTSTR lpFileName,
// _In_ DWORD dwDesiredAccess,
// _In_ DWORD dwShareMode,
// _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
// _In_ DWORD dwCreationDisposition,
// _In_ DWORD dwFlagsAndAttributes,
// _In_opt_ HANDLE hTemplateFile
// );
//fake func replace the one above
HANDLE WINAPI myCreateFile(
_In_ LPCTSTR lpFileName,
_In_ DWORD dwDesiredAccess,
_In_ DWORD dwShareMode,
_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
_In_ DWORD dwCreationDisposition,
_In_ DWORD dwFlagsAndAttributes,
_In_opt_ HANDLE hTemplateFile
)
{
WCHAR wFileName[MAX_PATH] ={0};
wcscpy(wFileName,lpFileName);
if (wcscmp(wcslwr(wFileName),L"c:\\test.txt")==0)
{
if (MessageBoxW(NULL,L"OPEN FILE?",L"NOTICE",MB_YESNO)==IDYES)
{
return dwCreateFileWAddr( //为什么没有用createFileW,而使用的是dwCreateFile呢,因为iat已经被劫持了,否则就成了递归,永远到不了真正的实现了。
lpFileName,
dwDesiredAccess,
dwShareMode,
lpSecurityAttributes,
dwCreationDisposition,
dwFlagsAndAttributes,
hTemplateFile
);
}
else
{
return INVALID_HANDLE_VALUE;
}
}
else
{
return dwCreateFileWAddr(
lpFileName,
dwDesiredAccess,
dwShareMode,
lpSecurityAttributes,
dwCreationDisposition,
dwFlagsAndAttributes,
hTemplateFile
);
}
}
VOID HookNotePadIAT()
{
//get iid_addr
HMODULE notepad_module = GetModuleHandleA(NULL);
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)notepad_module;
PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)notepad_module+pDosHeader->e_lfanew);
DWORD RVA_FirstIID = pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
PIMAGE_IMPORT_DESCRIPTOR pIID=(PIMAGE_IMPORT_DESCRIPTOR)((DWORD)notepad_module+RVA_FirstIID);
//PIMAGE_IMPORT_DESCRIPTOR pIIDtemp =pIID;
//real addr of "CreateFileW"
HMODULE handle=LoadLibraryA("kernel32.dll");
DWORD dwFuncAddr = (DWORD)GetProcAddress(handle,"CreateFileW");
//find the IID remains 'kernel32.dll'
BOOL isExist = FALSE;
while (pIID->Name)
{
char* dllname=(char*)(pIID->Name+(DWORD)notepad_module);
char szName[MAXBYTE]={0};
strcpy(szName,dllname);
if (strcmp(strlwr(szName),"kernel32.dll")==0) //wrong here KERNEL32.dll
{
isExist = TRUE;
break;
}
pIID++;
}
//check the iat addr is right ?
if (isExist==TRUE)
{
isExist= FALSE;
PIMAGE_THUNK_DATA pIAT=(PIMAGE_THUNK_DATA)(pIID->FirstThunk+(DWORD)notepad_module); //forget the baseimage...
while (pIAT->u1.Function)
{
DWORD* pAddr=(DWORD*)(&pIAT->u1.Function); //get IAT addr
if (*pAddr == dwFuncAddr) //check contents of iat if right.
{
printf("isExist\n");
isExist=TRUE;
dwCreateFileWAddr = (CRETEFILEW)*pAddr; //real addr.
DWORD dwMyHookAddr = (DWORD) myCreateFile;
WriteProcessMemory(GetCurrentProcess(),(LPVOID)pAddr,&dwMyHookAddr,sizeof(DWORD),NULL); //replace the iat content
break;
}
pIAT++;
}
}
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
HookNotePadIAT();
break;
}
return TRUE;
}
编码说明
上述代码原理:
1. 首先找到第一个IID的地址,用pIID
来保存。
2. 遍历IID,通过查找name字段
,来确定程序中目标函数所在的DLL在导入表中,找到的IID指针用pIID
来表示。
3. 用动态调用的方式得到目标函数的地址,保存为dwFuncAddr
。
4. 读取PIID
指向的IAT表,检查IAT表中的内容和动态获取的地址是否一致。
5. 内存中替换,写入伪造的函数地址。
注意
在恶意函数中,有时候必须要实现被挂钩函数的功能。在
inlineHook
中,通过unhook
操作来恢复内存。而在IAT挂钩中,首先保存了原来的函数入口地址。不管使用什么方式挂钩,原来的内容一定要通过一个变量保存下来。编译与运行过程出现的问题:
- 在查询IID的时候,
if (strcmp(strlwr(szName),"kernel32.dll")==0) //wrong here KERNEL32.dll
之前没有转小写,调试后发现kernel32.dll为KERNEL32.dll
。 while (pIAT->u1.Function)
测试错误,调试之后发现问题是pIAT
忘了没有加基址,FirstThunk
是个RVA
。- 不同类型的指针在使用时一定需要转化,一般而言,函数指针在赋值的时候必须转化。
- DLL主函数中:case不一定要全写,本例中就写了一个目标消息
DLL_PROCESS_ATTACH
这也是可以的。
- 在查询IID的时候,
证明
打开notepad.exe后用dlljector.exe注入此进程
C:\Documents and Settings\Administrator\桌面>DLLInjector.exe C:\VC6\MyProjects\i
atHook_dll\Debug\iatHook_dll.dll 456
有意思的是选中文件的时候也会调用CreateFileW
.
弹窗选择否之后,返回INVALID_HANDLE_VALUE
,也即打开失败。