You are reviewing an alert and notice that a signed Windows executable loaded a DLL from an unusual directory. Is that automatically malicious? Not necessarily.
Windows applications load DLLs constantly, and plenty of legitimate applications use uncommon paths. Before we can recognize DLL hijacking, we need to understand what normal DLL loading looks like and where an attacker can interfere with it.
01 / MECHANISM
How normal DLL loading works
A Windows application rarely contains every function it needs. Instead, it can use dynamic-link libraries, or DLLs, that provide reusable code and data. An application might rely on DLLs for graphics, cryptography, networking, or retrieving file-version information. Multiple programs can use that common functionality without each program implementing it independently.
Applications commonly use DLLs through either load-time or run-time linking.
Load-time linking
With load-time linking, information about the required DLLs and imported functions is stored in the executable's Portable Executable structure when the program is built. The PE Import Directory identifies these dependencies.
When Windows starts the program, the loader resolves the required DLLs and imported functions. It then places the resolved functions' memory addresses into the Import Address Table, or IAT. The program uses those addresses when it calls the imported functions.
Think of the Import Directory as a list of contacts the program expects to need. The IAT becomes the completed address book that tells the program where those contacts can be reached in memory. The analogy is simplified, but it gives us enough context to follow the loading process. Microsoft documents both structures in its PE format specification.
Run-time linking
Run-time linking happens after the program has already started. When the program needs a DLL, it can call LoadLibrary or LoadLibraryEx. Windows attempts to locate the requested module and map it into the program's virtual address space. If the operation succeeds, the function returns a handle to the loaded module.
An exported function is a function that a DLL intentionally makes available for other code to call. The program can pass the DLL handle and the exported function's name to GetProcAddress, which returns the function's memory address. The program then uses that address to call the function. If the requested module is already loaded under the applicable conditions, Windows may return a handle to the existing module instead of mapping another copy. Microsoft explains this process in Run-Time Dynamic Linking.
Once a DLL is mapped into a process, it does not run as a separate program. If a newly loaded DLL defines an entry point, Windows calls it with a DLL_PROCESS_ATTACH notification. This entry-point function is commonly named DllMain and gives the DLL an opportunity to perform limited initialization. DLL code can also execute later when the application calls one of its exported functions.
In either case, the code runs inside the loading process and under that process's existing security context. Loading a DLL does not automatically grant the process additional privileges.
02 / RESOLUTION
How Windows identifies the requested DLL
An application can identify a DLL in three basic ways:
- A full path, such as
C:\Program Files\Example\version.dll - A relative path, such as
plugins\version.dll - A bare filename, such as
version.dll
A full path normally directs Windows to a specific location. A relative path still needs to be interpreted using directory context. A bare filename identifies the requested module but does not tell Windows where the file is located.
When an application does not provide a fully qualified path, Windows may need to resolve the DLL's location. This is where analysts sometimes oversimplify the process by memorizing one universal search order. In reality, the applicable rules depend on the loading context. Windows may consider DLL redirection, API sets, side-by-side manifests, modules already loaded into the process, KnownDLLs, package dependencies, loader configuration, and eligible search directories.
The result can also depend on whether the application is packaged or unpackaged and whether it uses functions or flags that change the default search behavior. Microsoft breaks down these factors in Dynamic-link library search order.
Where side-by-side loading fits
Side-by-side loading is one legitimate mechanism that can influence DLL resolution. Windows uses side-by-side assemblies to support applications that need different versions of shared components. An application manifest identifies the assemblies and versions to which the application should bind at run time. This helps prevent one application's installation from replacing a component that another application needs.
For an analyst, this means that a simple directory search will not explain every DLL selection. If a DLL path looks unusual, the application's manifest may explain why Windows selected that component.
It is also important not to confuse legitimate side-by-side loading with the attacker technique called DLL side-loading. The names are similar, but they describe different things.
03 / PRECONDITIONS
How normal loading becomes an attack opportunity
The same loading decisions that allow applications to find their dependencies can create an opportunity for abuse. Suppose an application requests a DLL without fully identifying its intended location. If an attacker can write to an eligible location, they may be able to place a malicious DLL there using the filename the application expects.
When the application runs, Windows applies the resolution rules relevant to that load. If Windows selects the attacker-controlled file, it maps the DLL into the application's address space. Code from that DLL can then execute inside the application's process context. Microsoft describes this risk in Dynamic-Link Library Security.
Placing a DLL somewhere on disk is not enough by itself. Several conditions must line up:
- The application must request or resolve a DLL in a way that gives an attacker-controlled file a chance to be selected.
- The attacker must be able to place or replace a DLL in an eligible location.
- The susceptible application must run after the file is placed.
- The supplied DLL must match the loading process's architecture.
- The malicious DLL may need to provide expected exports or preserve enough legitimate functionality for the application to keep running.
If the DLL is selected, its code executes in the host process's existing security context. That does not automatically create persistence or elevate privileges. Persistence requires a repeatable execution trigger. Privilege escalation depends on whether the host process runs with privileges the attacker does not already have and whether the attacker can meet the other loading conditions.
Questions to answer during triage
- Which process loaded the DLL, and what is the process's full path?
- What is the DLL's complete path?
- When and how did the DLL arrive on disk?
- Can a standard user write to that directory?
- Does this process normally load this DLL from this location?
- Did the executable and DLL arrive together?
- What did the process do after loading the DLL?
The stronger signal is the relationship between file placement, process execution, DLL selection, module loading, and the behavior that followed. One unusual filename or path rarely tells the entire story.
04 / TECHNIQUES
Three common forms of DLL hijacking
DLL search-order hijacking
DLL search-order hijacking occurs when an attacker influences which matching DLL Windows finds during resolution. If an application requests a DLL without a fully qualified path, Windows applies the resolution rules and eligible directory order for that load.
An attacker who can write to an earlier eligible location may place a DLL there using the requested filename. If Windows encounters that file before the intended library, it may select and load the attacker-controlled copy. MITRE ATT&CK T1574.001 describes this as planting a trojan DLL in a location prioritized over the legitimate library.
DLL side-loading
DLL side-loading uses a legitimate application to load an attacker-controlled DLL. The attacker commonly places a legitimate executable and malicious DLL together, then runs the executable. When the executable requests its expected DLL, normal loader behavior may cause Windows to select the nearby attacker-controlled copy.
The executable may be correctly signed, but that signature applies to the executable. It does not automatically authenticate the separate DLL or make the combined behavior trustworthy. A signed executable is useful context, not a verdict.
Search-order hijacking and side-loading can overlap. Search-order hijacking describes the loader behavior that causes an unintended DLL to be selected. Side-loading describes the attacker's setup: pairing a legitimate executable with an attacker-controlled DLL.
Phantom DLL hijacking
Phantom DLL hijacking targets an application's reference to a DLL that is not normally present. The application may search several eligible locations for an optional, removed, or otherwise missing DLL. If an attacker identifies that behavior and can write to a suitable location, they may provide a DLL using the missing module's expected name. The application may then load the attacker-supplied file the next time it runs.
Procmon may reveal the original searches as NAME NOT FOUND events. Those events are common and only show that a file lookup failed. By themselves, they do not prove that the application is exploitable or that an attack occurred.
Stronger evidence connects the failed searches to the later creation and successful loading of a matching DLL. MITRE's DET0201 detection strategy follows the same principle by correlating file creation, process creation, and module-load telemetry instead of treating one failed lookup as proof.
05 / ESCALATION
The Tier 1 takeaway
Across all three techniques, the defensive lesson is the same: do not judge a DLL load using only one filename, path, signature, or event. Determine whether the process-DLL relationship is expected, establish how the DLL reached that location, and investigate what the process did after Windows loaded it.
You do not need to reverse-engineer the DLL before escalating a suspicious case. A well-supported Tier 1 escalation can be built from timeline and context: the file appeared in a user-writable directory, a legitimate executable launched from an unusual location, the process loaded the nearby DLL, and suspicious behavior followed.
06 / SOURCES
References and further reading
- Microsoft Learn, “Application Manifests.”
- Microsoft Learn, “DllMain Entry Point.”
- Microsoft Learn, “Dynamic-Link Library Search Order.”
- Microsoft Learn, “Dynamic-Link Library Security.”
- Microsoft Learn, “LoadLibraryA Function.”
- Microsoft Learn, “PE Format.”
- Microsoft Learn, “Process Interoperability.”
- Microsoft Learn, “Run-Time Dynamic Linking.”
- Microsoft Learn, “Side-by-Side Assembly Sharing.”
- MITRE ATT&CK, “Detection Strategy for Hijack Execution Flow for DLLs.”
- MITRE ATT&CK, “Hijack Execution Flow: DLL.”