Why Process Injection Fails

By Stephen Kellett
11 May, 2021

If you are reading this web page it is most likely because you have just tried to inject into a running process and the injection failed. You’ve probably just viewed an information dialog similar to the one shown below.

Process injection failed dialog

 

For the purposes of this article, we’ll talk about Memory Validator, but the points all apply to any of our software tools that support process injection (Bug Validator, Coverage Validator, Memory Validator, Performance Validator, Thread Validator)

When can injection fail?

There are three different places in our tools where injection can happen.

  1. Injecting into a running process.
  2. Waiting for a process to start and then attaching to it automatically when it starts.
  3. Launching a process when the launch method is set to any value other than CreateProcess. CreateProcess is the recommended method of launching a process. The other methods preceeded CreateProcess and all use process injection coupled with varying delays or varying process security settings to achieve their aim. Because launching with CreateProcess() is so reliable (close to 100%) you are unlikely to ever use any of these other methods (which are less reliable, less than 95%).

Causes of injection failure

  • A missing DLL in your application.
    This can only be a problem if you are launching an application (item 3 above). The application will fail to launch properly if all DLL dependencies are not met. The application will start and then very rapidly shut down due to a missing DLL dependency. When this happens it is not possible to inject into the application as it doesn’t run for long enough.
  • A missing DLL in the software tool (e.g. Memory Validator) you are using to inspect your application.
    This should never happen. This bug will only happen if a mistake has been made at Software Verify when creating the software installer. We list this here for completeness.
  • Multiple injection attempts.
    If you have already successfully injected into this running process you can’t inject into it again because the injected DLL is already loaded. The solution to this problem is to start a new process and inject into that process.
  • The application started and finished before the DLL could be injected.
    If your process only runs for a short amount of time the process may finish executing before process injection can complete. An example would a command line tool that loads, processes data then closes.
  • The application security settings do not allow process handles to be opened.
    If your application is running at a privilege level that means that Memory Validator cannot open the appropriate process handle to perform the injection it will be impossible to make the appropriate actions to perform a DLL injection into your application. This is quite common when working with services, but can apply to any process that is running with particular privileges. You can try launching Memory Validator “as admin” and then testing to see if Memory Validator can work with your application.
  • The application is a service.
    Services run in a different environment than regular applications. Services are controlled differently. Typically they run on different accounts to regular applications and with different security privileges and different access rights to parts of the system (for example, no disk access except for a particular folder, no shared memory access, etc). As such it is often impossible to gain access to the service to perform a process injection.
  • The service and Memory Validator should both run on the same user account.
    If you are working with a service, then ensuring that both the service and Memory Validator run on the same account can sometimes resolve some problems.
  • Injecting into some processes just does not work.
    Even when all of the above issues are resolved or do not apply some applications just do not want to be injected into. This applies for 32-bit processes injecting into 32-bit processes and 64-bit processes injecting into 64-bit processes. That sounds like a bogus claim with no data to back it up. When we first started testing our process injection software in 1999 we built a test rig that would test every application on the test computer. A test would be to start the process, inject into it, send the process a WM_QUIT and wait for it to quit. We’d monitor if the injection was successful for each test. A test run would take hours and would test several thousand applications. We started testing on Windows NT 4. Then when Windows 2000 was released we tested that. We did the same tests for Windows XP, etc. What we found was that the failure rate for injecting on Windows NT was 2%. On Windows 2000 it was 3%. On Windows XP it was 5%. Each new variant of windows was more complex, it typically ran on more complex and more modern hardware. These variables, changes in multi-tasking scheduling and changes in operating system behaviour and timing combined with the hardware seemed to make process injection less reliable with each new version of Windows. But couldn’t the problem be that we’re injecting a really complex DLL (for example, the Memory Validator DLL)? Surely the cause of the failure is the complexity of what the DLL is doing? That’s a really good question and that is why we did some tests with an empty DLL. An empty DLL for our experiments was a DLL that consists of nothing more than a DllMain(). See below.

    #include "stdafx.h"
    
    BOOL DllMain(HANDLE hModule,
                 DWORD dwReason,
                 LPVOID lpReserved)
    {
        return TRUE;
    }

    When we tested with a DLL consisting of nothing more than the code above compiled into a DLL we found that even this trivial DLL could not be injected into an application that we had previously had failures with.

    Clearly, the DLL injection problem we were encountering was real and had nothing to do with the code (or DLL dependencies) of the DLL we were trying to inject.

Solutions to being unable to inject into a process.

Given that we know from our testing (detailed above) that if injection doesn’t work for a particular application, it typically won’t work for that application, we strongly advise you to stop spending any more time trying to make process injection work for the particular application that is giving you trouble. We’ve spent years looking at this. It really is not worth your time and trouble.

So what can you do?

  • Launch
    Based on feedback we’ve found that some people were using process injection because they had not realised the power of the command line options for running our software tools from the command line. They were launching the process they wanted to instrument, then our software tool and doing the process injection because their application had already started. In some cases, they had launched their test application from a unit testing application. If this is your scenario it is worth examining the command line argument for the software tool to see if you can achieve what you need to achieve without process injection.
  • NT Service API
    If you are working with services we recommend using the NT Services API which is described in detail in the Help file for the software tool. The NT Services API also comes with example usage, an example client and an example service so that you can see how easy it is to integrate the NT Service API into your service. Using these APIs makes working with services really easy.
  • Linkable API
    Some of our software tools have an additional API known as the linkable API. This API is different to the NT Services API. You may find using this API helpful if you don’t wish to use the NT Services API.

Help files can be found from the Help menu on the software tool and also in our Help file download area..

Problems with Process Injection.

Even when process injection succeeds and our profiling DLL is loaded into the target application there can be problems.

The principle problem is with multi-threaded applications that already have more than one thread by the time the process injection completes. Because process injection is not supported by the operating system and is achieved through the use of some clever memory mapping and thread execution you cannot guarantee which synchronization primitives are locked at the time of the injection. For many applications this is not an issue and injection succeeds but for some applications locks can be held and these conflict with the locks required to be held to do the job of profiling the DLL during the injection process. When this happens the target process deadlocks. This cannot be foreseen. This behaviour is application and/or machine specific.

If process injection succeeds but then your process deadlocks this means you cannot reliably use process injection for this application. As such, even though we’ve provided the ability to do process injection we recommend launching your process with our software tool or using the NT Services API to ensure your process is instrumented.

Recommended course of action

In summary, if you ever have the opportunity to launch your process with Memory Validator (using the CreateProcess option) you should use that.

If you can’t launch your process you can try process injection (or wait for process). If those do not work for the reasons stated in this article we recommend that you use the appropriate NT Service API for the tool you are using (see the details in the Help file for that tool). If using the NT Service API sounds daunting, it isn’t – we’ve written an article about how easy it is to use.

Fully functional, free for 30 days