Why Manual Mapping?

Traditional DLL injection uses LoadLibrary, which is easily detected:

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
The driver only does basic import resolution. Full support would require handling delay-load imports, bound imports, and API sets. The target DLL typically handles runtime imports itself to avoid complexity.

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:

Rate Limiting

Maximum 5 injections per second to avoid:

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:

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:

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:

  1. Build internal cheat DLL with ENABLE_MANUAL_MAP = 1
  2. Load driver via kdmapper
  3. Use loader to inject into CS2
  4. Internal cheat runs with full game access