Two aspects weren't mentioned, so I wanted to add them. Igor's approach would be the least invasive, if it works.
- the first way to get around this would be a launcher. It is possible to create a process with the main thread suspended (
CREATE_SUSPENDED in CreateProcess()). This way you can inject shell code before the program gets to start. However, this won't work with static imports (delay loads are yet another thing not subject to the same limitation). One convenient way to create a launcher is by setting the launcher as debugger to a given binary (full path or base name) using Image File Execution Options (GFlagsX has a graphical way of manipulating this stuff). It should be noted that this is what Process Explorer does when you tell it to "replace" the task manager.
- known DLLs have to be taken into account. Your
d3d11.dll and dxgi.dll are not known DLLs (configured via HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs) on a vanilla Windows, but they could have been configured to be preloaded (unlikely though). These DLLs are preloaded by the system and have names in the object directories \KnownDlls and \KnownDlls32 respectively (use a tool like WinObj or WinDbg to inspect the object manager namespace). The objects therein are section objects (kernel mode lingo for memory mapped files), which take precedence whenever you attempt to load something, e.g. via DLL placement attack. This means even though you can coerce a program to load your placed DLL if it's not a known DLL, this won't work with known DLLs. The workaround is a highly privileged agent (usually a service) which would be able to tamper with the currently preloaded DLLs. This requires NT native APIs, though, because almost all functionality related to the object manager is hidden away from Win32 developers.
Suppose you have written a launcher, it may still not work, simply on account of the DLLs getting imported statically. The notion of a static import means that the statically imported DLLs get resolved and the imported functions resolved before your application entry point executes. This means you cannot do anything but manipulate the application or attempt a DLL placement attack in such a scenario. The launcher won't work (although it could provide ways to manipulate the running process early on and hook into the facilities used to load the DLL).
The message box you showed, however, suggests that your import happens at runtime (this is not the usual message box you would get to see when the image loader fails). This could suggest that inside your program someone/something sets the DLL search order. But it could just as well be a hardcoded full DLL path passed to LoadLibrary() et. al. One way of diagnosing this other than with a debugger would be ProcMon from Sysinternals/Microsoft. It has the ability to capture process events such as attempting to load a DLL.
Edit: There is one other option I am aware of that requires some agent running in the TCB, e.g. a service, and a kernel driver watching out for events like image loading and thread/process creation. The driver then subscribes to some notification scheme implemented between driver and service and performs the necessary steps from user mode early on before the process or most of its statically imported DLLs get to run any code.
LoadLibraryit won't pick up anything inntdll.dll. Just saying. – 0xC0000022L Jul 22 '22 at 09:54