Created
January 31, 2024 12:34
-
-
Save jwdb/2586628177772f566c62e0ff289dba16 to your computer and use it in GitHub Desktop.
HijackProperty
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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