Implementing Remote LoadLibrary and Remote GetProcAddress Using PowerShell and Assembly

Recently I have been working on reflective DLL injection in to remote processes in PowerShell. I encountered a problem; I need to call LoadLibrary to load libraries in the remote process and then call GetProcAddress to get function addresses in the remote process.

This post will start off with some review of DLL injection in a 32bit process, which is straightforward. Then I will talk about loading a DLL in a 64bit process. Last I’ll talk about calling GetProcAddress in a remote process. I’ll also include DLL injection references at the bottom of the post, as there are already numerous posts online about basic DLL injection.

Invoke-CreateRemoteThread

First, I want to show how to create threads in remote processes using PowerShell. The technique depends on which version of Windows you are using. For Windows XP, you can create a remote thread using CreateRemoteThread. Windows Vista/7 introduced session isolation, which broke CreateRemoteThread if the target process is in a different session. The solution is to use an undocumented function, NtCreateThreadEx, which will work cross session. In Windows 8, CreateRemoteThread once again works cross session.

For 32bit applications, calling LoadLibrary is relatively straightforward. CreateRemoteThread (and NtCreateThreadEx) take a function pointer (the initial address to begin thread execution) and a pointer to a single argument that can be passed to the function.

LoadLibrary takes a single parameter, a pointer to a string that contains the name of the DLL to load. Therefore, to call LoadLibrary in a remote process, I do the following:

Allocate memory in the remote process.

Write the DLL name string to this allocated memory.

Call Invoke-CreateRemoteThread with the address of the LoadLibrary function. The memory address allocated is passed as the optional argument of CreateRemoteThread.

Use WaitForSingleObject to wait until the thread finishes.

Call GetExitCodeThread to get the return value of the thread, which is the return value of LoadLibrary. This gets the address the specified DLL was loaded to in the remote process.

One thing to note: For the above to work, I have to know the address of LoadLibrary in the remote process. As it turns out, Kernel32.dll is loaded to the same memory location in every process (it changes at reboot, but every process always loads it to the same spot). LoadLibrary is contained in Kernel32.dll, so I can simply call GetProcAddress in the PowerShell process, which will get the address of LoadLibrary in the PowerShell process and therefore the address of LoadLibrary in the remote process.

Example code for both 32bit and 64bit remote LoadLibrary are located further below.

64Bit Remote LoadLibrary:

Unfortunately this doesn’t work for 64bit processes. GetExitCodeThread returns a 32bit value; in a 64bit process, LoadLibrary will return a 64bit value.

Here’s the workaround I came up with:

Write assembly code that calls LoadLibrary and writes the return value to a memory address in the remote process that I specify.

Write this assembly code in to the remote process.

Execute it using Invoke-CreateRemoteThread.

Once the thread finished, read the memory address in the remote process that the assembly writes the return value to.

The PowerShell script changes the addresses labeled 0x4141414141414141 to the actual addresses in the remote process during execution. The script allocates memory for the DLL name string, allocates memory for the return value of LoadLibrary, and gets the address of LoadLibrary, and writes these addresses in to the assembly.

So, using PowerShell and a little bit of assembly I can load libraries in both 32bit and 64bit remote processes. Time to move on to remote GetProcAddress.

Remote GetProcAddress:

Calling GetProcAddress in remote processes is something I haven’t found a ton of information about online, and I didn’t like the solutions I saw. Unfortunately, you cannot use the CreateRemoteThread trick for 32bit processes because GetProcAddress takes 2 parameters (and you can only pass 1 parameter through CreateRemoteThread). I was already writing assembly for other functionality, and I figured this would be pretty easy to write in assembly as well. Unlike with LoadLibrary, assembly must be written for both 32bit and 64bit processes.

General steps:

Write assembly code that calls GetProcAddress and writes the return value to a memory address in the remote process that I specify.

Write this assembly code in to the remote process.

Execute it using Invoke-CreateRemoteThread.

Once the thread finished, read the memory address in the remote process that the assembly writes the return value to.

As you can see, these steps are basically identical to the LoadLibrary assembly steps.

And there you have it. Using this code you can get the addresses to functions in remote 32bit and 64bit processes.

You might have noticed the code references the variables Win32Functions and Win32Constants. I define these in other functions. Here is some of the code from Win32Functions. You might have a define a few functions yourself, because I am extracting this out of a much larger script and might miss a few things. I recommend checking out this blog post for information on reflectively defining structures in PowerShell: http://www.exploit-monday.com/2012/07/structs-and-enums-using-reflection.html.

I think this should give you most of the information and code you need. Let me know if you’d like to see more material like this, and keep your eyes out for an update to my PE injection which allows reflectively injecting DLL’s in to remote processes.

2 comments on “Implementing Remote LoadLibrary and Remote GetProcAddress Using PowerShell and Assembly”

FYI, you can just call GetProcAddress in the running PowerShell process and be guaranteed that the address returned will be the same address in the remote process (as long as the module is loaded in the remote process, of course). Once one process loads a module, its address will remain fixed across all processes. For example, if process #1 was the first to load dbghelp.dll and it loaded at base address 0x40000000, then if process #2 loaded dbghelp.dll, it would be loaded at the same base address. I rely upon this trick in my Invoke-DllInjection function. You could validate my claim with the following short script: