DucatiNerd
DucatiNerd

Reputation: 467

.NET CLR Runtime Method Replacement

I have read quite a bit about the possibilities of overwriting C#/.NET methods at runtime. After looking at some example code, there is one question that I am not able to answer. A very simple PoC for runtime method replacement could look like this:

    class Program
    {
        public static void A(int x)
        {
            Console.WriteLine("A: " + x);
        }

        public static void B(int x)
        {
            Console.WriteLine("B: " + x);
        }

        internal static void Main(string[] args)
        {

            MethodInfo a = null;
            MethodInfo b = null;

            foreach(MethodInfo mi in typeof(Program).GetMethods())
            {
                if (mi.Name == "A" || mi.Name == "B")
                {
                    // make sure methods are jitted
                    RuntimeHelpers.PrepareMethod(mi.MethodHandle);
                }

                if (mi.Name == "A") a = mi;
                if (mi.Name == "B") b = mi;
            }

            unsafe
            {
                if (IntPtr.Size == 4) // x86
                {
                    int* inj = (int*)a.MethodHandle.Value.ToPointer() + 2;
                    int* tar = (int*)b.MethodHandle.Value.ToPointer() + 2;
                    *tar = *inj;
                }
                else  // x64
                {
                    ulong* inj = (ulong*)a.MethodHandle.Value.ToPointer() + 1;
                    ulong* tar = (ulong*)b.MethodHandle.Value.ToPointer() + 1;
                    *tar = *inj;
                }
            }

            Program.A(0);
            Program.B(1);

            Console.ReadLine();

        }
    }

As one would expect, the call to A(0) and B(1) now prints:

A: 0
A: 1

So far so good. However, I have also seen an example, where it seems that no differentiation is made between x86/x64 code:

...
int* inj = (int*)(a.MethodHandle.Value.ToPointer() + 8);
int* tar = (int*)(b.MethodHandle.Value.ToPointer() + 8);
...

In this case 8 is added to the pointer. Could somebody explain the reason behind this? Additionally, what exactly does this pointer offset mean? If somebody can recommend good reading materials about C#/.NET/CLR internals, please let me know.

Thanks

Upvotes: 2

Views: 280

Answers (1)

Sasha Alperovich
Sasha Alperovich

Reputation: 382

The actual pointer to the code of the method is 8 bytes after the method handle address. In your first example, you first cast to int (which is either 4 or 8 bytes) and then need to make a stride of 8 bytes, so you either add 1 or 2 ints, depends on the architecture.

In your second example, you first make the stride, using a byte*, adding 8 to the pointer, and only then cast. BTW, the later example should read

...
int* inj = (int*)(a.MethodHandle.Value.ToPointer() + 8);
int* tar = (int*)(b.MethodHandle.Value.ToPointer() + 8);
...

Upvotes: 2

Related Questions