【问题标题】:How to Modify Import Address Table for Run time Loaded DLL如何修改运行时加载的 DLL 的导入地址表
【发布时间】:2012-07-20 12:04:42
【问题描述】:

我想挂钩在运行时从加载的 DLL 调用的函数,我使用了“Windows Via C/C++”一书中的 CAPIHook 类(DLL 注入由 Install System Wide hook 完成,而 The hooking 由 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
   }
}

作者添加了cmets来添加这个功能,但是不知道怎么做

注意:该函数只有在导出模块的情况下才能被钩子 已经加载。一个解决方案可能是存储函数 姓名为会员;然后,在挂钩的 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);
}

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

【问题讨论】:

  • 您试图阻止 dll 注入?因为一个简单的JNZ 就可以绕过整个事情
  • 更改ReplaceIATEntryInOneMod以返回错误代码,以便您知道条目是否已成功替换,或者是否找到了条目。

标签: c++ winapi hook dll-injection setwindowshookex


【解决方案1】:

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

互联网上有一些关于如何执行此操作的示例。这是我刚刚找到的一个: http://board.cheat-project.com/showthread.php?t=10633

【讨论】:

  • 能否解释一下您关于挂钩 EAT 而不是 IAT 的意思?
  • 就是写在导入的DLL的导出地址表中,而不是写在自己模块的导入地址表中。如果您正在动态加载 dll 并从中调用某些内容,那么您正在调用的函数在模块的 IAT 中将没有条目。然而,它会出现在它自己的 DLL 的 EAT 中。
  • 不幸的是,您提供的链接不再有效 - 也许您可以将其替换为另一个有效的链接(或在您的答案中包含一些代码)?
  • @EddieBytes,我也需要。你能回答乔纳森的这个要求吗?
  • @Wajdy Essam,您可以根据 Eddie 的回答使用解决方案(代码)更新您的问题吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-18
  • 1970-01-01
相关资源
最近更新 更多