# How ModuleMgr in YimMenuV2 Handles Manual Mapping vs Normal Injection

> Discover how YimMenuV2's ModuleMgr uses LoadLibraryA for normal injection and custom PE loaders for manual mapping. Learn about runtime configuration for compatibility and stealth.

- Repository: [YimMenu/YimMenuV2](https://github.com/YimMenu/YimMenuV2)
- Tags: internals
- Published: 2026-07-17

---

**The ModuleMgr class in YimMenuV2 implements two distinct DLL loading strategies—normal injection via `LoadLibraryA` and manual mapping via a custom PE loader—selecting between them at runtime based on the `g_manualMapEnabled` configuration flag to balance compatibility against stealth.**

The `ModuleMgr` component serves as the centralized module loader for the YimMenuV2 mod menu, providing a unified interface to handle different injection methods depending on the target process security posture. According to the YimMenuV2 source code, this manager implements both standard Windows API injection and advanced manual mapping techniques within the `src/core/memory/` directory. Understanding how these two strategies differ is essential for developers working with process injection and anti-detection measures.

## Overview of Module Loading Strategies

The implementation resides in [`src/core/memory/ModuleMgr.cpp`](https://github.com/YimMenu/YimMenuV2/blob/main/src/core/memory/ModuleMgr.cpp) with declarations in [`src/core/memory/ModuleMgr.hpp`](https://github.com/YimMenu/YimMenuV2/blob/main/src/core/memory/ModuleMgr.hpp), while supporting structures reside in [`src/core/memory/Module.hpp`](https://github.com/YimMenu/YimMenuV2/blob/main/src/core/memory/Module.hpp) and [`src/core/memory/Module.cpp`](https://github.com/YimMenu/YimMenuV2/blob/main/src/core/memory/Module.cpp). The class exposes two primary loading methods that represent fundamentally different approaches to bringing DLL code into a target process address space.

- **Normal Injection**: Uses the Windows OS loader through `LoadLibraryA`, allowing the system to handle relocations, imports, and execution automatically.
- **Manual Mapping**: Implements a custom portable executable (PE) loader that manually allocates memory, copies sections, fixes relocations, resolves imports, and calls the entry point without notifying the OS loader.

Both methods ultimately store the resulting `HMODULE` or manual-mapped base address in an internal `std::unordered_map<std::string, ModuleInfo>` registry, allowing the rest of the codebase to retrieve module handles regardless of the loading strategy employed.

## Normal Injection: The LoadLibraryA Approach

The `Inject` method represents the straightforward loading path. When invoked, it calls `LoadLibraryA(path.c_str())` to request that the Windows loader map the specified DLL into the target process.

This approach triggers the standard OS loading sequence. The loader parses the PE headers, maps sections into memory, and performs base relocations if necessary. It resolves import address table (IAT) entries against already-loaded modules, executes TLS callbacks, and finally calls `DllMain` with `DLL_PROCESS_ATTACH`. Because the OS tracks this module in its internal list, it appears in tools like Process Explorer and can be detected by anti-cheat systems monitoring `LoadLibrary` calls.

The method returns `true` if the handle is non-null, registering the resulting `HMODULE` in the manager's internal map for subsequent lookups.

## Manual Mapping: The Custom PE Loader

The `ManualMap` method provides a stealth alternative that bypasses the Windows loader entirely. This implementation performs all loading steps manually, ensuring the module does not appear in the OS loader lists or standard module enumeration APIs.

The process follows these steps:

1. **File Reading**: Reads the DLL file from disk into a local buffer using standard file I/O (`std::ifstream`).
2. **Memory Allocation**: Calls `VirtualAllocEx` in the target process to reserve memory equal to the image size specified in the PE optional header.
3. **Section Mapping**: Copies the PE headers and individual section data (`.text`, `.data`, etc.) into the allocated memory at their respective virtual addresses.
4. **Base Relocation**: Walks the relocation table (`IMAGE_BASE_RELOCATION`) and patches addresses using `IMAGE_REL_BASED_HIGHLOW` or `IMAGE_REL_BASED_DIR64` fixups to account for the actual load address differing from the preferred base.
5. **Import Resolution**: Enumerates the import descriptor table, locates dependency modules via `GetModuleHandleA` in the remote process, and resolves function pointers using `GetProcAddress`.
6. **TLS Execution**: Iterates the `IMAGE_TLS_DIRECTORY` to invoke any thread-local storage callbacks before the main entry point.
7. **Entry Point Invocation**: Creates a remote thread or stub that calls `DllMain(moduleBase, DLL_PROCESS_ATTACH, nullptr)` manually.

Because no `LoadLibrary` call occurs, this method avoids standard detection vectors that monitor API hooks in `kernel32.dll` or `ntdll.dll`.

## Key Implementation Details

### Relocation Handling

The `ApplyRelocations` helper function processes the PE relocation directory. For each block, it calculates the delta between the preferred image base and the actual allocated address, then iterates through relocation entries to patch absolute addresses in the loaded image. This step is critical when the preferred base address is already occupied, which is common in heavily populated process address spaces.

### Import Resolution

The `ResolveImports` function handles the import address table reconstruction. For each imported DLL listed in the import directory, it obtains the module handle from already-loaded modules in the target process, then walks the import lookup table to resolve each symbol. This ensures dependencies are satisfied without invoking the Windows loader's import resolution logic.

### TLS Callback Execution

Before calling the main entry point, `ExecuteTLS` processes the thread-local storage directory. It casts the TLS directory virtual address to a callback array and invokes each function pointer with standard parameters. This maintains compatibility with compiler-generated TLS initialization code present in many C++ DLLs.

## Code Usage Examples

The following snippets demonstrate how the YimMenuV2 codebase utilizes these loading strategies:

```cpp
// Normal injection using standard Windows API
bool success = ModuleMgr::Instance().Inject("C:\\Mods\\Helper.dll");
if (success) {
    LOG("Module loaded via standard injection");
}

```

```cpp
// Manual mapping for stealth injection
bool success = ModuleMgr::Instance().ManualMap("C:\\Mods\\Stealth.dll");
if (success) {
    LOG("Module manually mapped without OS loader notification");
}

```

```cpp
// Unified interface with automatic strategy selection
bool ModuleMgr::Load(const std::string& path) {
    if (g_manualMapEnabled) {
        return ManualMap(path);
    }
    return Inject(path);
}

// Usage
bool loaded = ModuleMgr::Instance().Load("C:\\Mods\\Feature.dll");

```

The `g_manualMapEnabled` boolean flag, typically read from the menu configuration, determines which path `Load()` selects, allowing users to toggle between compatibility and stealth modes without changing calling code.

## Summary

- **ModuleMgr** serves as the centralized module loader in YimMenuV2, implemented in [`src/core/memory/ModuleMgr.cpp`](https://github.com/YimMenu/YimMenuV2/blob/main/src/core/memory/ModuleMgr.cpp) with interfaces in [`src/core/memory/ModuleMgr.hpp`](https://github.com/YimMenu/YimMenuV2/blob/main/src/core/memory/ModuleMgr.hpp).
- **Normal injection** relies on `LoadLibraryA`, utilizing the Windows loader for full PE processing but creating detectable OS artifacts in the module list.
- **Manual mapping** implements a complete custom PE loader in `ManualMap()`, bypassing OS notification through manual memory allocation, relocation, import resolution, and entry point execution.
- Both strategies store results in an internal map accessible via the unified `Load()` API, which selects the method based on the `g_manualMapEnabled` configuration flag.
- Manual mapping requires explicit handling of relocations via `ApplyRelocations`, imports via `ResolveImports`, TLS callbacks via `ExecuteTLS`, and entry point invocation to achieve functional parity with standard loading.

## Frequently Asked Questions

### What is the difference between ModuleMgr::Inject and ModuleMgr::ManualMap?

`Inject` calls `LoadLibraryA` to let the Windows OS loader handle all PE processing steps, resulting in visible module registration and simpler execution. `ManualMap` implements a custom loader that manually maps the DLL into memory, resolves dependencies, and calls the entry point without OS involvement, effectively hiding the module from standard enumeration APIs.

### Why would you choose manual mapping over normal injection?

Manual mapping avoids detection by anti-cheat and anti-virus systems that monitor `LoadLibrary` calls or scan the OS module list. When the target process blocks standard DLL injection or when stealth is required, manual mapping prevents the module from appearing in standard tool enumerations while still achieving full code execution.

### Where does ModuleMgr store information about loaded modules?

The class maintains an internal `std::unordered_map<std::string, ModuleInfo>` that maps module names to their base addresses and metadata. This registry is populated by both `Inject` and `ManualMap`, allowing the rest of YimMenuV2 to retrieve module handles via the manager's lookup methods regardless of which loading strategy was used.

### Does manual mapping execute TLS callbacks and DllMain?

Yes. The `ManualMap` implementation explicitly calls `ExecuteTLS` to invoke thread-local storage callbacks and `CallEntryPoint` to manually execute `DllMain` with `DLL_PROCESS_ATTACH`. This ensures the manually mapped module initializes correctly, matching the behavior of a normally loaded DLL.