by Kiy0w0
Full kernel-mode PE manual mapping · Zero usermode injection APIs · MDL Stealth IPC · Thread Hijacking
Nanahira is a ring-0 kernel manual map DLL injector. Every step of the injection PE parsing, section mapping, base relocations, import resolution, per-section memory protection, TLS callbacks, exception directory registration, and DllMain execution runs entirely inside the Windows kernel.
The usermode side (nanahira.exe) reads the DLL from disk and drops it into a shared memory section. That's it. No VirtualAllocEx, no WriteProcessMemory, no CreateRemoteThread the kernel driver does all of it.
Three injection modes are available depending on your situation:
| Mode | How it works |
|---|---|
kernel |
Full ring-0 manual map via driver (default) |
hook |
Shellcode injected via SetWinEventHook no CreateRemoteThread |
usermode |
Direct inject without driver VirtualAllocEx + self-contained shellcode |
nanahira.exe driver.sys
───────────── ─────────────────────────────────
Find target PID SharedMemory Parse PE headers
Read DLL from disk ════════════════► Allocate memory in target
Write to SHM Map sections
Send IPC command ◄════════════════ Fix relocations
Show progress + base Status / Base Resolve imports (name + ordinal)
Delay-load imports
Set per-section protections
Register exception table (.pdata)
Run TLS callbacks
Erase / stomp PE headers
Call DllMain
The IPC channel between usermode and the kernel driver uses an anonymous MDL-mapped buffer no named section objects, no visible handles in the global namespace. The driver auto-maps the buffer into nanahira.exe via a process notify callback when the injector starts.
| Feature | Details |
|---|---|
| Ghost Driver | Unlinks PsLoadedModuleList immediately after boot to hide driver from system module enumerators |
| Self-Erase Header | Zero-out driver MZ/PE headers from memory inside DriverEntry |
| W^X Execution (No RWX) | PAGE_READWRITE → PAGE_EXECUTE_READ promotion in all three injection modes — kernel, hook, and usermode — eliminates RWX pages entirely |
| Full kernel manual map | PE ops in ring 0 no usermode injection APIs |
| Thread hijacking execution | Hijacks an existing game thread instead of creating a new one avoids CreateThread callbacks monitored by Anti-Cheat |
| Import by name + ordinal | Both forms handled previously ordinal imports were skipped |
| Delay-load import support | IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT resolved at inject time |
| Forwarded export resolution | Chains like ntdll.RtlXxx → ntdllp.RtlXxx are followed |
| TLS callback execution | Callbacks run before DllMain, as the loader would |
| Exception directory (.pdata) | RtlAddFunctionTable called in-process so C++ exceptions / SEH work inside injected DLL |
| Header erase / stomp | Zero or LFSR-junk overwrite controlled per-inject via flags |
| Section name scrubbing | .text, .rdata etc. unconditionally zeroed in target after mapping |
| KEVENT injection mutex | Prevents concurrent inject races second inject waits up to 5 s before failing |
| PE deep validator | Checks SizeOfImage bounds and all section PointerToRawData offsets before mapping |
| Worker SEH recovery | __try/__except around worker loop AV in ring-0 resets state instead of BSOD |
| Stealth IPC | Anonymous MDL-mapped buffer no named kernel objects visible to scanners |
| WinEventHook injection | Alternative entry via SetWinEventHook + self-contained shellcode |
| Usermode fallback | Works without driver full PE shellcode runs inside target |
| Compile-time XOR strings | Sensitive literals encrypted at compile time via template metaprogramming |
| Signature randomization | Source-level identifier mutation + binary PE mutations every build |
| Settings panel (GUI) | ⚙ button in title bar with Always On Top, GitHub, and Patreon links |
| VAD node hiding | Zeroes StartingVpn / EndingVpn in the target process VAD tree after injection so the region is invisible to NtQueryVirtualMemory scanners |
| Per-section protection | Hook and usermode modes apply correct per-section page protections after shellcode execution instead of leaving the whole image RWX |
NtCreateThreadEx hidden thread |
Usermode mode replaces CreateRemoteThread with NtCreateThreadEx + THREAD_CREATE_FLAGS_HIDE_FROM_DEBUGGER to bypass thread-creation callbacks |
| Randomized LFSR seed | Header stomp junk seed derived from KeQueryPerformanceCounter XOR allocBase — unique per inject, no static fingerprint |
kernel-mmi/
├── nanahira.sln
├── build_release.bat
├── quick_spoof.bat
│
├── driver/
│ ├── driver.cpp Manual map engine
│ ├── driver.h Internal declarations + dynamic import typedefs
│ └── offsets.h Dynamically generated Windows build offsets (PDB parser output)
│
├── usermode/
│ ├── nanahira.cpp Injector UI + IPC client
│ ├── nanahira.h Utilities + mode definitions
│ ├── hook_inject.h WinEventHook injection mode
│ ├── usermode_inject.h Usermode fallback injection mode
│ ├── xor.h Compile-time XOR string obfuscation
│ ├── lazy_importer.h PEB-walk API resolution (LI_FN)
│ ├── discord_rpc.cpp Discord Rich Presence over named pipe
│ └── discord_rpc.h
│
├── tools/
│ ├── pdb-parser/ Rust-based Windows Kernel PDB offset parsing tool
│ ├── signature_randomizer.ps1 PE binary mutation script
│ └── source_randomizer.ps1 Identifier randomization script
│
└── shared/
└── protocol.h Shared memory layout + IPC commands + flags
| Details | |
|---|---|
| Windows 10 / 11 x64 | Tested on 22H2 / 23H2 |
| Administrator | Everything needs elevation |
| Visual Studio 2022 | Desktop development with C++ workload |
| Windows Driver Kit | Match your Windows SDK version |
| Rust & Cargo | Installed on build system (required for pdb-parser) |
| PowerShell 7+ | Required for signature randomization scripts |
See tutorial.md for full instructions on building, loading the driver, and running the injector.
Before compilation, identifiers in protocol.h are randomized shared memory name, magic value, pool tag. The compiled binary contains completely different strings and constants each time.
After compilation, 10 mutations are applied:
| # | Target | What changes |
|---|---|---|
| 1 | TimeDateStamp | Random compile timestamp |
| 2 | Checksum | Random PE checksum |
| 3 | Rich header | Destroys MSVC toolchain fingerprint |
| 4 | Section names | .text → .code, .rdata → .cnst, etc. |
| 5 | Debug directory | Wipes PDB path and CodeView GUID |
| 6 | Linker version | Fakes MSVC version fields |
| 7 | OS version | Randomizes minimum OS version fields |
| 8 | Code caves | NOP-like junk in padding regions |
| 9 | Build GUID | Unique 128-bit watermark per build |
| 10 | DOS stub | Randomizes unused DOS header bytes |
Every run produces binaries with a different SHA256 hash.
Build errors
| Error | Fix |
|---|---|
WDK not found |
Install WDK matching your SDK version |
'cl.exe' not recognized |
Use x64 Native Tools Command Prompt |
LNK2001 unresolved external |
Undocumented APIs are resolved dynamically don't link them statically |
Driver loading
| Error | Fix |
|---|---|
Access denied |
Run as Administrator |
StartService FAILED 577 |
Driver unsigned enable test signing + self-sign |
Value protected by Secure Boot |
Disable Secure Boot in BIOS first |
Memory Integrity blocking |
Windows Security → Core Isolation → Memory integrity → Off |
| BSOD on first run | Use test signing with bcdedit /set testsigning on and sign driver manually |
Injection issues
| Error | Fix |
|---|---|
Cannot connect |
Driver not loaded run sc start nanahira first |
Process not found |
Target must already be running |
Invalid PE |
Must be a valid x64 DLL |
Import resolution failed |
Required DLL not loaded in target try hook or usermode mode |
| Target crashes | Check Event Viewer for 0xC0000005 DLL access violation |
- KEVENT Injection Mutex Driver uses a
KEVENT(SynchronizationEvent) so only one injection runs at a time — prevents race conditions and shared memory corruption. - Enhanced PE Validator Validates
SizeOfImage(max 256 MB) and all sectionPointerToRawDataoffsets before mapping begins, preventing BSOD from malformed DLLs. - Worker Thread SEH Recovery
__try/__exceptwraps the worker switch — an access violation in ring-0 resets state toIPC_READYand releases the mutex instead of crashing the kernel. - Unconditional Section Name Scrubbing Section names (
.text,.rdata, etc.) zeroed in target memory after mapping without requiring any flag. - PDB Offset Parser Rust-based build-time symbol parser downloads
ntoskrnl.pdbfrom Microsoft symbol servers and auto-generatesoffsets.hto prevent update-induced BSODs. - VAD Node Hiding After injection,
StartingVpn/EndingVpnare zeroed in the target process VAD tree — the allocated region becomes invisible toNtQueryVirtualMemory-based scanners. - Full W^X Across All Modes Hook and usermode injection modes now allocate
PAGE_READWRITE, write shellcode, promote toPAGE_EXECUTE_READ, then apply correct per-section protections after execution — no RWX pages at any point. NtCreateThreadExHidden Thread Usermode mode replacesCreateRemoteThreadwithNtCreateThreadEx+THREAD_CREATE_FLAGS_HIDE_FROM_DEBUGGER, bypassing thread-creation callbacks monitored by anti-cheat.- Randomized LFSR Stomp Seed Header stomp junk seed is now derived from
KeQueryPerformanceCounterXORallocBaseper inject — eliminates static binary fingerprint from header stomping.
Special thanks to the following projects and authors for their contributions to the injection techniques used in this project:
- TTKKO/Kernel-Manual-Map-Injector WinEventHook and shellcode logic.
- TheCruZ/Simple-Manual-Map-Injector Manual mapping concepts and IAT references.
- fluffysnaff/fluffy-injector GUI inspiration and implementation logic.
For educational and research purposes only. The author takes no responsibility for misuse. Use responsibly and in compliance with applicable laws.
