使用C ++,我有一个应用程序,它创建一个远程进程并向其中注入一个DLL。有没有办法让远程应用程序执行从DLL导出的函数,从创建它的应用程序?是否可以向该功能发送参数?请注意,我正试图远离在DllMain中做任何事情。
答案 0 :(得分:6)
<强> 注意:的强>
要获得更好的答案,请see my update posted below!
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中写入任何内容。
答案 1 :(得分:2)
因此经过一些精心的测试后,似乎我之前的答案是万无一失的(或者甚至是100%功能性的),并且容易发生崩溃。在考虑之后,我决定采用一种完全不同的方法......使用Interprocess Communication。
注意 ...此方法使用DllMain
中的代码。
所以不要过分,并确保follow safe practices这样做,这样你就不会最终进入deadlock ......
最值得注意的是,Win32 API提供了以下有用功能:
通过使用这些,我们可以简单地告诉我们的Launcher进程远程init函数所在的位置,straight from the injected dll本身......
<小时/>
dllmain.cpp
// 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);
}
<小时/> 然后,从我们的Launcher应用程序中,我们将执行通常的
CreateProcess
+ VirtualAllocEx
+ CreateRemoteThread
技巧来注入我们的Dll,确保传入指向正确SECURITY_DESCRIPTOR
的指针作为CreateProcess
的第3个参数,以及在第6个参数中传递CREATE_SUSPENDED
标志。
这有助于确保您的子进程具有读取和写入全局共享内存命名空间的适当权限,尽管还有其他方法可以实现此目的(或者您可以在没有全局路径的情况下进行测试)。 / p>
CREATE_SUSPENDED
标志将确保dllmain入口点函数在加载其他库之前已完成写入我们的共享内存,这样可以在以后轻松进行本地挂钩...
Injector.cpp
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项目(当然,除了设置共享内存内容的部分) 。
然后,调用远程函数只是一个简单的问题:
// 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
在目标进程的主线程上,或从你的远程函数。
DllMain
中不要做太多,并且,如果可能的话,只需使用远程初始化函数(例如use named mutexes也是安全的)来创建一个单独的共享内存映射,并从那里继续通信。