Name);
DWORD dwAddr = (DWORD)((DWORD *)((DWORD)pNtHdr->OptionalHeader.ImageBase
+ pTmpImpDes->FirstThunk) + index);
printf("导入地址: 0x%08x \r\n", dwAddr);
}
thunk++;
index++;
}
pTmpImpDes++;
}
system("pause");
return 0;
}
读者可自行编译并运行上方代码片段,当运行后即可输出d://lyshark.exe
程序中所有的导入库与该库中的导入函数信息,输出效果如下图所示;
当有了枚举导入表功能,则下一步是寻找特定函数的导入地址,以MessageBoxA
函数为例,该函数的导入地址是0x0047d3a0
此时我们只需要在此处进行挂钩,并转向即可实现劫持效果,具体来说这个流程如下所示;
- 首先需要编写DLL文件,在DLL文件中找出
MessageBox
的原函数地址。
- 接着通过代码的方式找到
DOS/NT/FILE-Optional
头偏移地址。
- 通过
DataDirectory[1]
数组得到导入表的起始RVA
并与ImageBase
基址相加得到VA
内存地址。
- 循环遍历导入表中的
IAT
表,找到与MessageBox
地址相同的4字节位置。
- 找到后通过
VirtualProtect
设置内存属性可读写,并将自己的函数地址写入到目标IAT
表中。
- 没有找到的话直接
pFirstThunk++
循环遍历后面的4字节位置,直到找到为止。
- 最后将自身弹窗回调函数
MyMessageBoxA
与原函数做替换,则此时即可实现劫持功能。
通过上述开发流程,读者应该可以自行编写出这段劫持代码,如下代码则是完整的劫持实现,我们通过自定义MyMessageBoxA
函数,并通过IATHook()
实现对内存中导入函数地址的替换,此时当有新的访问时则会自动跳转到自定义函数上执行,执行结束后既跳转回OldMessageBoxA
原函数上返回。
#include <iostream>
#include <Windows.h>
#include <Dbghelp.h>
#pragma comment (lib, "Dbghelp")
typedef int(WINAPI *pfMessageBoxA)(HWND, LPCSTR, LPCSTR, UINT);
pfMessageBoxA OldMessageBoxA = NULL;
// 我们自己的回调函数
int WINAPI MyMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)
{
return OldMessageBoxA(hWnd, "hello lyshark", lpCaption, uType);
}
// 得到进程NT头部
PIMAGE_NT_HEADERS GetLocalNtHead()
{
DWORD dwTemp = NULL;
PIMAGE_DOS_HEADER pDosHead = NULL;
PIMAGE_NT_HEADERS pNtHead = NULL;
// 取自身ImageBase
HMODULE ImageBase = GetModuleHandle(NULL);
// 取pDosHead地址
pDosHead = (PIMAGE_DOS_HEADER)(DWORD)ImageBase;
dwTemp = (DWORD)pDosHead + (DWORD)pDosHead->e_lfanew;
// 取出NtHead头地址
pNtHead = (PIMAGE_NT_HEADERS)dwTemp;
return pNtHead;
}
// 劫持函数
void IATHook()
{
PVOID pFuncAddress = NULL;
// 取Hook函数地址
pFuncAddress = GetProcAddress(GetModuleHandleA("user32.dll"), "MessageBoxA");
// 保存原函数指针
OldMessageBoxA = (pfMessageBoxA)pFuncAddress;
// 获取到程序自身NtHead
PIMAGE_NT_HEADERS pNtHead = GetLocalNtHead();
PIMAGE_FILE_HEADER pFileHead = (PIMAGE_FILE_HEADER)&pNtHead->FileHeader;
PIMAGE_OPTIONAL_HEADER pOpHead = (PIMAGE_OPTIONAL_HEADER)&pNtHead->OptionalHeader;
// 找出导入表偏移
DWORD dwInputTable = pOpHead->DataDirectory[1].VirtualAddress;
DWORD dwTemp = (DWORD)GetModuleHandle(NULL) + dwInputTable;
PIMAGE_IMPORT_DESCRIPTOR pImport = (PIMAGE_IMPORT_DESCRIPTOR)dwTemp;
PIMAGE_IMPORT_DESCRIPTOR pCurrent = pImport;
// 导入表子表,IAT存储函数地址表
DWORD *pFirstThunk;
// 遍历导入表
while (pCurrent->Characteristics && pCurrent->FirstThunk != NULL)
{
// 找到内存中的导入表
dwTemp = pCurrent->FirstThunk + (DWORD)GetModuleHandle(NULL);
// 赋值 pFirstThunk
pFirstThunk = (DWORD *)dwTemp;
// 不为NULl说明没有结束
while (*(DWORD*)pFirstThunk != NULL)
{
// 相等则找到了
if (*(DWORD*)pFirstThunk == (DWORD)OldMessageBoxA)
{
DWORD oldProtected;
// 开启写权限
VirtualProtect(pFirstThunk, 0x1000, PAGE_EXECUTE_READWRITE, &oldProtected);
dwTemp = (DWORD)MyMessageBoxA;
// 将MyMessageBox地址拷贝替换
memcpy(pFirstThunk, (DWORD *)&dwTemp, 4);
// 关闭写保护
VirtualProtect(pFirstThunk, 0x1000, oldProtected, &oldProtected);
}
// 继续递增循环
pFirstThunk++;
}
// 每次是加1个导入表结构
pCurrent++;
}
}
BOOL APIENTRY DllMain(HANDLE