I need to launch other application and handle it's I/O operations. So when it tries to read/write a file, I need to catch this and change the path.
It should be possible, because there is programs that do something like this (like ModOrganizer).
The thing is I don't want to use File System Filter Drivers. I don't want to make users of my application to install such thing.
As I see, ModOrganizer does the trick with several ways including proxy.dll, hooks e.t.c. Somehow, it achieves the goal that almost any program can be launched from it and ModOrganizer will handle requests to the certain directory.
There is source code on github, but I don't really get it. That is why I'm asking this here.
Again, ModOrganizer achieves this without decompiling every possible program to know, where to inject. And it doesn't use System Filters.
(Please explain your minuses. Otherwise how am I going to improve my questions in the future?)
ModOrganizer uses remote thread injection to run a custom library (dll) in the context of the target process. The library then goes on to manipulate the process from within and gain control over it.
This is one of the most-known techniques user-space code injection and work pretty well with most processes.
Note: ModOrganizer doesn't support 64bit binaries, see the following exception in their code:
throw windows_error("failed to access thread context. Please note that Mod Organizer does not support 64bit binaries!");
Step 1: Obtaining a HANDLE to target thread & process
To interact with different threads & processes on the machine, you first have to obtain HANDLEs to them.
The method in ModOrganizer that obtains that handle is bool spawn(..), and more specifically the following line:
PROCESS_INFORMATION pi;
BOOL success =
     ::CreateProcess(nullptr,
                     commandLine,
                     nullptr, nullptr, // no special process or thread attributes
                     inheritHandles,   // inherit handles if we plan to use stdout or stderr reroute
                     CREATE_BREAKAWAY_FROM_JOB | (suspended ? CREATE_SUSPENDED : 0), // create suspended so I have time to inject the DLL
                     nullptr,          // same environment as parent
                     currentDirectory, // current directory
                     &si, &pi          // startup and process information
                     );
As you can see, this line obtains a PROCESS_INFORMATION object using CreateProcess. We now can extract the process HANDLE from pi.hProcess and the thread HANDLE from pi.hThread.
Moreover, this command not only obtains HANDLEs to the target process, but also puts it into suspended mode (CREATE_SUSPENDED). This allows us to manipulate it as we wish, and then resume its execution.
Step 2: Injecting your own dll to a target process
The injector method is the void injectDLL(..) method defined inside src/shared/inject.cpp.
The goal of this method is to load the dll specified by dllname into the target thread threadHandle inside the target process processHandle.
Let's go over the important stuff:
TParameters parameters; - This line and the 4 following ones allocate and set the values of a TParameters struct to include the path of the dll file we want to load into the target process and the parameters we will pass to the init function inside that dll file::LoadLibrary(__TEXT("kernel32.dll")) - Load kernel32.dll into our process space::GetProcAddress(k32mod, "LoadLibraryA") - Get the address of LoadLibraryA method inside the kernel32.dll::GetProcAddress(k32mod, "GetProcAddress") - Get the address of GetProcAddress method inside the kernel32.dll::VirtualAllocEx(..) - Allocate virtual memory inside the target process::WriteProcessMemory(.., ¶meters, ..) - Write the parameters struct we created earlier into the virtual memory we allocated inside the target processBYTE stubLocal[] = { .. } - Generate shell code that loads the dll inside the target process, and then call the Init function inside the dll with the values from the parameters structPBYTE stubRemote = reinterpret_cast<PBYTE>(::VirtualAllocEx(.., sizeof(stubLocal), ..)) - Allocate virtual memory inside our target process to accommodate our shell code. Save the address of the memory segment we allocated inside stubRemote::GetThreadContext(threadHandle, &threadContext) - Get the context of the target process, including the IP (Instruction Pointer) which points to the next instruction to execute::WriteProcessMemory(.., reinterpret_cast<LPCVOID>(stubLocal), ..) - Write the shell code to the virtual memory we allocated in the target processthreadContext.Eip = (ULONG)stubRemote; - Set the IP of the target process to the location of our shell code::SetThreadContext(...) - Resume the execution of the target process. The execution resumes at the address we set for IP, thus executing our injected shell code that loads the desired dll file and calling the Init function inside it, and once our method is finished, it returns execution to the original address inside the target process as if nothing has happened.At this point, our library is fully loaded inside the target process, and has had its chance to do all kinds of nasty hooks. But what exactly do the dlls we load do inside the target process?
Step 3: Understand the loaded dll
ModOrganizer actually has separate repositories for the injected dll: modorganizer-hookdll
The main module of the dll is defined in dllmain.cpp\h. This module:
Defines all the hooked method:
// hook declarations
CreateProcessA_type CreateProcessA_reroute = CreateProcessA;
CreateProcessW_type CreateProcessW_reroute = CreateProcessW;
[..]
GetModuleFileNameA_type GetModuleFileNameA_reroute = GetModuleFileNameA;
GetModuleFileNameW_type GetModuleFileNameW_reroute = GetModuleFileNameW;
Defines the Init function which is the method our shell code executes after loading the dll
Calls the InitHooks() method that is responsible for initializing all the hooks used by the application
At this point, I hope you have a clear picture of the steps and the APIs you have to use to achieve your goal, and understand which functions from ModOrganizer's code you should reuse.
Development Tip: I would recommend compiling your own dummy executable that prints Hello world! to the console every second, and testing your injector on it. From my own experience, developing such tools takes time and surgical precision. Don't scare off easily when your target crashes and make sure to add debug prints (If you print from your injected dll, you should see it print to the same console as the Hello world!s).
64bit systems
I saw some links that stating that ModOrganizer development is stopping and there's going to be a new mod manager that will be way more customizable, made by the same development team, so maybe they decided to skip 64bit support for it.
It is entirely possible to implement this for 64bit applications, but, it is harder to inject into a 32bit application running inside a 64bit system (Using the WOW infrastructure).
To change the existing code to support 64bit, I presume that step 1 might not require any changes, step 2 requires at least some adjustments to the shell code (the assembly instructions differ) and step 3 (the injected dlls) should be at least recompiled, and some of the hooked APIs might change.
More techniques and guides
Apart from the technique ModOrganizer uses, here are some tutorials and references:
Dll injection using CreateRemoteThread: Very similar, but it creates a thread in the remote process that loads the dll. A detailed tutorial can be found here.
InjectProc project (Github) that supports multiple injection implementations as a reference.
EasyHook project (Github) that support 32-bit and 64-bit DLL injections.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With