This tutorial will cover how to implement function alterations to a managed target using Mono.Cecil. As far as I know Mono.Cecil only works for recreating a binary before it is loaded. I am not sure if it will allow runtime reconstruction, nor have I tried to get it to so. So if you know if it can please do let me know.

Legal Bullshit No One Reads
This is a tutorial for research purposes only. I am not responsible for what you do with this material. You agree that by reading this that you take full responsibility of your actions. I am not responsible for what you do with this information!
Tools Needed
- Visual Studio 2010 (Visual C# 2010 Express | Microsoft Visual Studio)
- XNA Game Studio 4 (Download: Microsoft XNA Game Studio 4.0 - Microsoft Download Center - Download Details)
- Mono.Ceci
- Download: Mono.Cecil.dll.7z
- Credits / Info: Cecil - Mono
- .NET Target To Hook (I will be using Terraria as the example.)
- IL Disassembler if you wish to view the IL code with ease. (VS2010 comes with ildasm which you can run from the VS command line.)
- ILSpy: ILSpy - SharpDevelop Wiki
To start, we'll be working on attaching onto XNA's main functions for a normal game. Since Terraria is written in C# using XNA we can see that it inherits 'Game' in its main class.
If we use a .NET object browser we can see what Game contains. For the sake of this tutorial you can find the members here:
Game Members
So we are mainly interested in:
- Initialize
- LoadContent
- Update
- Draw
Preparing Our Project
We'll start by making a new XNA game, it is easier to do this since it will add all the XNA references for us even though we don't need any of the actual game code it makes. So start by opening VS2010 and going to:
File -> New -> Project -> C# -> XNA Game Studio 4.0 -> Windows Game (4.0)
Next, we'll remove the Content project because its useless to us (unless you plan to add custom content). So right-click the Content project and remove it. You will also need to remove the content reference in the main project's Content References if it wasn't removed automatically.
Next, we'll delete Game1.cs since we don't need it.
Create a new class, call it whatever you want. In this case I'm calling it Hooks.cs
Preparing Our Hooks Class
To start we will need four main class variables, one for the loaded assembly definition, one for the main module definition, one for the complete assembly, and one for the assemblies main class type. So we will add:
- private AssemblyDefinition m_vAsmDefinition = null;
- private ModuleDefinition m_vModDefinition = null;
- private Assembly m_vAssembly = null;
- private Type m_vMainType = null;
- public bool Initialize()
- {
- try
- {
- this.m_vAsmDefinition = AssemblyDefinition.ReadAssembly("Terraria.exe");
- this.m_vModDefinition = this.m_vAsmDefinition.MainModule;
- return true;
- }
- catch { return false; }
- }
- public bool Finialize()
- {
- try
- {
- // Validate we've loaded the main executable first..
- if (this.m_vAsmDefinition == null)
- return false;
- {
- // Write the edited data to the memory stream..
- this.m_vAsmDefinition.Write(mStream);
- // Load the new assembly from the memory stream buffer..
- this.m_vAssembly = Assembly.Load(mStream.GetBuffer());
- return true;
- }
- }
- catch { return false; }
- }
- public bool Run()
- {
- try
- {
- if (this.m_vAssembly == null)
- return false;
- // Get the main class type..
- this.m_vMainType = this.m_vAssembly.GetType("Terraria.Main");
- // Create the constructor call..
- // Obtain the main run method and invoke it..
- var method = this.m_vMainType.GetMethod("Run", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
- method.Invoke(constructor, null);
- return true;
- }
- catch { return false; }
- }
Inside of Program.cs, inside of Main() replace everything with:
- static void Main(string[] args)
- {
- hooks.Initialize();
- hooks.Finialize();
- hooks.Run();
- }
Hooking Onto Our Functions..
Next we want to hook onto the functions. Simple enough, Pre* hooks just get called at the start of the function, while Post* at the end.
(Please see the note below at the end of this tutorial, you may need to do more work in some cases for some hooks to work properly!)
We'll start with Initialize. Inside our Hooks class lets add two functions PreInitialize and PostInitialize following the same method definition from MSDN for Initialize. For now we'll out some console outputs to determine if they get called. (Be sure that these are marked public and static or you will get runtime errors!)
- public static void PreInitialize()
- {
- System.Diagnostics.Debug.WriteLine("Custom PreInitialize was called!");
- }
- public static void PostInitialize()
- {
- System.Diagnostics.Debug.WriteLine("Custom PostInitialize was called!");
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- //
- // Object Reflection From New Terraria Objects
- //
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- /// <summary>
- /// Gets a method definition from the new Terraria executable.
- /// </summary>
- /// <param name="t"></param>
- /// <param name="methodName"></param>
- /// <returns></returns>
- public MethodDefinition GetMethodDefinition(TypeDefinition t, String methodName)
- {
- return (from MethodDefinition m in t.Methods
- where m.Name == methodName
- select m).FirstOrDefault();
- }
- /// <summary>
- /// Gets a field definition from the new Terraria executable.
- /// </summary>
- /// <param name="t"></param>
- /// <param name="fieldName"></param>
- /// <returns></returns>
- public FieldDefinition GetFieldDefinition(TypeDefinition t, String fieldName)
- {
- return (from FieldDefinition f in t.Fields
- where f.Name == fieldName
- select f).FirstOrDefault();
- }
- /// <summary>
- /// Gets a property definition from the new Terraria executable.
- /// </summary>
- /// <param name="t"></param>
- /// <param name="propName"></param>
- /// <returns></returns>
- public PropertyDefinition GetPropertyDefinition(TypeDefinition t, String propName)
- {
- return (from PropertyDefinition p in t.Properties
- where p.Name == propName
- select p).FirstOrDefault();
- }
- /// <summary>
- /// Gets a type definition from the new Terraria executable.
- /// </summary>
- /// <param name="typeName"></param>
- /// <returns></returns>
- public TypeDefinition GetTypeDefinition(String typeName)
- {
- return (from TypeDefinition t in this.m_vModDefinition.Types
- where t.Name == typeName
- select t).FirstOrDefault();
- }
- /// <summary>
- /// Gets a type from within the new Terraria executable.
- /// </summary>
- /// <param name="typeName"></param>
- /// <returns></returns>
- public Type GetType(String typeName)
- {
- return (from Type t in this.m_vAssembly.GetTypes()
- where t.Name == typeName
- select t).FirstOrDefault();
- }
- /// <summary>
- /// Gets a method from within Terraria.Main.
- /// </summary>
- /// <param name="methodName"></param>
- /// <returns></returns>
- public MethodInfo GetMethod(String methodName)
- {
- return (from MethodInfo m in this.m_vMainType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static)
- where m.Name == methodName
- select m).FirstOrDefault();
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- //
- // Object Reflection From New Terraria.Main Object
- //
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- /// <summary>
- /// Gets the value of a field within Terraria.Main.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="fieldName"></param>
- /// <returns></returns>
- public T GetMainField<T>(String fieldName)
- {
- var field = (from FieldInfo f in this.m_vMainType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static)
- where f.Name == fieldName
- select f).FirstOrDefault();
- if (field == null)
- return default(T);
- return (T)field.GetValue(this.m_vMainType);
- }
- /// <summary>
- /// Sets the value of a field within Terraria.Main.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="fieldName"></param>
- /// <param name="objValue"></param>
- public void SetMainField<T>(String fieldName, T objValue)
- {
- var field = (from FieldInfo f in this.m_vMainType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static)
- where f.Name == fieldName
- select f).FirstOrDefault();
- if (field == null)
- return;
- field.SetValue(this.m_vMainType, objValue);
- }
- MethodDefinition initMethod = GetMethodDefinition(GetTypeDefinition("Main"), "Initialize");
- MethodDefinition initMethod = GetMethodDefinition(GetTypeDefinition("Main"), "Initialize");
- var initProc = initMethod.Body.GetILProcessor();
- initProc.InsertBefore(initMethod.Body.Instructions[0], initProc.Create(OpCodes.Call,
- this.m_vModDefinition.Import(typeof(Hooks).GetMethod("PreInitialize", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static))
- ));
Custom PreInitialize was called!
Great now we want to add our PostInitialize call to the end of the function. If we look at the all in a IL decoder (such as ILSpy) we can see the Initialize call ends with:
- IL_1e8f: ret
- initProc.InsertBefore(initMethod.Body.Instructions[initMethod.Body.Instructions.Count - 2], initProc.Create(OpCodes.Call,
- this.m_vModDefinition.Import(typeof(Hooks).GetMethod("PostInitialize", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static))
- ));
- public void ApplyHooks()
- {
- MethodDefinition initMethod = GetMethodDefinition(GetTypeDefinition("Main"), "Initialize");
- var initProc = initMethod.Body.GetILProcessor();
- initProc.InsertBefore(initMethod.Body.Instructions[0], initProc.Create(OpCodes.Call,
- this.m_vModDefinition.Import(typeof(Hooks).GetMethod("PreInitialize", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static))
- ));
- initProc.InsertBefore(initMethod.Body.Instructions[initMethod.Body.Instructions.Count - 2], initProc.Create(OpCodes.Call,
- this.m_vModDefinition.Import(typeof(Hooks).GetMethod("PostInitialize", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static))
- ));
- }
- public static void PreLoadContent()
- {
- System.Diagnostics.Debug.WriteLine("Custom PreLoadContent was called!");
- }
- public static void PostLoadContent()
- {
- System.Diagnostics.Debug.WriteLine("Custom PostLoadContent was called!");
- }
- MethodDefinition contMethod = GetMethodDefinition(GetTypeDefinition("Main"), "LoadContent");
- var contProc = contMethod.Body.GetILProcessor();
- contProc.InsertBefore(contMethod.Body.Instructions[0], contProc.Create(OpCodes.Call,
- this.m_vModDefinition.Import(typeof(Hooks).GetMethod("PreLoadContent", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static))
- ));
- contProc.InsertBefore(contMethod.Body.Instructions[contMethod.Body.Instructions.Count - 2], contProc.Create(OpCodes.Call,
- this.m_vModDefinition.Import(typeof(Hooks).GetMethod("PostLoadContent", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static))
- ));
Next, we want to hook onto Update and Draw. The difference here is that we now have functions that take arguments. In this case a 'GameTime' object which holds the time of the current XNA game. Looking at some of the function in a diassembler we'll see that using the GameTime object will look like this:
- IL_1922: ldarg.0
- IL_1923: ldarg.1
- IL_1924: call instance void [Microsoft.Xna.Framework.Game]Microsoft.Xna.Framework.Game::Update(class [Microsoft.Xna.Framework.Game]Microsoft.Xna.Framework.GameTime)
- IL_1929: ret
- public static void PreUpdate(GameTime gameTime)
- {
- }
- public static void PostUpdate(GameTime gameTime)
- {
- }
- MethodDefinition updateMethod = GetMethodDefinition(GetTypeDefinition("Main"), "Update");
- var updateProc = updateMethod.Body.GetILProcessor();
- var updateInst = updateMethod.Body.Instructions[0];
- updateProc.InsertBefore(updateInst, updateProc.Create(OpCodes.Ldarg_1)); // push gameTime
- updateProc.InsertBefore(updateInst, updateProc.Create(OpCodes.Call,
- this.m_vModDefinition.Import(typeof(Hooks).GetMethod("PreUpdate", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static))
- ));
Next we want to inject our PostUpdate call. The above IL code I posted about the Update function is the end of the function, so we know it ends with a Ret. We want to push our code, again, before the return.
- updateProc.InsertBefore(updateMethod.Body.Instructions[updateMethod.Body.Instructions.Count - 1], updateProc.Create(OpCodes.Ldarg_1)); // push gameTime
- updateProc.InsertBefore(updateMethod.Body.Instructions[updateMethod.Body.Instructions.Count - 1], updateProc.Create(OpCodes.Call,
- this.m_vModDefinition.Import(typeof(Hooks).GetMethod("PostUpdate", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static))
- ));
- public static void PreDraw(GameTime gameTime)
- {
- }
- public static void PostDraw(GameTime gameTime)
- {
- }
- MethodDefinition drawMethod = GetMethodDefinition(GetTypeDefinition("Main"), "Draw");
- var drawProc = drawMethod.Body.GetILProcessor();
- var drawInst = drawMethod.Body.Instructions[0];
- drawProc.InsertBefore(drawInst, drawProc.Create(OpCodes.Ldarg_1)); // push gameTime
- drawProc.InsertBefore(drawInst, drawProc.Create(OpCodes.Call,
- this.m_vModDefinition.Import(typeof(Hooks).GetMethod("PreDraw", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static))
- ));
- drawProc.InsertBefore(drawMethod.Body.Instructions[drawMethod.Body.Instructions.Count - 1], drawProc.Create(OpCodes.Ldarg_1));
- drawProc.InsertBefore(drawMethod.Body.Instructions[drawMethod.Body.Instructions.Count - 1], drawProc.Create(OpCodes.Call,
- this.m_vModDefinition.Import(typeof(Hooks).GetMethod("PostDraw", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static))
- ));

Hooking Other Methods
So now that we have hooked all the XNA methods, lets hook something else. In this case I will be using the collectors edition method CheckBunny. This method determines if you have the Collectors Edition of the game by looking in your Registry for a specific key. The IL of this code looks like:
- .try
- {
- IL_0000: ldsfld class [mscorlib]Microsoft.Win32.RegistryKey [mscorlib]Microsoft.Win32.Registry::CurrentUser
- IL_0005: stloc.0
- IL_0006: ldloc.0
- IL_0007: ldstr "Software\\Terraria"
- IL_000c: callvirt instance class [mscorlib]Microsoft.Win32.RegistryKey [mscorlib]Microsoft.Win32.RegistryKey::CreateSubKey(string)
- IL_0011: stloc.0
- IL_0012: ldloc.0
- IL_0013: brfalse.s IL_0044
- IL_0015: ldloc.0
- IL_0016: ldstr "Bunny"
- IL_001b: callvirt instance object [mscorlib]Microsoft.Win32.RegistryKey::GetValue(string)
- IL_0020: brfalse.s IL_0044
- IL_0022: ldloc.0
- IL_0023: ldstr "Bunny"
- IL_0028: callvirt instance object [mscorlib]Microsoft.Win32.RegistryKey::GetValue(string)
- IL_002d: callvirt instance string [mscorlib]System.Object::ToString()
- IL_0032: ldstr "1"
- IL_0037: call bool [mscorlib]System.String::op_Equality(string, string)
- IL_003c: brfalse.s IL_0044
- IL_003e: ldc.i4.1
- IL_003f: stsfld bool Terraria.Main::cEd
- IL_0044: leave.s IL_004f
- } // end .try
- catch [mscorlib]System.Object
- {
- IL_0046: pop
- IL_0047: ldc.i4.0
- IL_0048: stsfld bool Terraria.Main::cEd
- IL_004d: leave.s IL_004f
- } // end handler
- IL_004f: ret
- IL_003e: ldc.i4.1
- IL_003f: stsfld bool Terraria.Main::cEd
- MethodDefinition bunnyMethod = GetMethodDefinition(GetTypeDefinition("Main"), "CheckBunny");
- FieldDefinition bunnyField = GetFieldDefinition(GetTypeDefinition("Main"), "cEd");
- // Clear the old function instructions..
- bunnyMethod.Body.Instructions.Clear();
- // Remove the exception handler..
- bunnyMethod.Body.ExceptionHandlers.Clear();
- // Rewrite the function..
- var bunnyProc = bunnyMethod.Body.GetILProcessor();
- bunnyProc.Append(bunnyProc.Create(OpCodes.Nop)); // nop for alignment
- bunnyProc.Append(bunnyProc.Create(OpCodes.Ldc_I4_1)); // push true (1) onto the stack
- bunnyProc.Append(bunnyProc.Create(OpCodes.Stsfld, bunnyField)); // set field to the value on stack
- bunnyProc.Append(bunnyProc.Create(OpCodes.Ret)); // return
With Terraria as an example you may notice that your XNA hooks may not be called in the right order. This is due to how Terraria is written. For example, Draw has multiple returns within it to handle drawing certain parts of the game. For instance, it will attempt to Draw the menu specifically with:
- IL_52dd: ldarg.0
- IL_52de: call instance void Terraria.Main::DrawMenu()
- IL_52e3: ret
- Hook DrawMenu and call PostDraw at the end of it to ensure your PostDraw is called. (I recommend this method.)
- Inject your PostDraw call after every Ret inside the function to ensure it gets called.
- Disassemble the code and look to see where you should place each Post call to ensure that it will always be called. (This could become a tiresome process if its a large method.)