.NET CLR 运行时方法替换
.NET CLR Runtime Method Replacement
我已经阅读了很多有关在运行时覆盖 C#/.NET 方法的可能性的内容。在查看了一些示例代码后,有一个问题我无法回答。 非常 用于运行时方法替换的简单 PoC 可能如下所示:
class Program
{
public static void A(int x)
{
Console.WriteLine("A: " + x);
}
public static void B(int x)
{
Console.WriteLine("B: " + x);
}
internal static void Main(string[] args)
{
MethodInfo a = null;
MethodInfo b = null;
foreach(MethodInfo mi in typeof(Program).GetMethods())
{
if (mi.Name == "A" || mi.Name == "B")
{
// make sure methods are jitted
RuntimeHelpers.PrepareMethod(mi.MethodHandle);
}
if (mi.Name == "A") a = mi;
if (mi.Name == "B") b = mi;
}
unsafe
{
if (IntPtr.Size == 4) // x86
{
int* inj = (int*)a.MethodHandle.Value.ToPointer() + 2;
int* tar = (int*)b.MethodHandle.Value.ToPointer() + 2;
*tar = *inj;
}
else // x64
{
ulong* inj = (ulong*)a.MethodHandle.Value.ToPointer() + 1;
ulong* tar = (ulong*)b.MethodHandle.Value.ToPointer() + 1;
*tar = *inj;
}
}
Program.A(0);
Program.B(1);
Console.ReadLine();
}
}
正如人们所期望的那样,对 A(0) and B(1)
的调用现在打印:
A: 0
A: 1
到目前为止一切顺利。
但是,我也看到了一个例子,x86/x64 代码之间似乎没有区别:
...
int* inj = (int*)(a.MethodHandle.Value.ToPointer() + 8);
int* tar = (int*)(b.MethodHandle.Value.ToPointer() + 8);
...
在这种情况下,8 被添加到指针。有人可以解释这背后的原因吗?另外,这个指针偏移到底是什么意思?如果有人可以推荐有关 C#/.NET/CLR 内部原理的好的阅读材料,请告诉我。
谢谢
实际指向方法代码的指针在方法句柄地址后8 字节。
在您的第一个示例中,您首先转换为 int(4 或 8 个字节),然后需要跨 8 个字节,因此您可以添加 1 或 2 ints,具体取决于关于架构。
在您的第二个示例中,您首先使用 byte* 向指针添加 8,然后才强制转换。
顺便说一句,后面的例子应该是
...
int* inj = (int*)(a.MethodHandle.Value.ToPointer() + 8);
int* tar = (int*)(b.MethodHandle.Value.ToPointer() + 8);
...
我已经阅读了很多有关在运行时覆盖 C#/.NET 方法的可能性的内容。在查看了一些示例代码后,有一个问题我无法回答。 非常 用于运行时方法替换的简单 PoC 可能如下所示:
class Program
{
public static void A(int x)
{
Console.WriteLine("A: " + x);
}
public static void B(int x)
{
Console.WriteLine("B: " + x);
}
internal static void Main(string[] args)
{
MethodInfo a = null;
MethodInfo b = null;
foreach(MethodInfo mi in typeof(Program).GetMethods())
{
if (mi.Name == "A" || mi.Name == "B")
{
// make sure methods are jitted
RuntimeHelpers.PrepareMethod(mi.MethodHandle);
}
if (mi.Name == "A") a = mi;
if (mi.Name == "B") b = mi;
}
unsafe
{
if (IntPtr.Size == 4) // x86
{
int* inj = (int*)a.MethodHandle.Value.ToPointer() + 2;
int* tar = (int*)b.MethodHandle.Value.ToPointer() + 2;
*tar = *inj;
}
else // x64
{
ulong* inj = (ulong*)a.MethodHandle.Value.ToPointer() + 1;
ulong* tar = (ulong*)b.MethodHandle.Value.ToPointer() + 1;
*tar = *inj;
}
}
Program.A(0);
Program.B(1);
Console.ReadLine();
}
}
正如人们所期望的那样,对 A(0) and B(1)
的调用现在打印:
A: 0
A: 1
到目前为止一切顺利。 但是,我也看到了一个例子,x86/x64 代码之间似乎没有区别:
...
int* inj = (int*)(a.MethodHandle.Value.ToPointer() + 8);
int* tar = (int*)(b.MethodHandle.Value.ToPointer() + 8);
...
在这种情况下,8 被添加到指针。有人可以解释这背后的原因吗?另外,这个指针偏移到底是什么意思?如果有人可以推荐有关 C#/.NET/CLR 内部原理的好的阅读材料,请告诉我。
谢谢
实际指向方法代码的指针在方法句柄地址后8 字节。 在您的第一个示例中,您首先转换为 int(4 或 8 个字节),然后需要跨 8 个字节,因此您可以添加 1 或 2 ints,具体取决于关于架构。
在您的第二个示例中,您首先使用 byte* 向指针添加 8,然后才强制转换。 顺便说一句,后面的例子应该是
...
int* inj = (int*)(a.MethodHandle.Value.ToPointer() + 8);
int* tar = (int*)(b.MethodHandle.Value.ToPointer() + 8);
...