A minimal DLL-injection launcher for World of Warcraft: Wrath of the Lich King (3.3.5a).
LichLoader does one thing: it starts WoW.exe in a suspended state, injects any DLLs listed in lichloader.txt, and resumes the game. It performs no hooking, memory scanning, or patching of its own — it exists purely as a load mechanism for separate community DLLs that do.
It is a stripped-down fork of the loader half of VanillaFixes, retargeted for 3.3.5a.
CreateProcess(WoW.exe, ..., CREATE_SUSPENDED, ...)— the game's main thread is created but not started.- For each path in
lichloader.txt:- Allocate memory in the target process and copy the wide DLL path into it (
VirtualAllocEx+WriteProcessMemory). CreateRemoteThreadwithlpStartAddress = &LoadLibraryWand the remote path as its argument. This works becausekernel32.dllis mapped at the same base in both processes.- Wait for the thread, read its exit code (=
LoadLibraryW'sHMODULE). Zero means the load failed and the launcher aborts with a message box.
- Allocate memory in the target process and copy the wide DLL path into it (
ResumeThread— by the time the game executes a single instruction, every injected DLL'sDllMain(DLL_PROCESS_ATTACH)has already run, which is the window for installing hooks before the engine touches anything.
- Windows.
- A 32-bit 3.3.5a
WoW.exe. - DLLs you want to inject must also be Win32 (x86) — a load failure with no obvious reason is almost always an accidentally x64-built consumer DLL.
The launcher itself must be built x86 for the same reason: CreateRemoteThread into a 32-bit target needs a 32-bit LoadLibraryW address, which a 64-bit process can't provide.
Drop LichLoader.exe next to WoW.exe, then create a lichloader.txt in the same directory:
WoW/
├── WoW.exe
├── LichLoader.exe
├── lichloader.txt
└── ...
lichloader.txt is plain UTF-8, one DLL path per line, # introduces a comment. Relative paths resolve against the WoW directory; absolute paths are honored as-is. Lines pointing at files that don't exist are silently skipped — that lets you keep entries for optional/disabled mods without editing the file every time.
Example lichloader.txt:
# Community engine extensions
awesome_wotlk.dll
# Optional — only loaded if the file actually exists
mods/experimental.dll
Any CLI arguments passed to LichLoader.exe are forwarded verbatim to WoW.exe, so shortcuts that pass realmlist overrides or -console keep working.
awesome_wotlk is a popular set of engine extensions and Lua API additions for 3.3.5a, distributed as a single DLL. LichLoader is fully compatible with it — drop awesome_wotlk.dll next to WoW.exe and reference it in lichloader.txt:
awesome_wotlk.dll
That's all. Because the DLL is loaded while the main thread is still suspended, awesome_wotlk's hooks are in place before the engine starts, exactly the same as if it were loaded by any other launcher.
The launcher is C99, built with CMake, and must be configured as Win32.
From the x86 Visual Studio Native Tools command prompt:
cmake -B build -A Win32
cmake --build build --config ReleaseOr with Ninja from the same x86 VS environment:
cmake -B build -G Ninja
ninja -C buildThe CMake configure step intentionally fails with FATAL_ERROR if CMAKE_SIZEOF_VOID_P != 4, so an accidental x64 configure will not produce a broken binary.
The build output is written to bin/LichLoader.exe.
See the upstream VanillaFixes repository for the original loader sources this project is derived from.