在注入的DLL中调用函数

时间:2012-04-07 19:36:13

标签: c++ dll-injection

我想在我已经注入的DLL的远程进程中调用一个函数。

我已成功将我的DLL注入:

CreateRemoteThread(pHandle, NULL, 0, (LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle("kernel32"), "LoadLibraryA"), pLibRemote, 0, NULL);

执行DllMain并且DLL以待机模式运行。我想做的是以某种方式调用远程加载的DLL以便做一些工作。

我尝试过导出这样的函数:

extern "C" __declspec(dllexport) void MyFunc(void)

然后执行如下函数:

CreateRemoteThread(pHandle, NULL, 0, (LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle("mydll"), "MyFunc"), NULL, 0, NULL);

但会导致崩溃。

我该如何解决这个问题?

2 个答案:

答案 0 :(得分:13)

调用GetModuleHandle将获得DLL的基础,因为它映射到您的进程(如果有的话)。所以你需要做的是首先确保导出DLL中的函数。您可以像您一样完成或创建.def文件,如图所示here。此后:

理论上

  1. 将DLL注入目标进程并获取
  2. 加载的基址
  3. 将DLL注入当前进程。使用GetProcAddress查找导出的函数与DLL的基数之间的偏移量。
  4. 将此偏移量添加到从此位置的步骤1 CreateRemoteThread获得的基地址。

  5. 在实践中

    在进行DLL注入时,您可以获得将DLL加载到目标中的基础。

    HMODULE hInjected;
    
    hThread = CreateRemoteThread( hProcess, NULL, 0,
          (LPTHREAD_START_ROUTINE)( GetProcAddress( hMod,
          "LoadLibraryW" ) ), lpAddress, 0, NULL );
    
    // Locate address our payload was loaded
    if( hThread != 0 ) {
      WaitForSingleObject( hThread, INFINITE );
      GetExitCodeThread( hThread, ( LPDWORD )&hInjected );
      CloseHandle( hThread );
    }
    

    hInjected将成为注入DLL的基础。然后我有另一个功能:

    void* GetPayloadExportAddr( LPCWSTR lpPath, HMODULE hPayloadBase, LPCSTR lpFunctionName ) {
      // Load payload in our own virtual address space
      HMODULE hLoaded = LoadLibrary( lpPath );
    
      if( hLoaded == NULL ) {
        return NULL;
      } else {
        void* lpFunc   = GetProcAddress( hLoaded, lpFunctionName );
        DWORD dwOffset = (char*)lpFunc - (char*)hLoaded;
    
        FreeLibrary( hLoaded );
        return (DWORD)hPayloadBase + dwOffset;
      }
    }
    

    这样做首先将有效负载加载到我们自己的虚拟地址空间中。然后, 我们可以使用GetProcAddress来获取导出函数的地址。从这里,我们可以从DLL的基础获得函数的偏移量。将此偏移量添加到我们之前获得的hInjected将告诉我们应该在哪里进行CreateRemoteThread调用。所以你可以这样打电话:

    BOOL InitPayload( HANDLE hProcess, LPCWSTR lpPath, HMODULE hPayloadBase, HWND hwndDlg ) {
      void* lpInit = GetPayloadExportAddr( lpPath, hPayloadBase, "Init" );
      if( lpInit == NULL ) {
        return FALSE;
      } else {
        HANDLE hThread = CreateRemoteThread( hProcess, NULL, 0,
            lpInit, hwndDlg, 0, NULL );
    
        if( hThread == NULL ) {
          return FALSE;
        } else {
          CloseHandle( hThread );
        }
      }
    
      return TRUE;
    }
    

    这是从old project I have中删除的所有代码。欢迎你接受代码并随心所欲地做任何事情,但我知道如果我现在要改写代码,我会做很多不同的事情。

答案 1 :(得分:3)

如果您将32位DLL注入32位进程,Mike的答案会有效。

如果要将64位DLL注入64位进程,则无法从GetExitCodeThread获取DLL的基址,因为它只会为您提供64位地址的低32位。

要在这种情况下获取正确的地址,您必须将一段代码写入调用LoadLibrary的进程(将结果存储在进程内存中的特定位置),执行此代码块(使用{ {1}}),然后使用CreateRemoteThread从该位置回读地址。

您可以在此处找到有关此内容的更多详细信息(包括PowerShell和ASM代码): http://clymb3r.wordpress.com/2013/05/26/implementing-remote-loadlibrary-and-remote-getprocaddress-using-powershell-and-assembly/

然后,您可以像迈克描述的那样计算导出函数的偏移量,但要注意将差异存储在64位值中,而不是存储在DWORD(32位)中。