Strange as it is, two other methods have not been mentioned. The
AppInit_DLLs approach you mentioned doesn't work anymore to the best of my knowledge. At least not as easily, especially when UEFI Secure Boot is enabled.
In both cases it depends on the target binary, so let's go through it.
DLL placement attack
I've run this in the past on Windows 7, but the rules have changed since then. The way I made it work was as follows. I figured out one (vendor-provided) DLL loaded by the program without a full path (I picked the one with the least number of imports). I then wrote a stub DLL which would simply pass through all function calls to the real DLL (loaded by full path) and print out useful info for me. The return value would simply be returned.
It's easy to figure out the function prototypes of exported functions, so this was the lazy option for me.
This method is used by Process Explorer to make itself a replacement for Task Manager. Here's how that looks in action:
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\taskmgr.exe]
Now the value name probably gives it away already, this was previously used for a totally different purpose.
Either way you can (ab)use it to achieve what you want. You write a little stub loader, acting as the "debugger". There isn't much to it, you don't even have to implement any of the debugger APIs, although you could.
All you'll want to do is to use
CreateProcess or similar to create a process with its main thread in suspended state upon "start". You can pass on the command line arguments as you wish (although it is, admittedly) a bit involved with
CreateProcess and friends.
The suspended main thread is where you can detour to your own code ... typically a simple piece of shell code that does nothing other than load your DLL and then return to the original entry point. Et voila, you have loaded your DLL into an application.
The nice part about this second method is that it scales pretty well, since you basically only have to write this sort of "debugger" (loader) once. So it can be kept generic.
This method amounts to doing (from the command line):
loader.exe C:\path\to\hijacked\application.exe argument1 argument2 and more arguments
... except that to the user this will be transparent. And I think inside your loader you can even use
argv to get the path to the hijacked executable, IIRC.
Now you may think that this is some weird security issue and wonder why it's gone unnoticed. It's not a security issue insofar as you have to be privileged to set the
ImageFileExecutionOptions in the first place.
As a side-note: I think it's even permissible to name the containing key (
taskmgr.exe in the above registry file) with the full path to the executable to be hijacked.