Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/306.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 地址操作码在哪里。Ret使用存储的地址?可以改变吗?_C#_Cil_Reflection.emit - Fatal编程技术网

C# 地址操作码在哪里。Ret使用存储的地址?可以改变吗?

C# 地址操作码在哪里。Ret使用存储的地址?可以改变吗?,c#,cil,reflection.emit,C#,Cil,Reflection.emit,有没有办法更改地址操作码。Ret跳转到?IL中的方法能否更改C#使用的调用堆栈 P>正如我在C++中所知道的,你可以访问堆栈上的值,并改变返回地址等。在IL中,我尝试访问当前方法之外的堆栈的所有操作都失败,出现异常“InvalidProgrameException” 我的示例代码: public static void Test2() { var ma = typeof(Program).GetMethod("Test2"); DynamicMe

有没有办法更改地址操作码。Ret跳转到?IL中的方法能否更改C#使用的调用堆栈

<> P>正如我在C++中所知道的,你可以访问堆栈上的值,并改变返回地址等。在IL中,我尝试访问当前方法之外的堆栈的所有操作都失败,出现异常“InvalidProgrameException”

我的示例代码:

    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编译为本机机器代码并执行。为某些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运行此操作,它将打印:

A before
B before
A after
A after
这表明我已成功地从
Main
→ <代码>A→ <代码>B→ <代码>C至
主代码
→ <代码>A→ <代码>A→ <代码>C

但当以释放模式运行时,当使用F5运行时,或者当我向
B
C
添加一个局部变量时,它会停止工作,这表明它是多么脆弱


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

谁说
ret
跳转到一个地址?操作码规范没有提到“跳转到地址”。这两个问题都不涉及C++规范。如果你可以在C++中实现这一点,那就是实现定义的行为。是的,它可能编译依赖。也许这种访问在C#中是不可能的。我不确定。但在内部,CLI必须在RET命令中存储方法完成后的去向。我正在寻找一种不应该在真实环境中使用的黑客技术;它使用堆栈的方式是一个实现细节,您无法安全地对它如何使用该数据结构做出任何假设。例如,假设您更改了返回地址,然后发生了安全堆栈遍历,或者发生了异常?CLR可能需要堆栈上的数据结构正确,才能正确执行遍历。不要改变它;如果您的意图是使用此方法调用另一种方法,而不保留调用堆栈上的当前帧,那么您可能需要考虑尾调用操作码前缀。说实话,我也不认为这里有什么教育上的好处。如果有人想知道调用堆栈/调用约定是如何工作的,那么托管代码是非常错误的使用媒介。答案很好:)因此IL不允许在这个级别上进行操作,但在编译成机器代码后,它的行为类似于C/C++。我认为这是有教育意义的:)对于正确的编程来说,这可能是一个糟糕的例子,但从安全角度来看,这是非常有趣的。