如何修改运行时加载DLL的导入地址表



我想在运行时挂起从加载的DLL调用的函数,我使用了《Windows Via C/C++》一书中的类CAPIHook(DLL注入由Install System Wide钩子完成,挂起由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 %Srn"), 
         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并获取指向原始地址的指针实现函数挂钩。请注意函数需要作为类成员存储,并在构造函数。

直接更新的导出地址表中的此挂钩函数导出模块,如替换EATEntryInOneMod函数。这样,所有新模块调用挂钩函数将调用我们的处理程序

我试图在加载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);
}

那么,如何修改这些代码来处理动态加载情况呢?

我以前做过。你想要的是EAT挂钩而不是IAT。此外,挂钩:LoadLibrary API本身,以便您知道何时加载DLL,并在加载后从DLL挂钩所请求的API。

互联网上有一些关于如何做到这一点的例子。这是我刚刚发现的一个:http://board.cheat-project.com/showthread.php?t=10633

最新更新