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.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…