Unity (Mono)¶
Unity games built with Mono ship their code as .NET assemblies (Assembly-CSharp.dll and others) and compile it at run time. Grembyte talks to the Mono runtime in the game – libmono/mono-2.0-bdwgc.dll – through its exported API, natively on Linux and under Wine/Proton on Windows games.
Everything in the engine overview is available:
- The catalog lists the classes and methods of the game's assemblies, also offline for games read before.
- Assemblies … limits the catalog and the trace to chosen assemblies.
- The trace finds the methods a scenario calls, with preselection filters. Mono compiles every traced method first, which takes a moment for large selections; from a configurable number of methods Grembyte asks before it starts.
- The method monitor logs calls with arguments and results; method hooks change arguments, fields or results with templates of their own.
- The call graph shows which methods a method calls and which call it, from the IL code.
- The object inspector shows the fields of C# objects, and Call method runs a static method on the game's main thread.
- The object browser lists the live objects by class (see the guide).
An enum argument is chosen from the enum's names, e.g.
StrongholdAdventure.Type:Minor (1),Average (2)…; a number can be typed in, too. Nested classes read as C# writes them (StrongholdAdventure.Type), not as Mono does (StrongholdAdventure/Type).
Try it on HermesQuest
HermesQuest is a small Unity Mono role-playing game made for trying out Grembyte – the running example of the Examples section. Download it for Linux or Windows (also for Wine/Proton); both are zip archives. The examples walk through scans, pointer scans, value and method hooks and scripts on it.
Object browser¶
View → Object browser … lists the game's live objects by class and opens them in the object inspector. Unity's Mono has no heap walk, so Grembyte uses Unity's own liveness calculation: it finds the objects reachable from static fields and from Unity's objects, and the game stands still for a moment while it runs. The object browser guide explains it.
32-bit games¶
32-bit Unity games under Wine/Proton work with all Mono tools: catalog, trace, method monitor, object inspector, Call method and the object browser. Mono features always go through the Frida gadget; value hooks and the write watch use ptrace in 32-bit games (see backends).
Games that sleep out of focus¶
Some Unity games stop running frames when their window loses focus (Unity's Run In Background switched off), e.g. Might & Magic X – Legacy in window mode. Getters, Call method, Unity object names and the object browser run on the game's main thread, which then waits for the next window message instead of running game code. Grembyte wakes it with an empty window message and does its work there, so these tools also work while you are in Grembyte; the game itself stays paused.
Mono and value hooks¶
The pointer scan can start chains in the static fields of the classes in the marked assemblies, which Mono keeps on the heap.
Code compiled by Mono lies in no module and moves with every start. Grembyte therefore names a hit of the write watch in JIT code by its C# method, and a hook with a pointer chain remembers that method: after a restart Mono compiles it and the hook is installed in it right away.
Example: Pillars of Eternity¶
Pillars of Eternity (native Linux build) shows what the engine tools do in a real game.
A cheat function from the catalog¶
Its assembly keeps a static class Scripts with the developers' console commands. Typing scripts player into the catalog's filter finds them, among them GivePlayerMoney (int):
Screenshot to add: Catalog in Pillars of Eternity
The catalog filtered for scripts player: 8 of 30480 methods.
File: docs/assets/photos/mono-pillars-catalog-dark.webp and -light.webp; the versions there still show Hermes Hook and what was done back then.
A script with a number parameter amount calls it. Copy signature in the catalog gives the name to paste:
const value = Math.trunc(ctx.args.amount); // the method takes an int
ctx.call("Scripts:GivePlayerMoney (int)", value);
return "Gold increased by " + value;
ctx.call ignores the parameter list (int); it picks the method with as many parameters as arguments given. Math.trunc cuts off decimals that someone may type into the number field.
Screenshot to add: Script giving money in Pillars of Eternity
The script's result in the log, and the game's own message: the money went through the game's code.
File: docs/assets/photos/mono-pillars-script-money-dark.webp and -light.webp; the versions there still show Hermes Hook and what was done back then.
A method hook on the damage¶
Every hit in Pillars of Eternity goes through Health:DoDamage(DamageInfo damage, GameObject attacker). The method monitor shows a call with its arguments: the object hit (this), the DamageInfo and the attacking GameObject. Object details opens each in the object inspector, where the field m_damageBase of DamageInfo turns up.
A method hook before the call changes that field before the game uses it. The attacker is a GameObject; component("CharacterStats") gives its component with the character's stats, whose IsPartyMember tells the party from its enemies:
const stats = ctx.args.attacker && ctx.args.attacker.component("CharacterStats");
const base_old = ctx.args.damage.m_damageBase;
if (stats && !stats.IsPartyMember)
ctx.args.damage.m_damageBase *= 0.3; // enemies deal 30 %
if (stats && stats.IsPartyMember)
ctx.args.damage.m_damageBase *= 20; // the party deals 20 times the damage
ctx.log("Damagebase old/new:", base_old, "/", ctx.args.damage.m_damageBase);
ctx.args.attacker && … guards against hits without an attacker (traps, falling), where the argument is null.
Screenshot to add: Method monitor on Health:DoDamage
One call of Health:DoDamage: a companion hits a forest troll for 4.78.
File: docs/assets/photos/mono-pillars-dodamage-monitor-dark.webp and -light.webp; the versions there still show Hermes Hook and what was done back then.
Screenshot to add: Method hook on Health:DoDamage
The hook: template Method script, before the call.
File: docs/assets/photos/mono-pillars-dodamage-hook-definition-dark.webp and -light.webp; the versions there still show Hermes Hook and what was done back then.
Screenshot to add: The hook in the game
The hook log shows the base damage raised from 8.07 to 161.46; the game reports a critical hit for 469.
File: docs/assets/photos/mono-pillars-dodamage-hook-running-dark.webp and -light.webp; the versions there still show Hermes Hook and what was done back then.
The HermesQuest examples explain method hooks step by step.
Tested games¶
Pillars of Eternity, Pillars of Eternity II, Kathy Rain 2 (Proton), Shadowrun Returns (32-bit, Proton) and Might & Magic X – Legacy (Unity 4.2, 32-bit, Proton).
The screenshots of Pillars of Eternity on this page only illustrate how Grembyte works. All game names, trademarks and game content belong to their respective owners. Grembyte is not affiliated with, endorsed or sponsored by them.