Dynamically replace the contents of a C# method?

C#MethodsAssembliesCil

C# Problem Overview


What I want to do is change how a C# method executes when it is called, so that I can write something like this:

[Distributed]
public DTask<bool> Solve(int n, DEvent<bool> callback)
{
    for (int m = 2; m < n - 1; m += 1)
        if (m % n == 0)
            return false;
    return true;
}

At run-time, I need to be able to analyse methods that have the Distributed attribute (which I already can do) and then insert code before the body of the function executes and after the function returns. More importantly, I need to be able to do it without modifying code where Solve is called or at the start of the function (at compile time; doing so at run-time is the objective).

At the moment I have attempted this bit of code (assume t is the type that Solve is stored in, and m is a MethodInfo of Solve):

private void WrapMethod(Type t, MethodInfo m)
{
    // Generate ILasm for delegate.
    byte[] il = typeof(Dpm).GetMethod("ReplacedSolve").GetMethodBody().GetILAsByteArray();

    // Pin the bytes in the garbage collection.
    GCHandle h = GCHandle.Alloc((object)il, GCHandleType.Pinned);
    IntPtr addr = h.AddrOfPinnedObject();
    int size = il.Length;

    // Swap the method.
    MethodRental.SwapMethodBody(t, m.MetadataToken, addr, size, MethodRental.JitImmediate);
}

public DTask<bool> ReplacedSolve(int n, DEvent<bool> callback)
{
    Console.WriteLine("This was executed instead!");
    return true;
}

However, MethodRental.SwapMethodBody only works on dynamic modules; not those that have already been compiled and stored in the assembly.

So I'm looking for a way to effectively do SwapMethodBody on a method that is already stored in a loaded and executing assembly.

Note, it is not an issue if I have to completely copy the method into a dynamic module, but in this case I need to find a way to copy across the IL as well as update all of the calls to Solve() such that they would point to the new copy.

C# Solutions


Solution 1 - C#

> Disclosure: Harmony is a library that was written and is maintained by me, the author of this post.

Harmony 2 is an open source library (MIT license) designed to replace, decorate or modify existing C# methods of any kind during runtime. It main focus is games and plugins written in Mono or .NET. It takes care of multiple changes to the same method - they accumulate instead of overwrite each other.

It creates dynamic replacement methods for every original method and emits code to them that calls custom methods at the start and end. It also allows you to write filters to process the original IL code and custom exception handlers which allows for more detailed manipulation of the original method.

To complete the process, it writes a simple assembler jump into the trampoline of the original method that points to the assembler generated from compiling the dynamic method. This works for 32/64Bit on Windows, macOS and any Linux that Mono supports.

Documentation can be found here.

Example

(Source)

Original Code

public class SomeGameClass
{
	private bool isRunning;
	private int counter;

	private int DoSomething()
	{
		if (isRunning)
		{
			counter++;
			return counter * 10;
		}
	}
}

Patching with Harmony annotations

using SomeGame;
using HarmonyLib;

public class MyPatcher
{
	// make sure DoPatching() is called at start either by
	// the mod loader or by your injector

	public static void DoPatching()
	{
		var harmony = new Harmony("com.example.patch");
		harmony.PatchAll();
	}
}

[HarmonyPatch(typeof(SomeGameClass))]
[HarmonyPatch("DoSomething")]
class Patch01
{
	static FieldRef<SomeGameClass,bool> isRunningRef =
		AccessTools.FieldRefAccess<SomeGameClass, bool>("isRunning");

	static bool Prefix(SomeGameClass __instance, ref int ___counter)
	{
		isRunningRef(__instance) = true;
		if (___counter > 100)
			return false;
		___counter = 0;
		return true;
	}

	static void Postfix(ref int __result)
	{
		__result *= 2;
	}
}

Alternatively, manual patching with reflection

using SomeGame;
using System.Reflection;
using HarmonyLib;

public class MyPatcher
{
	// make sure DoPatching() is called at start either by
	// the mod loader or by your injector

	public static void DoPatching()
	{
		var harmony = new Harmony("com.example.patch");

		var mOriginal = typeof(SomeGameClass).GetMethod("DoSomething", BindingFlags.Instance | BindingFlags.NonPublic);
		var mPrefix = typeof(MyPatcher).GetMethod("MyPrefix", BindingFlags.Static | BindingFlags.Public);
		var mPostfix = typeof(MyPatcher).GetMethod("MyPostfix", BindingFlags.Static | BindingFlags.Public);
		// add null checks here

		harmony.Patch(mOriginal, new HarmonyMethod(mPrefix), new HarmonyMethod(mPostfix));
	}

	public static void MyPrefix()
	{
		// ...
	}

	public static void MyPostfix()
	{
		// ...
	}
}

Solution 2 - C#

For .NET 4 and above

using System;
using System.Reflection;
using System.Runtime.CompilerServices;


namespace InjectionTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Target targetInstance = new Target();

            targetInstance.test();

            Injection.install(1);
            Injection.install(2);
            Injection.install(3);
            Injection.install(4);

            targetInstance.test();

            Console.Read();
        }
    }

    public class Target
    {
        public void test()
        {
            targetMethod1();
            Console.WriteLine(targetMethod2());
            targetMethod3("Test");
            targetMethod4();
        }

        private void targetMethod1()
        {
            Console.WriteLine("Target.targetMethod1()");
            
        }

        private string targetMethod2()
        {
            Console.WriteLine("Target.targetMethod2()");
            return "Not injected 2";
        }

        public void targetMethod3(string text)
        {
            Console.WriteLine("Target.targetMethod3("+text+")");
        }

        private void targetMethod4()
        {
            Console.WriteLine("Target.targetMethod4()");
        }
    }

    public class Injection
    {        
        public static void install(int funcNum)
        {
            MethodInfo methodToReplace = typeof(Target).GetMethod("targetMethod"+ funcNum, BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
            MethodInfo methodToInject = typeof(Injection).GetMethod("injectionMethod"+ funcNum, BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
            RuntimeHelpers.PrepareMethod(methodToReplace.MethodHandle);
            RuntimeHelpers.PrepareMethod(methodToInject.MethodHandle);

            unsafe
            {
                if (IntPtr.Size == 4)
                {
                    int* inj = (int*)methodToInject.MethodHandle.Value.ToPointer() + 2;
                    int* tar = (int*)methodToReplace.MethodHandle.Value.ToPointer() + 2;
#if DEBUG
                    Console.WriteLine("\nVersion x86 Debug\n");

                    byte* injInst = (byte*)*inj;
                    byte* tarInst = (byte*)*tar;

                    int* injSrc = (int*)(injInst + 1);
                    int* tarSrc = (int*)(tarInst + 1);

                    *tarSrc = (((int)injInst + 5) + *injSrc) - ((int)tarInst + 5);
#else
                    Console.WriteLine("\nVersion x86 Release\n");
                    *tar = *inj;
#endif
                }
                else
                {

                    long* inj = (long*)methodToInject.MethodHandle.Value.ToPointer()+1;
                    long* tar = (long*)methodToReplace.MethodHandle.Value.ToPointer()+1;
#if DEBUG
                    Console.WriteLine("\nVersion x64 Debug\n");
                    byte* injInst = (byte*)*inj;
                    byte* tarInst = (byte*)*tar;


                    int* injSrc = (int*)(injInst + 1);
                    int* tarSrc = (int*)(tarInst + 1);

                    *tarSrc = (((int)injInst + 5) + *injSrc) - ((int)tarInst + 5);
#else
                    Console.WriteLine("\nVersion x64 Release\n");
                    *tar = *inj;
#endif
                }
            }
        }

        private void injectionMethod1()
        {
            Console.WriteLine("Injection.injectionMethod1");
        }

        private string injectionMethod2()
        {
            Console.WriteLine("Injection.injectionMethod2");
            return "Injected 2";
        }

        private void injectionMethod3(string text)
        {
            Console.WriteLine("Injection.injectionMethod3 " + text);
        }

        private void injectionMethod4()
        {
            System.Diagnostics.Process.Start("calc");
        }
    }

}

Solution 3 - C#

You CAN modify a method's content at runtime. But you're not supposed to, and it's strongly recommended to keep that for test purposes.

Just have a look at:

http://www.codeproject.com/Articles/463508/NET-CLR-Injection-Modify-IL-Code-during-Run-time

Basically, you can:

  1. Get IL method content via MethodInfo.GetMethodBody().GetILAsByteArray()

  2. Mess with these bytes.

    If you just wish to prepend or append some code, then just preprend/append opcodes you want (be careful about leaving stack clean, though)

    Here are some tips to "uncompile" existing IL:

    • Bytes returned are a sequence of IL instructions, followed by their arguments (if they have some - for instance, '.call' has one argument: the called method token, and '.pop' has none)
    • Correspondence between IL codes and bytes you find in the returned array may be found using OpCodes.YourOpCode.Value (which is the real opcode byte value as saved in your assembly)
    • Arguments appended after IL codes may have different sizes (from one to several bytes), depending on opcode called
    • You may find tokens that theses arguments are referring to via appropriate methods. For instance, if your IL contains ".call 354354" (coded as 28 00 05 68 32 in hexa, 28h=40 being '.call' opcode and 56832h=354354), corresponding called method can be found using MethodBase.GetMethodFromHandle(354354)
  3. Once modified, you IL byte array can be reinjected via InjectionHelper.UpdateILCodes(MethodInfo method, byte[] ilCodes) - see link mentioned above

    This is the "unsafe" part... It works well, but this consists in hacking internal CLR mechanisms...

Solution 4 - C#

you can replace it if the method is non virtual, non generic, not in generic type, not inlined and on x86 plateform:

MethodInfo methodToReplace = ...
RuntimeHelpers.PrepareMetod(methodToReplace.MethodHandle);

var getDynamicHandle = Delegate.CreateDelegate(Metadata<Func<DynamicMethod, RuntimeMethodHandle>>.Type, Metadata<DynamicMethod>.Type.GetMethod("GetMethodDescriptor", BindingFlags.Instance | BindingFlags.NonPublic)) as Func<DynamicMethod, RuntimeMethodHandle>;

var newMethod = new DynamicMethod(...);
var body = newMethod.GetILGenerator();
body.Emit(...) // do what you want.
body.Emit(OpCodes.jmp, methodToReplace);
body.Emit(OpCodes.ret);

var handle = getDynamicHandle(newMethod);
RuntimeHelpers.PrepareMethod(handle);

*((int*)new IntPtr(((int*)methodToReplace.MethodHandle.Value.ToPointer() + 2)).ToPointer()) = handle.GetFunctionPointer().ToInt32();

//all call on methodToReplace redirect to newMethod and methodToReplace is called in newMethod and you can continue to debug it, enjoy.

Solution 5 - C#

Based on the answer to this question and another, ive came up with this tidied up version:

// Note: This method replaces methodToReplace with methodToInject
// Note: methodToInject will still remain pointing to the same location
public static unsafe MethodReplacementState Replace(this MethodInfo methodToReplace, MethodInfo methodToInject)
        {
//#if DEBUG
            RuntimeHelpers.PrepareMethod(methodToReplace.MethodHandle);
            RuntimeHelpers.PrepareMethod(methodToInject.MethodHandle);
//#endif
            MethodReplacementState state;

            IntPtr tar = methodToReplace.MethodHandle.Value;
            if (!methodToReplace.IsVirtual)
                tar += 8;
            else
            {
                var index = (int)(((*(long*)tar) >> 32) & 0xFF);
                var classStart = *(IntPtr*)(methodToReplace.DeclaringType.TypeHandle.Value + (IntPtr.Size == 4 ? 40 : 64));
                tar = classStart + IntPtr.Size * index;
            }
            var inj = methodToInject.MethodHandle.Value + 8;
#if DEBUG
            tar = *(IntPtr*)tar + 1;
            inj = *(IntPtr*)inj + 1;
            state.Location = tar;
            state.OriginalValue = new IntPtr(*(int*)tar);

            *(int*)tar = *(int*)inj + (int)(long)inj - (int)(long)tar;
            return state;

#else
            state.Location = tar;
            state.OriginalValue = *(IntPtr*)tar;
            * (IntPtr*)tar = *(IntPtr*)inj;
            return state;
#endif
        }
    }

    public struct MethodReplacementState : IDisposable
    {
        internal IntPtr Location;
        internal IntPtr OriginalValue;
        public void Dispose()
        {
            this.Restore();
        }

        public unsafe void Restore()
        {
#if DEBUG
            *(int*)Location = (int)OriginalValue;
#else
            *(IntPtr*)Location = OriginalValue;
#endif
        }
    }

Solution 6 - C#

There exists a couple of frameworks that allows you to dynamically change any method at runtime (they use the ICLRProfiling interface mentioned by user152949):

There are also a few frameworks that mocks around with the internals of .NET, these are likely more fragile, and probably can't change inlined code, but on the other hand they are fully self-contained and does not require you to use a custom launcher.

  • Harmony: MIT licensed. Seems to actually have been used sucessfully in a few game mods, supports both .NET and Mono.
  • Deviare In Process Instrumentation Engine: GPLv3 and Commercial. .NET support currently marked as experimental, but on the other hand has the benefit of being commercially backed.

Solution 7 - C#

Logman's solution, but with an interface for swapping method bodies. Also, a simpler example.

using System;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;

namespace DynamicMojo
{
    class Program
    {
        static void Main(string[] args)
        {
            Animal kitty = new HouseCat();
            Animal lion = new Lion();
            var meow = typeof(HouseCat).GetMethod("Meow", BindingFlags.Instance | BindingFlags.NonPublic);
            var roar = typeof(Lion).GetMethod("Roar", BindingFlags.Instance | BindingFlags.NonPublic);

            Console.WriteLine("<==(Normal Run)==>");
            kitty.MakeNoise(); //HouseCat: Meow.
            lion.MakeNoise(); //Lion: Roar!

            Console.WriteLine("<==(Dynamic Mojo!)==>");
            DynamicMojo.SwapMethodBodies(meow, roar);
            kitty.MakeNoise(); //HouseCat: Roar!
            lion.MakeNoise(); //Lion: Meow.

            Console.WriteLine("<==(Normality Restored)==>");
            DynamicMojo.SwapMethodBodies(meow, roar);
            kitty.MakeNoise(); //HouseCat: Meow.
            lion.MakeNoise(); //Lion: Roar!

            Console.Read();
        }
    }

    public abstract class Animal
    {
        public void MakeNoise() => Console.WriteLine($"{this.GetType().Name}: {GetSound()}");

        protected abstract string GetSound();
    }

    public sealed class HouseCat : Animal
    {
        protected override string GetSound() => Meow();

        private string Meow() => "Meow.";
    }

    public sealed class Lion : Animal
    {
        protected override string GetSound() => Roar();

        private string Roar() => "Roar!";
    }

    public static class DynamicMojo
    {
        /// <summary>
        /// Swaps the function pointers for a and b, effectively swapping the method bodies.
        /// </summary>
        /// <exception cref="ArgumentException">
        /// a and b must have same signature
        /// </exception>
        /// <param name="a">Method to swap</param>
        /// <param name="b">Method to swap</param>
        public static void SwapMethodBodies(MethodInfo a, MethodInfo b)
        {
            if (!HasSameSignature(a, b))
            {
                throw new ArgumentException("a and b must have have same signature");
            }

            RuntimeHelpers.PrepareMethod(a.MethodHandle);
            RuntimeHelpers.PrepareMethod(b.MethodHandle);

            unsafe
            {
                if (IntPtr.Size == 4)
                {
                    int* inj = (int*)b.MethodHandle.Value.ToPointer() + 2;
                    int* tar = (int*)a.MethodHandle.Value.ToPointer() + 2;

                    byte* injInst = (byte*)*inj;
                    byte* tarInst = (byte*)*tar;

                    int* injSrc = (int*)(injInst + 1);
                    int* tarSrc = (int*)(tarInst + 1);

                    int tmp = *tarSrc;
                    *tarSrc = (((int)injInst + 5) + *injSrc) - ((int)tarInst + 5);
                    *injSrc = (((int)tarInst + 5) + tmp) - ((int)injInst + 5);
                }
                else
                {
                    throw new NotImplementedException($"{nameof(SwapMethodBodies)} doesn't yet handle IntPtr size of {IntPtr.Size}");
                }
            }
        }

        private static bool HasSameSignature(MethodInfo a, MethodInfo b)
        {
            bool sameParams = !a.GetParameters().Any(x => !b.GetParameters().Any(y => x == y));
            bool sameReturnType = a.ReturnType == b.ReturnType;
            return sameParams && sameReturnType;
        }
    }
}

Solution 8 - C#

You can replace a method at runtime by using the ICLRPRofiling Interface.

  1. Call AttachProfiler to attach to the process.
  2. Call SetILFunctionBody to replace the method code.

See this blog for more details.

Solution 9 - C#

Based on TakeMeAsAGuest's answer, here's a similar extension which does not require to use unsafe blocks.

Here's the Extensions class:

using System;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace MethodRedirect
{
    static class Extensions
    { 
        public static void RedirectTo(this MethodInfo origin, MethodInfo target)
        {
            IntPtr ori = GetMethodAddress(origin);
            IntPtr tar = GetMethodAddress(target);
         
            Marshal.Copy(new IntPtr[] { Marshal.ReadIntPtr(tar) }, 0, ori, 1);
        }

        private static IntPtr GetMethodAddress(MethodInfo mi)
        {
            const ushort SLOT_NUMBER_MASK = 0xffff; // 2 bytes mask
            const int MT_OFFSET_32BIT = 0x28;       // 40 bytes offset
            const int MT_OFFSET_64BIT = 0x40;       // 64 bytes offset

            IntPtr address;

            // JIT compilation of the method
            RuntimeHelpers.PrepareMethod(mi.MethodHandle);

            IntPtr md = mi.MethodHandle.Value;             // MethodDescriptor address
            IntPtr mt = mi.DeclaringType.TypeHandle.Value; // MethodTable address

            if (mi.IsVirtual)
            {
                // The fixed-size portion of the MethodTable structure depends on the process type
                int offset = IntPtr.Size == 4 ? MT_OFFSET_32BIT : MT_OFFSET_64BIT;

                // First method slot = MethodTable address + fixed-size offset
                // This is the address of the first method of any type (i.e. ToString)
                IntPtr ms = Marshal.ReadIntPtr(mt + offset);

                // Get the slot number of the virtual method entry from the MethodDesc data structure
                long shift = Marshal.ReadInt64(md) >> 32;
                int slot = (int)(shift & SLOT_NUMBER_MASK);
                
                // Get the virtual method address relative to the first method slot
                address = ms + (slot * IntPtr.Size);                                
            }
            else
            {
                // Bypass default MethodDescriptor padding (8 bytes) 
                // Reach the CodeOrIL field which contains the address of the JIT-compiled code
                address = md + 8;
            }

            return address;
        }
    }
}

And here's a simple usage example:

using System;
using System.Reflection;

namespace MethodRedirect
{
    class Scenario
    {    
      static void Main(string[] args)
      {
          Assembly assembly = Assembly.GetAssembly(typeof(Scenario));
          Type Scenario_Type = assembly.GetType("MethodRedirect.Scenario");

          MethodInfo Scenario_InternalInstanceMethod = Scenario_Type.GetMethod("InternalInstanceMethod", BindingFlags.Instance | BindingFlags.NonPublic);
          MethodInfo Scenario_PrivateInstanceMethod = Scenario_Type.GetMethod("PrivateInstanceMethod", BindingFlags.Instance | BindingFlags.NonPublic);

          Scenario_InternalInstanceMethod.RedirectTo(Scenario_PrivateInstanceMethod);

          // Using dynamic type to prevent method string caching
          dynamic scenario = (Scenario)Activator.CreateInstance(Scenario_Type);

          bool result = scenario.InternalInstanceMethod() == "PrivateInstanceMethod";

          Console.WriteLine("\nRedirection {0}", result ? "SUCCESS" : "FAILED");

          Console.ReadKey();
      }

      internal string InternalInstanceMethod()
      {
          return "InternalInstanceMethod";
      }

      private string PrivateInstanceMethod()
      {
          return "PrivateInstanceMethod";
      }
    }
}

This is distilled from a more detailed project I made available on Github (MethodRedirect).

Solution 10 - C#

I know it is not the exact answer to your question, but the usual way to do it is using factories/proxy approach.

First we declare a base type.

public class SimpleClass
{
	public virtual DTask<bool> Solve(int n, DEvent<bool> callback)
	{
		for (int m = 2; m < n - 1; m += 1)
			if (m % n == 0)
				return false;
		return true;
	}
}

Then we can declare a derived type (call it proxy).

public class DistributedClass
{
	public override DTask<bool> Solve(int n, DEvent<bool> callback)
	{
		CodeToExecuteBefore();
		return base.Slove(n, callback);
	}
}

// At runtime

MyClass myInstance;

if (distributed)
	myInstance = new DistributedClass();
else
	myInstance = new SimpleClass();
	

The derived type can be also generated at runtime.

public static class Distributeds
{
	private static readonly ConcurrentDictionary<Type, Type> pDistributedTypes = new ConcurrentDictionary<Type, Type>();
	
	public Type MakeDistributedType(Type type)
	{
		Type result;
		if (!pDistributedTypes.TryGetValue(type, out result))
		{
			if (there is at least one method that have [Distributed] attribute)
			{
				result = create a new dynamic type that inherits the specified type;
			}
			else
			{
				result = type;
			}

			pDistributedTypes[type] = result;
		}
		return result;
	}
	
	public T MakeDistributedInstance<T>()
		where T : class
	{
		Type type = MakeDistributedType(typeof(T));
		if (type != null)
		{
			// Instead of activator you can also register a constructor delegate generated at runtime if performances are important.
			return Activator.CreateInstance(type);
		}
		return null;
	}
}

// In your code...

MyClass myclass = Distributeds.MakeDistributedInstance<MyClass>();
myclass.Solve(...);

The only performance loss is during construction of the derived object, the first time is quite slow because it will use a lot of reflection and reflection emit. All other times, it is the cost of a concurrent table lookup and a constructor. As said, you can optimize construction using

ConcurrentDictionary<Type, Func<object>>.

Solution 11 - C#

have a look into Mono.Cecil:

using Mono.Cecil;
using Mono.Cecil.Inject;

public class Patcher
{    
   public void Patch()
   {
    // Load the assembly that contains the hook method
    AssemblyDefinition hookAssembly = AssemblyLoader.LoadAssembly("MyHookAssembly.dll");
    // Load the assembly
    AssemblyDefinition targetAssembly = AssemblyLoader.LoadAssembly("TargetAssembly.dll");

    // Get the method definition for the injection definition
    MethodDefinition myHook = hookAssembly.MainModule.GetType("HookNamespace.MyHookClass").GetMethod("MyHook");
    // Get the method definition for the injection target. 
    // Note that in this example class Bar is in the global namespace (no namespace), which is why we don't specify the namespace.
    MethodDefinition foo = targetAssembly.MainModule.GetType("Bar").GetMethod("Foo");

    // Create the injector
    InjectionDefinition injector = new InjectionDefinition(foo, myHook, InjectFlags.PassInvokingInstance | InjectFlags.passParametersVal);

    // Perform the injection with default settings (inject into the beginning before the first instruction)
    injector.Inject();

    // More injections or saving the target assembly...
   }
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJune RhodesView Question on Stackoverflow
Solution 1 - C#Andreas PardeikeView Answer on Stackoverflow
Solution 2 - C#LogmanView Answer on Stackoverflow
Solution 3 - C#OlivierView Answer on Stackoverflow
Solution 4 - C#Teter28View Answer on Stackoverflow
Solution 5 - C#TakeMeAsAGuestView Answer on Stackoverflow
Solution 6 - C#poizan42View Answer on Stackoverflow
Solution 7 - C#C. McCoy IVView Answer on Stackoverflow
Solution 8 - C#user152949View Answer on Stackoverflow
Solution 9 - C#SpinicoffeeView Answer on Stackoverflow
Solution 10 - C#Salvatore PrevitiView Answer on Stackoverflow
Solution 11 - C#Martin.MartinssonView Answer on Stackoverflow