C# 地址操作码在哪里。Ret使用存储的地址?可以改变吗?
有没有办法更改地址操作码。Ret跳转到?IL中的方法能否更改C#使用的调用堆栈 <> P>正如我在C++中所知道的,你可以访问堆栈上的值,并改变返回地址等。在IL中,我尝试访问当前方法之外的堆栈的所有操作都失败,出现异常“InvalidProgrameException” 我的示例代码: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
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指令是有意义的,但它可能不是,例如,因为整个方法可能是内联的
要做到这一点,您可以尝试使用指针修改堆栈,因为x86ret
跳转到存储地址的位置,但这样做非常危险(您可以很容易地修改错误的内存),而且非常脆弱(因为它依赖于堆栈的布局,很容易更改)
作为示例,请查看以下代码:
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++。我认为这是有教育意义的:)对于正确的编程来说,这可能是一个糟糕的例子,但从安全角度来看,这是非常有趣的。