DllStalker is a C++20 internal DLL toolkit for Unity games (IL2CPP and Mono). It combines runtime API resolution, preset-based hook installation and optional debug tooling (GUI + console) for reverse-engineering workflows.
- Resolve Unity runtime APIs dynamically (
il2cpp_*/mono_*) - Reduce hardcoded offsets by resolving methods/fields at runtime
- Install hooks through a self-registering preset system
- Provide an optional debug workflow for live inspection, scripting, and testing
| Audience | Start here |
|---|---|
| Scripting / modding | docs/examples/Scripting-Guide.md — Lua API; copy tutorial packages from docs/examples/packages/ |
| Manifest fields | docs/examples/Manifest-Schema.md |
Tutorial packages in this repo are reference copies only. Copy a package folder into stalker_runtime/mods next to the deployed version.dll (same directory as the game .exe). DllStalker does not load scripts from docs/examples/ in the repository.
For basic Safe mods, manifest.json is optional:
- Loose script — any
*.luafile placed directly undermods/(e.g.mods/heal.lua) is discovered by filename; no manifest needed. - Folder package — a subfolder under
mods/withoutmanifest.jsonmust usemain.luaas the entry file. For another name (e.g.watcher.luainside a folder), addmanifest.jsonwith"entry_file".
Add manifest.json when you need a display name, Curated profile, a non-main.lua entry inside a folder, or non-default timing budgets — see Manifest-Schema.md.
-
UnityResolver (
include/unity_resolver.h,src/unity_resolver.cpp→include/engine/,src/engine/)- Detects backend (
GameAssembly.dll/mono-2.0-bdwgc.dll) - Resolves runtime exports dynamically
- Exposes image/class/method/field lookup helpers
- Detects backend (
-
UnityDumper (
include/unity_dumper.h,src/unity_dumper.cpp→include/dumper/,src/dumper/)- Catalogs, field/method invoke, collection views, SDK export (
ENABLE_DUMPERonly)
- Catalogs, field/method invoke, collection views, SDK export (
-
Hook Services (
include/services/,src/services/)- MinHook installation helpers (
Hooks::) - Preset selection and installation entry point
- Main-thread dispatch bridge for safe managed invokes (debug tooling path)
- MinHook installation helpers (
-
Types (
include/types/,src/types/)- POD structs,
memory_guard, value decode/write, type classification
- POD structs,
-
Scripting (
include/scripting/,src/scripting/)- LuaJIT runtime bridge for Safe and Curated script packages under
stalker_runtime/mods - Exposes
ds.*, host-owned ticks/reload, opaque script handles and a flat CuratedDS_*ABI
- LuaJIT runtime bridge for Safe and Curated script packages under
-
Presets (
include/presets/,src/presets/)- Each preset lives in its own
.cppand self-registers - Installer chooses one preset name and applies all hooks from that preset
- Each preset lives in its own
-
GUI (
include/gui/,src/gui/)- Win32 + D3D11 + Dear ImGui control panel
- Runtime metadata browsing, field analysis, transform edit, live watch/plot, call logging, SDK export and scripting dock
| Requirement | Notes |
|---|---|
| OS | Windows x64 (build and runtime target) |
| Visual Studio | 2022 or later with the v145 platform toolset and Desktop development with C++ workload |
| vcpkg | Required for third-party dependencies; the project uses manifest mode (VcpkgEnableManifest in DllStalker.vcxproj) |
| Unity target | IL2CPP (GameAssembly.dll) or Mono (mono-2.0-bdwgc.dll) game on Windows |
Install and integrate vcpkg with Visual Studio (or set VCPKG_ROOT and use the MSBuild integration). On first build, vcpkg reads vcpkg.json from the repository and installs the declared packages (MinHook, Dear ImGui, LuaJIT, and their transitive deps) into a local vcpkg_installed tree. x64 configurations link those libraries statically (VcpkgUseStatic).
- Clone the repository and open
DllStalker.slnxat the repository root in Visual Studio. - Set the active configuration to
DebugRelease | x64for daily GUI work, orRelease | x64for a hook-only payload without the dumper/GUI. - Build the project (Build → Build DllStalker).
- Collect the output DLL from
DllStalker/x64/<Configuration>/version.dll(for exampleDllStalker/x64/DebugRelease/version.dllwhen the project lives in aDllStalker/subfolder).
If package restore fails, confirm vcpkg is on PATH or integrated in VS, then rebuild so manifest dependencies can download and compile.
ENABLE_DUMPER in include/build_config.h (included by pch.h) is defined when _DEBUG or DEBUGRELEASE. It gates the GUI, dumper and related types/ code.
| Config | Dumper GUI |
|---|---|
| DebugRelease | x64 | Yes — optimized; preferred daily driver |
| Debug | x64 | Yes — full GUI + native stepping/breakpoints |
| Release | x64 | No — hooks and engine only |
Use Release | x64 for a lean DLL focused on preset hooks.
- Optimized code generation
- Smaller/faster runtime footprint
- No GUI or dumper paths
- Best choice for stable, repeatable hook deployment
Use Debug | x64 when you need the control panel plus native debugging.
- Full GUI and runtime invoke tooling
- Console output for live feedback
- Easier iteration on hooks and inspector behavior
Use DebugRelease | x64 when smoke-testing the control panel.
- Optimized like Release, with all
ENABLE_DUMPERpaths enabled - Typical build for inspector, dock tabs, transform tab, call logger and scripting validation
Build output: version.dll.
- Copy
version.dllto the game root folder (same folder as the game.exe). - Keep the filename exactly
version.dll. - Run the game and verify startup logs.
Windows checks the game executable folder first for imported DLLs.
Placing version.dll next to the game .exe makes the game load DllStalker at startup.
DllStalker forwards the standard Windows version.dll exports to C:\Windows\System32\version.dll, so callers of the real version API keep working.
After a successful deploy of a dumper-enabled build:
- Debug console — a console window opens with bootstrap logs (
Unity.Init, hook status, warnings). - Control panel — a separate desktop window titled DllStalker - Control Panel opens automatically. It is not an in-game overlay; alt-tab to it like any other Win32 app.
- In the control panel, click Init Dumper Engine before browsing assemblies (see GUI Capabilities).
Release | x64 builds do not spawn the control panel or dumper UI; only the console-less hook/engine path runs.
When the bootstrap thread initializes successfully, DllStalker allocates a console window and writes runtime logs to stdout.
Typical console usage includes:
- Startup status (
Unity.Init, preset install progress) - Hook creation/enable results and error codes
- Runtime warnings (missing exports, unresolved targets)
- Live operational logs from active hooks
Why this matters:
- Fast validation that hooks were mounted
- Immediate signal when a method/assembly lookup fails
- Easier triage of Debug vs Release behavior differences
The debug GUI is for runtime exploration, controlled edits and safe invocation.
First step: click Init Dumper Engine in the control panel before browsing assemblies.
Layout: left metadata browser (~30%); right workspace (Methods | Fields | Transform); bottom Utilities Dock (Bookmarks → History → Watcher → Logger → Exporter → Scripting). Dock tabs restore navigation or host live tooling; they do not replace the main inspector models.
| Tab | Purpose |
|---|---|
| Bookmarks | Saved inspector locations; restore with shared navigation validation |
| History | Navigation timeline and audit rows (read-only; distinct from call log) |
| Watcher | Live field watchlist (cap 32); configurable Poll interval; Default chart mode; inner Watchlist / Charts for numeric plots |
| Logger | Inner Hooks / Log; native call hooks and rolling args-only call log |
| Exporter | Export field offsets (optional methods/enums) to C++ .h or C# .cs beside the injected DLL |
| Scripting | Discover Lua packages, Start/Stop/Reload scripts, show console output and audit counters |
From Watcher or History, Jump restores the workspace to the linked location.
- Image Selection
- Shows loaded assemblies/images with class counts
- Filter is case-sensitive (
strstrbehavior)
- Class Browser
- Fuzzy filter over class name + namespace
- Filter is case-insensitive
- Sorting behavior
- Data is rendered from runtime snapshots/caches
- For Mono image enumeration, duplicates are normalized internally (sorted + deduplicated before display)
In the Fields tab, Find Instances supports two discovery modes:
- Static discovery — scans static-instance candidates
- Live API — uses Unity live object lookup APIs
Switch source mode, run discovery and pick the active instance from the candidates combo.
On the Fields tab (analysis toolbar):
- Snapshot baseline → Show changes tints rows when values drift (green/red/yellow).
- Compare two instances — class-level A vs B from root instance finds; Current path mode for drilled paths and collection element index.
[W]— add/remove a field on the Watcher dock tab (live reads; plottable numerics can open Charts).[T]— focus Transform on a Transform/GameObject pointer field.- Enum fields — decoded literals; editable via combo when supported.
Watcher dock (Utilities → Watcher):
- Poll — how often watched values are read:
100ms,250ms,500ms,1s,2s, or5s(default100ms). This is separate from Fields auto-refresh, which reloads inspector metadata. - Default chart —
Every sample(ring buffer of recent points) orOn change(plot only when the decoded value changes; better for slow-updating fields). New watches inherit this default. - Watchlist — live value column, Jump (restore navigation), Remove, Plot (focus Charts for plottable int/float fields).
- Charts — select a series; Mode per series:
Default(follow toolbar default),Every sample, orOn change. Charts use evenly spaced points (no wall-clock axis).
Run flow (invoke from GUI):
The Run button is available when prerequisites are met:
- main-thread dispatcher hook is active,
- main thread is captured,
- method signature is supported,
- an instance is selected for instance methods.
For zero-argument methods, execution is immediate. For methods with arguments, the invoke modal is used.
Log (call logger):
- Toggle Log on supported methods (
*when active) to install a native MinHook detour (cap 16 hooks). - Lines appear in Utilities → Logger → Log as
[HH:MM:SS] Class::Method(arg: val, …)(register args only; no return suffix). - Manage hooks on Logger → Hooks; Clear log on the Log subtab only.
- Eligibility matches the invoke path where possible: known signature, primitive/string/pointer params, ≤3 instance args or ≤4 static register args. Mono methods with unknown params stay disabled.
Inspect and edit Transform/GameObject state via Unity API invokers (not raw offset reads):
- Sources — Self, implicit transform/gameObject and field pointers
- Live — periodic refetch; Edit lock — pause live refresh while editing
- Fetch / apply — main-thread getters/setters for local/world position, rotation, scale, activeSelf
- Requires the same main-thread capture as Run on Methods
Runtime-authored Lua scripts live under stalker_runtime/mods next to the injected version.dll.
- Safe (default):
ds.*lookup, instance field get/set, explicit-signature invoke, ticks, sleep, logging, and cooperative stop/reload. - Curated: adds
ds_abiandds.types.Instancehelpers backed by DllStalker'sDS_*ABI on the proxy DLL. Raw userffistays blocked. - Script-visible identity is an opaque handle, not a native pointer. Address helpers are for logs only; Curated raw addresses are transient.
- Getting started: copy a folder from
docs/examples/packages/, edit the constants at the top ofmain.lua, then Refresh and Start in the dock. Full API:docs/examples/Scripting-Guide.md.
Simple field editing is supported for primitive categories (numeric + boolean) and enums (combo).
- Typical workflow: edit value in-place, press OK, auto-refresh verifies the write.
- String/reference/container direct overwrite is intentionally restricted in this path.
Navigate object graphs directly from field values:
- Pointer fields — drill into referenced object
- Array/List fields — open synthesized collection view (vector-like navigation)
- Breadcrumbs — jump back to any previous level
- Click Init Dumper Engine.
- Open Image Selection and choose the target image.
- Use Class Browser filter to find your class.
- In Fields, click Find Instances and choose source mode (Static discovery / Live API).
- Pick an instance from the candidates combo.
- Go to Methods and click Run:
- immediate call for 0-arg methods,
- arg modal for methods with parameters.
- Select image, class and active instance.
- In Fields, edit a primitive or enum value.
- Click OK.
- Use Refresh Fields (or auto-refresh) to confirm the new value.
- In Fields, click a pointer value to open nested object view.
- Click array/list value to open collection view.
- Continue drilling as needed.
- Use Breadcrumbs to jump back to any previous level.
- Select image, class and instance; open Fields.
- Toggle
[W]on a numeric field. - Open Utilities → Watcher for live values.
- Optional: set Poll (e.g.
2sfor slow fields) and Default chart →On changebefore or after adding watches. - Use Plot on a plottable row to open Charts, or pick a series there; override Mode per series if needed (e.g. one field every sample, another on change only).
- Open Methods for the target class.
- Click Log on a supported method (disabled rows show a tooltip why).
- Trigger the method in-game; open Utilities → Logger → Log to tail lines.
- Use Hooks to Remove hooks or toggle Log off on Methods; Clear log clears lines only.
- Select image, class and instance.
- Open Transform or press
[T]on a Transform/GameObject field in Fields. - Select a source, wait for fetch (main thread must be captured).
- Edit values and apply; use Live for periodic refresh.
- Browse to the desired scope (active class, entire image or bookmarked classes).
- Open Utilities → Exporter.
- Choose format (C++ or C#), options and filename; export.
- Output is written next to the injected
version.dll.
- Copy a tutorial folder from
docs/examples/packages/intostalker_runtime/modsbesideversion.dll(or author your own folder withmain.lua;manifest.jsonis optional for basic Safe scripts). - Edit
IMAGE,CLASS, and other constants at the top ofmain.luafor the target game. - Open Utilities → Scripting and click Refresh.
- Select the package, then click Start.
- Use Stop or Reload while iterating in an external editor; tick scripts keep running until stopped.
Presets self-register at static init: add a new .cpp under src/presets/ and list it in DllStalker.vcxproj.
Example path:
src/presets/example/example.cpp
Minimal shape:
#include "pch.h"
#include "presets/hook_preset.h"
#include "services/hook_installer.h"
#include "unity_resolver.h"
namespace
{
using _TargetMethod = void(__fastcall*)(void* __this);
_TargetMethod oTargetMethod = nullptr;
void __fastcall hkTargetMethod(void* __this) {
// custom behavior
oTargetMethod(__this);
}
void Install(void* assemblyImage) {
HOOK_FUNCTION(
Engine::Unity.GetMethodAddress(assemblyImage, "SomeClass", "TargetMethod", 0, "SomeNamespace"),
hkTargetMethod,
oTargetMethod
);
}
const Presets::HookPreset kPreset = { "Example", "Assembly-CSharp", &Install };
const bool kRegistered = (Presets::Register(kPreset), true); // <---- Function call
}In src/services/hook_installer.cpp, set:
kSelectedPresetName = "Example";— install that preset's hookskSelectedPresetName = "-";— no preset hooks (default; avoids colliding with GUI call-logger hooks)
- Build DebugRelease | x64 or Debug | x64 for console logs + GUI
- Build Release | x64 for hook-only payload
- Deploy
version.dlland confirm preset install messages in the console
DllMainstarts bootstrap thread (and GUI thread when dumper is enabled).Engine::Unity.Init()resolves runtime exports.- Console is allocated for runtime logs.
MainThreadDispatcher::InstallRuntimeInvokeHook()(dumper builds) — non-fatal if it fails.Hooks::StartHooking()initializes MinHook and installs the selected preset.- GUI thread runs the control panel; user clicks Init Dumper Engine to create
UnityDumperand load images.
DllStalker/
├── docs/
│ └── examples/
│ └── packages/
├── include/
│ ├── engine/
│ ├── dumper/
│ ├── gui/
│ │ ├── app/
│ │ ├── infra/
│ │ ├── state/
│ │ │ ├── core/
│ │ │ ├── navigation/
│ │ │ ├── history/
│ │ │ ├── fields/
│ │ │ ├── runtime/
│ │ │ └── transform/
│ │ └── views/
│ │ ├── fields/
│ │ ├── transform/
│ │ └── dock/
│ ├── presets/
│ ├── scripting/
│ │ ├── abi/
│ │ ├── bridge/
│ │ ├── core/
│ │ ├── handles/
│ │ └── runtime/
│ ├── services/
│ └── types/
└── src/
├── engine/
├── dumper/
├── gui/
│ ├── app/
│ ├── infra/
│ ├── state/
│ │ ├── core/
│ │ ├── navigation/
│ │ ├── history/
│ │ ├── fields/
│ │ ├── runtime/
│ │ └── transform/
│ └── views/
│ ├── fields/
│ ├── transform/
│ └── dock/
├── presets/
├── scripting/
│ ├── abi/
│ ├── bridge/
│ ├── core/
│ ├── handles/
│ └── runtime/
├── services/
└── types/
- Check
kSelectedPresetNameinsrc/services/hook_installer.cpp(must match a registered name, not"-") - Ensure the preset
.cppis inDllStalker.vcxprojand callsPresets::Register(...)
- Verify the preset assembly name (
Assembly-CSharp, etc.) - Confirm target runtime is fully initialized before install
- Check console output for MinHook status
- Verify method signature/namespace/arg count used in
GetMethodAddress(...) - Re-check Debug vs Release behavior for target function layout differences
Common causes shown by tooltip/status:
- runtime invoke hook unavailable
- main thread not captured yet
- no active instance for instance method
- unsupported parameter types in current invoke path
- missing method handle/signature
This is expected behavior:
- class filter is case-insensitive
- image filter is case-sensitive
- Log disabled: no native address, unknown Mono params, unsupported arg types or too many register arguments.
- No lines: hook not armed, method not called yet or hook removed; check Logger → Hooks.
- Cap / install errors: max 16 hooks; duplicate native target or MinHook install failure shows a status toast.
- Deploy under
stalker_runtime/modsnext toversion.dll, not from the repodocs/examples/path. - Directory packages need a root-level entry file. Without
manifest.json, the entry must bemain.lua. Withmanifest.json, set"entry_file"for another root-level name. - Loose
.luafiles directly undermodsrun as Safe scripts with default timing budgets. entry_filemust be a file name in the package root, not a nested or absolute path.IMAGEstrings must match runtime assembly names (oftenAssembly-CSharp.dll, not always the short name).- Curated helpers require
"profile": "Curated"(or"Advanced") inmanifest.json; unknown profiles run as Safe with a warning. - See
docs/examples/Scripting-Guide.mdanddocs/examples/Manifest-Schema.md.
This project is strictly for educational, research and local development debugging purposes.
This tool is a standard user-mode utility. It does not contain any kernel-mode bypasses, driver exploits, signature cloaking or thread-hiding mechanisms.
- Running or injecting this tool into games protected by modern kernel-level anti-cheats (such as Easy Anti-Cheat, BattlEye, Ricochet, Vanguard or similar) WILL result in an immediate, automated and permanent ban.
- If you are a game developer testing your own project with this tool, you must disable your game's anti-cheat modules in your compilation configuration before attaching it.
Because this tool performs direct memory queries and interacts natively with runtime objects (IL2CPP/Mono) via pointers:
- Invoking methods or modifying unaligned fields can cause immediate process instability, memory access violations or fatal game crashes.
- Use this tool entirely at your own risk. The author(s) are not responsible for lost game data, corrupted saves or account restrictions.
This tool does not modify game binaries on disk, distribute protected assets or facilitate online matchmaking manipulation. It is designed to help developers and security researchers analyze object structures and runtime behavior in controlled, offline or authorized testing environments.
