A Windows x64 library for intercepting NtMapViewOfSection to redirect DLL loading at runtime.
Zero function hooks. Zero IAT patching. Just one hardware breakpoint and a VEH handler.
中文 | English
Vectored Overloading hijacks the Windows PE loader at NtMapViewOfSection — the exact point where LoadLibrary maps a DLL into memory. By swapping the section handle in RCX (x64 calling convention) via a Vectored Exception Handler, the loader is tricked into loading your payload instead of the real DLL.
The payload's DllMain runs naturally with full import resolution — because the loader processes it as if it were the legitimate image.
| Concern | Traditional DLL hijacking | Vectored Overloading |
|---|---|---|
| File on disk | Must replace or proxy a real DLL file | No disk writes — lives in a section object |
| Detection surface | File system monitoring + signature scanning | Only a VEH registration and a debug register |
| Import resolution | Manual — complex for real payloads | Automatic — the loader does it for you |
| Digital signature | Broken on replacement | Real DLL's signature is intact on disk |
This technique sits at the intersection of hardware debugging, exception handling, and NT kernel primitives.
┌─────────────────────────────────────────────────────────┐
│ Stage 1 — Prepare │
│ │
│ NtCreateSection(SEC_IMAGE, payload.dll) │
│ → fakeSection (looks like a real DLL to the │
│ loader, backed by payload.dll) │
└───────────────────────┬─────────────────────────────────┘
│
┌───────────────────────▼─────────────────────────────────┐
│ Stage 2 — Arm │
│ │
│ DR0 = NtMapViewOfSection │
│ DR7 = 0x1 (execute breakpoint) │
│ AddVectoredExceptionHandler(VehHandler) │
└───────────────────────┬─────────────────────────────────┘
│
┌───────────────────────▼─────────────────────────────────┐
│ Stage 3 — Hijack │
│ │
│ LoadLibrary("winhttp.dll") │
│ ↓ │
│ Loader: NtMapViewOfSection(winhttpSection, ...) │
│ ↓ │
│ Hardware breakpoint → EXCEPTION_SINGLE_STEP │
│ ↓ │
│ VEH handler: RCX ← fakeSection (swap!) │
│ ↓ │
│ Loader maps payload.dll instead of winhttp.dll │
│ ↓ │
│ payload.dll!DllMain(DLL_PROCESS_ATTACH) executes │
└─────────────────────────────────────────────────────────┘
The key insight: The Windows loader uses NtMapViewOfSection to map every DLL. By intercepting this single NT API call on the first invocation and swapping the section handle in the RCX register, we redirect the loader's mapping target.
- Visual Studio 2022 (v143 toolset)
- Windows 10/11 x64
- MSVC 14.44+
# 1. Open the solution
start "Vectored Overloading.sln"
# 2. Build both projects (Release | x64)
MSBuild Vectored\ Overloading.sln -p:Configuration=Release -p:Platform=x64Output:
compileDirectory/Release/x64/
├── Vectored Overloading.exe # Demo app
└── payload.dll # Example payload (shows MessageBox)
#include <Vectored Overloading/Vectored Overloading.h>
using namespace VectoredOverloading;
// 1. Create a fake image section from your payload DLL
Prepare(L"payLoad.dll");
// 2. Arm the trap
Arm();
// 3. Trigger — LoadLibrary will load YOUR payload instead
HijackResult r = Hijack(L"winhttp.dll");
if (r.success) {
printf("Payload loaded at 0x%p\n", r.hModule);
}
// 4. Restore debug registers
Cleanup();
// The payload stays loaded until you free it
FreeLibrary(r.hModule);struct HijackResult {
HMODULE hModule; // DLL handle (NULL on failure)
LONG interceptCount; // VEH interception count
bool success; // hModule != NULL && interceptCount > 0
DWORD lastError; // GetLastError() on failure
};| Function | Return | Description |
|---|---|---|
Prepare( szPayloadDll ) |
bool |
Opens payload.dll, creates SEC_IMAGE section, validates PE structure |
Arm() |
bool |
Registers VEH handler, sets hardware breakpoint on NtMapViewOfSection |
Hijack( szTargetDll ) |
HijackResult |
Calls LoadLibrary(szTargetDll), the VEH swaps the section → payload loads |
Cleanup() |
void |
Restores DR0–DR7 debug registers |
GetFakeSection() |
HANDLE |
Read-only: the malicious section handle |
GetHijackCount() |
LONG |
Read-only: how many times the VEH fired |
- Call
Hijack()immediately afterArm(). Any DLL load between them will consume the hijack. Hijack()does not callFreeLibrary. The caller owns the module lifetime.- x64 only — the RCX register swap relies on the x64 calling convention.
- VEH gets first-chance handling before the debugger. Running under a debugger is usually safe; conflicts only arise if the debugger itself sets hardware breakpoints on the same address.
src/
├── Vectored Overloading/ # Core library
│ ├── Vectored Overloading.h # Public API declarations
│ └── Vectored Overloading.cpp# Implementation
├── PayLoad/ # Example payload DLL
│ └── Main.cpp # DllMain with MessageBox
└── App/ # Demo application
└── App.cpp # Minimal usage example
project/
├── Vectored Overloading/ # Library project (.vcxproj)
└── PayLoad/ # Payload project (.vcxproj)
namespace VectoredOverloading {
// Public API
struct HijackResult { ... };
bool Prepare(...);
bool Arm();
HijackResult Hijack(...);
void Cleanup();
HANDLE GetFakeSection();
LONG GetHijackCount();
namespace _detail {
// Internal: NT API typedefs, global state, VEH handler,
// hardware breakpoint helpers — not exposed to callers.
}
}A minimal payload is just a DllMain:
#include <Windows.h>
BOOL WINAPI DllMain(HINSTANCE hinst, DWORD reason, LPVOID reserved)
{
if (reason == DLL_PROCESS_ATTACH) {
// Your code here — imports are resolved by the loader!
MessageBoxW(NULL, L"Hello", L"Payload", MB_OK);
}
return TRUE;
}Compile without CRT for minimal dependencies:
cl /LD /GS- /O1 payload.cpp /link /NODEFAULTLIB \
/ENTRY:DllMain /SUBSYSTEM:WINDOWS kernel32.lib user32.libSEC_IMAGE produces a proper Image Section Object. The loader recognizes it as a valid PE image and proceeds through its standard pipeline: relocation processing → import resolution → DllMain. SEC_COMMIT sections are treated as raw memory — no imports are resolved.
NtCreateSection(SEC_IMAGE) rejects PAGE_EXECUTE_READWRITE with 0xC0000022 (STATUS_ACCESS_DENIED). The image section protection is derived from the PE section headers. PAGE_EXECUTE is the correct initial protection.
Choose a DLL that:
- Is not already loaded in the process (check with
GetModuleHandle) - Has few deep dependencies (to minimize VEH noise)
- Is commonly loaded but not a known DLL
Good candidates: winhttp.dll, wmp.dll, dxgi.dll, amsi.dll
Call Hijack immediately after Arm — do NOT execute anything that might trigger a DLL load between them. This includes:
system("pause")— can load console subsystem DLLsLoadLibrary/GetModuleHandleon new DLLs- COM initialization, Windows sockets, etc.
MIT