I realize the title’s a bit convoluted, so let me explain what I’m trying to do:
I just finished writing a simple DLL injector for a proof of concept I’m trying to write. The program takes a snapshot of the current processes, enumerates the process tree, and injects a DLL into its direct parent process. Now, under ideal conditions, that works fine: the 32-bit version of the injector can inject into 32-bit parent processes, and the 64-bit version of the injector can inject into the 64-bit parent processes.
What I’m now looking to do, though, is to inject a 32-bit DLL into 32-bit parent process from the x64 injector. Once that DLL is injected, I was hoping to then inject a call to one of the functions exported by the injected DLL. I’m not sure, though, if that’s actually possible to do. (I’ve already put together some code to identify whether the parent process is a 32-bit process or a 64-bit process, so that won’t be an issue)
Now, I’ve already found some code that seems to do the first part by injecting precompiled machine code into the process. (At least, I think that’s what it’s doing) Normally, after injecting the call to LoadLibraryW, I’d get the address returned by that call, add the relative offset to the exported function I want to call, and inject a call to that function. In this case though, I can’t load the 32-bit library into my 64-bit injector, so I can’t find the relative offset of the function by using GetProcAddress like I normally would. I got past this issue by doing the following:
Since I can’t find the function offset of the 32-bit DLL using normal means, I’m currently reading the file into a buffer, using that buffer to populate a IMAGE_NT_HEADERS32 struct, and enumerating the IMAGE_EXPORT_DIRECTORY to find the names and relative offsets of all exported functions.
So at this point, I have the following:
- The 32-bit DLL loaded into the 32-bit Process
- A value that is the equivalent of funcAddr when running the following code in the 32-bit process:
Code:
HMODULE hInjectedDLL = LoadLibrary("mydll.dll");
DWORD funcAddr = (DWORD)GetProcAddress(hInjectedDLL, "ExportedFunc") - (DWORD)hInjectedDLL;
Theoretically, all I need now is the value of hInjectedDLL, and I should be able to make a call to that function. Unfortunately, though, I don’t really know enough about assembly or machine code to know how to go about getting that value, though.
Any ideas?
(Also, I’m aware that I could save myself a lot of trouble by just compiling two versions of the injector, and having one run the other when the parent process’s processor architecture doesn’t match up. I’m trying to avoid going this route, though.)
Edit: Figured it might help to explain what I’m actually trying to accomplish in this proof of concept.
I’m experimenting with an idea I had to allow the execution a child process in the current console, without the need for the original process to wait around for the child to finish. Since there’s no built-in API for doing this in a console application, you’re normally stuck with a tree of processes, all waiting for their respective child process to complete. To facilitate this functionality, I want to do the following:
Injection
The DLL injector would play the role of “executing process”. (the process that would normally have to wait until the child process finished) When run, it determines the platform of its parent process, and whether or not the parent process is even a console-based application. If it’s not, the process simply uses the exec family of functions to run the desired subprocess, exiting immediately. If the parent process is a console application, the injector determines which DLL to use, suspends the thread that originally created the injector process, and then injects the DLL into the parent process.
Resolving Our Function
Once the DLL is in place, the injector determines that address of a function exported by the DLL. (Normally, I’d do this by calling CreateRemoteThread to do the initial injection, and then use GetExitCodeThread on that thread to get the base address of the DLL in the parent process. Once I have that, it’s simple arithmetic to find the address of our exported function, which I can then use to inject a second call to that function.
Calling Our Function
The exported function will be something along the lines of:
BOOL RewriteHProcess(HANDLE hProcess)
The injector would again use CreateRemoteThread to call this function from the context of the parent process, with hProcess being a handle to the injector process. On the DLL side, the function would do one of two things (I’m not quite sure if my first idea is possible, given the security restrictions of memory access across threads, so I put together the second idea to fallback on, if the first doesn’t work out.)
-
The RewriteHProcess will open up the previously suspended thread for reading and writing, and using ReadProcessMemory, it will search the process’s memory for the HANDLE to our injector process. (We’re making the assumption that the parent process is currently blocking further execution with the WaitForSingleObject function. I know that Command Prompt does, at the very least, and that’s my focus for the time being) The DLL then calls an internal function to create our desired child process, closes the old handle, and overwrites the memory with the handle to our new child process. At this point, it cleans up what it can, and returns. The injector would then do any remaining cleanup that it needed, resume the suspended thread, close the handles to the process and thread, and exit, leaving the parent process to continue blocking as it waits for the new child process to end.
-
If that route isn’t possible, my fallback was to just suspend the blocking thread from the injector, create the new child process in the injected DLL, cleanup and exit the injector, and wait in the DLL until the child process completed. At that point, the DLL would clean up, resume the suspended thread, and unload itself. (The drawback with this route, though, is that the return code that the parent process gets back from the injector might not be the same as the return code from our target child process)
Use
VirtualAllocEx()to allocate a block of executable memory inside of the target process, then useWriteProcessMemory()to write x86 or x64 machine instructions into that memory block as needed. Have those instructions callLoadLibrary(),GetProcAddress(), an the exported DLL function as needed. Then useCreateRemoteThread()to execute the memory block. Your injector cannot call the exported DLL function directly if it is running in a separate process. The exported function has to be loaded and called within the context of the target process. And do not subtract the return value ofLoadLibrary()from the return value ofGetProcAddress().GetProcAddress()returns a direct memory pointer to the function so it can be called directly.Update: a variation of this is to put all of your injected code inside of the DLL’s entry point (or have the entry point spawn a thread to run the code) when it is called with the
DLL_ATTACH_PROCESSreason. Thus no need to export any functions from the DLL. Then you can useVirtualAllocEx()andWriteProcessMemory()to store the DLL’s path into the target process, and then useCreateRemoteThread()to invokeLoadLibrary()directly. Kernel functions always have the same memory address across processes, so your injecting process can callGetProcAddress()within its own address space to get the address ofLoadLibrary()and then pass that pointer to thelpStartAddressparameter ofCreateRemoteThread(). This way, you don’t have to worry about writing any x86/x64 assembly code.This technique is described in more detail in Section 3 of this article:
Three Ways to Inject Your Code into Another Process