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

Waj*_*sam 6 c++ hook winapi dll-injection setwindowshookex

我想在运行时挂钩从加载的DLL调用的函数,我使用了"Windows Via C/C++"一书中的CAPIHook类(通过Install System Wide挂钩完成的DLL注入和通过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);
}
Run Code Online (Sandbox Code Playgroud)

这是钩子函数:

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);
}
Run Code Online (Sandbox Code Playgroud)

替换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
   }
}
Run Code Online (Sandbox Code Playgroud)

作者添加了评论来添加此功能,但我不知道该怎么做

注意:只有在已加载导出模块的情况下才能挂接该功能.解决方案可以是将函数名称存储为成员; 然后,在钩住的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);
}
Run Code Online (Sandbox Code Playgroud)

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

Edd*_*tes 8

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

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

  • 这意味着在导入的DLL的导出地址表中写入而不是在您自己的模块的导入地址表中写入.如果您正在动态加载DLL并从中调用某些内容,则您调用的函数将不会在模块的IAT中输入任何内容.然而,它将出现在它自己的DLL的EAT中. (5认同)
  • 不幸的是,您提供的链接不再有效 - 也许您可以将其替换为另一个(或在您的答案中包含一些代码)? (2认同)