Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to inject IL into a method at runtime

Title more or less says it all. Based on this article, I've come up with this:

public static unsafe void Replace(this MethodBase destination, MethodBase source)
{
    IntPtr srcHandle = source.MethodHandle.GetFunctionPointer();
    IntPtr dstHandle = destination.MethodHandle.GetFunctionPointer();

    int* dstPtr = (int*)dstHandle.ToPointer();
    *dstPtr = srcHandle.ToInt32();
}

This actually works... occasionally -.-

For example, this works.

public static class Program
{
    public static void Main(string[] args)
    {
        MethodInfo methodA = typeof(Program).GetMethod("A", BindingFlags.Public | BindingFlags.Static);
        MethodInfo methodB = typeof(Program).GetMethod("B", BindingFlags.Public | BindingFlags.Static);

        methodA.Replace(methodB);

        A();
        B();
    }

    public static void A()
    {
        Console.WriteLine("Hai World");
    }

    public static void B()
    {
        Console.WriteLine("Bai World");
    }
}

However, this doesn't (SEHException). All I did was change the order in which the functions were defined.

public static class Program
{
    public static void Main(string[] args)
    {
        MethodInfo methodA = typeof(Program).GetMethod("A", BindingFlags.Public | BindingFlags.Static);
        MethodInfo methodB = typeof(Program).GetMethod("B", BindingFlags.Public | BindingFlags.Static);

        methodA.Replace(methodB);

        A();
        B();
    }

    public static void B()
    {
        Console.WriteLine("Bai World");
    }

    public static void A()
    {
        Console.WriteLine("Hai World");
    }
}

As for the code in the article... I couldn't get it to work at all.

Any ideas/alternatives?

like image 680
YellPika Avatar asked Jan 29 '11 22:01

YellPika


1 Answers

This is making a lot of bad assumptions which will bite you in the ass.

First things first, the structure returned via reflection is not guaranteed, in any way, to point to any runtime structures at all. As such attempting to modify the pointers it contains or returns is just plain wrong.

Secondly, if you wish to do something like injecting pre/post method call invariants (as an example) then you should probably construct runtime proxy objects and inject those instead. Or use dynamic method construction through the Emit namespace. Attempting to manipulate things through undocumented/unknown behaviors (such as the above code) just wont work.

You also need to realize that the JIT decides when it will run. Sometimes it runs against an entire class, and sometimes it just runs against a single method. Your code makes no attempt at determining if the method has been JITted yet, and blindly assumes that the function pointer returned can be modified.

like image 137
Washu Avatar answered Oct 08 '22 12:10

Washu