关于c ++:在注入的DLL中调用函数?

Calling a function in an injected DLL?

使用C ++,我有一个创建远程进程并将DLL注入其中的应用程序。 有没有办法让远程应用程序执行从DLL以及创建它的应用程序导出的函数? 是否可以将参数发送到该函数? 请注意,我正在努力避免在DllMain中执行任何操作。


注意:
要获得更好的答案,请参阅下面的更新!


好的,这就是我能够完成此操作的方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
BOOL RemoteLibraryFunction( HANDLE hProcess, LPCSTR lpModuleName, LPCSTR lpProcName, LPVOID lpParameters, SIZE_T dwParamSize, PVOID *ppReturn )
{
    LPVOID lpRemoteParams = NULL;

    LPVOID lpFunctionAddress = GetProcAddress(GetModuleHandleA(lpModuleName), lpProcName);
    if( !lpFunctionAddress ) lpFunctionAddress = GetProcAddress(LoadLibraryA(lpModuleName), lpProcName);
    if( !lpFunctionAddress ) goto ErrorHandler;

    if( lpParameters )
    {
        lpRemoteParams = VirtualAllocEx( hProcess, NULL, dwParamSize, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE );
        if( !lpRemoteParams ) goto ErrorHandler;

        SIZE_T dwBytesWritten = 0;
        BOOL result = WriteProcessMemory( hProcess, lpRemoteParams, lpParameters, dwParamSize, &dwBytesWritten);
        if( !result || dwBytesWritten < 1 ) goto ErrorHandler;
    }

    HANDLE hThread = CreateRemoteThread( hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)lpFunctionAddress, lpRemoteParams, NULL, NULL );
    if( !hThread ) goto ErrorHandler;

    DWORD dwOut = 0;
    while(GetExitCodeThread(hThread, &dwOut)) {
        if(dwOut != STILL_ACTIVE) {
            *ppReturn = (PVOID)dwOut;
            break;
        }
    }

    return TRUE;

ErrorHandler:
    if( lpRemoteParams ) VirtualFreeEx( hProcess, lpRemoteParams, dwParamSize, MEM_RELEASE );
    return FALSE;
}

//...
CStringA targetDll ="injected.dll"

    // Inject the target library into the remote process
PVOID lpReturn = NULL;
RemoteLibraryFunction( hProcess,"kernel32.dll","LoadLibraryA", targetDll.GetBuffer(MAX_PATH), targetDll.GetLength(), &lpReturn );
HMODULE hInjected = reinterpret_cast<HMODULE>( lpReturn );

    // Call our exported function
lpReturn = NULL;
RemoteLibraryFunction( hProcess, targetDll,"Initialize", NULL, 0, &lpReturn );
BOOL RemoteInitialize = reinterpret_cast<BOOL>( lpReturn );


这也可以用于通过指向结构体或联合体的指针将参数发送到远程函数,从而避免了在DllMain中编写任何内容的麻烦。


因此,经过一些精心的测试,看来我以前的回答不是万无一失的
(就此而言,甚至是100%功能的),而且容易崩溃。经过考虑后,我决定采用一种完全不同的方法...使用进程间通信。

请注意...此方法利用了DllMain中的代码。
因此,不要太过分,并且在执行此操作时请务必遵循安全做法,以免最终陷入僵局...

最值得注意的是,Win32 API提供了以下有用的功能:

  • CreateFileMapping
  • MapViewOfFile
  • OpenFileMapping

使用这些,我们可以直接从注入的dll本身告诉我们的Launcher进程确切的远程初始化函数所在的位置...


dllmain.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
// Data struct to be shared between processes
struct TSharedData
{
    DWORD dwOffset = 0;
    HMODULE hModule = nullptr;
    LPDWORD lpInit = nullptr;
};
// Name of the exported function you wish to call from the Launcher process
#define DLL_REMOTEINIT_FUNCNAME"RemoteInit"
// Size (in bytes) of data to be shared
#define SHMEMSIZE sizeof(TSharedData)
// Name of the shared file map (NOTE: Global namespaces must have the SeCreateGlobalPrivilege privilege)
#define SHMEMNAME"Global\\InjectedDllName_SHMEM"
static HANDLE hMapFile;
static LPVOID lpMemFile;

BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved )
{
    TSharedData data;

    switch (ul_reason_for_call)
    {
        case DLL_PROCESS_ATTACH:
            DisableThreadLibraryCalls(hModule);

            // Get a handle to our file map
            hMapFile = CreateFileMappingA(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, SHMEMSIZE, SHMEMNAME);
            if (hMapFile == nullptr) {
                MessageBoxA(nullptr,"Failed to create file mapping!","DLL_PROCESS_ATTACH", MB_OK | MB_ICONERROR);
                return FALSE;
            }

            // Get our shared memory pointer
            lpMemFile = MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, 0);
            if (lpMemFile == nullptr) {
                MessageBoxA(nullptr,"Failed to map shared memory!","DLL_PROCESS_ATTACH", MB_OK | MB_ICONERROR);
                return FALSE;
            }

            // Set shared memory to hold what our remote process needs
            memset(lpMemFile, 0, SHMEMSIZE);
            data.hModule = hModule;
            data.lpInit = LPDWORD(GetProcAddress(hModule, DLL_REMOTEINIT_FUNCNAME));
            data.dwOffset = DWORD(data.lpInit) - DWORD(data.hModule);
            memcpy(lpMemFile, &data, sizeof(TSharedData));
        case DLL_THREAD_ATTACH:
            break;
        case DLL_THREAD_DETACH:
            break;
        case DLL_PROCESS_DETACH:
            // Tie up any loose ends
            UnmapViewOfFile(lpMemFile);
            CloseHandle(hMapFile);
            break;
    }
    return TRUE;
    UNREFERENCED_PARAMETER(lpReserved);
}


然后,从启动器应用程序中,我们将执行通常的CreateProcess + VirtualAllocEx + CreateRemoteThread技巧来注入Dll,确保将指向正确的SECURITY_DESCRIPTOR的指针作为CreateProcess的第三个参数传递,并在第6个参数中传递CREATE_SUSPENDED标志。

这有助于确保您的子进程具有读取和写入全局共享内存名称空间的适当特权,尽管还有其他方法可以实现此目的(或者您可以完全不使用全局路径进行测试)。

CREATE_SUSPENDED标志将确保dllmain入口点函数在加载其他库之前已经完成了对我们共享内存的写入,这使得以后可以更轻松地进行本地挂钩...


Injector.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
SECURITY_ATTRIBUTES SecAttr, *pSec = nullptr;
SECURITY_DESCRIPTOR SecDesc;

if (InitializeSecurityDescriptor(&SecDesc, SECURITY_DESCRIPTOR_REVISION) &&
    SetSecurityDescriptorDacl(&SecDesc, TRUE, PACL(nullptr), FALSE))
{
    SecAttr.nLength = sizeof(SecAttr);
    SecAttr.lpSecurityDescriptor = &SecDesc;
    SecAttr.bInheritHandle = TRUE;
    pSec = &SecAttr;
}

CreateProcessA(szTargetExe, nullptr, pSec, nullptr, FALSE, CREATE_SUSPENDED, nullptr, nullptr, &si, &pi);


将DLL注入目标进程后,您需要做的就是使用DLL项目中相同(或多或少)的文件映射代码到Launcher项目中(当然,设置共享内存内容的部分除外) 。

然后,调用远程函数只是一个简单的问题:

1
2
3
4
5
6
7
8
9
// Copy from shared memory
TSharedData data;
memcpy(&data, lpMemFile, SHMEMSIZE);
// Clean up
UnmapViewOfFile(lpMemFile);
CloseHandle(hMapFile);
// Call the remote function
DWORD dwThreadId = 0;
auto hThread = CreateRemoteThread(hProcess, nullptr, 0, LPTHREAD_START_ROUTINE(data.lpInit), nullptr, 0, &dwThreadId);

然后,您可以在目标进程的主线程上ResumeThread或从您的远程功能上ResumeThread


作为额外的奖励...使用这种交流形式还可以为我们的Launcher流程打开几扇门,因为它现在可以直接与目标流程进行交流了。
但是同样,请确保不要在DllMain中做太多事情,并且尽可能使用远程init函数(例如,使用命名的互斥体也是安全的)来创建单独的共享内存映射并从那里继续通信。

希望这对某人有帮助! =)