跳转至

放置 IAT 钩子

从这里开始,放置钩子只是找到正确函数名并替换函数地址的问题。你可以在嵌套的 while 循环内找到名称,如清单 8-8 所示。

char* importFunctionName =
    pointMemory<char>(baseAddr + 
(DWORD)thunkData->u1.AddressOfData + 2);

清单 8-8:找到函数名

每个 thunk 的函数名存储在模块内 thunkData->u1.Address OfData + 2 字节处,所以你可以把该值加到模块基址上,在内存中定位函数名。

获得函数名指针后,用 strcmp() 检查它是否是目标函数,像这样:

if (strcmp(importFuncName, funcName) == 0) {
    // 最后一步发生在这里
}

一旦你用名称定位目标函数,只需用你自己的函数地址覆盖函数地址。与函数名不同,函数地址存储在每个导入描述符开头的数组中。使用 thunk 循环中的 n,你终于能设置钩子,如清单 8-9 所示。

auto vfTable = pointMemory<DWORD>(baseAddr + 
impDesc->FirstThunk);
DWORD original = vfTable[n];
 auto oldProtection = protectMemory<DWORD>((DWORD)
&vfTable[n], PAGE_READWRITE);
 vfTable[n] = newFunc;
protectMemory<DWORD>((DWORD)&vfTable[n], 
oldProtection);

清单 8-9:找到函数地址

这段代码通过把第一个 thunk 的地址加到模块基址上来定位当前描述符的 VF 表。VF 表是函数地址数组,所以代码用 n 变量作为索引定位目标函数地址。

找到地址后,清单 8-9 中的代码就像一个典型的 VF 钩子:它存储原始函数地址,把 VF 表中索引 n 的保护设置为 PAGE_READWRITE①,把新函数地址插入 VF 表②,最后恢复旧保护。

如果你把清单 8-4 到 8-9 的代码拼接在一起,最终的 IAT 钩子函数看起来像清单 8-10。

DWORD hookIAT(const char* funcName, DWORD newFunc)
{
    DWORD baseAddr = (DWORD)GetModuleHandle(NULL);
baseAddr);
    if (dosHeader->e_magic != 0x5A4D)
        return 0;
    auto optHeader =
        pointMemory<IMAGE_OPTIONAL_HEADER>(baseAddr
 + dosHeader->e_lfanew + 24);
    if (optHeader->Magic != 0x10B)
        return 0;
    auto IAT =
        optHeader->DataDirectory[IMAGE_DIRECTORY_EN
TRY_IMPORT];
    if (IAT.Size == 0 || IAT.VirtualAddress == 0)
        return 0;
    auto impDesc =
        pointMemory<IMAGE_IMPORT_DESCRIPTOR>(baseAd
dr + IAT.VirtualAddress);
    while (impDesc->FirstThunk) {
        auto thunkData =
            pointMemory<IMAGE_THUNK_DATA>(baseAddr 
+ impDesc->OriginalFirstThunk);
        int n = 0;
        while (thunkData->u1.Function) {
            char* importFuncName = 
pointMemory<char>
                (baseAddr + 
(DWORD)thunkData->u1.AddressOfData + 2);
            if (strcmp(importFuncName, funcName) 
                auto vfTable = 
pointMemory<DWORD>(baseAddr + impDesc->FirstThunk);
                DWORD original = vfTable[n];
                auto oldProtection =
                    protectMemory<DWORD>((DWORD)&vf
Table[n], PAGE_READWRITE);
                vfTable[n] = newFunc;
                protectMemory<DWORD>((DWORD)&vfTabl
e[n], oldProtection);
                return original;
            }
            n++;
            thunkData++;
        }
        impDesc++;
    }
}

清单 8-10:完整的 IAT 钩子函数

这是我们目前组合起来最复杂的代码,压缩到一页时很难阅读。如果你还没完全理解它在做什么,在继续之前,你可能想研究一下本书资源文件中的示例代码。