Reputation: 467
I have read quite a bit about the possibilities of overwriting C#/.NET methods at runtime. After looking at some example code, there is one question that I am not able to answer. A very simple PoC for runtime method replacement could look like this:
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();
}
}
As one would expect, the call to A(0) and B(1)
now prints:
A: 0
A: 1
So far so good. However, I have also seen an example, where it seems that no differentiation is made between x86/x64 code:
...
int* inj = (int*)(a.MethodHandle.Value.ToPointer() + 8);
int* tar = (int*)(b.MethodHandle.Value.ToPointer() + 8);
...
In this case 8 is added to the pointer. Could somebody explain the reason behind this? Additionally, what exactly does this pointer offset mean? If somebody can recommend good reading materials about C#/.NET/CLR internals, please let me know.
Thanks
Upvotes: 2
Views: 280
Reputation: 382
The actual pointer to the code of the method is 8 bytes after the method handle address. In your first example, you first cast to int (which is either 4 or 8 bytes) and then need to make a stride of 8 bytes, so you either add 1 or 2 ints, depends on the architecture.
In your second example, you first make the stride, using a byte*, adding 8 to the pointer, and only then cast. BTW, the later example should read
...
int* inj = (int*)(a.MethodHandle.Value.ToPointer() + 8);
int* tar = (int*)(b.MethodHandle.Value.ToPointer() + 8);
...
Upvotes: 2