In
computer programming
Computer programming or coding is the composition of sequences of instructions, called computer program, programs, that computers can follow to perform tasks. It involves designing and implementing algorithms, step-by-step specifications of proc ...
, DLL injection is a technique used for running
code
In communications and information processing, code is a system of rules to convert information—such as a letter, word, sound, image, or gesture—into another form, sometimes shortened or secret, for communication through a communicati ...
within the
address space
In computing, an address space defines a range of discrete addresses, each of which may correspond to a network host, peripheral device, disk sector, a memory cell or other logical or physical entity.
For software programs to save and retrieve ...
of another
process
A process is a series or set of activities that interact to produce a result; it may occur once-only or be recurrent or periodic.
Things called a process include:
Business and management
* Business process, activities that produce a specific s ...
by forcing it to load a
dynamic-link library
A dynamic-link library (DLL) is a shared library in the Microsoft Windows or OS/2 operating system. A DLL can contain executable code (functions), data, and resources.
A DLL file often has file extension .dll even though this is not required ...
.
DLL injection is often used by external programs to influence the behavior of another program in a way its
authors
In legal discourse, an author is the creator of an original work that has been published, whether that work exists in written, graphic, visual, or recorded form. The act of creating such a work is referred to as authorship. Therefore, a sculpt ...
did not anticipate or intend.
For example, the injected code could
hook
A hook is a tool consisting of a length of material, typically metal, that contains a portion that is curved/bent back or has a deeply grooved indentation, which serves to grab, latch or in any way attach itself onto another object. The hook's d ...
system function calls,
or read the contents of
password
A password, sometimes called a passcode, is secret data, typically a string of characters, usually used to confirm a user's identity. Traditionally, passwords were expected to be memorized, but the large number of password-protected services t ...
textboxes, which cannot be done the usual way.
A program used to inject arbitrary code into arbitrary processes is called a DLL injector.
Approaches on Microsoft Windows
There are multiple ways on
Microsoft Windows
Windows is a Product lining, product line of Proprietary software, proprietary graphical user interface, graphical operating systems developed and marketed by Microsoft. It is grouped into families and subfamilies that cater to particular sec ...
to force a process to load and execute code in a DLL that the authors did not intend:
* DLLs listed in the
registry entry
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows\AppInit_DLLs
are loaded into every process that loads
User32.dll during the initial call of that DLL. Beginning with
Windows Vista
Windows Vista is a major release of the Windows NT operating system developed by Microsoft. It was the direct successor to Windows XP, released five years earlier, which was then the longest time span between successive releases of Microsoft W ...
, AppInit_DLLs are disabled by default. Beginning with Windows 7, the AppInit_DLL infrastructure supports
code signing. Starting with
Windows 8
Windows 8 is a major release of the Windows NT operating system developed by Microsoft. It was Software release life cycle#Release to manufacturing (RTM), released to manufacturing on August 1, 2012, made available for download via Microsoft ...
, the entire AppInit_DLL functionality is disabled when
Secure Boot is enabled, regardless of code signing or registry settings.
* DLLs listed under the registry key
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\AppCertDLLs
are loaded into every process that calls the
Win32 API functions
CreateProcess
,
CreateProcessAsUser
,
CreateProcessWithLogonW
,
CreateProcessWithTokenW
and
WinExec
. That is the right way to use legal DLL injection on current version of Windows - Windows 10. DLL must be
signed by a valid certificate.
* Process manipulation functions such as
CreateRemoteThread
or
code injection
Code injection is a computer security exploit where a program fails to correctly process external data, such as user input, causing it to interpret the data as executable commands. An attacker using this method "injects" code into the program whi ...
techniques such as AtomBombing, can be used to inject a DLL into a program after it has started.
*# Open a
handle
A handle is a part of, or an attachment to, an object that allows it to be grasped and object manipulation, manipulated by hand. The design of each type of handle involves substantial ergonomics, ergonomic issues, even where these are dealt wi ...
to the target process. This can be done by spawning the process or by keying off something created by that process that is known to exist – for instance, a
window
A window is an opening in a wall, door, roof, or vehicle that allows the exchange of light and may also allow the passage of sound and sometimes air. Modern windows are usually glazed or covered in some other transparent or translucent ma ...
with a predictable title, or by obtaining a list of running processes and scanning for the target
executable
In computer science, executable code, an executable file, or an executable program, sometimes simply referred to as an executable or binary, causes a computer "to perform indicated tasks according to encoded instruction (computer science), in ...
's filename.
*# Allocate some memory in the target process, and the name of the DLL to be injected is written to it.
*#: This step can be skipped if a suitable DLL name is already available in the target process. For example, if a process links to
User32.dll,
GDI32.dll,
Kernel32.dll or any other library whose name ends in
32.dll
, it would be possible to load a library named
32.dll
. This technique has in the past been demonstrated to be effective against a method of guarding processes against DLL injection.
*# Create a new
thread in the target process with the thread's start address set to be the address of
LoadLibrary
and the argument set to the address of the string just uploaded into the target.
*#: Instead of writing the name of a DLL-to-load to the target and starting the new thread at
LoadLibrary
, one can write the code-to-be-executed to the target and start the thread at that code.
*# The
operating system
An operating system (OS) is system software that manages computer hardware and software resources, and provides common daemon (computing), services for computer programs.
Time-sharing operating systems scheduler (computing), schedule tasks for ...
then calls the initialization routine of the injected DLL.
*: Note that without precautions, this approach can be detected by the target process due to the
DLL_THREAD_ATTACH
notifications sent to every loaded module as a thread starts.
* Windows hooking calls such as
SetWindowsHookEx
.
* Use the
SuspendThread
or
NtSuspendThread
function to suspend all threads, and then use
SetThreadContext
or
NtSetContextThread
function to modify an existing thread's context in the application to execute injected code, that in turn could load a DLL.
* Exploit design limitations in Windows and applications that call the
LoadLibrary
or
LoadLibraryEx
function without specifying a full-qualified path to the DLL being loaded.
* Operating system-level
shims.
* Substituting an application-specific DLL with a rogue replacement that implements the same function exports as the original.
* Reflective DLLs or DLLs that are able to load themselves. The reflective DLL is copied into memory space of the target process and execution is passed to the entry point of the DLL which then loads itself from memory.
Approaches on Unix-like systems
On
Unix-like
A Unix-like (sometimes referred to as UN*X, *nix or *NIX) operating system is one that behaves in a manner similar to a Unix system, although not necessarily conforming to or being certified to any version of the Single UNIX Specification. A Uni ...
operating systems with the dynamic linker based on ld.so (on
BSD
The Berkeley Software Distribution (BSD), also known as Berkeley Unix or BSD Unix, is a discontinued Unix operating system developed and distributed by the Computer Systems Research Group (CSRG) at the University of California, Berkeley, beginni ...
) and ld-linux.so (on
Linux
Linux ( ) is a family of open source Unix-like operating systems based on the Linux kernel, an kernel (operating system), operating system kernel first released on September 17, 1991, by Linus Torvalds. Linux is typically package manager, pac ...
), arbitrary libraries can be linked to a new process by giving the library's pathname in the
LD_PRELOAD
In computing, a dynamic linker is the part of an operating system that loads and links the shared libraries needed by an executable when it is executed (at " run time"), by copying the content of libraries from persistent storage to RAM, fill ...
environment variable, that can be set globally or individually for a single process.
For example, on a Linux system, this command launches the command "prog" with the shared library from file "test.so" linked into it at the launchtime:
LD_PRELOAD="./test.so" prog
Such a library can be created in the same way as other
shared objects. With
GCC, this involves compiling the source file containing the new globals to be linked, with the or option, and linking with the option. The library has access to external symbols declared in the program like any other library.
On
macOS
macOS, previously OS X and originally Mac OS X, is a Unix, Unix-based operating system developed and marketed by Apple Inc., Apple since 2001. It is the current operating system for Apple's Mac (computer), Mac computers. With ...
, the following command launches the command "prog" with the shared library from file "test.dylib" linked into it at the launchtime:
DYLD_INSERT_LIBRARIES="./test.dylib" DYLD_FORCE_FLAT_NAMESPACE=1 prog
It is also possible to use debugger-based techniques on Unix-like systems.
Sample code
Copying a LoadLibrary-loaded DLL to a remote process
As there is no
LoadLibrary()
call to load a DLL into a foreign process you have to copy a locally loaded DLL into remotely allocated memory.
The following commented code shows how to do that.
#include
#include
#include
#include
#include
#include
#include
#include
#if defined(_MSC_VER)
#pragma warning(disable: 6387)
#endif
using namespace std;
using XHANDLE = unique_ptr;
using XHMODULE = unique_ptr, decltype([]( HMODULE hm ) )>;
MODULEENTRY32W getModuleDescription( HMODULE hmModule );
size_t maxReadableRange( void *pRegion );
string getAbsolutePathA( char const *fileName, char const *err );
DWORD dumbParseDWORD( wchar_t const *str );
wstring getAbsolutePath( wchar_t const *makeAbsolute, char const *errStr );
noreturn
void throwSysErr( char const *str );
constexpr wchar_t const *LOADER_DLL_NAME = L"loaderDll.dll";
constexpr char const *LOADER_THREAD_PROC = "loadLibraryThread";
int wmain( int argc, wchar_t **argv )
MODULEENTRY32W getModuleDescription( HMODULE hmModule )
noreturn
void throwSysErr( char const *str )
DWORD dumbParseDWORD( wchar_t const *str )
wstring getAbsolutePath( wchar_t const *makeAbsolute, char const *errStr )
The main issue solved here is that a locally loaded DLL copied to a remote process must occupy the same addresses as in the injecting process. The above code does this by
allocating memory for the same address range as occupied before in the injecting process. If this fails the DLL is locally freed, the former address range is marked as reserved, and the
LoadLibrary()
call is tried again. By reserving the former address range the code prevents that the next
LoadLibrary()
attempt will assign the same address range as used before.
The main drawback with that approach is that the DLL copied into the foreign process is that there aren't any other DLL library dependencies of that DLL loaded into the foreign address space or pointers, f.e. function calls, to DLLs loaded by the foreign process are adjusted according to the dependencies of the copied DLL. Luckily DLLs usually have preferred loading addresses which are honored by the
kernel's loader. Some DLLs like
kernel32.dll
are reliably loaded in the early beginning when the process address space is occupied by the executable image and its depending DLLs. These normally have reliable and non-conflicting addresses. So the copied DLL can use any
kernel32.dll
calls, f.e. to load another DLL with full advantages of a locally loaded DLL, i.e. having all relative library-dependencies. The path to that DLL is copied to the foreign address space and given as a void-parameter to the thread-function. The above implementation also allows to have additional parameters, which are passed to the remotely copied DLL after the string with the DLL to remotely loaded to passed to that DLL.
The following code is the source of the remotely copied loader DLL which only does
kernel32.dll
calls:
#include
#include
using namespace std;
BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved )
DWORD WINAPI loadLibraryThread( LPVOID lpvThreadParam );
// MSVC / clang-cl mangling
#if defined(_M_IX86)
#pragma comment(linker, "/export:loadLibraryThread=?loadLibraryThread@@YGKPAX@Z")
#elif defined(_M_X64)
#pragma comment(linker, "/export:loadLibraryThread=?loadLibraryThread@@YAKPEAX@Z")
#else
#error unsupported platform
#endif
DWORD WINAPI loadLibraryThread( LPVOID lpvThreadParam )
The last code shows an example of a DLL loaded by the loader DLL which prints the parameters to a file.
#include
#include
#include
using namespace std;
#if defined(_MSC_VER)
#pragma warning(disable: 6387) // returned handle could be null
#endif
#if defined(_M_IX86)
#pragma comment(linker, "/export:DllMain=_DllMain@12")
#elif defined(_M_X64)
#pragma comment(linker, "/export:DllMain=_DllMain@12")
#else
#error unsupported platform
#endif
using namespace std;
DWORD WINAPI myThread( LPVOID lpvThreadParam );
BOOL APIENTRY DllMain( HMODULE hModule, DWORD dwReason, LPVOID lpReserved )
extern "C"
__declspec(dllexport)
wchar_t initData AX_PATH= ;
DWORD WINAPI myThread( LPVOID lpvThreadParam )
One important fact is that there are no exports called from the loader DLL, but instead all initialization is done from
DllMain
. The only export is that of
initData
, which receives the parameters given by the injecting process through the loader DLL. And one must be aware that the thread created from a DllMain-function isn't scheduled until after its
DLL_THREAD_ATTACH
-function has succeeded. So there may not be any synchronization from inside
DllMain
with the created thread.
References
{{DEFAULTSORT:Dll Injection
Computer libraries
Windows administration
Threads (computing)