CreateRemoteThread, LoadLibrary, and PostThreadMes

2019-01-27 07:01发布

问题:

Alright, I'm injecting some code into another process using the CreateRemoteThread/LoadLibrary "trick".

I end up with a thread id, and a process with a DLL of my choice spinning up. At least in theory, the DLL does nothing at the moment so verifying this is a little tricky. For the time being I'm willing to accept it on faith alone. Besides, this question needs to be answered before I push to hard in this direction.

Basically, you can't block in DllMain. However, all I've got to communicate with the remote thread is its id. This practically begs for PostThreadMessage/GetMessage shenanigans which block. I could spin up another thread in DllMain, but I have no way of communicating its id back to the creating thread and no way of passing the another thread's id to the remote one.

In a nutshell, if I'm creating a remote thread in a process how should I be communicating with the original process?

回答1:

Step zero; the injected DLL should have an entry point, lets call it Init() that takes a LPCWSTR as its single parameter and returns an int; i.e. the same signature as LoadLibrary() and therefore equally valid as a thread start function address...

Step one; inject using load library and a remote thread. Do nothing clever in the injected DLLs DLLMain(). Store the HMODULE that is returned as the exit code of the injecting thread, this is the HMODULE of the injected DLL and the return value of LoadLibrary().

Note that this is no longer a reliable approach on x64 if /DYNAMICBASE and ASLR (Address space layout randomisation) is enabled as the HMODULE on x64 is larger than the DWORD value returned from GetThreadExitCode() and the address space changes mean that it's no longer as likely that the HMODULE's value is small enough to fit into the DWORD. See the comments below and the linked question (here) for a work around using shared memory to communicate the HMODULE

Step two; load the injected DLL using LoadLibrary into the process that is doing the injecting. Then find the offset of your Init() entrypoint in your address space and subtract from it the HMODULE of your injected DLL in your address space. You now have the relative offset of the Init() function. Take the HMODULE of the injected DLL in the target process (i.e. the value you saved in step one) and add the relative address of Init() to it. You now have the address of Init() in your target process.

Step three; call Init() in the target process using the same 'remote thread' approach that you used to call LoadLibrary(). You can pass a string to the Init() call, this can be anything you fancy.

What I tend to do is pass a unique string key that I use as part of a named pipe name. The Injected DLL and the injecting process now both know the name of a named pipe and you can communicate between them. The Init() function isn't DLLMain() and doesn't suffer from the restrictions that affect DLLMain() (as it's not called from within LoadLibrary, etc) and so you can do normal stuff in it. Once the injected DLL and the injecting process are connected via a named pipe you can pass commands and data results back and forth as you like. Since you pass the Init() function a string you can make sure that the named pipe is unique for this particular instance of your injecting process and this particular injected DLL which means you can run multiple instances of the injecting process at the same time and each process can inject into multiple target processes and all of these communication channels are unique and controllable.



回答2:

You don't have the thread id of a thread in the remote process, because the one you used to load the dll exited when your module was successfully loaded into the address space of your process.

You can easily use the normal interprocess communication methods like named sections/pipes/creating a named window/etc. to communicate with your 'injecting' process.



标签: c++ winapi dll ipc