7

実行時にロードされた 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);
}

これはフック関数です:

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* 関数を使用して、モジュールがパッチが適用されていないフックされた関数をエクスポートしていることを検出し、次に 2 つのアクションを実行することです。

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);
}

では、このコードを変更して動的ローディングケースを処理するにはどうすればよいですか?

4

1 に答える 1

8

私は前にこれをやったことがあります。あなたが望むのは、IAT ではなく EAT フッキングです。また、::LoadLibrary API 自体をフックして、DLL がいつロードされるかを把握し、ロード後に DLL から要求された API をフックします。

これを行う方法については、インターネット上にいくつかの例があります。これは私が今見つけたものです: http://board.cheat-project.com/showthread.php?t=10633

于 2012-07-25T14:24:18.753 に答える