接着我们继续来分析IMAGE_IMPORT_DESCRIPTOR
导入结构中的Name
字段,其对应的是第一张图中的红色部分0001A54A
将该偏移与基址00400000
相加后直接定位过去,可以看到0041A54A
对应的字符串正是USER32.dll
动态链接库,而后面会有两个00
标志着字符串的结束。
最后我们来分析IMAGE_IMPORT_DESCRIPTOR
中最复杂的一个字段OriginalFirstThunk
为什么说它复杂呢?是因为他的内部并不是一个数值而是嵌套了另一个结构体 IMAGE_THUNK_DATA
,我们先来看一下微软对该结构的定义:
typedef struct _IMAGE_THUNK_DATA32
{
union
{
DWORD ForwarderString; // PBYTE
DWORD Function; // PDWORD
DWORD Ordinal; // 序号
DWORD AddressOfData; // 指向 PIMAGE_IMPORT_BY_NAME
} u1;
} IMAGE_THUNK_DATA32;
typedef IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32;
接着来找到OriginalFirstThunk
字段在内存中的位置,由第一张图可知,图中的标红部分第一个四字节0001A38C
就是它。我们加上基址00400000
然后直接怼过去,并结合上方的结构定义研究一下;
该结构中我们需要关注AddressOfData
结构成员,该成员中的数据最高位(红色)如果为1(去掉1)说明是函数的导出序号,而如果最高位为0则说明是一个指向IMAGE_IMPROT_BY_NAME
结构(导入表)的RVA(蓝色)地址,此处因为我们找的是导入表所以最高位全部为零。
我们以上图中的第一个RVA地址0001A53E
与基址相加,来看下该AddressOfData
字段中所指向的内容是什么。
上图黄色部分是编译器生成的,而蓝色部分则为LoadIconW
字符串与FirstThunk
中的0041A15C
地址指针是相互对应的,而最后面的00
则表明字符串的结束,对比以下结构声明就很好理解了。
typedef struct _IMAGE_IMPORT_BY_NAME
{
WORD Hint; // 编译器生成的
CHAR Name[1]; // 函数名称,以0结尾的字符串
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
为了能更加充分的理解,笔者为大家用Excel
画了一张图,如下所示:
如上图IMAGE_IMPORT_DESCRIPTO
导入表结构中的FirstThunk
和OriginalFirstThunk
分别指向两个相同的IMAGE_THUNK_DATA
结构,其中内存INT(Improt Name Table)
表中存储的就是导入函数的名称,而IAT(Improt Address Table)
表中存放的是导入函数的地址,他们都共同指向IMAGE_IMPORT_BY_NAME
结构,而之所以使用两份IMAGE_THUNK_DATA
结构,是为了最后还可以留下一份备份数据用来反过来查询地址所对应的导入函数名,看了这张图再结合上面的实验相信你已经理解了;
实现导入表劫持
在之前的内容中我们已经分析了导入表结构,接着我们将实现对导入表的劫持功能,我们需要使用IAT Hook
就必须要首先找到导入表中特定的函数地址,首先我们先实现枚举定位功能,通过枚举程序中的IMAGE_IMPORT_DESCRIPTOR
结构在其中找到对应的导入模块user32.dll
并在该模块内寻找对应的函数名MessageBox
,通过使用双层循环即可实现对特定导入函数的枚举,如下是一段枚举导入表函数的功能;
#include <iostream>
#include <Windows.h>
#include <Dbghelp.h>
#pragma comment (lib, "Dbghelp")
int main(int argc, char* argv[])
{
// 打开文件
HANDLE hFile = CreateFile("d://lyshark.exe", GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
// 创建内存映射
HANDLE hMap = CreateFileMapping(hFile, NULL, PAGE_READONLY | SEC_IMAGE, 0, 0, 0);
LPVOID lpBase = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
// 得到DOS头部
PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER)lpBase;
if (pDosHdr->e_magic != IMAGE_DOS_SIGNATURE)
{
UnmapViewOfFile(lpBase);
return -1;
}
// 得到NT头部
PIMAGE_NT_HEADERS pNtHdr = (PIMAGE_NT_HEADERS)((DWORD)lpBase + pDosHdr->e_lfanew);
if (pNtHdr->Signature != IMAGE_NT_SIGNATURE)
{
return -1;
}
DWORD dwNum = 0;
// 数据目录表
PIMAGE_IMPORT_DESCRIPTOR pImpDes = (PIMAGE_IMPORT_DESCRIPTOR)
ImageDirectoryEntryToData(lpBase, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &dwNum);
PIMAGE_IMPORT_DESCRIPTOR pTmpImpDes = pImpDes;
// 枚举导入表
while (pTmpImpDes->Name)
{
printf("[*] 链接库名称: %s \n", (DWORD)lpBase + (DWORD)pTmpImpDes->Name);
PIMAGE_THUNK_DATA thunk = (PIMAGE_THUNK_DATA)(pTmpImpDes->FirstThunk + (DWORD)lpBase);
int index = 0;
while (thunk->u1.Function)
{
if (thunk->u1.Ordinal & IMAGE_ORDINAL_FLAG)
{
printf("导入序号: %08X \r\n", thunk->u1.Ordinal & 0xFFFF);
}
else
{
PIMAGE_IMPORT_BY_NAME pImName = (PIMAGE_IMPORT_BY_NAME)thunk->u1.Function;
printf("函数名称: %-30s \t", (DWORD)lpBase + pImName->