C# 运行时动态附加到方法的内存冲突

C# 运行时动态附加到方法的内存冲突,c#,methods,dynamic,reflection,runtime,C#,Methods,Dynamic,Reflection,Runtime,免责声明:我这样做是为了学习。这不会在代码中使用 我试图理解泛型的方法表是如何构成的,我想在运行时动态地附加到方法。我发现了一个非常有用的参考资料,可以帮助我开始学习 我有一个简单的控制器,我正在使用它作为测试,以验证我的方法是否正在交换: public class ValuesController : ControllerBase { static ValuesController() { var methodToReplace = typeof(ValuesContr

免责声明:我这样做是为了学习。这不会在代码中使用

我试图理解泛型的方法表是如何构成的,我想在运行时动态地附加到方法。我发现了一个非常有用的参考资料,可以帮助我开始学习

我有一个简单的控制器,我正在使用它作为测试,以验证我的方法是否正在交换:

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;
    }
}
我有一个类
初始值设定项
,负责处理方法的附加

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;
    }
}
然后我得到了从原始stackoverflow问题得到的扩展:

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
    }
}
注意:使用当前设置,一切正常。但是,第二次我将
初始值设定项
类更改为泛型类
初始值设定项
我得到了内存冲突:

System.AccessViolationException:'尝试读取或写入受保护的内存。这通常表示其他内存已损坏。”

我的猜测是,泛型的
方法replace.DeclaringType.TypeHandle.Value
计算是不同的,还是因为编译器是生成泛型类并将其写入受保护内存的人

编辑 我发现在使用通用参数时,我需要更多信息来正确准备方法,例如:

RuntimeHelpers.PrepareMethod(methodToInject.MethodHandle, new[] { typeof(T).TypeHandle });
然而,仍然有一些问题需要解决,以使其发挥作用

编辑

有一些开源项目,比如做类似的事情的项目,但是看起来它们发出自己的程序集。虽然我已经考虑过这个选项,但我仍然希望了解我的方法表是如何与泛型一起工作的


如何将附加到驻留在泛型类中的方法?

我想您已经看到了:

我在自己的项目中采用了其中一些方法@

我认为问题在于,如果您在运行时附加了调试器,那么您还需要在编译时处理当前由
IFDEF
定义的逻辑部分,并将其替换为
System.Diagnostics.Debugger.IsAttached
,尽管(为了跳过调试器注入的代码)可能必须根据各种情况进行更改,如使用的框架版本

在.Net Core 3.1中,当未连接调试器且我在发布模式下运行时,在调试模式下运行(无论是否连接调试器)或在发布模式下运行(无论是否连接调试器),我都会收到不同的异常。(在调试中,我会收到算术溢出,而在发布中,我会收到执行引擎异常)

此外,这只在JIT分层开始之前有效,如果我第二次运行该方法而没有附加调试器,我将得到一个内部CLR错误

我相信这与附加时调试器注入的代码有关,老实说,我没有掌握附加时调试器注入的确切内容


我会对这个问题做一个简化的回复,并问一个问题@如果你需要这个来与附带的调试器一起工作,我相信那里的人会给你指引正确的方向。

DeclaringType
在这种情况下是泛型的,对吗?你需要像这样重建方法签名吗?@timur,DeclaringType是泛型的,但是,该方法没有对泛型类型的引用。泛型显然对方法交换有一些影响。但是我没有足够的信息来理解,我猜声明类型会修改类start的地址计算。因此我正在写入受保护内存,因为我正在覆盖内存屏障。我是我对你重建方法签名的意思有点困惑,我不是通过反射调用方法,我只是交换指针。我通常在调试模式下运行,附加了调试器,我发布的代码工作正常。只有当我将初始化器切换为泛型时,它才会中断,或者如果我尝试交换泛型方法,我会这是因为泛型是如何在引擎盖下完成的。你是说这将在发布模式下工作吗?这可能至少是第一次。