Skip to content

Instantly share code, notes, and snippets.

@jwdb
Created January 31, 2024 12:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jwdb/2586628177772f566c62e0ff289dba16 to your computer and use it in GitHub Desktop.
Save jwdb/2586628177772f566c62e0ff289dba16 to your computer and use it in GitHub Desktop.
HijackProperty
// Example Usage:
// class FakeDateTime {
// public DateTime UtcNow => DateTime.Parse("1970-01-01");
// }
//
// using (var hijack = new HijackProperty(typeof(DateTime), "UtcNow", typeof(FakeDateTime))) {
// Console.WriteLine($"It's currently: {DateTime.Now}");
// }
public class HijackProperty : IDisposable
{
private readonly IntPtr originalData;
private readonly IntPtr originalAddress;
// Hijacks any property and replaces it with the same property on another type
public HijackProperty(Type sourceType, string property, Type targetType)
{
var source = sourceType.GetProperty(property)?.GetMethod;
var target = targetType.GetProperty(property)?.GetMethod;
if (source == null || target == null)
{
return;
}
originalAddress = GetMethodAddress(source);
IntPtr tar = GetMethodAddress(target);
originalData = Marshal.ReadIntPtr(originalAddress);
Marshal.Copy(new[] { Marshal.ReadIntPtr(tar) }, 0, originalAddress, 1);
}
// Gets the memory pointer to the requested method by looking it up on the MethodTable
private static IntPtr GetMethodAddress(MethodInfo mi)
{
const ushort slotNumberMask = 0xffff;
const int mtOffset32Bit = 0x28;
const int mtOffset64Bit = 0x40;
IntPtr address;
RuntimeHelpers.PrepareMethod(mi.MethodHandle);
IntPtr md = mi.MethodHandle.Value;
IntPtr mt = mi.DeclaringType!.TypeHandle.Value;
if (mi.IsVirtual)
{
int offset = IntPtr.Size == 4 ? mtOffset32Bit : mtOffset64Bit;
IntPtr ms = Marshal.ReadIntPtr(mt + offset);
long shift = Marshal.ReadInt64(md) >> 32;
int slot = (int)(shift & slotNumberMask);
address = ms + slot * IntPtr.Size;
}
else
{
address = md + 8;
}
return address;
}
public void Dispose()
{
Marshal.Copy(new[] { originalData }, 0, originalAddress, 1);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment