Why Manual Mapping?
Traditional DLL injection uses LoadLibrary, which is easily detected:
- LoadLibrary calls are logged by anti-cheat
- Loader data table entries are scanned
- Memory patterns match known DLLs
- Digital signatures are verified
Manual mapping bypasses all of this by replicating the loader's work manually, without calling any loader APIs.
The Manual Mapping Process
Phase 1: Setup
1. Open target process with appropriate rights
2. Allocate memory for the DLL image
3. Read source DLL from disk
Phase 2: Section Mapping
4. Copy PE headers to allocated memory
5. For each section in PE:
- Map section to correct virtual address
- Apply section characteristics (R/W/X)
6. Zero remaining memory (alignment)
Phase 3: Relocations
The DLL may not load at its preferred base address. The .reloc section contains a list of addresses that need to be patched with the delta between preferred and actual base:
delta = actual_base - preferred_base
for each relocation entry:
address = base + entry.offset
*address += delta
Phase 4: Import Resolution
The Import Address Table (IAT) lists required DLLs and functions. These need to be resolved at runtime since we're not using the loader:
for each imported DLL:
load_library(dll_name) // Or use existing mapping
for each imported function:
func_addr = get_proc_address(dll, func_name)
iat_slot[func_index] = func_addr
Phase 5: Execution
6. Build ManualMapParam_t structure with:
- Original DLL path
- Loader information
- Argument data
7. Call DllMain with:
- fdwReason = DLL_PROCESS_ATTACH
- lpReserved = pointer to ManualMapParam_t
Security Features
Process Whitelisting
The driver enforces a write-once whitelist:
- First IOCTL sets the allowed process ID
- Subsequent whitelist changes are rejected
- All operations validated against this PID
Rate Limiting
Maximum 5 injections per second to avoid:
- Detection via timing analysis
- Accidental system instability
- Abuse if driver is compromised
Driver Unlinking
The driver removes itself from PsLoadedModuleList to hide from enumeration tools that scan loaded drivers.
Architecture: Driver + Loader
ManualMapDrv is a two-component system:
- ManualMapDrv (kernel) — Performs the actual injection
- ManualMapLoader (user-mode) — GUI tool to control injection
Communication Flow
User-mode Loader Kernel Driver
-------------- -------------
Find CS2 PID ----------->
<----------- Validate PID (whitelist)
Select DLL ----------->
Send IOCTL -----------> IOCTL_MM_INJECT_DLL
<----------- Return base address
Display result Inject DLL into target
Threading Challenges
DllMain is called from a kernel thread, not a user-mode thread. This causes issues with:
- User-mode APCs (won't execute)
- Window creation (requires message queue)
- Some CRT functions expecting TEB setup
The solution is for the DLL to spawn its own user-mode thread early, or use special compiler flags to minimize CRT dependencies.
What I Learned
Manual mapping taught me the PE format in detail: sections, relocations, imports, exports, and how the Windows loader actually works. The complexity of making a DLL work without the loader's help gives appreciation for how much the OS does automatically.
Integration with Cheats
This driver is designed for CS2 cheats:
- Build internal cheat DLL with ENABLE_MANUAL_MAP = 1
- Load driver via kdmapper
- Use loader to inject into CS2
- Internal cheat runs with full game access