mercredi 20 mai 2020

C# - Replace methods body with DynamicMethods body

I try to replace the body of a method with the body of a DynamicMethod. So far I used the following resources to get the pointer to a "normal" method and to a DynamicMethod. With the functionality from the first link I have the functionality to replace the body of "normal" method with the body of another "normal" method. But if I try to use the functionality with the DynamicMethod it doesn't work. In the following is an example test:

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

public class Test
{
    public void Test_Dynamic()
    {
        // --- Gemerate new dynamic method ---
        DynamicMethod dynamicMethod = new DynamicMethod(
            "My_DynamicMethod",
            typeof(int),
            new Type[] { typeof(int) },
            typeof(Test).Module)
        ;

        ILGenerator il = dynamicMethod.GetILGenerator();
        il.Emit(OpCodes.Ldarg_0);
        il.Emit(OpCodes.Ret);


        // --- Check expected results ---
        var normalOriginalValue = TestFunc(78); // is 73
        var normalInjectValue = InjectFunc(78); // is 28
        var dynamicValue = dynamicMethod.Invoke(null, new object[] { 78 }); // is 78


        // --- Get pointer of DynamicMethod ---
        var getDynamicHandle = Delegate.CreateDelegate(
            typeof(Func<DynamicMethod, RuntimeMethodHandle>),
            typeof(DynamicMethod).GetMethod("GetMethodDescriptor",(BindingFlags)(-1))
        ) as Func<DynamicMethod, RuntimeMethodHandle>;

        var handle = getDynamicHandle(dynamicMethod);
        var dynamicMethodPtr = handle.Value;


        // --- Get pointer of "normal" method ---
        var testFunc = typeof(Test).GetMethod("TestFunc", (BindingFlags)(-1));
        RuntimeHelpers.PrepareMethod(testFunc.MethodHandle);
        var normalTargetMethodPtr = testFunc.MethodHandle.Value;

        var injectFunc = typeof(Test).GetMethod("InjectFunc", (BindingFlags)(-1));
        RuntimeHelpers.PrepareMethod(injectFunc.MethodHandle);
        var normalInjectMethodPtr = injectFunc.MethodHandle.Value;


        // --- Swap "normal" method bodies ---
        SwapBodies(normalTargetMethodPtr, normalInjectMethodPtr);
        var injectedNormalValue = TestFunc(78); // is 28


        // --- Swap method bodies with a DynamicMethod ---
        // Should be 78 but throws an System.AccessViolationException
        SwapBodies(normalTargetMethodPtr, dynamicMethodPtr);
        var injectedDynamicMethodValue = TestFunc(78);
    }

    public static int TestFunc(int value)
    {
        return value - 5;
    }

    public static int InjectFunc(int value)
    {
        return value - 50;
    }

    private unsafe static void SwapBodies(IntPtr toReplace, IntPtr toInject)
    {
        // For simplicity here I only show the procedure for a debug run
        // and not a production run.
        if (IntPtr.Size == 4)
        {
            int* inj = (int*)toInject.ToPointer() + 2;
            int* tar = (int*)toReplace.ToPointer() + 2;

            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
        {
            long* inj = (long*)toInject.ToPointer() + 1;
            long* tar = (long*)toReplace.ToPointer() + 1;

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

Can someone help me to get it working to replace a method body with the body of a DynamicMethod?





Aucun commentaire:

Enregistrer un commentaire