This is the first part of the DLL Injection series. For your convenience you can find other parts using the links below :
Part 1 — Registry
Part 2 — Hooks
Part 3 — Creating remote thread
Part 4 — Injecting managed DLL
Part 5 — Hacking Minesweeper
Part 6 — Injecting managed code into specific App Domain
Part 7 — Injecting Java code
Hello. In this post I start a new series in which we will see some methods of injecting DLLs. Today we start with an introduction and a few simple methods.
Why would you want to inject DLL? Usually DLL injection is used for running custom code within the address space of another process. There are multiple applications which do that:
- ConEmu uses this technique to hijack console windows
- EMET loads its custom DLL to enhance application security
- Sophos injects its libraries to protect system
- ForceBindIP loadss its DLLs to modify sockets behavior
Usually we want to inject our library to modify behavior of some system APIs, simplify reading and writing memory, add plugins, or to control another application.
In order to execute our code within different process we need to consider some basics.
First, we are not going to hack a machine. We assume that we control the system (however, we might not have an administrator privileges) and we are able to freely save files, run applications, and execute code we like.
We do not want to modify existing application’s source code, especially we do not want to recompile it. We want to start application normally and then inject our code.
We need to remember about virtual memory. Every application runs in its own memory address space and, from its point of view, the memory is linear. The process doesn’t care about physical mapping of memory frames, however, we need to be aware of this process.
We want the process to be clean and reliable.
Virtual Address Space
Every process run in its own address space and memory is divided into partitions. On x86 architecture there are four of them:
- NULL-Pointer assignment
- 64KB Off-Limits
Below are ranges of these partitions:
Ranges for x64:
As we can see, process thinks that its memory is linear. It is up to the operating system how the actual frames of memory are stored — they can be in RAM, paged to disk, mapped to files etc.
In order to load and execute code from library, process needs to load the library into its address space. Every binary (executable or library) has its preferred address meaning where the binary would like to be loaded. It is important because if we load binary to different place we need to rebase the binary. We also need to remember that the same library can be loaded into different address in different processes. There is also a mechanism for randomizing address space layout.
So when we load our code we need to be very careful. We cannot just reference memory by addresses blindly because they can differ between processes.
Global Loader Lock
During execution of
LoadLibrary your application holds a lock. Because of this there are things which should not be done when loading library. This means that even if we are able to load our library, we cannot execute every code during initialization and we need to take another actions. We will see some details in next parts of this series.
When process wants to load a library, the following places are searched for matching file:
- The directory containing the executable image file
- The Windows system directory returned by GetWindowsDirectory function
- The 16-bit system directory (System subfolder under the Windows directory)
- The Windows directory returned by GetSystemDirectory
- The process’ current directory
- The directories listed in the PATH environment variable
We can change order of last two steps in registry.
This gives us first method of injecting our code into another process: we simply replace the DLL file stored on the disk with our library, which should export methods with the same signatures as the original library.
There is an easy way to inject DLL into a process which loads
User32.dll. During initialization
User32.dll looks into the registry and checks if
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Windows\LoadAppInit_DLLs is set to
1. If it is so, then library loads all DLLs listed in
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Windows\AppInit_DLLs. So we can just add our DLL in the registry and have it loaded into every process which loads
User32.dll. Good news is: most of Windows applications loads this library so it is fairly common.
Of course there are shortcomings of this method. First, we need to have an administrator privileges in order to modify the registry. What’s more, our library is loaded into every process loading system library, so we need to make sure that we are executing code in a correct process. Finally, we cannot control the moment of library injection and we are unable to load our library into already running process.
Injecting into spawned process
There is another way using registry. If we add library to
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\AppCertDLLs key, our library will be loaded into every process that calls the Win32 API functions like CreateProcess. Shortcomings are almost the same as in the previous method.
In the next part we will see another method based on system hooks.