151

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.

Liam
  • 27,717
  • 28
  • 128
  • 190
June Rhodes
  • 3,047
  • 4
  • 23
  • 29
  • 3
    Not possible to swap methods already loaded. Otherwise Spring.Net wouldn't have to make strange things with proxies and interfaces :-) Read this question, it's tangent to your problem: http://stackoverflow.com/questions/25803/how-do-i-intercept-a-method-call-in-c (if you can intercept it, you can something-like-swap it... If you can't 1 then clearly you can't 2). – xanatos Sep 04 '11 at 12:12
  • In that case, is there a way to copy a method into a dynamic module, and update the rest of the assembly such that calls to that method point to the new copy? – June Rhodes Sep 04 '11 at 12:15
  • Same old-same old. If it could be done easily, all the various IoC containers would probably do it. They don't do it->99% it can't be done :-) (without terrible and innominable hacks). There is a single hope: they promised metaprogramming and async in C# 5.0. Async we have seen... Metaprogramming nothing... BUT it could be it! – xanatos Sep 04 '11 at 12:16
  • Could you make `Solve()` virtual and create a class during runtime that inherits from the class `Solve()` is in? – svick Sep 04 '11 at 12:16
  • Why woud you like to swap the body? Do you know what your are swapping to? If so, why not make the method take in the body as a `Func`? – Tomas Jansson Sep 04 '11 at 12:30
  • If you can't rewrite solve in any way, I don't think in C# there is anything you can do... Well in C++ you can inject dll and Proxy all functions, but I don't think is a good idea – Francesco Belladonna Sep 04 '11 at 12:32
  • 1
    You really haven't explained why you want to let yourself in for something so painful. – DanielOfTaebl Sep 04 '11 at 12:41
  • Possible duplicate of [How do I replace a method implementation at runtime?](http://stackoverflow.com/questions/3062830/how-do-i-replace-a-method-implementation-at-runtime) – poizan42 Dec 07 '16 at 14:21
  • 8
    Please see my answer below. This is totally possible. On code you don't own and during runtime. I don't understand why so many think this is not possible. – Andreas Pardeike Mar 14 '17 at 07:36

11 Answers11

417

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. Its 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/64-bit 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()
    {
        // ...
    }
}
Andreas
  • 5,393
  • 9
  • 44
  • 53
Andreas Pardeike
  • 4,622
  • 1
  • 17
  • 27
  • Had a look at the source code, very interesting! Can you explain (here and/or in documentation) how the specific instructions work which are used to perform the jump (in `Memory.WriteJump`)? – Tom Oct 29 '18 at 10:18
  • To partially answer my own comment: `48 B8 ` moves a QWord immediate value to `rax`, then `FF E0` is `jmp rax` - all clear there! My remaining question is about the `E9 ` case (a near jump): it seems in this case the near jump is preserved and the modification is on the target of the jump; when does Mono generate such code in the first place, and why does it get this special treatment? – Tom Oct 30 '18 at 00:29
  • Don’t just look at mono. A lot of people use Harmony on different versions of .Net and I observed this indirection on some trampoline code while testing. There are many more cases not covered but my time is limited and it works good enough for now. – Andreas Pardeike Oct 30 '18 at 03:17
  • 2
    As far as I can tell it doesn't support .NET Core 2 yet, getting some exceptions with AppDomain.CurrentDomain.DefineDynamicAssembly – Max Nov 17 '18 at 21:58
  • Yes, Max, I hadn’t have the time to build and test Harmony with .NET Core 2 yet. – Andreas Pardeike Nov 17 '18 at 23:38
  • 2
    A friend of mine, 0x0ade did mention to me that there's a less mature alternative which works on .NET Core, namely MonoMod.RuntimeDetour on NuGet. – Andreas Pardeike Feb 08 '19 at 17:21
  • 2
    Update: By including a reference to System.Reflection.Emit, Harmony now compiles and tests OK with .NET Core 3 – Andreas Pardeike May 02 '19 at 05:48
  • Harmony 2 now uses MonoMod.Common to avoid Reflection.Emit and uses in-memory Cecil to generate the new method. It works with all .NET versions. – Andreas Pardeike Jan 28 '20 at 12:00
  • Do you have and example of a patch, not prefix, postfix or transpiler, but just a method that replaces the original. Kind of like the oposite of reverse patch. – rraallvv Aug 25 '20 at 20:17
  • 2
    @rraallvv To replace a method, you would create a Prefix that has all the arguments of the original plus `__instance` (If not static) and `ref __result` and let it return false to skip the original. In it, you use __instance and you assign the result to __result and then return false. – Andreas Pardeike Aug 26 '20 at 03:19
  • This will not support .NET 7 – kemsky Feb 04 '23 at 16:49
  • @AndreasPardeike I am trying to use this to create 'fakes' for my Unit tests (basically trying to replace calls to static functions while doing the xunit tests). The same simple code that works correctly in a console application doesn't work at all when running the tests (it is just ignored). Any ideas? Thanks! – rufo Jun 27 '23 at 15:29
  • @rufo Your tests most likely run in release mode and your methods become inlined (which defeats patches). Not sure which version of Harmony you are using but newer versions try their best to prevent inlining. It also depends on the .NET version. – Andreas Pardeike Jun 27 '23 at 20:24
223

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");
        }
    }

}
senshin
  • 10,022
  • 7
  • 46
  • 59
Logman
  • 4,031
  • 1
  • 23
  • 35
  • 21
    This deserves so many more upvotes. I have a completely different scenario but this snippet is exactly what I needed to set me in the right direction. Thanks. – S.C. Apr 15 '16 at 18:32
  • Thanks for great answer. Is there a way to get this to work in full release mode with optimizations (i.e. run without debugging) when the methods are inlined? (When I add `[MethodImpl(MethodImplOptions.NoInlining)]` it works, but is there a way to replace it without this?) – Mr Anderson Jul 07 '16 at 01:15
  • @MrAnderson Yes but it's not so simple. It's more like asembler programming then c# as you need to get machine code of your inline function and then scan your application for that code. After that replace every place where you found your function with new code. It's not simple and it's prone to errors but you can do it. – Logman Jul 07 '16 at 01:55
  • @Logman I downloaded and am studying the CLI source code, I figured out how to prevent inlining of method. Good results so far, still working though. Another question - when `methodToInject` is a `DynamicMethod`, your process fails. Any idea how to get this to work? – Mr Anderson Jul 08 '16 at 04:14
  • 2
    @Logman great answer. But my question is: What's going on in debug mode? And is it possible to replace only one instruction? For example if I want to replace conditional jump on unconditional one? AFAIK you are replacing compiled method, so it's not easy to determine which condition we should replace... – Alex Zhukovskiy Aug 04 '16 at 09:51
  • @AlexZhukovskiy in debug compiler adds some middle man code and to inject your method you need to recalculate address of your method. For the second question yes you can, you can modify assembly code of your application and swap one instruction to another. Trick is to find correct memory address to override. – Logman Aug 04 '16 at 13:09
  • @Logman problem is that i wasn't able to recognize any asselmbly instruction. For example for a method `return 15`, it's just should be `mov` and `ret`, but I got an unrecognized sequence of bytes. I just casted a pointer to (byte*) and moved until `0xc3` (ret), but didn't encounter it. Am I doing it wrong? How can I get asselbmy representation from pointer? – Alex Zhukovskiy Aug 04 '16 at 13:42
  • @AlexZhukovskiy try to map some of your code (convert it to string) and past it to [this](https://defuse.ca/online-x86-assembler.htm). There could be multiple reason why you not encounter 0xc3 opcode. – Logman Aug 05 '16 at 01:08
  • fyi There is a [question](http://stackoverflow.com/questions/38782934/how-to-replace-the-pointer-to-the-overridden-virtual-method-in-the-pointer-of/) pointing here... –  Aug 05 '16 at 07:56
  • @Logman I know this site which is very useful. But I failed to map in on something. I'l post an example later if you wish. – Alex Zhukovskiy Aug 05 '16 at 09:28
  • 2
    @AlexZhukovskiy if you like post it on stack and send me link. I will look into it and give you an answer after weekend. Machine I will look also into your question after weekend. – Logman Aug 05 '16 at 10:07
  • I think I've just found a solution for the above said question: I've posted my answer [there](http://stackoverflow.com/a/38783635/6298965), IMHO it works fine. Thanks anyway. –  Aug 05 '16 at 17:34
  • 2
    Two things I noticed when doing this for an integration test with MSTest: (1) When you use `this` inside `injectionMethod*()` it will reference an `Injection` instance during _compile time_, but a `Target` instance during _runtime_ (this is true for all references to instance members you use inside an injected method). (2) For some reason the `#DEBUG` part was only working when _debugging_ a test, but not when _running_ a test that has been debug-compiled. I ended up _always_ using the `#else` part. I don't understand why this works but it does. – Good Night Nerd Pride Sep 07 '16 at 11:54
  • `this` is tricky at runtime as method don't know for what object it was called and `this` need to be passed to method from outer method. But on compile time compiler know what is where and what call what. – Logman Sep 09 '16 at 18:14
  • 2
    very nice. time to break everything ! @GoodNightNerdPride use `Debugger.IsAttached` instead of `#if` preprocessor – M.kazem Akhgary Dec 25 '16 at 16:01
  • 2
    @Logman When injecting methods created with Reflection.Emit.MethodBuilder, throws AccessViolationException in debug mode (works in release). Any thoughts? – Mr Anderson Jan 19 '17 at 05:52
  • 1
    @MrAnderson Probably dynamic generated method has no debug code in it. Try use release version in debug for such methods. I will look closely into it in a free time. – Logman Jan 20 '17 at 14:27
  • Is there any way to get this to work with virtual methods? I am getting an AccessViolationException for methods which are implementations of an interface, I assume it's because they're virtual? – Red Taz May 22 '17 at 10:59
  • @RedTaz yes. Check User1892538 [answer](http://stackoverflow.com/a/38783635/2483065). – Logman May 22 '17 at 11:53
  • @nrofis can you explain what you have in mind saying "No" and referring to `DynamicMethod`? – Logman Jun 07 '17 at 10:55
  • @Logman about the answer that you wrote to MrAnderson. If I am trying to use `DynamicMethod` as the injected method, `DynamicMethod.MethodHandle` throws an exception. It is not working even in release version – nrofis Jun 07 '17 at 11:05
  • @nrofis Please ask new question about injecting dynamic methods as problem is to long to resolve it in comment. – Logman Jun 07 '17 at 11:27
  • Any idea how I could make it work in .Net 3.5 running in Mono CLI? – djk Nov 09 '17 at 10:05
  • Nope. I try it on mono inside unity but I have only partial results and I don't have time right now to make it work. You may always analyze machine code and try to figure it out by yourself. You just need to have basic knowledge about how CPU works and spend some time analyzing memory as I do. Its time consuming but not too difficult task. – Logman Nov 09 '17 at 13:36
  • 2
    In my case, when I hit the line right before the `#else`, I am getting: `Exception thrown: 'System.AccessViolationException' in CustomActionsTest.dll An exception of type 'System.AccessViolationException' occurred in CustomActionsTest.dll but was not handled in user code Additional information: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.` – Jon Mar 20 '18 at 17:44
  • I have the following error when run this code on .NET Core 2.0 and call the injected methods: `An unhandled exception of type 'System.ExecutionEngineException' occurred in Unknown Module.` – aelbatal Apr 09 '18 at 19:58
  • @Jon I have the same result, did you find a solution? Thanks – Tom Sawyer Apr 11 '18 at 12:49
  • What's the reasoning for the +2? `(int*)methodToInject.MethodHandle.Value.ToPointer() + 2;` – P.Brian.Mackey Apr 11 '18 at 13:09
  • 1
    @jon - This happened when I changed the `+2` value in the pointer area. Also, I only get a valid injection when I hit F5. For some reason CTRL + F5 doesn't work as expected. It doesn't make all the method calls in this case. I also used a console app. LINQPad didn't handle this code well. – P.Brian.Mackey Apr 11 '18 at 13:37
  • 1
    Can someone explain the parts of this? What are the offsets for? I need to modify this method so that it can patch code that was compiled in release with code that was compiled in debug. Unmodified, the current implementation hard crashes the app. I'm not sure why, for example, in DEBUG with IntPrt.Size == 8 the code deals in int* instead of long*. Is that a bug or intentional? – Zachary Burns Apr 17 '18 at 14:30
  • @ZacharyBurns Probably you should create a new question as your problem is much more complex then this. To answer your second question, offset is necessary to access "real" pointer, but in some cases it may differ. Check [this answer](https://stackoverflow.com/a/38783635/2483065) for ex. – Logman Apr 17 '18 at 18:59
  • 1
    Answered my own question. The +2 is pointer arithmetic. In x86 the code is working with int (System.Int32) on a 64 bit system (8 byte words). x64 builds uses long (System.Int64) and because expressions such as `longVar + 1` produce `long` types we only add `1` in the latter case. productutorialspoint.com/cprogramming/c_pointer_arithmetic.htm – P.Brian.Mackey May 03 '18 at 16:16
  • This is awesome, thank you. One issue: the debug code doesn't seem to work with .NET core (getting NRE's when calling the patched method). Removing that makes this function for me – Max Nov 18 '18 at 17:45
  • This triggers a `System.ExecutionEngineException` on instance methods with a return value and parameters. Doesn't work for parameterless functions either but does return uninitialized memory if you debug through it. (behaves differently with vs without debugger attached). – Sellorio Jul 25 '19 at 22:41
  • In .NETCore 3.X, it partially works but only for a tiny while. It looks like the pointers are being reverted by something. These are static methods that I'm trying to swap btw. – Dealdiane May 13 '20 at 10:13
  • Does anyone already know how to get the code running when the debugger is not attached during a unit test? If the debugger is attached the code works fine. – Dragonblf May 24 '20 at 18:24
  • I've reversed #IF DEBUG for that. But for me it does not work when I have debugger attached @Dragonblf – Evgeny Gorbovoy Nov 09 '20 at 01:22
  • This appears to no longer work if the debugger is attached as of some very recent version of .NET (I suspect either 6.0.6 or 6.0.5) – Walt D Jun 21 '22 at 01:26
27

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...

Olivier
  • 5,578
  • 2
  • 31
  • 46
  • 9
    Just to be pedantic, 354354 (0x00056832) is not a valid metadata token, the high-order byte should be 0x06 (MethodDef), 0x0A (MemberRef) or 0x2B (MethodSpec). Also, the metadata token should be written in little-endian byte order. Finally, the metadata token is module specific and MethodInfo.MetadataToken will return the token from the declaring module, making it unusable if you want to call a method not defined in the same module as the method you are modifying. – Brian Reichle Jan 02 '15 at 01:56
14

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.
Teter28
  • 504
  • 5
  • 8
  • That looks crazy dangerous. I really hope nobody uses it in production code. – Brian Reichle Jan 02 '15 at 02:07
  • 2
    Such this is used by application performance monitoring (APM) tools and is used in production as well. – Martin Kersten Sep 17 '15 at 23:29
  • 1
    Thank you for reply, i am working on a project to offer this kind of capability as Aspect Oriented Programming API. I resolved my limitation to manage virtual method and generic method on both x86 & x64. Let me know if you need more details. – Teter28 Sep 18 '15 at 12:26
  • 7
    What is the class Metadata? – Sebastian Sep 20 '16 at 07:04
  • This answer is pseudo code and outdated. Many of the methods no longer exist. – N-ate Dec 12 '17 at 01:18
  • @Teter28, I'm trying to figure out how I can replace a method inside a generic. I know this doesn't work with generics but do you know why? – johnny 5 Jan 17 '20 at 19:43
14

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
        }
    }
johnny 5
  • 19,893
  • 50
  • 121
  • 195
TakeMeAsAGuest
  • 957
  • 6
  • 11
10

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.
  • [Pose][7]: MIT licensed but not updated since 2021.
  • 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. Unfortunately doesn't seems to have been updated since 2020.
poizan42
  • 1,461
  • 17
  • 22
8

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;
        }
    }
}
C. McCoy IV
  • 887
  • 7
  • 14
  • 1
    This gave me: An exception of type 'System.AccessViolationException' occurred in MA.ELCalc.FunctionalTests.dll but was not handled in user code Additional information: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.,,,When replacing a getter. – N-ate Dec 12 '17 at 00:18
  • I got exception "wapMethodBodies doesn't yet handle IntPtr size of 8" – Phong Dao Feb 18 '19 at 13:09
8

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).

Remark : The code was implemented using .NET Framework 4 and it has not been tested on newer version of .NET.

Spinicoffee
  • 171
  • 2
  • 8
  • Will this allow the replacement of a property setter method with an Action or a normal method. Additionally, are we able to replace a constructor with an Action? With my own code (.NET 6.0) I've been able to swap constructors, but can't seem to repoint a setter. Thanks! – N-ate Jul 19 '22 at 02:42
  • The restore token operation doesn't seem to work in Net Core from this example – Red Riding Hood Sep 22 '22 at 18:39
  • For property accessors redirection (Get or Set), you may use reflection to get the corresponding `PropertyInfo` from which you can obtain the respective `MethodInfo` using `GetGetMethod` and/or `GetSetMethod` as needed. – Spinicoffee Sep 26 '22 at 14:58
5

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.

3

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>>.
Salvatore Previti
  • 8,956
  • 31
  • 37
  • 1
    Hmm.. that still requires work on the programmer's behalf to actively be aware of the distributed processing; I was looking for a solution that relies only on them setting the [Distributed] attribute on the method (and not subclassing or inheriting from ContextBoundObject). Looks like I might need to do some post-compiling modifications on the assemblies using Mono.Cecil or something like that. – June Rhodes Sep 04 '11 at 22:15
  • I would not say, that this is usual way. This way is simple in terms of skills required (no need to understand CLR), but it requires to repeat same steps for each of replaced method/class. If later you want to change something (for example, execute some code after, not only before) then you will have to do it N times (in contrast with unsafe code which requires to do it once). So it's N hours job vs 1 hour job) – Evgeny Gorbovoy Jun 16 '20 at 20:55
1

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...
   }
}
Martin.Martinsson
  • 1,894
  • 21
  • 25