I want to intercept functions called from a loaded DLL at runtime, I used the CAPIHook class from the book "Windows Via C / C ++" (DLL Injecting made by installing System Wide hook and intercepting using Modify IAT), but this code works only if the dll name / characters exist in the IAT in the executable. (i.e. for implicit DLL binding)
this is the dll code:
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); }
these are hook functions:
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 replacement method:
void CAPIHook::ReplaceIATEntryInOneMod(PCSTR pszCalleeModName, PROC pfnCurrent, PROC pfnNew, HMODULE hmodCaller) { // Get the address of the module 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 functions for (; pImportDesc->Name; pImportDesc++) { PSTR pszModName = (PSTR) ((PBYTE) hmodCaller + pImportDesc->Name); if (lstrcmpiA(pszModName, pszCalleeModName) == 0) { // Get caller import address table (IAT) for the callee 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 } }
the author added comments to add this functionality, but I'm not sure how to do it
Note: the function can only be enabled if the export module is already loaded. The solution may be to keep the member name function; then in the handlers processed by LoadLibrary *, analyze the list of CAPIHook instances, check if pszCalleeModName is the name of the loaded module to bind its export table, and reload the import tables of all loaded modules.
he also writes this in a book, but again I donβt know what to do
A possible solution is to use the LoadLibrary * functions with hooks to detect when the module exports an unsupported hook function and then do two things:
Fix the import table of the already loaded module again, since it can now be called GetProcAddress and get a pointer to the original execution of the function to intercept. Note that the name of the function must be saved as a member of the class and set to the constructor.
Directly update this function with hooks in the export address table of the export module, as shown in Replace the functionEATEntryInOneMod. Thus, all new modules calling the hooked function will call our handler
I am trying to change the IAT after loading the DLL, but my connection function is not called
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); }
so how to change this code to handle dynamic boot case?