我想挂钩在运行时从加载的 DLL 调用的函数,我使用了“Windows Via C/C++”一书中的 CAPIHook 类(由 Install System Wide hook 完成的 DLL Injecting 和 Modify IAT 完成的挂钩)但是这个仅当可执行文件的 IAT 中存在 DLL 名称/符号时,代码才起作用。(即用于隐式 DLL 链接)
这是 DLL 代码:
CAPIHook::CAPIHook(PSTR pszCalleeModName, PSTR pszFuncName, PROC pfnHook) {
// Note: the function can be hooked only if the exporting module
// is already loaded. A solution could be to store the function
// name as a member; then, in the hooked LoadLibrary* handlers, parse
// the list of CAPIHook instances, check if pszCalleeModName
// is the name of the loaded module to hook its export table and
// re-hook the import tables of all loaded modules.
m_pNext = sm_pHead; // The next node was at the head
sm_pHead = this; // This node is now at the head
// Save information about this hooked function
m_pszCalleeModName = pszCalleeModName;
m_pszFuncName = pszFuncName;
m_pfnHook = pfnHook;
m_pfnOrig = GetProcAddressRaw(GetModuleHandleA(pszCalleeModName), m_pszFuncName);
// If function does not exit,... bye bye
// This happens when the module is not already loaded
if (m_pfnOrig == NULL)
{
wchar_t szPathname[MAX_PATH];
GetModuleFileNameW(NULL, szPathname, _countof(szPathname));
wchar_t sz[1024];
StringCchPrintfW(sz, _countof(sz),
TEXT("[%4u - %s] impossible to find %S\r\n"),
GetCurrentProcessId(), szPathname, pszFuncName);
OutputDebugString(sz);
return;
}
// Hook this function in all currently loaded modules
ReplaceIATEntryInAllMods(m_pszCalleeModName, m_pfnOrig, m_pfnHook);
}
这是钩子函数:
HMODULE WINAPI CAPIHook::LoadLibraryA(PCSTR pszModulePath) {
HMODULE hmod = ::LoadLibraryA(pszModulePath);
FixupNewlyLoadedModule(hmod, 0);
return(hmod);
}
HMODULE WINAPI CAPIHook::LoadLibraryW(PCWSTR pszModulePath) {
HMODULE hmod = ::LoadLibraryW(pszModulePath);
FixupNewlyLoadedModule(hmod, 0);
return(hmod);
}
HMODULE WINAPI CAPIHook::LoadLibraryExA(PCSTR pszModulePath,
HANDLE hFile, DWORD dwFlags) {
HMODULE hmod = ::LoadLibraryExA(pszModulePath, hFile, dwFlags);
FixupNewlyLoadedModule(hmod, dwFlags);
return(hmod);
}
HMODULE WINAPI CAPIHook::LoadLibraryExW(PCWSTR pszModulePath,
HANDLE hFile, DWORD dwFlags) {
HMODULE hmod = ::LoadLibraryExW(pszModulePath, hFile, dwFlags);
FixupNewlyLoadedModule(hmod, dwFlags);
return(hmod);
}
更换IAT的方法:
void CAPIHook::ReplaceIATEntryInOneMod(PCSTR pszCalleeModName,
PROC pfnCurrent, PROC pfnNew, HMODULE hmodCaller) {
// Get the address of the module's import section
ULONG ulSize;
// An exception was triggered by Explorer (when browsing the content of
// a folder) into imagehlp.dll. It looks like one module was unloaded...
// Maybe some threading problem: the list of modules from Toolhelp might
// not be accurate if FreeLibrary is called during the enumeration.
PIMAGE_IMPORT_DESCRIPTOR pImportDesc = NULL;
__try {
pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR) ImageDirectoryEntryToData(
hmodCaller, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &ulSize);
}
__except (InvalidReadExceptionFilter(GetExceptionInformation())) {
// Nothing to do in here, thread continues to run normally
// with NULL for pImportDesc
}
if (pImportDesc == NULL)
return; // This module has no import section or is no longer loaded
// Find the import descriptor containing references to callee's functions
for (; pImportDesc->Name; pImportDesc++) {
PSTR pszModName = (PSTR) ((PBYTE) hmodCaller + pImportDesc->Name);
if (lstrcmpiA(pszModName, pszCalleeModName) == 0) {
// Get caller's import address table (IAT) for the callee's functions
PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA)
((PBYTE) hmodCaller + pImportDesc->FirstThunk);
// Replace current function address with new function address
for (; pThunk->u1.Function; pThunk++) {
// Get the address of the function address
PROC* ppfn = (PROC*) &pThunk->u1.Function;
// Is this the function we're looking for?
BOOL bFound = (*ppfn == pfnCurrent);
if (bFound) {
if (!WriteProcessMemory(GetCurrentProcess(), ppfn, &pfnNew,
sizeof(pfnNew), NULL) && (ERROR_NOACCESS == GetLastError())) {
DWORD dwOldProtect;
if (VirtualProtect(ppfn, sizeof(pfnNew), PAGE_WRITECOPY,
&dwOldProtect)) {
WriteProcessMemory(GetCurrentProcess(), ppfn, &pfnNew,
sizeof(pfnNew), NULL);
VirtualProtect(ppfn, sizeof(pfnNew), dwOldProtect,
&dwOldProtect);
}
}
return; // We did it, get out
}
}
} // Each import section is parsed until the right entry is found and patched
}
}
作者添加了注释以添加此功能,但我不知道该怎么做
注意:只有在导出模块已经加载的情况下才能挂钩该函数。一种解决方案是将函数名称存储为成员;然后,在挂钩的 LoadLibrary* 处理程序中,解析 CAPIHook 实例列表,检查 pszCalleeModName 是否是已加载模块的名称以挂钩其导出表并重新挂钩所有已加载模块的导入表。
他也在书上写了这个,但我又不知道该怎么办
一种可能的解决方案是使用挂钩的 LoadLibrary* 函数来检测模块何时导出未修补的挂钩函数,然后执行两个操作:
再次挂钩已加载模块的导入表,因为现在可以调用 GetProcAddress 并获取指向要挂钩的函数的原始实现的指针。请注意,函数的名称需要存储为类成员并在构造函数中设置。
直接在导出模块的导出地址表中更新这个钩子函数,如 ReplaceEATEntryInOneMod 函数的实现所示。这样,所有调用挂钩函数的新模块都会调用我们的处理程序
我尝试在加载 DLL 后修改 IAT,但未调用我的挂钩函数
HMODULE WINAPI CAPIHook::LoadLibraryW(PCWSTR pszModulePath) {
HMODULE hmod = ::LoadLibraryW(pszModulePath);
if (StrCmpIW(pszModulePath, myDLLUnicodeName.c_str()) == 0 ) {
PROC proc = GetProcAddressRaw(GetModuleHandleA(myDLLName.c_str()), myFunctionName.c_str());
if ( proc != NULL ) {
for (CAPIHook* p = sm_pHead; p != NULL; p = p->m_pNext) {
if (StrCmpIA(p->m_pszCalleeModName, myDLLName.c_str()) == 0) {
MessageBox(NULL, L"This is the New Dynamic DLL", L"Test!", 0);
ReplaceIATEntryInAllMods(p->m_pszCalleeModName, proc , p->m_pfnHook);
}
}
}
}
FixupNewlyLoadedModule(hmod, 0);
return(hmod);
}
那么,如何修改此代码以处理动态加载情况?