Skip to content

KyuuChiaki/Vectored-Overloading

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Vectored Overloading

DLL Hijacking via VEH + Hardware Breakpoints

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.

Language Platform MSVC License

中文 | English


What is Vectored Overloading?

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.

Why this approach?

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.

How it works

┌─────────────────────────────────────────────────────────┐
│  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.

Quick start

Prerequisites

  • Visual Studio 2022 (v143 toolset)
  • Windows 10/11 x64
  • MSVC 14.44+

Build

# 1. Open the solution
start "Vectored Overloading.sln"

# 2. Build both projects (Release | x64)
MSBuild Vectored\ Overloading.sln -p:Configuration=Release -p:Platform=x64

Output:

compileDirectory/Release/x64/
├── Vectored Overloading.exe    # Demo app
└── payload.dll                 # Example payload (shows MessageBox)

Usage

#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);

API Reference

HijackResult

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
};

Functions

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

⚠️ Important notes

  • Call Hijack() immediately after Arm(). Any DLL load between them will consume the hijack.
  • Hijack() does not call FreeLibrary. 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.

Architecture

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)

Internal namespace structure

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.
    }
}

Writing your own payload

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.lib

Technical notes

Why SEC_IMAGE and not SEC_COMMIT?

SEC_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.

Why PAGE_EXECUTE and not PAGE_EXECUTE_READWRITE?

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.

Target DLL selection

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

Avoiding premature hijack consumption

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 DLLs
  • LoadLibrary / GetModuleHandle on new DLLs
  • COM initialization, Windows sockets, etc.

License

MIT

About

Using Vectored Exception Handling and hardware breakpoint to hijack DLL loading

Topics

Resources

Stars

3 stars

Watchers

0 watching

Forks

Contributors

Languages