c# - OpCodes.Ret 使用的地址存储在哪里?可以改变吗?

标签 c# cil reflection.emit

有什么办法可以改变OpCodes.Ret跳转到的地址吗? IL 中的方法可以更改 C# 使用的调用堆栈吗?

据我所知,在 C++ 中,您可以只访问堆栈上的值并更改返回地址等。在 IL 中,我尝试访问当前方法之外的堆栈的所有内容都会失败,并出现异常“InvalidProgramException”。

我的示例代码:

    public static void Test2()
    {
        var ma = typeof(Program).GetMethod("Test2");
        DynamicMethod meth = new DynamicMethod("", null, null, ma.Module, true);
        ILGenerator il = meth.GetILGenerator();



        il.EmitWriteLine("Start to run the IL method");

        var t = il.DeclareLocal(typeof(int));



        //take the top of the stack (from the caller method) and put it into the variable t
        //this seems to be not possible?
        il.Emit(OpCodes.Stloc, t);

        //print the value from the stack
        il.EmitWriteLine(t);

        //load the value back from the field onto the stack to allow everything to preceed normally
        il.Emit(OpCodes.Ldloc, t);

        //return
        il.Emit(OpCodes.Ret);

        Action a = (Action)meth.CreateDelegate(typeof(Action));

        a();
    }

最佳答案

嗯,OpCodes.Ret IL 指令实际上并没有进行任何跳转。相反,IL 代码由 CLR 编译为 native 机器代码并执行。某些 IL 代码生成的机器代码取决于您的体系结构(x86、ARM 等),并且它是 CLR 的实现细节。

假设在 x86 上,ret IL 指令被编译为 ret x86 指令是有道理的,但事实可能并非如此,例如,因为整个方法可能是内联的。

为此,您可以尝试使用指针修改堆栈,因为这是存储 x86 ret 跳转到的地址的位置,但这样做非常危险(您很容易修改错误的内存)并且极其脆弱(因为它依赖于堆栈的布局,很容易改变)。

作为示例,请查看以下代码:

using System;

static class Program
{
    static void Main()
    {
        A();
    }

    static void A()
    {
        Console.WriteLine("A before");
        B();
        Console.WriteLine("A after");
    }

    static void B()
    {
        Console.WriteLine("B before");
        C();
        Console.WriteLine("B after");
    }

    static unsafe void C()
    {
        int local;

        int* localPointer = &local;

        localPointer[2] = localPointer[4];
    }
}

如果我在我的计算机上使用 Ctrl+F5 在 Debug模式下运行此,它会打印:

A before
B before
A after
A after

这表明我已经成功修改了从 MainABC 的调用堆栈到 MainAAC

但是当在 Release模式下运行时,当使用 F5 运行时或当我将局部变量添加到 BC 时,它会停止工作,可见这是多么脆弱。

所以,这是可以做到的,但是你永远不应该这样做(除非出于教育目的)。

关于c# - OpCodes.Ret 使用的地址存储在哪里?可以改变吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25778356/

相关文章:

c# - 我可以有一个带有通用列表的类并将其公开为默认值吗

c# - 干净利落地中断 HttpListener 的 BeginGetContext 方法

c# - 为什么 `dynamicMethod.CreateDelegate(typeof(Action)).Method.Invoke(null,new object[0]);` 抛出异常?

c# - Property Getter 的反射发射

c# - Microsoft.AspNetCore.Odata 配置日期时间序列化以使用 Utc 或省略 Timezone

c# - 在 C# 中从 FTP 中删除文件

.NET 错误 : ldtoken of an instantiated generic method

c# - 迭代变量如何只读?

c# - 通过 Emit(Opcodes.Call, methodinfo) 创建一个类型的实例

.net - 是否有工具可以简化 Reflection.Emit 的工作?