x86 way of doing this is easy and straightforward - through GetExitCodeThread. Unfortunately it's limited to returning 32 bit values. As I understand it WinAPI provides no 64 bit alternative.
So the problem is - I have no trouble calling the injected function by finding its base address through CreateToolhelp32Snapshot module loop then running CreateRemoteThread using it, it does whatever I wrote in it as it should but how exactly do I retrieve its return value without GetExitCodeThread? As an example I want to retrieve a 64bit pointer or even a struct (or a 64bit pointer to one) as a result of this function. What would be a correct way of doing it? And if it's ReadProcessMemory - then which memory address/offset should I read for a return value?
Edit: additional info: I'm calling a function inside an injected DLL. The function executes some stuff (e.g. collection of data from the process it's injected into, which is successful) - the problem is I want to retrieve one of those variables back into the calling process (the one that calls CreateRemoteThread). GetExitCodeThread is a no go because variables are 64 bit.
code snippet for reference (hExportThread function returns uint64_t in DLL):
// LLAddr = LoadLibraryA address, lpBaseAddr = dll path related argument
HANDLE hInjectionThread = CreateRemoteThread(hProc, NULL, NULL, LLAddr, lpBaseAddr, NULL, &idThread);
WaitForSingleObject(hInjectionThread, INFINITE);
dllBaseAddr = getDLLBaseAddr(); // gets base address of the injected DLL
dllExportOffset = getDLLExportOffset(dllExportName.c_str()); // opens the DLL in the local buffer and gets the correct offset
LPTHREAD_START_ROUTINE lpNewThread = LPTHREAD_START_ROUTINE(dllBaseAddr dllExportOffset); // gets the correct address of the function in the injected dll
HANDLE hExportThread = CreateRemoteThread(hProc, NULL, NULL, lpNewThread, NULL, NULL, 0); // executes injected function
WaitForSingleObject(hExportThread, INFINITE);
CodePudding user response:
I suggest that you use a shared memory region, have it open in both the injecting process and the injected DLL. When the injected library finishes you know that the memory should be ready.
Doing this, you aren't limited to 4 or 8 bytes, you can make the region of whatever size is needed to return the collected data.
CodePudding user response:
One option would be, rather than asking the remote thread to execute the target DLL function directly, you can have the injector use VirtualAllocEx()
to allocate a small block of executable memory in the remote process, and then use WriteProcessMemory()
to fill that block with a minimum set of Assembly instructions needed to call the target DLL function and save its result to a memory location where the injector can then access the data from. That could be a block of shared memory, or a block of VirtualAllocEx
-ed memory that you read with ReadProcessMemory()
.
Or, if you are allowed to change the signature of your target DLL function, then have it take a pointer to a local memory address where it can write its output to, and then the injector can VirtualAllocEx()
a block of read/write memory and pass its address in the lpParameter
parameter of CreateRemoteThread(), and then
ReadProcessMemory()` from that address after the thread exits.
Otherwise, rather than setting all of this up manually at the call site where you launch the remote thread, it would be easier if you wrapped the target DLL function in another DLL function that did this work in plain C instead of in Assembly. And then you can have the remote thread call that wrapper function instead.