Memory Violation Dynamically Appending to Methods at runtime

爷,独闯天下 提交于 2020-01-30 05:44:26

问题


Disclaimer: I'm doing this for learning purposes. This is not going to be used in code.

I'm trying to understand how method table are structure for generics, I want to dynamically appending to methods at runtime. I found a very useful stack overflow question reference for getting me started.

I have a simple controller which I'm using as a test to verify my methods are swapping:

public class ValuesController : ControllerBase
{
    static ValuesController() {
        var methodToReplace = typeof(ValuesController).GetMethod(nameof(ValuesController.Seven),
            BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);

        var methodToAppend = typeof(ValuesController).GetMethod(nameof(ValuesController.Eight),
            BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);

        new Initializer(methodToReplace, methodToAppend);
    }

    [HttpGet("Seven")]
    public int Seven(string id)
    {
        return 7;
    }

    [HttpGet("Eight")]
    public int Eight(string id)
    {
        return 8;
    }
}

I have a class Initializer which is in charge of handling appending to the method.

public class Initializer
{
    public Initializer(MethodInfo methodToReplace, MethodInfo methodToAppend)
    {
        var dummyMethod = typeof(Initializer).GetMethod(nameof(Dummy),
            BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);

        var proxyMethod = typeof(Initializer).GetMethod(nameof(Proxy),
            BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);

        var appendedMethod = typeof(Initializer).GetMethod(nameof(Appended),
            BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);

        dummyMethod.OneWayReplace(methodToReplace);
        methodToReplace.OneWayReplace(proxyMethod);
        appendedMethod.OneWayReplace(methodToAppend);
    }

    public int Proxy(string id)
    {
        Dummy(id);
        return Appended(id);
    }

    public int Dummy(string id)
    {
        return 0;
    }

    public int Appended(string id)
    {
        return 0;
    }
}

And then I have the Extensions which I've obtained from the original stackoverflow question:

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

        IntPtr tar = methodToReplace.MethodHandle.Value;
        var inj = methodToInject.MethodHandle.Value + 8;

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

Note: Using the current setup everything works fine. However, the second I change the Initializer class to be a generic class Initializer<T> I get a memory violation:

System.AccessViolationException: 'Attempted to read or write protected memory. This is often an indication that other memory is corrupt.'

My guess is that either the methodToReplace.DeclaringType.TypeHandle.Value calculation differs for generics, Or since the compiler is the one who generates the generic class it written to protected memory?

Edit I've found more information I need to prepare the method properly when using generic parameters e.g:

RuntimeHelpers.PrepareMethod(methodToInject.MethodHandle, new[] { typeof(T).TypeHandle });

However there are still a few more pieces to the puzzle to get this working.

Edit

There are a few open source project's such as harmony that do similar things, However it looks like their emitting their own assemblies. While I've considered the option, I would still prefer to understand how I method tables work with generics

How can I append to methods that reside in generic classes?

来源:https://stackoverflow.com/questions/59778004/memory-violation-dynamically-appending-to-methods-at-runtime

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!