Charles Grunwald
Charles Grunwald

Reputation: 1471

x86 Code Injection into an x86 Process from a x64 Process

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:

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.)

  1. 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.

  2. 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)

Upvotes: 4

Views: 5041

Answers (2)

Charles Grunwald
Charles Grunwald

Reputation: 1471

In case someone finds this question and is still confused after reading through the comments, you can find an (admittedly ugly) proof of concept here. Assuming you already know the normal strategy for DLL injection, here's an explanation of the what you'll need to do differently. (with the relevant code):


Locating our Exports (injdll32_64.c)

Normally we could go with the LoadLibrary/GetProcAddress route, but since the two modules in question are targeted for different processors, we need to do things differently. As Remy Lebeau suggested in his answer, we could do this completely on the assembly side. From my perspective, however, writing assembly to locate the base address of kernel32.dll, find the export table, and locate LoadLibrary and GetProcAddress seemed like a pain in the ass. Instead, I handled it on the C side by reading in each DLL, searching the IMAGE_EXPORT_DIRECTORY for the necessary exports, and storing their RVA's for later use.


Calling our Exports by RVA (injdll32.c, x86.final.asm)

For my purposes, I needed to have the injected DLL execute within the main thread of the target application. To do that, I suspended the main thread of the target application, allocated memory for my pre-assembled machine code, filled in the placeholders with the appropriate export RVAs, changed the EIP of the target application with calls to GetThreadContext/SetThreadContext, and resumed the suspended thread. (Not necessarily in that order) Unfortunately, I never got around to writing a mechanism for freeing up the memory we VirtualAlloc'ed in our target application, but one of the solutions for doing so would be to implement a mechanism for notifying the injector when the target process returns to its original EIP. At that point, it would be safe for the injector to VirtualFree the allocated memory. (Alternatively, you could always find a suitable code cave and eliminate the even worry about freeing up memory)

Upvotes: 1

Remy Lebeau
Remy Lebeau

Reputation: 595981

Use VirtualAllocEx() to allocate a block of executable memory inside of the target process, then use WriteProcessMemory() to write x86 or x64 machine instructions into that memory block as needed. Have those instructions call LoadLibrary(), GetProcAddress(), an the exported DLL function as needed. Then use CreateRemoteThread() 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 of LoadLibrary() from the return value of GetProcAddress(). 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_PROCESS reason. Thus no need to export any functions from the DLL. Then you can use VirtualAllocEx() and WriteProcessMemory() to store the DLL's path into the target process, and then use CreateRemoteThread() to invoke LoadLibrary() directly. Kernel functions always have the same memory address across processes, so your injecting process can call GetProcAddress() within its own address space to get the address of LoadLibrary() and then pass that pointer to the lpStartAddress parameter of CreateRemoteThread(). 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

Upvotes: 3

Related Questions