using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; using System.Dynamic; using System.Globalization; using System.IO; using System.Linq; using System.Linq.Expressions; using System.Net; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Threading; using Mono.Cecil; using Mono.Cecil.Cil; using Mono.Collections.Generic; using MonoMod.Cil; using MonoMod.RuntimeDetour.HookGen; using MonoMod.RuntimeDetour.Platforms; using MonoMod.Utils; using MonoMod.Utils.Cil; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETCoreApp,Version=v5.0", FrameworkDisplayName = "")] [assembly: AssemblyCompany("0x0ade")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("Copyright 2022 0x0ade")] [assembly: AssemblyDescription("Flexible and easily extensible runtime detouring library. Wrap, replace and manipulate (Mono.Cecil) methods at runtime.")] [assembly: AssemblyFileVersion("22.5.1.1")] [assembly: AssemblyInformationalVersion("22.05.01.01")] [assembly: AssemblyProduct("MonoMod.RuntimeDetour")] [assembly: AssemblyTitle("MonoMod.RuntimeDetour")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("22.5.1.1")] [module: UnverifiableCode] internal static class MultiTargetShims { private static readonly object[] _NoArgs = new object[0]; public static string Replace(this string self, string oldValue, string newValue, StringComparison comparison) { return self.Replace(oldValue, newValue); } public static bool Contains(this string self, string value, StringComparison comparison) { return self.Contains(value); } public static int GetHashCode(this string self, StringComparison comparison) { return self.GetHashCode(); } public static int IndexOf(this string self, char value, StringComparison comparison) { return self.IndexOf(value); } public static int IndexOf(this string self, string value, StringComparison comparison) { return self.IndexOf(value); } public static Module[] GetModules(this Assembly asm) { return asm.Modules.ToArray(); } public static Module GetModule(this Assembly asm, string name) { return asm.Modules.FirstOrDefault((Module module) => module.Name == name); } public static byte[] GetBuffer(this MemoryStream ms) { long position = ms.Position; byte[] array = new byte[ms.Length]; ms.Read(array, 0, array.Length); ms.Position = position; return array; } public static TypeReference GetConstraintType(this GenericParameterConstraint constraint) { return constraint.ConstraintType; } } namespace MonoMod { internal static class MMDbgLog { public static readonly string Tag; public static TextWriter Writer; public static bool Debugging; static MMDbgLog() { Tag = typeof(MMDbgLog).Assembly.GetName().Name; if (Environment.GetEnvironmentVariable("MONOMOD_DBGLOG") == "1" || (Environment.GetEnvironmentVariable("MONOMOD_DBGLOG")?.ToLower(CultureInfo.InvariantCulture)?.Contains(Tag.ToLower(CultureInfo.InvariantCulture), StringComparison.Ordinal)).GetValueOrDefault()) { Start(); } } public static void WaitForDebugger() { if (!Debugging) { Debugging = true; Debugger.Launch(); Thread.Sleep(6000); Debugger.Break(); } } public static void Start() { if (Writer != null) { return; } string text = Environment.GetEnvironmentVariable("MONOMOD_DBGLOG_PATH"); if (text == "-") { Writer = Console.Out; return; } if (string.IsNullOrEmpty(text)) { text = "mmdbglog.txt"; } text = Path.GetFullPath(Path.GetFileNameWithoutExtension(text) + "-" + Tag + Path.GetExtension(text)); try { if (File.Exists(text)) { File.Delete(text); } } catch { } try { string directoryName = Path.GetDirectoryName(text); if (!Directory.Exists(directoryName)) { Directory.CreateDirectory(directoryName); } Writer = new StreamWriter(new FileStream(text, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite | FileShare.Delete), Encoding.UTF8); } catch { } } public static void Log(string str) { TextWriter writer = Writer; if (writer != null) { writer.WriteLine(str); writer.Flush(); } } public static T Log(string str, T value) { TextWriter writer = Writer; if (writer == null) { return value; } writer.WriteLine(string.Format(CultureInfo.InvariantCulture, str, value)); writer.Flush(); return value; } } } namespace MonoMod.RuntimeDetour { public struct DetourConfig { public bool ManualApply; public int Priority; public string ID; public IEnumerable Before; public IEnumerable After; } public class Detour : ISortableDetour, IDetour, IDisposable { private static ConcurrentDictionary> _DetourMap = new ConcurrentDictionary>((IEqualityComparer?)new GenericMethodInstantiationComparer()); private static Dictionary _BackupMethods = new Dictionary(); private static uint _GlobalIndexNext = 0u; public static Func OnDetour; public static Func OnUndo; public static Func OnGenerateTrampoline; private readonly uint _GlobalIndex; private int _Priority; private string _ID; private List _Before = new List(); private ReadOnlyCollection _BeforeRO; private List _After = new List(); private ReadOnlyCollection _AfterRO; public readonly MethodBase Method; public readonly MethodBase Target; public readonly MethodBase TargetReal; private NativeDetour _TopDetour; private MethodInfo _ChainedTrampoline; private static int compileMethodSubscribed = 0; private List _DetourChain { get { if (!_DetourMap.TryGetValue(Method, out var value)) { return null; } return value; } } public bool IsValid => Index != -1; public bool IsApplied { get; private set; } private bool IsTop => _TopDetour != null; public int Index => _DetourChain?.IndexOf(this) ?? (-1); public int MaxIndex => _DetourChain?.Count ?? (-1); public uint GlobalIndex => _GlobalIndex; public int Priority { get { return _Priority; } set { if (_Priority != value) { _Priority = value; _RefreshChain(Method); } } } public string ID { get { return _ID; } set { if (string.IsNullOrEmpty(value)) { value = Extensions.GetID(Target, (string)null, (string)null, true, false, true); } if (!(_ID == value)) { _ID = value; _RefreshChain(Method); } } } public IEnumerable Before { get { return _BeforeRO ?? (_BeforeRO = _Before.AsReadOnly()); } set { lock (_Before) { _Before.Clear(); if (value != null) { foreach (string item in value) { _Before.Add(item); } } _RefreshChain(Method); } } } public IEnumerable After { get { return _AfterRO ?? (_AfterRO = _After.AsReadOnly()); } set { lock (_After) { _After.Clear(); if (value != null) { foreach (string item in value) { _After.Add(item); } } _RefreshChain(Method); } } } public Detour(MethodBase from, MethodBase to, ref DetourConfig config) { //IL_0264: Unknown result type (might be due to invalid IL or missing references) //IL_026b: Expected O, but got Unknown from = from.GetIdentifiable(); if (from.Equals(to)) { throw new ArgumentException("Cannot detour a method to itself!"); } MMDbgLog.Log("detour from " + Extensions.GetID(from, (string)null, (string)null, true, false, false) + " to " + Extensions.GetID(to, (string)null, (string)null, true, false, false)); Method = from; Target = to.Pin(); TargetReal = DetourHelper.Runtime.GetDetourTarget(from, to); _GlobalIndex = _GlobalIndexNext++; _Priority = config.Priority; _ID = config.ID; if (config.Before != null) { foreach (string item in config.Before) { _Before.Add(item); } } if (config.After != null) { foreach (string item2 in config.After) { _After.Add(item2); } } lock (_BackupMethods) { if ((!_BackupMethods.TryGetValue(Method, out var value) || value == null) && (value = Method.CreateILCopy()) != null) { _BackupMethods[Method] = value.Pin(); } } ParameterInfo[] parameters = Method.GetParameters(); Type[] array; if (!Method.IsStatic) { array = new Type[parameters.Length + 1]; array[0] = Extensions.GetThisParamType(Method); for (int i = 0; i < parameters.Length; i++) { array[i + 1] = parameters[i].ParameterType; } } else { array = new Type[parameters.Length]; for (int j = 0; j < parameters.Length; j++) { array[j] = parameters[j].ParameterType; } } DynamicMethodDefinition val = new DynamicMethodDefinition($"Chain<{Extensions.GetID(Method, (string)null, (string)null, true, false, true)}>?{GetHashCode()}", (Method as MethodInfo)?.ReturnType ?? typeof(void), array); try { _ChainedTrampoline = val.StubCriticalDetour().Generate().Pin(); } finally { ((IDisposable)val)?.Dispose(); } List orAdd = _DetourMap.GetOrAdd(Method, (MethodBase m) => new List()); lock (orAdd) { orAdd.Add(this); } if (!config.ManualApply) { Apply(); } } public Detour(MethodBase from, MethodBase to, DetourConfig config) : this(from, to, ref config) { } public Detour(MethodBase from, MethodBase to) : this(from, to, DetourContext.Current?.DetourConfig ?? default(DetourConfig)) { } public Detour(MethodBase method, IntPtr to, ref DetourConfig config) : this(method, DetourHelper.GenerateNativeProxy(to, method), ref config) { } public Detour(MethodBase method, IntPtr to, DetourConfig config) : this(method, DetourHelper.GenerateNativeProxy(to, method), ref config) { } public Detour(MethodBase method, IntPtr to) : this(method, DetourHelper.GenerateNativeProxy(to, method)) { } public Detour(Delegate from, IntPtr to, ref DetourConfig config) : this(from.Method, to, ref config) { } public Detour(Delegate from, IntPtr to, DetourConfig config) : this(from.Method, to, ref config) { } public Detour(Delegate from, IntPtr to) : this(from.Method, to) { } public Detour(Delegate from, Delegate to, ref DetourConfig config) : this(from.Method, to.Method, ref config) { } public Detour(Delegate from, Delegate to, DetourConfig config) : this(from.Method, to.Method, ref config) { } public Detour(Delegate from, Delegate to) : this(from.Method, to.Method) { } public Detour(Expression from, IntPtr to, ref DetourConfig config) : this(((MethodCallExpression)from).Method, to, ref config) { } public Detour(Expression from, IntPtr to, DetourConfig config) : this(((MethodCallExpression)from).Method, to, ref config) { } public Detour(Expression from, IntPtr to) : this(((MethodCallExpression)from).Method, to) { } public Detour(Expression from, Expression to, ref DetourConfig config) : this(((MethodCallExpression)from).Method, ((MethodCallExpression)to).Method, ref config) { } public Detour(Expression from, Expression to, DetourConfig config) : this(((MethodCallExpression)from).Method, ((MethodCallExpression)to).Method, ref config) { } public Detour(Expression from, Expression to) : this(((MethodCallExpression)from).Method, ((MethodCallExpression)to).Method) { } public Detour(Expression from, IntPtr to, ref DetourConfig config) : this(from.Body, to, ref config) { } public Detour(Expression from, IntPtr to, DetourConfig config) : this(from.Body, to, ref config) { } public Detour(Expression from, IntPtr to) : this(from.Body, to) { } public Detour(Expression from, Expression to, ref DetourConfig config) : this(from.Body, to.Body, ref config) { } public Detour(Expression from, Expression to, DetourConfig config) : this(from.Body, to.Body, ref config) { } public Detour(Expression from, Expression to) : this(from.Body, to.Body) { } public void Apply() { if (!IsValid) { throw new ObjectDisposedException("Detour"); } if (!IsApplied) { Func onDetour = OnDetour; if (onDetour == null || Extensions.InvokeWhileTrue((MulticastDelegate)onDetour, new object[3] { this, Method, Target })) { IsApplied = true; _RefreshChain(Method); } } } public void Undo() { if (!IsValid) { throw new ObjectDisposedException("Detour"); } if (IsApplied) { Func onUndo = OnUndo; if (onUndo == null || Extensions.InvokeWhileTrue((MulticastDelegate)onUndo, new object[1] { this })) { IsApplied = false; _RefreshChain(Method); } } } public void Free() { if (!IsValid) { return; } Undo(); List detourChain = _DetourChain; lock (detourChain) { detourChain.Remove(this); if (detourChain.Count == 0) { lock (_BackupMethods) { if (_BackupMethods.TryGetValue(Method, out var value)) { value.Unpin(); _BackupMethods.Remove(Method); } } _DetourMap.TryRemove(Method, out var _); } } _ChainedTrampoline.Unpin(); Target.Unpin(); } public void Dispose() { if (IsValid) { Undo(); Free(); } } public MethodBase GenerateTrampoline(MethodBase signature = null) { //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Expected O, but got Unknown //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) Func onGenerateTrampoline = OnGenerateTrampoline; MethodBase methodBase = ((onGenerateTrampoline != null) ? Extensions.InvokeWhileNull((MulticastDelegate)onGenerateTrampoline, new object[2] { this, signature }) : null); if (methodBase != null) { return methodBase; } if (signature == null) { signature = Target; } Type type = (signature as MethodInfo)?.ReturnType ?? typeof(void); ParameterInfo[] parameters = signature.GetParameters(); Type[] array = new Type[parameters.Length]; for (int i = 0; i < parameters.Length; i++) { array[i] = parameters[i].ParameterType; } DynamicMethodDefinition val = new DynamicMethodDefinition($"Trampoline<{Extensions.GetID(Method, (string)null, (string)null, true, false, true)}>?{GetHashCode()}", type, array); try { ILProcessor iLProcessor = val.GetILProcessor(); for (int j = 0; j < 32; j++) { iLProcessor.Emit(OpCodes.Nop); } for (int k = 0; k < array.Length; k++) { iLProcessor.Emit(OpCodes.Ldarg, k); } Extensions.Emit(iLProcessor, OpCodes.Call, (MethodBase)_ChainedTrampoline); iLProcessor.Emit(OpCodes.Ret); return val.Generate(); } finally { ((IDisposable)val)?.Dispose(); } } public T GenerateTrampoline() where T : Delegate { if (!typeof(Delegate).IsAssignableFrom(typeof(T))) { throw new InvalidOperationException($"Type {typeof(T)} not a delegate type."); } return Extensions.CreateDelegate(GenerateTrampoline(typeof(T).GetMethod("Invoke")), typeof(T)) as T; } private void _TopUndo() { if (_TopDetour != null) { _TopDetour.Undo(); _TopDetour.Free(); _TopDetour = null; Method.Unpin(); TargetReal.Unpin(); } } private void _TopApply() { if (_TopDetour == null) { _TopDetour = new NativeDetour(Method.Pin().GetNativeStart(), TargetReal.Pin().GetNativeStart()); } } private static void _OnCompileMethod(MethodBase method, IntPtr codeStart, ulong codeLen) { if (method == null) { return; } MMDbgLog.Log("compiling: " + Extensions.GetID(method, (string)null, (string)null, true, false, false)); if (_DetourMap.TryGetValue(method, out var value)) { value.FindLast((Detour d) => d.IsTop)?._TopDetour?.ChangeSource(codeStart); } } private static void _RefreshChain(MethodBase method) { if (Interlocked.CompareExchange(ref compileMethodSubscribed, 1, 0) == 0) { DetourHelper.Runtime.OnMethodCompiled += _OnCompileMethod; } MMDbgLog.Log("detours applying for " + Extensions.GetID(method, (string)null, (string)null, true, false, false)); List list = _DetourMap[method]; lock (list) { DetourSorter.Sort(list); Detour detour = list.FindLast((Detour d) => d.IsTop); Detour detour2 = list.FindLast((Detour d) => d.IsApplied); if (detour != detour2) { detour?._TopUndo(); } if (list.Count == 0) { return; } MethodBase method2 = _BackupMethods[method]; foreach (Detour item in list) { if (item.IsApplied) { _ = item._ChainedTrampoline; using (NativeDetour nativeDetour = new NativeDetour(item._ChainedTrampoline.GetNativeStart(), method2.GetNativeStart())) { nativeDetour.Free(); } method2 = item.Target; } } if (detour != detour2) { detour2?._TopApply(); } } } } public class Detour : Detour where T : Delegate { public Detour(T from, IntPtr to, ref DetourConfig config) : base(from, to, ref config) { } public Detour(T from, IntPtr to, DetourConfig config) : base(from, to, ref config) { } public Detour(T from, IntPtr to) : base(from, to) { } public Detour(T from, T to, ref DetourConfig config) : base(from, to, ref config) { } public Detour(T from, T to, DetourConfig config) : base(from, to, ref config) { } public Detour(T from, T to) : base(from, to) { } } public sealed class DetourContext : IDisposable { [ThreadStatic] private static List _Contexts; [ThreadStatic] private static DetourContext Last; private MethodBase Creator; public int Priority; private readonly string _FallbackID; private string _ID; public List Before = new List(); public List After = new List(); private bool IsDisposed; private static List Contexts => _Contexts ?? (_Contexts = new List()); internal static DetourContext Current { get { DetourContext last = Last; if (last != null && last.IsValid) { return Last; } List contexts = Contexts; int num = contexts.Count - 1; while (num > -1) { DetourContext detourContext = contexts[num]; if (!detourContext.IsValid) { contexts.RemoveAt(num); num--; continue; } return Last = detourContext; } return null; } } public string ID { get { return _ID ?? _FallbackID; } set { _ID = (string.IsNullOrEmpty(value) ? null : value); } } public DetourConfig DetourConfig { get { DetourConfig result = default(DetourConfig); result.Priority = Priority; result.ID = ID; result.Before = Before; result.After = After; return result; } } public HookConfig HookConfig { get { HookConfig result = default(HookConfig); result.Priority = Priority; result.ID = ID; result.Before = Before; result.After = After; return result; } } public ILHookConfig ILHookConfig { get { ILHookConfig result = default(ILHookConfig); result.Priority = Priority; result.ID = ID; result.Before = Before; result.After = After; return result; } } internal bool IsValid { get { if (IsDisposed) { return false; } if (Creator == null) { return true; } StackTrace stackTrace = new StackTrace(); int frameCount = stackTrace.FrameCount; for (int i = 0; i < frameCount; i++) { if (stackTrace.GetFrame(i).GetMethod() == Creator) { return true; } } return false; } } public DetourContext(int priority, string id) { StackTrace stackTrace = new StackTrace(); int frameCount = stackTrace.FrameCount; for (int i = 0; i < frameCount; i++) { MethodBase method = stackTrace.GetFrame(i).GetMethod(); if (!(method?.DeclaringType == typeof(DetourContext))) { Creator = method; break; } } object obj = Creator?.DeclaringType?.Assembly?.GetName().Name; if (obj == null) { MethodBase creator = Creator; obj = (((object)creator != null) ? Extensions.GetID(creator, (string)null, (string)null, true, false, true) : null); } _FallbackID = (string)obj; Last = this; Contexts.Add(this); Priority = priority; ID = id; } public DetourContext(string id) : this(0, id) { } public DetourContext(int priority) : this(priority, null) { } public DetourContext() : this(0, null) { } public void Dispose() { if (IsDisposed) { IsDisposed = true; Last = null; Contexts.Remove(this); } } } public sealed class DetourModManager : IDisposable { private readonly Dictionary DetourOwners = new Dictionary(); private readonly Dictionary> OwnedDetourLists = new Dictionary>(); public HashSet Ignored = new HashSet(); private bool Disposed; private static readonly string[] HookTypeNames = new string[4] { "MonoMod.RuntimeDetour.NativeDetour", "MonoMod.RuntimeDetour.Detour", "MonoMod.RuntimeDetour.Hook", "MonoMod.RuntimeDetour.ILHook" }; public event Action OnILHook; public event Action OnHook; public event Action OnDetour; public event Action OnNativeDetour; public DetourModManager() { Ignored.Add(typeof(DetourModManager).Assembly); ILHook.OnDetour = (Func)Delegate.Combine(ILHook.OnDetour, new Func(RegisterILHook)); ILHook.OnUndo = (Func)Delegate.Combine(ILHook.OnUndo, new Func(UnregisterDetour)); Hook.OnDetour = (Func)Delegate.Combine(Hook.OnDetour, new Func(RegisterHook)); Hook.OnUndo = (Func)Delegate.Combine(Hook.OnUndo, new Func(UnregisterDetour)); Detour.OnDetour = (Func)Delegate.Combine(Detour.OnDetour, new Func(RegisterDetour)); Detour.OnUndo = (Func)Delegate.Combine(Detour.OnUndo, new Func(UnregisterDetour)); NativeDetour.OnDetour = (Func)Delegate.Combine(NativeDetour.OnDetour, new Func(RegisterNativeDetour)); NativeDetour.OnUndo = (Func)Delegate.Combine(NativeDetour.OnUndo, new Func(UnregisterDetour)); } public void Dispose() { if (!Disposed) { Disposed = true; OwnedDetourLists.Clear(); ILHook.OnDetour = (Func)Delegate.Remove(ILHook.OnDetour, new Func(RegisterILHook)); ILHook.OnUndo = (Func)Delegate.Remove(ILHook.OnUndo, new Func(UnregisterDetour)); Hook.OnDetour = (Func)Delegate.Remove(Hook.OnDetour, new Func(RegisterHook)); Hook.OnUndo = (Func)Delegate.Remove(Hook.OnUndo, new Func(UnregisterDetour)); Detour.OnDetour = (Func)Delegate.Remove(Detour.OnDetour, new Func(RegisterDetour)); Detour.OnUndo = (Func)Delegate.Remove(Detour.OnUndo, new Func(UnregisterDetour)); NativeDetour.OnDetour = (Func)Delegate.Remove(NativeDetour.OnDetour, new Func(RegisterNativeDetour)); NativeDetour.OnUndo = (Func)Delegate.Remove(NativeDetour.OnUndo, new Func(UnregisterDetour)); } } public void Unload(Assembly asm) { if (asm == null || Ignored.Contains(asm)) { return; } HookEndpointManager.RemoveAllOwnedBy(asm); if (OwnedDetourLists.TryGetValue(asm, out var value)) { IDetour[] array = value.ToArray(); for (int i = 0; i < array.Length; i++) { array[i].Dispose(); } if (value.Count > 0) { throw new Exception("Some detours failed to unregister in " + asm.FullName); } OwnedDetourLists.Remove(asm); } } internal Assembly GetHookOwner(StackTrace stack = null) { if (stack == null) { stack = new StackTrace(); } Assembly assembly = null; int frameCount = stack.FrameCount; string text = null; for (int i = 0; i < frameCount; i++) { MethodBase method = stack.GetFrame(i).GetMethod(); if (method?.DeclaringType == null) { continue; } string fullName = method.DeclaringType.FullName; if (text == null) { if (HookTypeNames.Contains(fullName)) { text = method.DeclaringType.FullName; } } else if (!(fullName == text)) { assembly = method?.DeclaringType.Assembly; break; } } if (Ignored.Contains(assembly)) { return null; } return assembly; } internal void TrackDetour(Assembly owner, IDetour detour) { if (!OwnedDetourLists.TryGetValue(owner, out var value)) { value = (OwnedDetourLists[owner] = new List()); } value.Add(detour); DetourOwners[detour] = owner; } internal bool RegisterILHook(ILHook _detour, MethodBase from, Manipulator manipulator) { Assembly hookOwner = GetHookOwner(); if (hookOwner == null) { return true; } this.OnILHook?.Invoke(hookOwner, from, manipulator); TrackDetour(hookOwner, _detour); return true; } internal bool RegisterHook(Hook _detour, MethodBase from, MethodBase to, object target) { Assembly hookOwner = GetHookOwner(); if (hookOwner == null) { return true; } this.OnHook?.Invoke(hookOwner, from, to, target); TrackDetour(hookOwner, _detour); return true; } internal bool RegisterDetour(Detour _detour, MethodBase from, MethodBase to) { Assembly hookOwner = GetHookOwner(); if (hookOwner == null) { return true; } this.OnDetour?.Invoke(hookOwner, from, to); TrackDetour(hookOwner, _detour); return true; } internal bool RegisterNativeDetour(NativeDetour _detour, MethodBase method, IntPtr from, IntPtr to) { Assembly hookOwner = GetHookOwner(); if (hookOwner == null) { return true; } this.OnNativeDetour?.Invoke(hookOwner, method, from, to); TrackDetour(hookOwner, _detour); return true; } internal bool UnregisterDetour(IDetour _detour) { if (DetourOwners.TryGetValue(_detour, out var value)) { DetourOwners.Remove(_detour); OwnedDetourLists[value].Remove(_detour); } return true; } } public sealed class DynamicHookGen : DynamicObject { private enum ActionType { Add, Remove } public enum HookType { OnOrIL, On, IL } private DynamicHookGen Parent; private string Name; private Type Type; private HookType NodeHookType; public static dynamic On = new DynamicHookGen(HookType.On); public static dynamic IL = new DynamicHookGen(HookType.IL); public static dynamic OnOrIL = new DynamicHookGen(HookType.OnOrIL); private int OwnLendID; private int NextLendID; private List> Actions = new List>(); private string Path { get { if (Parent?.Name == null) { return Name; } List list = new List(); DynamicHookGen dynamicHookGen = this; while (dynamicHookGen != null && dynamicHookGen.Name != null) { list.Add(dynamicHookGen.Name); dynamicHookGen = dynamicHookGen.Parent; } list.Reverse(); return string.Join(".", list); } } private DynamicHookGen(HookType hookType) { NodeHookType = hookType; } private DynamicHookGen(DynamicHookGen parent, string name) { Parent = parent; Name = name; NodeHookType = parent.NodeHookType; OwnLendID = parent.NextLendID++; } private DynamicHookGen(DynamicHookGen source) { Parent = source.Parent; Name = source.Name; NodeHookType = source.NodeHookType; OwnLendID = source.OwnLendID; Actions.AddRange(source.Actions); } public DynamicHookGen(Type type) : this(type, HookType.OnOrIL) { } public DynamicHookGen(Type type, HookType hookType) { Name = type.FullName; Type = type; NodeHookType = hookType; } private void Apply() { string path = Parent.Path; Type type = Parent.Type ?? ReflectionHelper.GetType(path); if (type == null) { throw new ArgumentException("Couldn't find type " + path); } MethodBase methodBase = null; methodBase = ((!(Name == "ctor")) ? ((MethodBase)type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == Name)) : ((MethodBase)type.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault())); if (methodBase == null) { throw new ArgumentException("Couldn't find method " + path + "::" + Name); } foreach (Tuple action in Actions) { Delegate item = action.Item2; HookType hookType = NodeHookType; if (hookType == HookType.OnOrIL) { MethodInfo? method = item.GetType().GetMethod("Invoke"); ParameterInfo[] parameters = method.GetParameters(); hookType = ((!(method.ReturnType == typeof(void)) || parameters.Length != 1 || !Extensions.IsCompatible(parameters[0].ParameterType, typeof(ILContext)) || parameters[0].IsOut) ? HookType.On : HookType.IL); } switch (action.Item1) { case ActionType.Add: if (hookType == HookType.IL) { HookEndpointManager.Modify(methodBase, item); } else { HookEndpointManager.Add(methodBase, item); } break; case ActionType.Remove: if (hookType == HookType.IL) { HookEndpointManager.Unmodify(methodBase, item); } else { HookEndpointManager.Remove(methodBase, item); } break; } } Actions.Clear(); } public override bool TryInvoke(InvokeBinder binder, object[] args, out object result) { if (args.Length != 1 || !(args[0] is Type type)) { throw new ArgumentException("Expected type."); } result = new DynamicHookGen(type, NodeHookType); return true; } public override bool TryGetMember(GetMemberBinder binder, out object result) { result = new DynamicHookGen(this, binder.Name); return true; } public override bool TrySetMember(SetMemberBinder binder, object value) { DynamicHookGen obj = (value as DynamicHookGen) ?? throw new ArgumentException("Incompatible dynamic hooks type. Did you use += / -= properly?"); if (obj.Parent != this) { throw new ArgumentException("Dynamic hooks target parent not matching."); } if (obj.Name != binder.Name) { throw new ArgumentException("Dynamic hooks target name not matching."); } if (obj.OwnLendID != NextLendID++ - 1) { throw new ArgumentException("Dynamic hooks object expired."); } obj.Apply(); return true; } public static DynamicHookGen operator +(DynamicHookGen ctx, Delegate target) { ctx = new DynamicHookGen(ctx); ctx.Actions.Add(new Tuple(ActionType.Add, target)); return ctx; } public static DynamicHookGen operator -(DynamicHookGen ctx, Delegate target) { ctx = new DynamicHookGen(ctx); ctx.Actions.Add(new Tuple(ActionType.Remove, target)); return ctx; } } public static class HarmonyDetourBridge { public enum Type { Auto, Basic, AsOriginal, Override } private class DetourToRDAttribute : Attribute { public string Type { get; } public int SkipParams { get; } public string Name { get; } public DetourToRDAttribute(string type, int skipParams = 0, string name = null) { Type = type; SkipParams = skipParams; Name = name; } } private class DetourToHAttribute : Attribute { public string Type { get; } public int SkipParams { get; } public string Name { get; } public DetourToHAttribute(string type, int skipParams = 0, string name = null) { Type = type; SkipParams = skipParams; Name = name; } } private class TranspileAttribute : Attribute { public string Type { get; } public string Name { get; } public TranspileAttribute(string type, string name = null) { Type = type; Name = name; } } private class CriticalAttribute : Attribute { } private static Type CurrentType; private static Assembly _HarmonyASM; private static readonly HashSet _Detours; private static readonly Dictionary _Emitters; [ThreadStatic] private static DynamicMethodDefinition _LastWrapperDMD; private static Assembly _SharedStateASM; private static DetourConfig _DetourConfig; public static bool Initialized { get; private set; } static HarmonyDetourBridge() { _Detours = new HashSet(); _Emitters = new Dictionary(); System.Type typeFromHandle = typeof(OpCode); System.Type proxyType = ILGeneratorShim.GetProxyType(); MethodInfo[] methods = proxyType.GetMethods(); foreach (MethodInfo methodInfo in methods) { if (methodInfo.Name != "Emit") { continue; } ParameterInfo[] parameters = methodInfo.GetParameters(); if (parameters.Length == 2 && !(parameters[0].ParameterType != typeFromHandle)) { System.Type parameterType = parameters[1].ParameterType; if (!_Emitters.ContainsKey(parameterType) || !(methodInfo.DeclaringType != proxyType)) { _Emitters[parameterType] = methodInfo; } } } } public static bool Init(bool forceLoad = true, Type type = Type.Auto) { //IL_0236: Unknown result type (might be due to invalid IL or missing references) //IL_023d: Expected O, but got Unknown //IL_0247: Unknown result type (might be due to invalid IL or missing references) //IL_024c: Unknown result type (might be due to invalid IL or missing references) //IL_0259: Expected O, but got Unknown if (_HarmonyASM == null) { _HarmonyASM = _FindHarmony(); } if (_HarmonyASM == null && forceLoad) { _HarmonyASM = Assembly.Load(new AssemblyName { Name = "0Harmony" }); } if (_HarmonyASM == null) { return false; } if (Initialized) { return true; } Initialized = true; if (type == Type.Auto) { type = Type.AsOriginal; } DetourConfig detourConfig = default(DetourConfig); detourConfig.Priority = type switch { Type.Override => 536870911, Type.AsOriginal => -536870912, _ => 0, }; _DetourConfig = detourConfig; CurrentType = type; try { MethodInfo[] methods = typeof(HarmonyDetourBridge).GetMethods(BindingFlags.Static | BindingFlags.NonPublic); foreach (MethodInfo methodInfo in methods) { bool flag = methodInfo.GetCustomAttributes(typeof(CriticalAttribute), inherit: false).Any(); object[] customAttributes = methodInfo.GetCustomAttributes(typeof(DetourToRDAttribute), inherit: false); for (int j = 0; j < customAttributes.Length; j++) { DetourToRDAttribute detourToRDAttribute = (DetourToRDAttribute)customAttributes[j]; foreach (MethodInfo item in GetHarmonyMethod(methodInfo, detourToRDAttribute.Type, detourToRDAttribute.SkipParams, detourToRDAttribute.Name)) { flag = false; _Detours.Add(new Hook(item, methodInfo)); } } customAttributes = methodInfo.GetCustomAttributes(typeof(DetourToHAttribute), inherit: false); for (int j = 0; j < customAttributes.Length; j++) { DetourToHAttribute detourToHAttribute = (DetourToHAttribute)customAttributes[j]; foreach (MethodInfo item2 in GetHarmonyMethod(methodInfo, detourToHAttribute.Type, detourToHAttribute.SkipParams, detourToHAttribute.Name)) { flag = false; _Detours.Add(new Detour(methodInfo, item2)); } } customAttributes = methodInfo.GetCustomAttributes(typeof(TranspileAttribute), inherit: false); for (int j = 0; j < customAttributes.Length; j++) { TranspileAttribute transpileAttribute = (TranspileAttribute)customAttributes[j]; foreach (MethodInfo item3 in GetHarmonyMethod(methodInfo, transpileAttribute.Type, -1, transpileAttribute.Name)) { DynamicMethodDefinition val = new DynamicMethodDefinition((MethodBase)item3); try { flag = false; ILContext val2 = new ILContext(val.Definition) { ReferenceBag = (IILReferenceBag)(object)RuntimeILReferenceBag.Instance }; _Detours.Add((IDisposable)val2); val2.Invoke(methodInfo.CreateDelegate()); if (val2.IsReadOnly) { val2.Dispose(); _Detours.Remove((IDisposable)val2); } else { _Detours.Add(new Detour(item3, val.Generate())); } } finally { ((IDisposable)val)?.Dispose(); } } } if (flag) { throw new Exception("Cannot apply HarmonyDetourBridge rule " + methodInfo.Name); } } } catch { _EarlyReset(); throw; } return true; } private static bool _EarlyReset() { foreach (IDisposable detour in _Detours) { detour.Dispose(); } _Detours.Clear(); return false; } public static void Reset() { if (Initialized) { Initialized = false; _EarlyReset(); } } private static System.Type GetHarmonyType(string typeName) { return _HarmonyASM.GetType(typeName) ?? _HarmonyASM.GetType("HarmonyLib." + typeName) ?? _HarmonyASM.GetType("Harmony." + typeName) ?? _HarmonyASM.GetType("Harmony.ILCopying." + typeName); } private static IEnumerable GetHarmonyMethod(MethodInfo ctx, string typeName, int skipParams, string name) { System.Type harmonyType = GetHarmonyType(typeName); if (harmonyType == null) { return null; } if (string.IsNullOrEmpty(name)) { name = ctx.Name; } if (skipParams < 0) { return from method in harmonyType.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) where method.Name == name select method; } return new MethodInfo[1] { harmonyType.GetMethod(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, (from p in ctx.GetParameters().Skip(skipParams) select p.ParameterType).ToArray(), null) }; } private static DynamicMethodDefinition CreateDMD(MethodBase original, string suffix) { //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Expected O, but got Unknown if (original == null) { throw new ArgumentNullException("original"); } ParameterInfo[] parameters = original.GetParameters(); System.Type[] array; if (!original.IsStatic) { array = new System.Type[parameters.Length + 1]; array[0] = Extensions.GetThisParamType(original); for (int i = 0; i < parameters.Length; i++) { array[i + 1] = parameters[i].ParameterType; } } else { array = new System.Type[parameters.Length]; for (int j = 0; j < parameters.Length; j++) { array[j] = parameters[j].ParameterType; } } return new DynamicMethodDefinition((original.Name + suffix).Replace("<>", "", StringComparison.Ordinal), (original as MethodInfo)?.ReturnType ?? typeof(void), array); } [DetourToRD("Memory", 0, null)] private static long GetMethodStart(MethodBase method, out Exception exception) { exception = null; try { _Detours.Add((IDisposable)new LazyDisposable(method, (Action)delegate(MethodBase m) { m.Unpin(); })); return (long)method.Pin().GetNativeStart(); } catch (Exception ex) { exception = ex; return 0L; } } [DetourToRD("Memory", 0, null)] [Critical] private static string WriteJump(long memory, long destination) { _Detours.Add(new NativeDetour((IntPtr)memory, (IntPtr)destination)); return null; } [DetourToRD("Memory", 0, null)] [Critical] private static string DetourMethod(MethodBase original, MethodBase replacement) { if (replacement == null) { replacement = _LastWrapperDMD.Generate(); _LastWrapperDMD.Dispose(); _LastWrapperDMD = null; } _Detours.Add(new Detour(original, replacement, ref _DetourConfig)); return null; } [DetourToRD("MethodBodyReader", 1, null)] private static MethodInfo EmitMethodForType(object self, System.Type type) { foreach (KeyValuePair emitter in _Emitters) { if (emitter.Key == type) { return emitter.Value; } } foreach (KeyValuePair emitter2 in _Emitters) { if (emitter2.Key.IsAssignableFrom(type)) { return emitter2.Value; } } return null; } [DetourToRD("PatchProcessor", 2, null)] [Critical] private static List Patch(Func> orig, object self) { orig(self); return new List(); } [Transpile("PatchFunctions", null)] [Critical] private static void UpdateWrapper(ILContext il) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Expected O, but got Unknown //IL_003c: Unknown result type (might be due to invalid IL or missing references) ILCursor val = new ILCursor(il); val.GotoNext(new Func[1] { (Instruction i) => ILPatternMatchingExt.MatchThrow(i) }); val.Next.OpCode = OpCodes.Pop; } [Transpile("MethodPatcher", null)] [Critical] private static void CreatePatchedMethod(ILContext il) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Expected O, but got Unknown //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) ILCursor val = new ILCursor(il); System.Type t_DynamicTools = GetHarmonyType("DynamicTools"); if (!val.TryGotoNext(new Func[1] { (Instruction i) => ILPatternMatchingExt.MatchCall(i, t_DynamicTools, "CreateDynamicMethod") })) { il.MakeReadOnly(); return; } val.Next.OpCode = OpCodes.Call; val.Next.Operand = il.Import((MethodBase)typeof(HarmonyDetourBridge).GetMethod("CreateDMD", BindingFlags.Static | BindingFlags.NonPublic)); int varDMDi = -1; val.GotoNext(new Func[1] { (Instruction i) => ILPatternMatchingExt.MatchStloc(i, ref varDMDi) }); ((VariableReference)il.Body.Variables[varDMDi]).VariableType = il.Import(typeof(DynamicMethodDefinition)); val.GotoNext(new Func[1] { (Instruction i) => ILPatternMatchingExt.MatchCallvirt(i, "GetILGenerator") }); val.Next.OpCode = OpCodes.Call; val.Next.Operand = il.Import((MethodBase)typeof(DynamicMethodDefinition).GetMethod("GetILGenerator", BindingFlags.Instance | BindingFlags.Public)); val.GotoNext(new Func[1] { (Instruction i) => ILPatternMatchingExt.MatchCall(i, t_DynamicTools, "PrepareDynamicMethod") }); val.Next.OpCode = OpCodes.Pop; val.Next.Operand = null; val.GotoNext(new Func[1] { (Instruction i) => ILPatternMatchingExt.MatchLdloc(i, varDMDi) }); int index = val.Index; val.Index = index + 1; val.EmitDelegate>((Func)delegate(DynamicMethodDefinition dmd) { _LastWrapperDMD = dmd; return null; }); } [DetourToRD("HarmonySharedState", 1, null)] private static Assembly SharedStateAssembly(Func orig) { //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Expected O, but got Unknown //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Expected O, but got Unknown //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Expected O, but got Unknown //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Expected O, but got Unknown Assembly assembly = orig(); if (assembly != null) { return assembly; } if (_SharedStateASM != null) { return _SharedStateASM; } string text = (string)GetHarmonyType("HarmonySharedState").GetField("name", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null); ModuleDefinition val = ModuleDefinition.CreateModule("MonoMod.RuntimeDetour." + text, new ModuleParameters { Kind = (ModuleKind)0, ReflectionImporterProvider = MMReflectionImporter.Provider }); try { TypeDefinition val2 = new TypeDefinition("", text, (TypeAttributes)385) { BaseType = val.TypeSystem.Object }; val.Types.Add(val2); val2.Fields.Add(new FieldDefinition("state", (FieldAttributes)22, val.ImportReference(typeof(Dictionary)))); val2.Fields.Add(new FieldDefinition("version", (FieldAttributes)22, val.ImportReference(typeof(int)))); return _SharedStateASM = ReflectionHelper.Load(val); } finally { ((IDisposable)val)?.Dispose(); } } private static Assembly _FindHarmony() { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { if (assembly.GetName().Name == "0Harmony" || assembly.GetName().Name == "Harmony" || assembly.GetType("Harmony.HarmonyInstance") != null || assembly.GetType("HarmonyLib.Harmony") != null) { return assembly; } } object obj = System.Type.GetType("Harmony.HarmonyInstance", throwOnError: false, ignoreCase: false)?.Assembly; if (obj == null) { System.Type? type = System.Type.GetType("HarmonyLib.Harmony", throwOnError: false, ignoreCase: false); if ((object)type == null) { return null; } obj = type.Assembly; } return (Assembly)obj; } } public struct HookConfig { public bool ManualApply; public int Priority; public string ID; public IEnumerable Before; public IEnumerable After; } public class Hook : IDetour, IDisposable { public static Func OnDetour; public static Func OnUndo; public static Func OnGenerateTrampoline; public readonly MethodBase Method; public readonly MethodBase Target; public readonly MethodBase TargetReal; public readonly object DelegateTarget; private Detour _Detour; private readonly Type _OrigDelegateType; private readonly MethodInfo _OrigDelegateInvoke; private int? _RefTarget; private int? _RefTrampoline; private int? _RefTrampolineTmp; public bool IsValid => _Detour.IsValid; public bool IsApplied => _Detour.IsApplied; public Detour Detour => _Detour; public Hook(MethodBase from, MethodInfo to, object target, ref HookConfig config) { //IL_025c: Unknown result type (might be due to invalid IL or missing references) //IL_0261: Unknown result type (might be due to invalid IL or missing references) //IL_0264: Expected O, but got Unknown //IL_0266: Expected O, but got Unknown //IL_02a9: Unknown result type (might be due to invalid IL or missing references) //IL_02c4: Unknown result type (might be due to invalid IL or missing references) //IL_02d6: Unknown result type (might be due to invalid IL or missing references) //IL_0386: Unknown result type (might be due to invalid IL or missing references) //IL_038b: Unknown result type (might be due to invalid IL or missing references) //IL_038e: Expected O, but got Unknown //IL_0390: Expected O, but got Unknown //IL_03ae: Unknown result type (might be due to invalid IL or missing references) //IL_03e4: Unknown result type (might be due to invalid IL or missing references) //IL_03ff: Unknown result type (might be due to invalid IL or missing references) //IL_040d: Unknown result type (might be due to invalid IL or missing references) from = from.GetIdentifiable(); Method = from; Target = to; DelegateTarget = target; Type type = (from as MethodInfo)?.ReturnType ?? typeof(void); if (to.ReturnType != type && !Extensions.IsCompatible(to.ReturnType, type)) { throw new InvalidOperationException($"Return type of hook for {from} doesn't match, must be {((from as MethodInfo)?.ReturnType ?? typeof(void)).FullName}"); } if (target == null && !to.IsStatic) { throw new InvalidOperationException($"Hook for method {from} must be static, or you must pass a target instance."); } ParameterInfo[] parameters = Target.GetParameters(); ParameterInfo[] parameters2 = Method.GetParameters(); Type[] array; if (!Method.IsStatic) { array = new Type[parameters2.Length + 1]; array[0] = Extensions.GetThisParamType(Method); for (int i = 0; i < parameters2.Length; i++) { array[i + 1] = parameters2[i].ParameterType; } } else { array = new Type[parameters2.Length]; for (int j = 0; j < parameters2.Length; j++) { array[j] = parameters2[j].ParameterType; } } Type type2 = null; if (parameters.Length == array.Length + 1 && typeof(Delegate).IsAssignableFrom(parameters[0].ParameterType)) { type2 = (_OrigDelegateType = parameters[0].ParameterType); } else if (parameters.Length != array.Length) { throw new InvalidOperationException($"Parameter count of hook for {from} doesn't match, must be {array.Length}"); } for (int k = 0; k < array.Length; k++) { Type type3 = array[k]; Type parameterType = parameters[k + ((!(type2 == null)) ? 1 : 0)].ParameterType; if (!Extensions.IsCompatible(type3, parameterType)) { throw new InvalidOperationException($"Parameter #{k} of hook for {from} doesn't match, must be {type3.FullName} or related"); } } MethodInfo methodInfo = (_OrigDelegateInvoke = type2?.GetMethod("Invoke")); DynamicMethodDefinition val = new DynamicMethodDefinition($"Hook<{Extensions.GetID(Method, (string)null, (string)null, true, false, true)}>?{GetHashCode()}", (Method as MethodInfo)?.ReturnType ?? typeof(void), array); DynamicMethodDefinition val2 = val; DynamicMethodDefinition val3 = val; try { ILProcessor iLProcessor = val2.GetILProcessor(); if (target != null) { _RefTarget = DynamicMethodHelper.EmitReference(iLProcessor, target); } if (type2 != null) { _RefTrampoline = DynamicMethodHelper.EmitReference(iLProcessor, (Delegate)null); } for (int l = 0; l < array.Length; l++) { iLProcessor.Emit(OpCodes.Ldarg, l); } Extensions.Emit(iLProcessor, OpCodes.Call, Target); iLProcessor.Emit(OpCodes.Ret); TargetReal = val2.Generate().Pin(); } finally { ((IDisposable)val3)?.Dispose(); } if (type2 != null) { ParameterInfo[] parameters3 = methodInfo.GetParameters(); Type[] array2 = new Type[parameters3.Length]; for (int m = 0; m < parameters3.Length; m++) { array2[m] = parameters3[m].ParameterType; } DynamicMethodDefinition val4 = new DynamicMethodDefinition($"Chain:TMP<{Extensions.GetID(Method, (string)null, (string)null, true, false, true)}>?{GetHashCode()}", methodInfo?.ReturnType ?? typeof(void), array2); val2 = val4; val3 = val4; try { ILProcessor iLProcessor = val2.GetILProcessor(); _RefTrampolineTmp = DynamicMethodHelper.EmitReference(iLProcessor, (Delegate)null); iLProcessor.Emit(OpCodes.Brfalse, iLProcessor.Body.Instructions[0]); DynamicMethodHelper.EmitGetReference(iLProcessor, _RefTrampolineTmp.Value); for (int n = 0; n < array.Length; n++) { iLProcessor.Emit(OpCodes.Ldarg, n); } Extensions.Emit(iLProcessor, OpCodes.Callvirt, (MethodBase)methodInfo); iLProcessor.Emit(OpCodes.Ret); DynamicMethodHelper.SetReference(_RefTrampoline.Value, (object)val2.Generate().CreateDelegate(type2)); } finally { ((IDisposable)val3)?.Dispose(); } } _Detour = new Detour(Method, TargetReal, new DetourConfig { ManualApply = true, Priority = config.Priority, ID = config.ID, Before = config.Before, After = config.After }); _UpdateOrig(null); if (!config.ManualApply) { Apply(); } } public Hook(MethodBase from, MethodInfo to, object target, HookConfig config) : this(from, to, target, ref config) { } public Hook(MethodBase from, MethodInfo to, object target) : this(from, to, target, DetourContext.Current?.HookConfig ?? default(HookConfig)) { } public Hook(MethodBase from, MethodInfo to, ref HookConfig config) : this(from, to, null, ref config) { } public Hook(MethodBase from, MethodInfo to, HookConfig config) : this(from, to, null, ref config) { } public Hook(MethodBase from, MethodInfo to) : this(from, to, null) { } public Hook(MethodBase method, IntPtr to, ref HookConfig config) : this(method, DetourHelper.GenerateNativeProxy(to, method), null, ref config) { } public Hook(MethodBase method, IntPtr to, HookConfig config) : this(method, DetourHelper.GenerateNativeProxy(to, method), null, ref config) { } public Hook(MethodBase method, IntPtr to) : this(method, DetourHelper.GenerateNativeProxy(to, method), null) { } public Hook(MethodBase method, Delegate to, ref HookConfig config) : this(method, to.Method, to.Target, ref config) { } public Hook(MethodBase method, Delegate to, HookConfig config) : this(method, to.Method, to.Target, ref config) { } public Hook(MethodBase method, Delegate to) : this(method, to.Method, to.Target) { } public Hook(Delegate from, IntPtr to, ref HookConfig config) : this(from.Method, to, ref config) { } public Hook(Delegate from, IntPtr to, HookConfig config) : this(from.Method, to, ref config) { } public Hook(Delegate from, IntPtr to) : this(from.Method, to) { } public Hook(Delegate from, Delegate to, ref HookConfig config) : this(from.Method, to, ref config) { } public Hook(Delegate from, Delegate to, HookConfig config) : this(from.Method, to, ref config) { } public Hook(Delegate from, Delegate to) : this(from.Method, to) { } public Hook(Expression from, IntPtr to, ref HookConfig config) : this(((MethodCallExpression)from).Method, to, ref config) { } public Hook(Expression from, IntPtr to, HookConfig config) : this(((MethodCallExpression)from).Method, to, ref config) { } public Hook(Expression from, IntPtr to) : this(((MethodCallExpression)from).Method, to) { } public Hook(Expression from, Delegate to, ref HookConfig config) : this(((MethodCallExpression)from).Method, to, ref config) { } public Hook(Expression from, Delegate to, HookConfig config) : this(((MethodCallExpression)from).Method, to, ref config) { } public Hook(Expression from, Delegate to) : this(((MethodCallExpression)from).Method, to) { } public Hook(Expression from, IntPtr to, ref HookConfig config) : this(from.Body, to, ref config) { } public Hook(Expression from, IntPtr to, HookConfig config) : this(from.Body, to, ref config) { } public Hook(Expression from, IntPtr to) : this(from.Body, to) { } public Hook(Expression from, Delegate to, ref HookConfig config) : this(from.Body, to, ref config) { } public Hook(Expression from, Delegate to, HookConfig config) : this(from.Body, to, ref config) { } public Hook(Expression from, Delegate to) : this(from.Body, to) { } public void Apply() { if (!IsValid) { throw new ObjectDisposedException("Hook"); } if (!IsApplied) { Func onDetour = OnDetour; if (onDetour != null && !Extensions.InvokeWhileTrue((MulticastDelegate)onDetour, new object[4] { this, Method, Target, DelegateTarget })) { return; } } _Detour.Apply(); } public void Undo() { if (!IsValid) { throw new ObjectDisposedException("Hook"); } if (IsApplied) { Func onUndo = OnUndo; if (onUndo != null && !Extensions.InvokeWhileTrue((MulticastDelegate)onUndo, new object[1] { this })) { return; } } _Detour.Undo(); if (!IsValid) { _Free(); } } public void Free() { if (IsValid) { _Detour.Free(); _Free(); } } public void Dispose() { if (IsValid) { Undo(); Free(); } } private void _Free() { if (_RefTarget.HasValue) { DynamicMethodHelper.FreeReference(_RefTarget.Value); } if (_RefTrampoline.HasValue) { DynamicMethodHelper.FreeReference(_RefTrampoline.Value); } if (_RefTrampolineTmp.HasValue) { DynamicMethodHelper.FreeReference(_RefTrampolineTmp.Value); } TargetReal.Unpin(); } public MethodBase GenerateTrampoline(MethodBase signature = null) { Func onGenerateTrampoline = OnGenerateTrampoline; MethodBase methodBase = ((onGenerateTrampoline != null) ? Extensions.InvokeWhileNull((MulticastDelegate)onGenerateTrampoline, new object[2] { this, signature }) : null); if (methodBase != null) { return methodBase; } return _Detour.GenerateTrampoline(signature); } public T GenerateTrampoline() where T : Delegate { if (!typeof(Delegate).IsAssignableFrom(typeof(T))) { throw new InvalidOperationException($"Type {typeof(T)} not a delegate type."); } return Extensions.CreateDelegate(GenerateTrampoline(typeof(T).GetMethod("Invoke")), typeof(T)) as T; } internal void _UpdateOrig(MethodBase invoke) { if (!(_OrigDelegateType == null)) { Delegate @delegate = Extensions.CreateDelegate(invoke ?? GenerateTrampoline(_OrigDelegateInvoke), _OrigDelegateType); DynamicMethodHelper.SetReference(_RefTrampoline.Value, (object)@delegate); DynamicMethodHelper.SetReference(_RefTrampolineTmp.Value, (object)@delegate); } } } public class Hook : Hook { public Hook(Expression from, T to, ref HookConfig config) : base(from.Body, to as Delegate, ref config) { } public Hook(Expression from, T to, HookConfig config) : base(from.Body, to as Delegate, ref config) { } public Hook(Expression from, T to) : base(from.Body, to as Delegate) { } public Hook(Expression> from, IntPtr to, ref HookConfig config) : base(from.Body, to, ref config) { } public Hook(Expression> from, IntPtr to, HookConfig config) : base(from.Body, to, ref config) { } public Hook(Expression> from, IntPtr to) : base(from.Body, to) { } public Hook(Expression> from, Delegate to, ref HookConfig config) : base(from.Body, to, ref config) { } public Hook(Expression> from, Delegate to, HookConfig config) : base(from.Body, to, ref config) { } public Hook(Expression> from, Delegate to) : base(from.Body, to) { } public Hook(T from, IntPtr to, ref HookConfig config) : base(from as Delegate, to, ref config) { } public Hook(T from, IntPtr to, HookConfig config) : base(from as Delegate, to, ref config) { } public Hook(T from, IntPtr to) : base(from as Delegate, to) { } public Hook(T from, T to, ref HookConfig config) : base(from as Delegate, to as Delegate, ref config) { } public Hook(T from, T to, HookConfig config) : base(from as Delegate, to as Delegate, ref config) { } public Hook(T from, T to) : base(from as Delegate, to as Delegate) { } } public class Hook : Hook { public Hook(Expression> from, TTo to, ref HookConfig config) : base(from.Body, to as Delegate) { } public Hook(Expression> from, TTo to, HookConfig config) : base(from.Body, to as Delegate) { } public Hook(Expression> from, TTo to) : base(from.Body, to as Delegate) { } public Hook(TFrom from, TTo to, ref HookConfig config) : base(from as Delegate, to as Delegate) { } public Hook(TFrom from, TTo to, HookConfig config) : base(from as Delegate, to as Delegate) { } public Hook(TFrom from, TTo to) : base(from as Delegate, to as Delegate) { } } public interface IDetour : IDisposable { bool IsValid { get; } bool IsApplied { get; } void Apply(); void Undo(); void Free(); MethodBase GenerateTrampoline(MethodBase signature = null); T GenerateTrampoline() where T : Delegate; } public interface ISortableDetour : IDetour, IDisposable { uint GlobalIndex { get; } int Priority { get; set; } string ID { get; set; } IEnumerable Before { get; set; } IEnumerable After { get; set; } } public struct ILHookConfig { public bool ManualApply; public int Priority; public string ID; public IEnumerable Before; public IEnumerable After; } public class ILHook : ISortableDetour, IDetour, IDisposable { private class Context { public List Chain = new List(); public HashSet Active = new HashSet(); public MethodBase Method; public Detour Detour; public Context(MethodBase method) { Method = method; } public void Add(ILHook hook) { List chain = Chain; lock (chain) { chain.Add(hook); } } public void Remove(ILHook hook) { List chain = Chain; lock (chain) { chain.Remove(hook); if (chain.Count == 0) { Refresh(); lock (_Map) { _Map.Remove(Method); return; } } } } public void Refresh() { //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Expected O, but got Unknown List chain = Chain; lock (chain) { foreach (ILContext item in Active) { item.Dispose(); } Active.Clear(); Detour?.Dispose(); Detour = null; if (chain.Count == 0) { return; } bool flag = false; foreach (ILHook item2 in chain) { if (item2.IsApplied) { flag = true; break; } } if (!flag) { return; } DetourSorter.Sort(chain); DynamicMethodDefinition val = new DynamicMethodDefinition(Method); MethodBase to; try { MethodDefinition definition = val.Definition; foreach (ILHook item3 in chain) { if (item3.IsApplied) { InvokeManipulator(definition, item3.Manipulator); } } to = val.Generate(); } finally { ((IDisposable)val)?.Dispose(); } Detour = new Detour(Method, to, ref ILDetourConfig); } } private void InvokeManipulator(MethodDefinition def, Manipulator cb) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Expected O, but got Unknown ILContext val = new ILContext(def); val.ReferenceBag = (IILReferenceBag)(object)RuntimeILReferenceBag.Instance; val.Invoke(cb); if (val.IsReadOnly) { val.Dispose(); return; } val.MakeReadOnly(); Active.Add(val); } } public static Func OnDetour; public static Func OnUndo; private static DetourConfig ILDetourConfig = new DetourConfig { Priority = -268435456, Before = new string[1] { "*" } }; private static Dictionary _Map = new Dictionary(); private static uint _GlobalIndexNext = 0u; private readonly uint _GlobalIndex; private int _Priority; private string _ID; private List _Before = new List(); private ReadOnlyCollection _BeforeRO; private List _After = new List(); private ReadOnlyCollection _AfterRO; public readonly MethodBase Method; public readonly Manipulator Manipulator; private Context _Ctx { get { if (!_Map.TryGetValue(Method, out var value)) { return null; } return value; } } public bool IsValid => Index != -1; public bool IsApplied { get; private set; } public int Index => _Ctx?.Chain.IndexOf(this) ?? (-1); public int MaxIndex => _Ctx?.Chain.Count ?? (-1); public uint GlobalIndex => _GlobalIndex; public int Priority { get { return _Priority; } set { if (_Priority != value) { _Priority = value; _Ctx.Refresh(); } } } public string ID { get { return _ID; } set { if (string.IsNullOrEmpty(value)) { MethodInfo method = ((Delegate)(object)Manipulator).Method; value = (((object)method != null) ? Extensions.GetID((MethodBase)method, (string)null, (string)null, true, false, true) : null) ?? GetHashCode().ToString(CultureInfo.InvariantCulture); } if (!(_ID == value)) { _ID = value; _Ctx.Refresh(); } } } public IEnumerable Before { get { return _BeforeRO ?? (_BeforeRO = _Before.AsReadOnly()); } set { lock (_Before) { _Before.Clear(); if (value != null) { foreach (string item in value) { _Before.Add(item); } } _Ctx.Refresh(); } } } public IEnumerable After { get { return _AfterRO ?? (_AfterRO = _After.AsReadOnly()); } set { lock (_After) { _After.Clear(); if (value != null) { foreach (string item in value) { _After.Add(item); } } _Ctx.Refresh(); } } } public ILHook(MethodBase from, Manipulator manipulator, ref ILHookConfig config) { from = from.GetIdentifiable(); Method = from.Pin(); Manipulator = manipulator; _GlobalIndex = _GlobalIndexNext++; _Priority = config.Priority; _ID = config.ID; if (config.Before != null) { foreach (string item in config.Before) { _Before.Add(item); } } if (config.After != null) { foreach (string item2 in config.After) { _After.Add(item2); } } Context value; lock (_Map) { if (!_Map.TryGetValue(Method, out value)) { value = (_Map[Method] = new Context(Method)); } } lock (value) { value.Add(this); } if (!config.ManualApply) { Apply(); } } public ILHook(MethodBase from, Manipulator manipulator, ILHookConfig config) : this(from, manipulator, ref config) { } public ILHook(MethodBase from, Manipulator manipulator) : this(from, manipulator, DetourContext.Current?.ILHookConfig ?? default(ILHookConfig)) { } public void Apply() { if (!IsValid) { throw new ObjectDisposedException("ILHook"); } if (!IsApplied) { Func onDetour = OnDetour; if (onDetour == null || Extensions.InvokeWhileTrue((MulticastDelegate)onDetour, new object[3] { this, Method, Manipulator })) { IsApplied = true; _Ctx.Refresh(); } } } public void Undo() { if (!IsValid) { throw new ObjectDisposedException("ILHook"); } if (IsApplied) { Func onUndo = OnUndo; if (onUndo == null || Extensions.InvokeWhileTrue((MulticastDelegate)onUndo, new object[1] { this })) { IsApplied = false; _Ctx.Refresh(); } } } public void Free() { if (IsValid) { Undo(); _Ctx.Remove(this); Method.Unpin(); } } public void Dispose() { if (IsValid) { Undo(); Free(); } } public MethodBase GenerateTrampoline(MethodBase signature = null) { throw new NotSupportedException(); } public T GenerateTrampoline() where T : Delegate { throw new NotSupportedException(); } } public struct NativeDetourConfig { public bool ManualApply; public bool SkipILCopy; } public class NativeDetour : IDetour, IDisposable { public static Func OnDetour; public static Func OnUndo; public static Func OnGenerateTrampoline; private NativeDetourData _Data; public readonly MethodBase Method; private readonly MethodInfo _BackupMethod; private readonly IntPtr _BackupNative; private HashSet _Pinned = new HashSet(); public bool IsValid { get; private set; } public bool IsApplied { get; private set; } public NativeDetourData Data => _Data; public NativeDetour(MethodBase method, IntPtr from, IntPtr to, ref NativeDetourConfig config) { if (from == to) { throw new InvalidOperationException($"Cannot detour from a location to itself! (from: {from:X16} to: {to:X16} method: {method})"); } method = method?.GetIdentifiable(); Method = method; Func onDetour = OnDetour; if (onDetour == null || Extensions.InvokeWhileTrue((MulticastDelegate)onDetour, new object[4] { this, method, from, to })) { IsValid = true; _Data = DetourHelper.Native.Create(from, to); if (!config.SkipILCopy) { method?.TryCreateILCopy(out _BackupMethod); } _BackupNative = DetourHelper.Native.MemAlloc(_Data.Size); if (!config.ManualApply) { Apply(); } } } public NativeDetour(MethodBase method, IntPtr from, IntPtr to, NativeDetourConfig config) : this(method, from, to, ref config) { } public NativeDetour(MethodBase method, IntPtr from, IntPtr to) : this(method, from, to, default(NativeDetourConfig)) { } public NativeDetour(IntPtr from, IntPtr to, ref NativeDetourConfig config) : this(null, from, to, ref config) { } public NativeDetour(IntPtr from, IntPtr to, NativeDetourConfig config) : this(null, from, to, ref config) { } public NativeDetour(IntPtr from, IntPtr to) : this(null, from, to) { } public NativeDetour(MethodBase from, IntPtr to, ref NativeDetourConfig config) : this(from, from.Pin().GetNativeStart(), to, ref config) { _Pinned.Add(from); } public NativeDetour(MethodBase from, IntPtr to, NativeDetourConfig config) : this(from, from.Pin().GetNativeStart(), to, ref config) { _Pinned.Add(from); } public NativeDetour(MethodBase from, IntPtr to) : this(from, from.Pin().GetNativeStart(), to) { _Pinned.Add(from); } public NativeDetour(IntPtr from, MethodBase to, ref NativeDetourConfig config) : this(from, to.Pin().GetNativeStart(), ref config) { _Pinned.Add(to); } public NativeDetour(IntPtr from, MethodBase to, NativeDetourConfig config) : this(from, to.Pin().GetNativeStart(), ref config) { _Pinned.Add(to); } public NativeDetour(IntPtr from, MethodBase to) : this(from, to.Pin().GetNativeStart()) { _Pinned.Add(to); } public NativeDetour(MethodBase from, MethodBase to, ref NativeDetourConfig config) : this(from.Pin().GetNativeStart(), DetourHelper.Runtime.GetDetourTarget(from, to), ref config) { _Pinned.Add(from); } public NativeDetour(MethodBase from, MethodBase to, NativeDetourConfig config) : this(from.Pin().GetNativeStart(), DetourHelper.Runtime.GetDetourTarget(from, to), ref config) { _Pinned.Add(from); } public NativeDetour(MethodBase from, MethodBase to) : this(from.Pin().GetNativeStart(), DetourHelper.Runtime.GetDetourTarget(from, to)) { _Pinned.Add(from); } public NativeDetour(Delegate from, IntPtr to, ref NativeDetourConfig config) : this(from.Method, to, ref config) { } public NativeDetour(Delegate from, IntPtr to, NativeDetourConfig config) : this(from.Method, to, ref config) { } public NativeDetour(Delegate from, IntPtr to) : this(from.Method, to) { } public NativeDetour(IntPtr from, Delegate to, ref NativeDetourConfig config) : this(from, to.Method, ref config) { } public NativeDetour(IntPtr from, Delegate to, NativeDetourConfig config) : this(from, to.Method, ref config) { } public NativeDetour(IntPtr from, Delegate to) : this(from, to.Method) { } public NativeDetour(Delegate from, Delegate to, ref NativeDetourConfig config) : this(from.Method, to.Method, ref config) { } public NativeDetour(Delegate from, Delegate to, NativeDetourConfig config) : this(from.Method, to.Method, ref config) { } public NativeDetour(Delegate from, Delegate to) : this(from.Method, to.Method) { } public void Apply() { if (!IsValid) { throw new ObjectDisposedException("NativeDetour"); } if (!IsApplied) { IsApplied = true; DetourHelper.Native.Copy(_Data.Method, _BackupNative, _Data.Type); DetourHelper.Native.MakeWritable(_Data); DetourHelper.Native.Apply(_Data); DetourHelper.Native.MakeExecutable(_Data); DetourHelper.Native.FlushICache(_Data); } } public void Undo() { if (!IsValid) { throw new ObjectDisposedException("NativeDetour"); } Func onUndo = OnUndo; if ((onUndo == null || Extensions.InvokeWhileTrue((MulticastDelegate)onUndo, new object[1] { this })) && IsApplied) { IsApplied = false; DetourHelper.Native.MakeWritable(_Data); DetourHelper.Native.Copy(_BackupNative, _Data.Method, _Data.Type); DetourHelper.Native.MakeExecutable(_Data); DetourHelper.Native.FlushICache(_Data); } } public void ChangeSource(IntPtr newSource) { if (!IsValid) { throw new ObjectDisposedException("NativeDetour"); } NativeDetourData data = _Data; _Data = DetourHelper.Native.Create(newSource, _Data.Target); IsApplied = false; Apply(); DetourHelper.Native.Free(data); } public void ChangeTarget(IntPtr newTarget) { if (!IsValid) { throw new ObjectDisposedException("NativeDetour"); } NativeDetourData data = _Data; _Data = DetourHelper.Native.Create(_Data.Method, newTarget); IsApplied = false; Apply(); DetourHelper.Native.Free(data); } public void Free() { if (!IsValid) { return; } IsValid = false; DetourHelper.Native.MemFree(_BackupNative); DetourHelper.Native.Free(_Data); if (IsApplied) { return; } foreach (MethodBase item in _Pinned) { item.Unpin(); } _Pinned.Clear(); } public void Dispose() { if (IsValid) { Undo(); Free(); } } public MethodBase GenerateTrampoline(MethodBase signature = null) { //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Expected O, but got Unknown //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Expected O, but got Unknown //IL_01a8: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Expected O, but got Unknown //IL_01b5: Expected O, but got Unknown //IL_01cf: Unknown result type (might be due to invalid IL or missing references) //IL_01f3: Unknown result type (might be due to invalid IL or missing references) //IL_020f: Unknown result type (might be due to invalid IL or missing references) //IL_0255: Unknown result type (might be due to invalid IL or missing references) //IL_0247: Unknown result type (might be due to invalid IL or missing references) //IL_0306: Unknown result type (might be due to invalid IL or missing references) //IL_02d2: Unknown result type (might be due to invalid IL or missing references) Func onGenerateTrampoline = OnGenerateTrampoline; MethodBase methodBase = ((onGenerateTrampoline != null) ? Extensions.InvokeWhileNull((MulticastDelegate)onGenerateTrampoline, new object[2] { this, signature }) : null); if (methodBase != null) { return methodBase; } if (!IsValid) { throw new ObjectDisposedException("NativeDetour"); } if (_BackupMethod != null) { return _BackupMethod; } if (signature == null) { throw new ArgumentNullException("A signature must be given if the NativeDetour doesn't hold a reference to a managed method."); } MethodBase methodBase2 = Method; if (methodBase2 == null) { methodBase2 = DetourHelper.GenerateNativeProxy(_Data.Method, signature); } Type type = (signature as MethodInfo)?.ReturnType ?? typeof(void); ParameterInfo[] parameters = signature.GetParameters(); Type[] array = new Type[parameters.Length]; for (int i = 0; i < parameters.Length; i++) { array[i] = parameters[i].ParameterType; } MethodBase method = Method; DynamicMethodDefinition val = new DynamicMethodDefinition(string.Format("Trampoline:Native<{0}>?{1}", (((object)method != null) ? Extensions.GetID(method, (string)null, (string)null, true, false, true) : null) ?? ((long)_Data.Method).ToString("X16", CultureInfo.InvariantCulture), GetHashCode()), type, array); try { ILProcessor iLProcessor = val.GetILProcessor(); ExceptionHandler val2 = new ExceptionHandler((ExceptionHandlerType)2); iLProcessor.Body.ExceptionHandlers.Add(val2); iLProcessor.EmitDetourCopy(_BackupNative, _Data.Method, _Data.Type); VariableDefinition val3 = null; if (type != typeof(void)) { Collection variables = iLProcessor.Body.Variables; VariableDefinition val4 = new VariableDefinition(Extensions.Import(iLProcessor, type)); val3 = val4; variables.Add(val4); } int count = iLProcessor.Body.Instructions.Count; for (int j = 0; j < array.Length; j++) { iLProcessor.Emit(OpCodes.Ldarg, j); } if (methodBase2 is MethodInfo) { Extensions.Emit(iLProcessor, OpCodes.Call, (MethodBase)(MethodInfo)methodBase2); } else { if (!(methodBase2 is ConstructorInfo)) { throw new NotSupportedException("Method type " + methodBase2.GetType().FullName + " not supported."); } Extensions.Emit(iLProcessor, OpCodes.Call, (MethodBase)(ConstructorInfo)methodBase2); } if (val3 != null) { iLProcessor.Emit(OpCodes.Stloc, val3); } Extensions.Emit(iLProcessor, OpCodes.Leave, (object)null); Instruction obj = iLProcessor.Body.Instructions[iLProcessor.Body.Instructions.Count - 1]; int count2 = iLProcessor.Body.Instructions.Count; _ = iLProcessor.Body.Instructions.Count; iLProcessor.EmitDetourApply(_Data); int count3 = iLProcessor.Body.Instructions.Count; Instruction val5 = null; if (val3 != null) { iLProcessor.Emit(OpCodes.Ldloc, val3); val5 = iLProcessor.Body.Instructions[iLProcessor.Body.Instructions.Count - 1]; } iLProcessor.Emit(OpCodes.Ret); val5 = val5 ?? iLProcessor.Body.Instructions[iLProcessor.Body.Instructions.Count - 1]; obj.Operand = val5; val2.TryStart = iLProcessor.Body.Instructions[count]; val2.TryEnd = iLProcessor.Body.Instructions[count2]; val2.HandlerStart = iLProcessor.Body.Instructions[count2]; val2.HandlerEnd = iLProcessor.Body.Instructions[count3]; return val.Generate(); } finally { ((IDisposable)val)?.Dispose(); } } public T GenerateTrampoline() where T : Delegate { if (!typeof(Delegate).IsAssignableFrom(typeof(T))) { throw new InvalidOperationException($"Type {typeof(T)} not a delegate type."); } return Extensions.CreateDelegate(GenerateTrampoline(typeof(T).GetMethod("Invoke")), typeof(T)) as T; } } internal static class DetourSorter where T : ISortableDetour { private sealed class Group { public readonly string StepName; public List Items = new List(); public List Children = new List(); public List NonMatching = new List(); public Group(string stepName) { StepName = stepName; } public Group(string stepName, List items) : this(stepName) { Items.AddRange(items); } public void Step(Step step) { if (Children.Count != 0) { foreach (Group child in Children) { child.Step(step); } return; } if (Items.Count <= 1) { return; } if ((Items.Count == 2 && !((!step.IsFlat) ?? false)) || step.IsFlat.GetValueOrDefault()) { Items.Sort(step); return; } string name = step.GetType().Name; Group group = new Group(name, new List { Items[0] }); Children.Add(group); for (int i = 1; i < Items.Count; i++) { T val = Items[i]; if (step.Any(group.Items, val)) { Group group2 = group; group = null; if (Children.Count > 1) { foreach (Group child2 in Children) { if (child2 != group2 && !step.Any(child2.Items, val) && !step.Any(child2.NonMatching, val)) { group = child2; break; } } } if (group == null) { group = new Group(name); Children.Add(group); group.NonMatching.Add(group2); group2.NonMatching.Add(group); } } group.Items.Add(val); } if (Children.Count == 1) { Children.Clear(); } else { Children.Sort(step.ForGroup); } } public void Flatten() { if (Children.Count != 0) { Items.Clear(); Flatten(Items); } } public void Flatten(List total) { if (Children.Count == 0) { total.AddRange(Items); return; } foreach (Group child in Children) { child.Flatten(total); } } } private abstract class Step : IComparer { public abstract GroupComparer ForGroup { get; } public virtual bool? IsFlat => null; public abstract int Compare(T x, T y); public bool Any(List xlist, T y) { foreach (T item in xlist) { if (Compare(item, y) != 0) { return true; } } return false; } public bool Any(List groups, T y) { foreach (Group group in groups) { if (Any(group.Items, y)) { return true; } } return false; } } private sealed class GroupComparer : IComparer { public Step Step; public GroupComparer(Step step) { Step = step; } public int Compare(Group xg, Group yg) { foreach (T item in xg.Items) { foreach (T item2 in yg.Items) { int result; if ((result = Step.Compare(item, item2)) != 0) { return result; } } } return 0; } } private sealed class BeforeAfterAll : Step { public static readonly BeforeAfterAll _ = new BeforeAfterAll(); public static readonly GroupComparer Group = new GroupComparer(_); public override GroupComparer ForGroup => Group; public override bool? IsFlat => false; public override int Compare(T a, T b) { if (a.Before.Contains("*") && !b.Before.Contains("*")) { return -1; } if (!a.Before.Contains("*") && b.Before.Contains("*")) { return 1; } if (a.After.Contains("*") && !b.After.Contains("*")) { return 1; } if (!a.After.Contains("*") && b.After.Contains("*")) { return -1; } return 0; } } private sealed class BeforeAfter : Step { public static readonly BeforeAfter _ = new BeforeAfter(); public static readonly GroupComparer Group = new GroupComparer(_); public override GroupComparer ForGroup => Group; public override int Compare(T a, T b) { if (a.Before.Contains(b.ID)) { return -1; } if (a.After.Contains(b.ID)) { return 1; } if (b.Before.Contains(a.ID)) { return 1; } if (b.After.Contains(a.ID)) { return -1; } return 0; } } private sealed class Priority : Step { public static readonly Priority _ = new Priority(); public static readonly GroupComparer Group = new GroupComparer(_); public override GroupComparer ForGroup => Group; public override int Compare(T a, T b) { int num = a.Priority - b.Priority; if (num != 0) { return num; } return 0; } } private sealed class GlobalIndex : Step { public static readonly GlobalIndex _ = new GlobalIndex(); public static readonly GroupComparer Group = new GroupComparer(_); public override GroupComparer ForGroup => Group; public override int Compare(T a, T b) { return a.GlobalIndex.CompareTo(b.GlobalIndex); } } public static void Sort(List detours) { lock (detours) { if (detours.Count > 1) { detours.Sort(GlobalIndex._); Group group = new Group("Init", detours); group.Step(BeforeAfterAll._); group.Step(BeforeAfter._); group.Step(Priority._); group.Step(GlobalIndex._); detours.Clear(); group.Flatten(detours); } } } } public static class DetourHelper { private static readonly object _RuntimeLock = new object(); private static bool _RuntimeInit = false; private static IDetourRuntimePlatform _Runtime; private static readonly object _NativeLock = new object(); private static bool _NativeInit = false; private static IDetourNativePlatform _Native; private static readonly FieldInfo _f_Native = typeof(DetourHelper).GetField("_Native", BindingFlags.Static | BindingFlags.NonPublic); private static readonly MethodInfo _m_ToNativeDetourData = typeof(DetourHelper).GetMethod("ToNativeDetourData", BindingFlags.Static | BindingFlags.NonPublic); private static readonly MethodInfo _m_Copy = typeof(IDetourNativePlatform).GetMethod("Copy"); private static readonly MethodInfo _m_Apply = typeof(IDetourNativePlatform).GetMethod("Apply"); private static readonly ConstructorInfo _ctor_Exception = typeof(Exception).GetConstructor(new Type[1] { typeof(string) }); public static IDetourRuntimePlatform Runtime { get { if (_Runtime != null) { return _Runtime; } lock (_RuntimeLock) { if (_Runtime != null) { return _Runtime; } if (_RuntimeInit) { return null; } _RuntimeInit = true; if (ReflectionHelper.IsMono) { _Runtime = new DetourRuntimeMonoPlatform(); } else if (ReflectionHelper.IsCore) { _Runtime = DetourRuntimeNETCorePlatform.Create(); } else { _Runtime = new DetourRuntimeNETPlatform(); } return _Runtime; } } set { _Runtime = value; } } public static IDetourNativePlatform Native { get { if (_Native != null) { return _Native; } lock (_NativeLock) { if (_Native != null) { return _Native; } if (_NativeInit) { return null; } _NativeInit = true; IDetourNativePlatform detourNativePlatform = ((!PlatformHelper.Is((Platform)65536)) ? ((IDetourNativePlatform)new DetourNativeX86Platform()) : ((IDetourNativePlatform)new DetourNativeARMPlatform())); if (PlatformHelper.Is((Platform)37)) { return _Native = new DetourNativeWindowsPlatform(detourNativePlatform); } if (ReflectionHelper.IsMono) { try { return _Native = new DetourNativeMonoPlatform(detourNativePlatform, "libmonosgen-2.0." + PlatformHelper.LibrarySuffix); } catch { } } string environmentVariable = Environment.GetEnvironmentVariable("MONOMOD_RUNTIMEDETOUR_MONOPOSIXHELPER"); if ((ReflectionHelper.IsMono && environmentVariable != "0") || environmentVariable == "1") { try { return _Native = new DetourNativeMonoPosixPlatform(detourNativePlatform); } catch { } } try { return _Native = new DetourNativeLibcPlatform(detourNativePlatform); } catch { } return detourNativePlatform; } } set { _Native = value; } } public static void MakeWritable(this IDetourNativePlatform plat, NativeDetourData detour) { plat.MakeWritable(detour.Method, detour.Size); } public static void MakeExecutable(this IDetourNativePlatform plat, NativeDetourData detour) { plat.MakeExecutable(detour.Method, detour.Size); } public static void FlushICache(this IDetourNativePlatform plat, NativeDetourData detour) { plat.FlushICache(detour.Method, detour.Size); } public unsafe static void Write(this IntPtr to, ref int offs, byte value) { *(byte*)((long)to + offs) = value; offs++; } public unsafe static void Write(this IntPtr to, ref int offs, ushort value) { *(ushort*)((long)to + offs) = value; offs += 2; } public unsafe static void Write(this IntPtr to, ref int offs, uint value) { *(uint*)((long)to + offs) = value; offs += 4; } public unsafe static void Write(this IntPtr to, ref int offs, ulong value) { *(ulong*)((long)to + offs) = value; offs += 8; } public static MethodBase GetIdentifiable(this MethodBase method) { return Runtime.GetIdentifiable(method); } public static IntPtr GetNativeStart(this MethodBase method) { return Runtime.GetNativeStart(method); } public static IntPtr GetNativeStart(this Delegate method) { return method.Method.GetNativeStart(); } public static IntPtr GetNativeStart(this Expression method) { return ((MethodCallExpression)method).Method.GetNativeStart(); } public static MethodInfo CreateILCopy(this MethodBase method) { return Runtime.CreateCopy(method); } public static bool TryCreateILCopy(this MethodBase method, out MethodInfo dm) { return Runtime.TryCreateCopy(method, out dm); } public static T Pin(this T method) where T : MethodBase { Runtime.Pin(method); return method; } public static T Unpin(this T method) where T : MethodBase { Runtime.Unpin(method); return method; } public static MethodInfo GenerateNativeProxy(IntPtr target, MethodBase signature) { //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Expected O, but got Unknown Type type = (signature as MethodInfo)?.ReturnType ?? typeof(void); ParameterInfo[] parameters = signature.GetParameters(); Type[] array = new Type[parameters.Length]; for (int i = 0; i < parameters.Length; i++) { array[i] = parameters[i].ParameterType; } DynamicMethodDefinition val = new DynamicMethodDefinition("Native<" + ((long)target).ToString("X16", CultureInfo.InvariantCulture) + ">", type, array); MethodInfo methodInfo; try { methodInfo = val.StubCriticalDetour().Generate().Pin(); } finally { ((IDisposable)val)?.Dispose(); } NativeDetourData detour = Native.Create(methodInfo.GetNativeStart(), target); Native.MakeWritable(detour); Native.Apply(detour); Native.MakeExecutable(detour); Native.FlushICache(detour); Native.Free(detour); return methodInfo; } private static NativeDetourData ToNativeDetourData(IntPtr method, IntPtr target, uint size, byte type, IntPtr extra) { NativeDetourData result = default(NativeDetourData); result.Method = method; result.Target = target; result.Size = size; result.Type = type; result.Extra = extra; return result; } public static DynamicMethodDefinition StubCriticalDetour(this DynamicMethodDefinition dm) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) ILProcessor iLProcessor = dm.GetILProcessor(); ModuleDefinition module = ((MemberReference)iLProcessor.Body.Method).Module; for (int i = 0; i < 32; i++) { iLProcessor.Emit(OpCodes.Nop); } iLProcessor.Emit(OpCodes.Ldstr, ((MemberReference)dm.Definition).Name + " should've been detoured!"); iLProcessor.Emit(OpCodes.Newobj, module.ImportReference((MethodBase)_ctor_Exception)); iLProcessor.Emit(OpCodes.Throw); return dm; } public static void EmitDetourCopy(this ILProcessor il, IntPtr src, IntPtr dst, byte type) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) ModuleDefinition module = ((MemberReference)il.Body.Method).Module; il.Emit(OpCodes.Ldsfld, module.ImportReference(_f_Native)); il.Emit(OpCodes.Ldc_I8, (long)src); il.Emit(OpCodes.Conv_I); il.Emit(OpCodes.Ldc_I8, (long)dst); il.Emit(OpCodes.Conv_I); il.Emit(OpCodes.Ldc_I4, (int)type); il.Emit(OpCodes.Conv_U1); il.Emit(OpCodes.Callvirt, module.ImportReference((MethodBase)_m_Copy)); } public static void EmitDetourApply(this ILProcessor il, NativeDetourData data) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) ModuleDefinition module = ((MemberReference)il.Body.Method).Module; il.Emit(OpCodes.Ldsfld, module.ImportReference(_f_Native)); il.Emit(OpCodes.Ldc_I8, (long)data.Method); il.Emit(OpCodes.Conv_I); il.Emit(OpCodes.Ldc_I8, (long)data.Target); il.Emit(OpCodes.Conv_I); il.Emit(OpCodes.Ldc_I4, (int)data.Size); il.Emit(OpCodes.Ldc_I4, (int)data.Type); il.Emit(OpCodes.Conv_U1); il.Emit(OpCodes.Ldc_I8, (long)data.Extra); il.Emit(OpCodes.Conv_I); il.Emit(OpCodes.Call, module.ImportReference((MethodBase)_m_ToNativeDetourData)); il.Emit(OpCodes.Callvirt, module.ImportReference((MethodBase)_m_Apply)); } } public interface IDetourNativePlatform { NativeDetourData Create(IntPtr from, IntPtr to, byte? type = null); void Free(NativeDetourData detour); void Apply(NativeDetourData detour); void Copy(IntPtr src, IntPtr dst, byte type); void MakeWritable(IntPtr src, uint size); void MakeExecutable(IntPtr src, uint size); void MakeReadWriteExecutable(IntPtr src, uint size); void FlushICache(IntPtr src, uint size); IntPtr MemAlloc(uint size); void MemFree(IntPtr ptr); } public interface IDetourRuntimePlatform { bool OnMethodCompiledWillBeCalled { get; } event OnMethodCompiledEvent OnMethodCompiled; MethodBase GetIdentifiable(MethodBase method); IntPtr GetNativeStart(MethodBase method); MethodInfo CreateCopy(MethodBase method); bool TryCreateCopy(MethodBase method, out MethodInfo dm); void Pin(MethodBase method); void Unpin(MethodBase method); MethodBase GetDetourTarget(MethodBase from, MethodBase to); uint TryMemAllocScratchCloseTo(IntPtr target, out IntPtr ptr, int size); } public delegate void OnMethodCompiledEvent(MethodBase method, IntPtr codeStart, ulong codeSize); public struct NativeDetourData { public IntPtr Method; public IntPtr Target; public byte Type; public uint Size; public IntPtr Extra; } } namespace MonoMod.RuntimeDetour.Platforms { public class DetourNativeARMPlatform : IDetourNativePlatform { public enum DetourType : byte { Thumb, ThumbBX, AArch32, AArch32BX, AArch64 } [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate int d_flushicache(IntPtr code, ulong size); private static readonly uint[] DetourSizes = new uint[5] { 8u, 12u, 8u, 12u, 16u }; public bool ShouldFlushICache = true; private readonly byte[] _FlushCache32 = new byte[44] { 128, 64, 45, 233, 0, 48, 160, 225, 1, 192, 128, 224, 20, 224, 159, 229, 3, 0, 160, 225, 12, 16, 160, 225, 14, 112, 160, 225, 0, 32, 160, 227, 0, 0, 0, 239, 128, 128, 189, 232, 2, 0, 15, 0 }; private readonly byte[] _FlushCache64 = new byte[76] { 1, 0, 1, 139, 0, 244, 126, 146, 63, 0, 0, 235, 201, 0, 0, 84, 226, 3, 0, 170, 34, 126, 11, 213, 66, 16, 0, 145, 63, 0, 2, 235, 168, 255, 255, 84, 159, 59, 3, 213, 63, 0, 0, 235, 169, 0, 0, 84, 32, 117, 11, 213, 0, 16, 0, 145, 63, 0, 0, 235, 168, 255, 255, 84, 159, 59, 3, 213, 223, 63, 3, 213, 192, 3, 95, 214 }; private static DetourType GetDetourType(IntPtr from, IntPtr to) { if (IntPtr.Size >= 8) { return DetourType.AArch64; } bool num = ((long)from & 1) == 1; bool flag = ((long)to & 1) == 1; if (num) { if (flag) { return DetourType.Thumb; } return DetourType.ThumbBX; } if (flag) { return DetourType.AArch32BX; } return DetourType.AArch32; } public NativeDetourData Create(IntPtr from, IntPtr to, byte? type) { NativeDetourData nativeDetourData = default(NativeDetourData); nativeDetourData.Method = (IntPtr)((long)from & -2); nativeDetourData.Target = (IntPtr)((long)to & -2); NativeDetourData result = nativeDetourData; uint[] detourSizes = DetourSizes; int num = ((int?)type) ?? ((int)GetDetourType(from, to)); byte b = (byte)num; result.Type = (byte)num; result.Size = detourSizes[b]; return result; } public void Free(NativeDetourData detour) { } public void Apply(NativeDetourData detour) { int offs = 0; switch ((DetourType)detour.Type) { case DetourType.Thumb: detour.Method.Write(ref offs, 223); detour.Method.Write(ref offs, 248); detour.Method.Write(ref offs, 0); detour.Method.Write(ref offs, 240); detour.Method.Write(ref offs, (uint)(int)detour.Target | 1u); break; case DetourType.ThumbBX: detour.Method.Write(ref offs, 223); detour.Method.Write(ref offs, 248); detour.Method.Write(ref offs, 4); detour.Method.Write(ref offs, 160); detour.Method.Write(ref offs, 80); detour.Method.Write(ref offs, 71); detour.Method.Write(ref offs, 0); detour.Method.Write(ref offs, 191); detour.Method.Write(ref offs, (uint)(int)detour.Target | 0u); break; case DetourType.AArch32: detour.Method.Write(ref offs, 4); detour.Method.Write(ref offs, 240); detour.Method.Write(ref offs, 31); detour.Method.Write(ref offs, 229); detour.Method.Write(ref offs, (uint)(int)detour.Target | 0u); break; case DetourType.AArch32BX: detour.Method.Write(ref offs, 0); detour.Method.Write(ref offs, 128); detour.Method.Write(ref offs, 159); detour.Method.Write(ref offs, 229); detour.Method.Write(ref offs, 24); detour.Method.Write(ref offs, byte.MaxValue); detour.Method.Write(ref offs, 47); detour.Method.Write(ref offs, 225); detour.Method.Write(ref offs, (uint)(int)detour.Target | 1u); break; case DetourType.AArch64: detour.Method.Write(ref offs, 79); detour.Method.Write(ref offs, 0); detour.Method.Write(ref offs, 0); detour.Method.Write(ref offs, 88); detour.Method.Write(ref offs, 224); detour.Method.Write(ref offs, 1); detour.Method.Write(ref offs, 31); detour.Method.Write(ref offs, 214); detour.Method.Write(ref offs, (ulong)(long)detour.Target); break; default: throw new NotSupportedException($"Unknown detour type {detour.Type}"); } } public unsafe void Copy(IntPtr src, IntPtr dst, byte type) { switch ((DetourType)type) { case DetourType.Thumb: *(int*)(long)dst = *(int*)(long)src; *(int*)((long)dst + 4) = *(int*)((long)src + 4); break; case DetourType.ThumbBX: *(int*)(long)dst = *(int*)(long)src; *(short*)((long)dst + 4) = *(short*)((long)src + 4); *(short*)((long)dst + 6) = *(short*)((long)src + 6); *(int*)((long)dst + 8) = *(int*)((long)src + 8); break; case DetourType.AArch32: *(int*)(long)dst = *(int*)(long)src; *(int*)((long)dst + 4) = *(int*)((long)src + 4); break; case DetourType.AArch32BX: *(int*)(long)dst = *(int*)(long)src; *(int*)((long)dst + 4) = *(int*)((long)src + 4); *(int*)((long)dst + 8) = *(int*)((long)src + 8); break; case DetourType.AArch64: *(int*)(long)dst = *(int*)(long)src; *(int*)((long)dst + 4) = *(int*)((long)src + 4); *(long*)((long)dst + 8) = *(long*)((long)src + 8); break; default: throw new NotSupportedException($"Unknown detour type {type}"); } } public void MakeWritable(IntPtr src, uint size) { } public void MakeExecutable(IntPtr src, uint size) { } public void MakeReadWriteExecutable(IntPtr src, uint size) { } public unsafe void FlushICache(IntPtr src, uint size) { if (ShouldFlushICache) { byte[] array = ((IntPtr.Size >= 8) ? _FlushCache64 : _FlushCache32); fixed (byte* ptr = array) { DetourHelper.Native.MakeExecutable((IntPtr)ptr, (uint)array.Length); (Marshal.GetDelegateForFunctionPointer((IntPtr)ptr, typeof(d_flushicache)) as d_flushicache)(src, size); } } } public IntPtr MemAlloc(uint size) { return Marshal.AllocHGlobal((int)size); } public void MemFree(IntPtr ptr) { Marshal.FreeHGlobal(ptr); } } public class DetourNativeLibcPlatform : IDetourNativePlatform { [Flags] private enum MmapProts { PROT_READ = 1, PROT_WRITE = 2, PROT_EXEC = 4, PROT_NONE = 0, PROT_GROWSDOWN = 0x1000000, PROT_GROWSUP = 0x2000000 } private readonly IDetourNativePlatform Inner; private readonly long _Pagesize; public DetourNativeLibcPlatform(IDetourNativePlatform inner) { Inner = inner; _Pagesize = Environment.SystemPageSize; } private void SetMemPerms(IntPtr start, ulong len, MmapProts prot) { long pagesize = _Pagesize; long num = (long)start & ~(pagesize - 1); long num2 = ((long)start + (long)len + pagesize - 1) & ~(pagesize - 1); if (mprotect((IntPtr)num, (IntPtr)(num2 - num), prot) != 0) { throw new Win32Exception(); } } public void MakeWritable(IntPtr src, uint size) { SetMemPerms(src, size, MmapProts.PROT_READ | MmapProts.PROT_WRITE | MmapProts.PROT_EXEC); } public void MakeExecutable(IntPtr src, uint size) { SetMemPerms(src, size, MmapProts.PROT_READ | MmapProts.PROT_WRITE | MmapProts.PROT_EXEC); } public void MakeReadWriteExecutable(IntPtr src, uint size) { SetMemPerms(src, size, MmapProts.PROT_READ | MmapProts.PROT_WRITE | MmapProts.PROT_EXEC); } public void FlushICache(IntPtr src, uint size) { Inner.FlushICache(src, size); } public NativeDetourData Create(IntPtr from, IntPtr to, byte? type) { return Inner.Create(from, to, type); } public void Free(NativeDetourData detour) { Inner.Free(detour); } public void Apply(NativeDetourData detour) { Inner.Apply(detour); } public void Copy(IntPtr src, IntPtr dst, byte type) { Inner.Copy(src, dst, type); } public IntPtr MemAlloc(uint size) { return Inner.MemAlloc(size); } public void MemFree(IntPtr ptr) { Inner.MemFree(ptr); } [DllImport("libc", CallingConvention = CallingConvention.Cdecl, SetLastError = true)] private static extern int mprotect(IntPtr start, IntPtr len, MmapProts prot); } public class DetourNativeMonoPlatform : IDetourNativePlatform { [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate int d_mono_pagesize(); [UnmanagedFunctionPointer(CallingConvention.Cdecl, SetLastError = true)] private delegate int d_mono_mprotect(IntPtr addr, IntPtr length, int flags); [Flags] private enum MmapProts { PROT_READ = 1, PROT_WRITE = 2, PROT_EXEC = 4, PROT_NONE = 0, PROT_GROWSDOWN = 0x1000000, PROT_GROWSUP = 0x2000000 } private readonly IDetourNativePlatform Inner; private readonly long _Pagesize; [DynDllImport("mono", new string[] { })] private d_mono_pagesize mono_pagesize; [DynDllImport("mono", new string[] { })] private d_mono_mprotect mono_mprotect; public DetourNativeMonoPlatform(IDetourNativePlatform inner, string libmono) { Inner = inner; Dictionary> dictionary = new Dictionary>(); if (!string.IsNullOrEmpty(libmono)) { dictionary.Add("mono", new List { DynDllMapping.op_Implicit(libmono) }); } DynDll.ResolveDynDllImports((object)this, dictionary); _Pagesize = mono_pagesize(); } private void SetMemPerms(IntPtr start, ulong len, MmapProts prot) { long pagesize = _Pagesize; long num = (long)start & ~(pagesize - 1); long num2 = ((long)start + (long)len + pagesize - 1) & ~(pagesize - 1); if (mono_mprotect((IntPtr)num, (IntPtr)(num2 - num), (int)prot) != 0 && Marshal.GetLastWin32Error() != 0) { throw new Win32Exception(); } } public void MakeWritable(IntPtr src, uint size) { SetMemPerms(src, size, MmapProts.PROT_READ | MmapProts.PROT_WRITE | MmapProts.PROT_EXEC); } public void MakeExecutable(IntPtr src, uint size) { SetMemPerms(src, size, MmapProts.PROT_READ | MmapProts.PROT_WRITE | MmapProts.PROT_EXEC); } public void MakeReadWriteExecutable(IntPtr src, uint size) { SetMemPerms(src, size, MmapProts.PROT_READ | MmapProts.PROT_WRITE | MmapProts.PROT_EXEC); } public void FlushICache(IntPtr src, uint size) { Inner.FlushICache(src, size); } public NativeDetourData Create(IntPtr from, IntPtr to, byte? type) { return Inner.Create(from, to, type); } public void Free(NativeDetourData detour) { Inner.Free(detour); } public void Apply(NativeDetourData detour) { Inner.Apply(detour); } public void Copy(IntPtr src, IntPtr dst, byte type) { Inner.Copy(src, dst, type); } public IntPtr MemAlloc(uint size) { return Inner.MemAlloc(size); } public void MemFree(IntPtr ptr) { Inner.MemFree(ptr); } } public class DetourNativeMonoPosixPlatform : IDetourNativePlatform { [Flags] private enum MmapProts { PROT_READ = 1, PROT_WRITE = 2, PROT_EXEC = 4, PROT_NONE = 0, PROT_GROWSDOWN = 0x1000000, PROT_GROWSUP = 0x2000000 } public enum SysconfName { _SC_ARG_MAX = 0, _SC_CHILD_MAX = 1, _SC_CLK_TCK = 2, _SC_NGROUPS_MAX = 3, _SC_OPEN_MAX = 4, _SC_STREAM_MAX = 5, _SC_TZNAME_MAX = 6, _SC_JOB_CONTROL = 7, _SC_SAVED_IDS = 8, _SC_REALTIME_SIGNALS = 9, _SC_PRIORITY_SCHEDULING = 10, _SC_TIMERS = 11, _SC_ASYNCHRONOUS_IO = 12, _SC_PRIORITIZED_IO = 13, _SC_SYNCHRONIZED_IO = 14, _SC_FSYNC = 15, _SC_MAPPED_FILES = 16, _SC_MEMLOCK = 17, _SC_MEMLOCK_RANGE = 18, _SC_MEMORY_PROTECTION = 19, _SC_MESSAGE_PASSING = 20, _SC_SEMAPHORES = 21, _SC_SHARED_MEMORY_OBJECTS = 22, _SC_AIO_LISTIO_MAX = 23, _SC_AIO_MAX = 24, _SC_AIO_PRIO_DELTA_MAX = 25, _SC_DELAYTIMER_MAX = 26, _SC_MQ_OPEN_MAX = 27, _SC_MQ_PRIO_MAX = 28, _SC_VERSION = 29, _SC_PAGESIZE = 30, _SC_RTSIG_MAX = 31, _SC_SEM_NSEMS_MAX = 32, _SC_SEM_VALUE_MAX = 33, _SC_SIGQUEUE_MAX = 34, _SC_TIMER_MAX = 35, _SC_BC_BASE_MAX = 36, _SC_BC_DIM_MAX = 37, _SC_BC_SCALE_MAX = 38, _SC_BC_STRING_MAX = 39, _SC_COLL_WEIGHTS_MAX = 40, _SC_EQUIV_CLASS_MAX = 41, _SC_EXPR_NEST_MAX = 42, _SC_LINE_MAX = 43, _SC_RE_DUP_MAX = 44, _SC_CHARCLASS_NAME_MAX = 45, _SC_2_VERSION = 46, _SC_2_C_BIND = 47, _SC_2_C_DEV = 48, _SC_2_FORT_DEV = 49, _SC_2_FORT_RUN = 50, _SC_2_SW_DEV = 51, _SC_2_LOCALEDEF = 52, _SC_PII = 53, _SC_PII_XTI = 54, _SC_PII_SOCKET = 55, _SC_PII_INTERNET = 56, _SC_PII_OSI = 57, _SC_POLL = 58, _SC_SELECT = 59, _SC_UIO_MAXIOV = 60, _SC_IOV_MAX = 60, _SC_PII_INTERNET_STREAM = 61, _SC_PII_INTERNET_DGRAM = 62, _SC_PII_OSI_COTS = 63, _SC_PII_OSI_CLTS = 64, _SC_PII_OSI_M = 65, _SC_T_IOV_MAX = 66, _SC_THREADS = 67, _SC_THREAD_SAFE_FUNCTIONS = 68, _SC_GETGR_R_SIZE_MAX = 69, _SC_GETPW_R_SIZE_MAX = 70, _SC_LOGIN_NAME_MAX = 71, _SC_TTY_NAME_MAX = 72, _SC_THREAD_DESTRUCTOR_ITERATIONS = 73, _SC_THREAD_KEYS_MAX = 74, _SC_THREAD_STACK_MIN = 75, _SC_THREAD_THREADS_MAX = 76, _SC_THREAD_ATTR_STACKADDR = 77, _SC_THREAD_ATTR_STACKSIZE = 78, _SC_THREAD_PRIORITY_SCHEDULING = 79, _SC_THREAD_PRIO_INHERIT = 80, _SC_THREAD_PRIO_PROTECT = 81, _SC_THREAD_PROCESS_SHARED = 82, _SC_NPROCESSORS_CONF = 83, _SC_NPROCESSORS_ONLN = 84, _SC_PHYS_PAGES = 85, _SC_AVPHYS_PAGES = 86, _SC_ATEXIT_MAX = 87, _SC_PASS_MAX = 88, _SC_XOPEN_VERSION = 89, _SC_XOPEN_XCU_VERSION = 90, _SC_XOPEN_UNIX = 91, _SC_XOPEN_CRYPT = 92, _SC_XOPEN_ENH_I18N = 93, _SC_XOPEN_SHM = 94, _SC_2_CHAR_TERM = 95, _SC_2_C_VERSION = 96, _SC_2_UPE = 97, _SC_XOPEN_XPG2 = 98, _SC_XOPEN_XPG3 = 99, _SC_XOPEN_XPG4 = 100, _SC_CHAR_BIT = 101, _SC_CHAR_MAX = 102, _SC_CHAR_MIN = 103, _SC_INT_MAX = 104, _SC_INT_MIN = 105, _SC_LONG_BIT = 106, _SC_WORD_BIT = 107, _SC_MB_LEN_MAX = 108, _SC_NZERO = 109, _SC_SSIZE_MAX = 110, _SC_SCHAR_MAX = 111, _SC_SCHAR_MIN = 112, _SC_SHRT_MAX = 113, _SC_SHRT_MIN = 114, _SC_UCHAR_MAX = 115, _SC_UINT_MAX = 116, _SC_ULONG_MAX = 117, _SC_USHRT_MAX = 118, _SC_NL_ARGMAX = 119, _SC_NL_LANGMAX = 120, _SC_NL_MSGMAX = 121, _SC_NL_NMAX = 122, _SC_NL_SETMAX = 123, _SC_NL_TEXTMAX = 124, _SC_XBS5_ILP32_OFF32 = 125, _SC_XBS5_ILP32_OFFBIG = 126, _SC_XBS5_LP64_OFF64 = 127, _SC_XBS5_LPBIG_OFFBIG = 128, _SC_XOPEN_LEGACY = 129, _SC_XOPEN_REALTIME = 130, _SC_XOPEN_REALTIME_THREADS = 131, _SC_ADVISORY_INFO = 132, _SC_BARRIERS = 133, _SC_BASE = 134, _SC_C_LANG_SUPPORT = 135, _SC_C_LANG_SUPPORT_R = 136, _SC_CLOCK_SELECTION = 137, _SC_CPUTIME = 138, _SC_THREAD_CPUTIME = 139, _SC_DEVICE_IO = 140, _SC_DEVICE_SPECIFIC = 141, _SC_DEVICE_SPECIFIC_R = 142, _SC_FD_MGMT = 143, _SC_FIFO = 144, _SC_PIPE = 145, _SC_FILE_ATTRIBUTES = 146, _SC_FILE_LOCKING = 147, _SC_FILE_SYSTEM = 148, _SC_MONOTONIC_CLOCK = 149, _SC_MULTI_PROCESS = 150, _SC_SINGLE_PROCESS = 151, _SC_NETWORKING = 152, _SC_READER_WRITER_LOCKS = 153, _SC_SPIN_LOCKS = 154, _SC_REGEXP = 155, _SC_REGEX_VERSION = 156, _SC_SHELL = 157, _SC_SIGNALS = 158, _SC_SPAWN = 159, _SC_SPORADIC_SERVER = 160, _SC_THREAD_SPORADIC_SERVER = 161, _SC_SYSTEM_DATABASE = 162, _SC_SYSTEM_DATABASE_R = 163, _SC_TIMEOUTS = 164, _SC_TYPED_MEMORY_OBJECTS = 165, _SC_USER_GROUPS = 166, _SC_USER_GROUPS_R = 167, _SC_2_PBS = 168, _SC_2_PBS_ACCOUNTING = 169, _SC_2_PBS_LOCATE = 170, _SC_2_PBS_MESSAGE = 171, _SC_2_PBS_TRACK = 172, _SC_SYMLOOP_MAX = 173, _SC_STREAMS = 174, _SC_2_PBS_CHECKPOINT = 175, _SC_V6_ILP32_OFF32 = 176, _SC_V6_ILP32_OFFBIG = 177, _SC_V6_LP64_OFF64 = 178, _SC_V6_LPBIG_OFFBIG = 179, _SC_HOST_NAME_MAX = 180, _SC_TRACE = 181, _SC_TRACE_EVENT_FILTER = 182, _SC_TRACE_INHERIT = 183, _SC_TRACE_LOG = 184, _SC_LEVEL1_ICACHE_SIZE = 185, _SC_LEVEL1_ICACHE_ASSOC = 186, _SC_LEVEL1_ICACHE_LINESIZE = 187, _SC_LEVEL1_DCACHE_SIZE = 188, _SC_LEVEL1_DCACHE_ASSOC = 189, _SC_LEVEL1_DCACHE_LINESIZE = 190, _SC_LEVEL2_CACHE_SIZE = 191, _SC_LEVEL2_CACHE_ASSOC = 192, _SC_LEVEL2_CACHE_LINESIZE = 193, _SC_LEVEL3_CACHE_SIZE = 194, _SC_LEVEL3_CACHE_ASSOC = 195, _SC_LEVEL3_CACHE_LINESIZE = 196, _SC_LEVEL4_CACHE_SIZE = 197, _SC_LEVEL4_CACHE_ASSOC = 198, _SC_LEVEL4_CACHE_LINESIZE = 199 } public enum Errno { EPERM = 1, ENOENT = 2, ESRCH = 3, EINTR = 4, EIO = 5, ENXIO = 6, E2BIG = 7, ENOEXEC = 8, EBADF = 9, ECHILD = 10, EAGAIN = 11, ENOMEM = 12, EACCES = 13, EFAULT = 14, ENOTBLK = 15, EBUSY = 16, EEXIST = 17, EXDEV = 18, ENODEV = 19, ENOTDIR = 20, EISDIR = 21, EINVAL = 22, ENFILE = 23, EMFILE = 24, ENOTTY = 25, ETXTBSY = 26, EFBIG = 27, ENOSPC = 28, ESPIPE = 29, EROFS = 30, EMLINK = 31, EPIPE = 32, EDOM = 33, ERANGE = 34, EDEADLK = 35, ENAMETOOLONG = 36, ENOLCK = 37, ENOSYS = 38, ENOTEMPTY = 39, ELOOP = 40, EWOULDBLOCK = 11, ENOMSG = 42, EIDRM = 43, ECHRNG = 44, EL2NSYNC = 45, EL3HLT = 46, EL3RST = 47, ELNRNG = 48, EUNATCH = 49, ENOCSI = 50, EL2HLT = 51, EBADE = 52, EBADR = 53, EXFULL = 54, ENOANO = 55, EBADRQC = 56, EBADSLT = 57, EDEADLOCK = 35, EBFONT = 59, ENOSTR = 60, ENODATA = 61, ETIME = 62, ENOSR = 63, ENONET = 64, ENOPKG = 65, EREMOTE = 66, ENOLINK = 67, EADV = 68, ESRMNT = 69, ECOMM = 70, EPROTO = 71, EMULTIHOP = 72, EDOTDOT = 73, EBADMSG = 74, EOVERFLOW = 75, ENOTUNIQ = 76, EBADFD = 77, EREMCHG = 78, ELIBACC = 79, ELIBBAD = 80, ELIBSCN = 81, ELIBMAX = 82, ELIBEXEC = 83, EILSEQ = 84, ERESTART = 85, ESTRPIPE = 86, EUSERS = 87, ENOTSOCK = 88, EDESTADDRREQ = 89, EMSGSIZE = 90, EPROTOTYPE = 91, ENOPROTOOPT = 92, EPROTONOSUPPORT = 93, ESOCKTNOSUPPORT = 94, EOPNOTSUPP = 95, EPFNOSUPPORT = 96, EAFNOSUPPORT = 97, EADDRINUSE = 98, EADDRNOTAVAIL = 99, ENETDOWN = 100, ENETUNREACH = 101, ENETRESET = 102, ECONNABORTED = 103, ECONNRESET = 104, ENOBUFS = 105, EISCONN = 106, ENOTCONN = 107, ESHUTDOWN = 108, ETOOMANYREFS = 109, ETIMEDOUT = 110, ECONNREFUSED = 111, EHOSTDOWN = 112, EHOSTUNREACH = 113, EALREADY = 114, EINPROGRESS = 115, ESTALE = 116, EUCLEAN = 117, ENOTNAM = 118, ENAVAIL = 119, EISNAM = 120, EREMOTEIO = 121, EDQUOT = 122, ENOMEDIUM = 123, EMEDIUMTYPE = 124, ECANCELED = 125, ENOKEY = 126, EKEYEXPIRED = 127, EKEYREVOKED = 128, EKEYREJECTED = 129, EOWNERDEAD = 130, ENOTRECOVERABLE = 131, EPROCLIM = 1067, EBADRPC = 1072, ERPCMISMATCH = 1073, EPROGUNAVAIL = 1074, EPROGMISMATCH = 1075, EPROCUNAVAIL = 1076, EFTYPE = 1079, EAUTH = 1080, ENEEDAUTH = 1081, EPWROFF = 1082, EDEVERR = 1083, EBADEXEC = 1085, EBADARCH = 1086, ESHLIBVERS = 1087, EBADMACHO = 1088, ENOATTR = 1093, ENOPOLICY = 1103 } private readonly IDetourNativePlatform Inner; private readonly long _Pagesize; public DetourNativeMonoPosixPlatform(IDetourNativePlatform inner) { Inner = inner; _Pagesize = sysconf(SysconfName._SC_PAGESIZE, (Errno)0); } private static string GetLastError(string name) { int num = _GetLastError(); if (ToErrno(num, out var rval) == 0) { return $"{name} returned {rval}"; } return $"{name} returned 0x${num:X8}"; } private void SetMemPerms(IntPtr start, ulong len, MmapProts prot) { long pagesize = _Pagesize; long num = (long)start & ~(pagesize - 1); long num2 = ((long)start + (long)len + pagesize - 1) & ~(pagesize - 1); if (mprotect((IntPtr)num, (ulong)(num2 - num), prot) != 0) { throw new Exception(GetLastError("mprotect")); } } public void MakeWritable(IntPtr src, uint size) { SetMemPerms(src, size, MmapProts.PROT_READ | MmapProts.PROT_WRITE | MmapProts.PROT_EXEC); } public void MakeExecutable(IntPtr src, uint size) { SetMemPerms(src, size, MmapProts.PROT_READ | MmapProts.PROT_WRITE | MmapProts.PROT_EXEC); } public void MakeReadWriteExecutable(IntPtr src, uint size) { SetMemPerms(src, size, MmapProts.PROT_READ | MmapProts.PROT_WRITE | MmapProts.PROT_EXEC); } public void FlushICache(IntPtr src, uint size) { Inner.FlushICache(src, size); } public NativeDetourData Create(IntPtr from, IntPtr to, byte? type) { return Inner.Create(from, to, type); } public void Free(NativeDetourData detour) { Inner.Free(detour); } public void Apply(NativeDetourData detour) { Inner.Apply(detour); } public void Copy(IntPtr src, IntPtr dst, byte type) { Inner.Copy(src, dst, type); } public IntPtr MemAlloc(uint size) { return Inner.MemAlloc(size); } public void MemFree(IntPtr ptr) { Inner.MemFree(ptr); } [DllImport("MonoPosixHelper", EntryPoint = "Mono_Posix_Syscall_sysconf", SetLastError = true)] public static extern long sysconf(SysconfName name, Errno defaultError); [DllImport("MonoPosixHelper", EntryPoint = "Mono_Posix_Syscall_mprotect", SetLastError = true)] private static extern int mprotect(IntPtr start, ulong len, MmapProts prot); [DllImport("MonoPosixHelper", CallingConvention = CallingConvention.Cdecl, EntryPoint = "Mono_Posix_Stdlib_GetLastError")] private static extern int _GetLastError(); [DllImport("MonoPosixHelper", EntryPoint = "Mono_Posix_ToErrno")] private static extern int ToErrno(int value, out Errno rval); } public class DetourNativeWindowsPlatform : IDetourNativePlatform { [Flags] private enum PAGE : uint { UNSET = 0u, NOACCESS = 1u, READONLY = 2u, READWRITE = 4u, WRITECOPY = 8u, EXECUTE = 0x10u, EXECUTE_READ = 0x20u, EXECUTE_READWRITE = 0x40u, EXECUTE_WRITECOPY = 0x80u, GUARD = 0x100u, NOCACHE = 0x200u, WRITECOMBINE = 0x400u } private enum MEM : uint { UNSET = 0u, MEM_COMMIT = 0x1000u, MEM_RESERVE = 0x2000u, MEM_FREE = 0x10000u, MEM_PRIVATE = 0x20000u, MEM_MAPPED = 0x40000u, MEM_IMAGE = 0x1000000u } private struct MEMORY_BASIC_INFORMATION { public IntPtr BaseAddress; public IntPtr AllocationBase; public PAGE AllocationProtect; public IntPtr RegionSize; public MEM State; public PAGE Protect; public MEM Type; } private readonly IDetourNativePlatform Inner; public DetourNativeWindowsPlatform(IDetourNativePlatform inner) { Inner = inner; } public void MakeWritable(IntPtr src, uint size) { if (!VirtualProtect(src, (IntPtr)size, PAGE.EXECUTE_READWRITE, out var _)) { throw LogAllSections(Marshal.GetLastWin32Error(), "MakeWriteable", src, size); } } public void MakeExecutable(IntPtr src, uint size) { if (!VirtualProtect(src, (IntPtr)size, PAGE.EXECUTE_READWRITE, out var _)) { throw LogAllSections(Marshal.GetLastWin32Error(), "MakeExecutable", src, size); } } public void MakeReadWriteExecutable(IntPtr src, uint size) { if (!VirtualProtect(src, (IntPtr)size, PAGE.EXECUTE_READWRITE, out var _)) { throw LogAllSections(Marshal.GetLastWin32Error(), "MakeExecutable", src, size); } } public void FlushICache(IntPtr src, uint size) { if (!FlushInstructionCache(GetCurrentProcess(), src, (UIntPtr)size)) { throw LogAllSections(Marshal.GetLastWin32Error(), "FlushICache", src, size); } } private unsafe Exception LogAllSections(int error, string from, IntPtr src, uint size) { Exception ex = new Win32Exception(error); if (MMDbgLog.Writer == null) { return ex; } MMDbgLog.Log($"{from} failed for 0x{(long)src:X16} + {size} - logging all memory sections"); MMDbgLog.Log("reason: " + ex.Message); try { IntPtr currentProcess = GetCurrentProcess(); IntPtr intPtr = (IntPtr)65536; int num = 0; MEMORY_BASIC_INFORMATION lpBuffer; while (VirtualQueryEx(currentProcess, intPtr, out lpBuffer, sizeof(MEMORY_BASIC_INFORMATION)) != 0) { ulong num2 = (ulong)(long)src; ulong num3 = num2 + size; long num4 = (long)lpBuffer.BaseAddress; ulong num5 = (ulong)(num4 + (long)lpBuffer.RegionSize); bool flag = (ulong)num4 <= num3 && num2 <= num5; MMDbgLog.Log(string.Format("{0} #{1}", flag ? "*" : "-", num++)); MMDbgLog.Log($"addr: 0x{(long)lpBuffer.BaseAddress:X16}"); MMDbgLog.Log($"size: 0x{(long)lpBuffer.RegionSize:X16}"); MMDbgLog.Log($"aaddr: 0x{(long)lpBuffer.AllocationBase:X16}"); MMDbgLog.Log($"state: {lpBuffer.State}"); MMDbgLog.Log($"type: {lpBuffer.Type}"); MMDbgLog.Log($"protect: {lpBuffer.Protect}"); MMDbgLog.Log($"aprotect: {lpBuffer.AllocationProtect}"); try { IntPtr intPtr2 = intPtr; intPtr = (IntPtr)((long)lpBuffer.BaseAddress + (long)lpBuffer.RegionSize); if ((ulong)(long)intPtr <= (ulong)(long)intPtr2) { break; } } catch (OverflowException) { MMDbgLog.Log("overflow"); break; } } } catch { throw ex; } return ex; } public NativeDetourData Create(IntPtr from, IntPtr to, byte? type) { return Inner.Create(from, to, type); } public void Free(NativeDetourData detour) { Inner.Free(detour); } public void Apply(NativeDetourData detour) { Inner.Apply(detour); } public void Copy(IntPtr src, IntPtr dst, byte type) { Inner.Copy(src, dst, type); } public IntPtr MemAlloc(uint size) { return Inner.MemAlloc(size); } public void MemFree(IntPtr ptr) { Inner.MemFree(ptr); } [DllImport("kernel32.dll", SetLastError = true)] private static extern bool VirtualProtect(IntPtr lpAddress, IntPtr dwSize, PAGE flNewProtect, out PAGE lpflOldProtect); [DllImport("kernel32.dll", SetLastError = true)] private static extern IntPtr GetCurrentProcess(); [DllImport("kernel32.dll", SetLastError = true)] private static extern bool FlushInstructionCache(IntPtr hProcess, IntPtr lpBaseAddress, UIntPtr dwSize); [DllImport("kernel32.dll", SetLastError = true)] private static extern int VirtualQueryEx(IntPtr hProcess, IntPtr lpAddress, out MEMORY_BASIC_INFORMATION lpBuffer, int dwLength); } public class DetourNativeX86Platform : IDetourNativePlatform { public enum DetourType : byte { Rel32, Abs32, Abs64, Abs64Split } private static readonly uint[] DetourSizes = new uint[4] { 5u, 6u, 14u, 6u }; private static bool Is32Bit(long to) { return (to & 0x7FFFFFFF) == to; } private unsafe static DetourType GetDetourType(IntPtr from, IntPtr to, ref IntPtr extra) { long num = (long)to - ((long)from + 5); if ((Is32Bit(num) || Is32Bit(-num)) && ((byte*)(void*)from)[5] != 95) { return DetourType.Rel32; } if (Is32Bit((long)to)) { return DetourType.Abs32; } if ((DetourHelper.Runtime?.TryMemAllocScratchCloseTo(from, out extra, 8) ?? 0) >= 8) { num = (long)extra - ((long)from + 6); if (Is32Bit(num) || Is32Bit(-num)) { return DetourType.Abs64Split; } } return DetourType.Abs64; } public NativeDetourData Create(IntPtr from, IntPtr to, byte? type) { NativeDetourData nativeDetourData = default(NativeDetourData); nativeDetourData.Method = from; nativeDetourData.Target = to; NativeDetourData result = nativeDetourData; uint[] detourSizes = DetourSizes; int num = ((int?)type) ?? ((int)GetDetourType(from, to, ref result.Extra)); byte b = (byte)num; result.Type = (byte)num; result.Size = detourSizes[b]; return result; } public void Free(NativeDetourData detour) { _ = detour.Type; _ = 3; } public void Apply(NativeDetourData detour) { int offs = 0; switch ((DetourType)detour.Type) { case DetourType.Rel32: detour.Method.Write(ref offs, 233); detour.Method.Write(ref offs, (uint)((long)detour.Target - ((long)detour.Method + offs + 4))); break; case DetourType.Abs32: detour.Method.Write(ref offs, 104); detour.Method.Write(ref offs, (uint)(int)detour.Target); detour.Method.Write(ref offs, 195); break; case DetourType.Abs64: case DetourType.Abs64Split: detour.Method.Write(ref offs, byte.MaxValue); detour.Method.Write(ref offs, 37); if (detour.Type == 3) { detour.Method.Write(ref offs, (uint)((long)detour.Extra - ((long)detour.Method + offs + 4))); offs = 0; detour.Extra.Write(ref offs, (ulong)(long)detour.Target); } else { detour.Method.Write(ref offs, 0u); detour.Method.Write(ref offs, (ulong)(long)detour.Target); } break; default: throw new NotSupportedException($"Unknown detour type {detour.Type}"); } } public unsafe void Copy(IntPtr src, IntPtr dst, byte type) { switch ((DetourType)type) { case DetourType.Rel32: *(int*)(long)dst = *(int*)(long)src; *(sbyte*)((long)dst + 4) = *(sbyte*)((long)src + 4); break; case DetourType.Abs32: case DetourType.Abs64Split: *(int*)(long)dst = *(int*)(long)src; *(short*)((long)dst + 4) = *(short*)((long)src + 4); break; case DetourType.Abs64: *(long*)(long)dst = *(long*)(long)src; *(int*)((long)dst + 8) = *(int*)((long)src + 8); *(short*)((long)dst + 12) = *(short*)((long)src + 12); break; default: throw new NotSupportedException($"Unknown detour type {type}"); } } public void MakeWritable(IntPtr src, uint size) { } public void MakeExecutable(IntPtr src, uint size) { } public void MakeReadWriteExecutable(IntPtr src, uint size) { } public void FlushICache(IntPtr src, uint size) { } public IntPtr MemAlloc(uint size) { return Marshal.AllocHGlobal((int)size); } public void MemFree(IntPtr ptr) { Marshal.FreeHGlobal(ptr); } } public abstract class DetourRuntimeILPlatform : IDetourRuntimePlatform { private struct _SelftestStruct { private readonly short Value; private readonly byte E1; private readonly byte E2; private readonly byte E3; [MethodImpl(MethodImplOptions.NoInlining)] public short _SelftestGetInStruct() { Console.Error.WriteLine("If you're reading this, the MonoMod.RuntimeDetour selftest failed."); return -1; } } protected class PrivateMethodPin { public MethodPinInfo Pin; } public struct MethodPinInfo { public int Count; public MethodBase Method; public RuntimeMethodHandle Handle; public override string ToString() { return $"(MethodPinInfo: {Count}, {Method}, 0x{(long)Handle.Value:X})"; } } protected enum GlueThiscallStructRetPtrOrder { Original, ThisRetArgs, RetThisArgs } protected GlueThiscallStructRetPtrOrder GlueThiscallStructRetPtr; protected GlueThiscallStructRetPtrOrder GlueThiscallInStructRetPtr; protected ConcurrentDictionary PinnedMethods = new ConcurrentDictionary(); protected ConcurrentDictionary PinnedHandles = new ConcurrentDictionary(); private IntPtr ReferenceNonDynamicPoolPtr; private IntPtr ReferenceDynamicPoolPtr; protected static readonly uint _MemAllocScratchDummySafeSize = 16u; protected static readonly MethodInfo _MemAllocScratchDummy = typeof(DetourRuntimeILPlatform).GetMethod("MemAllocScratchDummy", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); public abstract bool OnMethodCompiledWillBeCalled { get; } public abstract event OnMethodCompiledEvent OnMethodCompiled; protected abstract RuntimeMethodHandle GetMethodHandle(MethodBase method); public unsafe DetourRuntimeILPlatform() { //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_0191: Expected O, but got Unknown MethodInfo method = typeof(DetourRuntimeILPlatform).GetMethod("_SelftestGetRefPtr", BindingFlags.Instance | BindingFlags.NonPublic); MethodInfo method2 = typeof(DetourRuntimeILPlatform).GetMethod("_SelftestGetRefPtrHook", BindingFlags.Static | BindingFlags.NonPublic); _HookSelftest(method, method2); IntPtr arg = ((Func)Delegate.CreateDelegate(typeof(Func), this, method))(); MethodInfo method3 = typeof(DetourRuntimeILPlatform).GetMethod("_SelftestGetStruct", BindingFlags.Instance | BindingFlags.NonPublic); MethodInfo method4 = typeof(DetourRuntimeILPlatform).GetMethod("_SelftestGetStructHook", BindingFlags.Static | BindingFlags.NonPublic); _HookSelftest(method3, method4); fixed (GlueThiscallStructRetPtrOrder* ptr = &GlueThiscallStructRetPtr) { ((Func)Delegate.CreateDelegate(typeof(Func), this, method3))((IntPtr)ptr, (IntPtr)ptr, arg); } MethodInfo method5 = typeof(_SelftestStruct).GetMethod("_SelftestGetInStruct", BindingFlags.Instance | BindingFlags.Public); MethodInfo method6 = typeof(DetourRuntimeILPlatform).GetMethod("_SelftestGetInStructHook", BindingFlags.Static | BindingFlags.NonPublic); _HookSelftest(method5, method6); fixed (GlueThiscallStructRetPtrOrder* ptr2 = &GlueThiscallInStructRetPtr) { *ptr2 = (GlueThiscallStructRetPtrOrder)((Func)Delegate.CreateDelegate(firstArgument: default(_SelftestStruct), type: typeof(Func), method: method5))(); if (*ptr2 == (GlueThiscallStructRetPtrOrder)(-1)) { throw new Exception("_SelftestGetInStruct failed!"); } } Pin(method); ReferenceNonDynamicPoolPtr = GetNativeStart(method); if (DynamicMethodDefinition.IsDynamicILAvailable) { DynamicMethodDefinition val = new DynamicMethodDefinition((MethodBase)_MemAllocScratchDummy); MethodBase method7; try { val.Name = "MemAllocScratch"; method7 = DMDGenerator.Generate(val, (object)null); } finally { ((IDisposable)val)?.Dispose(); } Pin(method7); ReferenceDynamicPoolPtr = GetNativeStart(method7); } } private void _HookSelftest(MethodInfo from, MethodInfo to) { Pin(from); Pin(to); NativeDetourData detour = DetourHelper.Native.Create(GetNativeStart(from), GetNativeStart(to)); DetourHelper.Native.MakeWritable(detour); DetourHelper.Native.Apply(detour); DetourHelper.Native.MakeExecutable(detour); DetourHelper.Native.FlushICache(detour); DetourHelper.Native.Free(detour); } [MethodImpl(MethodImplOptions.NoInlining)] private IntPtr _SelftestGetRefPtr() { Console.Error.WriteLine("If you're reading this, the MonoMod.RuntimeDetour selftest failed."); throw new Exception("This method should've been detoured!"); } private static IntPtr _SelftestGetRefPtrHook(IntPtr self) { return self; } [MethodImpl(MethodImplOptions.NoInlining)] private _SelftestStruct _SelftestGetStruct(IntPtr x, IntPtr y, IntPtr thisPtr) { Console.Error.WriteLine("If you're reading this, the MonoMod.RuntimeDetour selftest failed."); throw new Exception("_SelftestGetStruct failed!"); } private unsafe static void _SelftestGetStructHook(IntPtr a, IntPtr b, IntPtr c, IntPtr d, IntPtr e) { if (b == c) { *(int*)(void*)b = 0; } else if (b == e) { *(int*)(void*)c = 2; } else { *(int*)(void*)c = 1; } } private unsafe static short _SelftestGetInStructHook(IntPtr a) { *(short*)(void*)a = 2; return 0; } protected virtual IntPtr GetFunctionPointer(MethodBase method, RuntimeMethodHandle handle) { return handle.GetFunctionPointer(); } protected virtual void PrepareMethod(MethodBase method, RuntimeMethodHandle handle) { RuntimeHelpers.PrepareMethod(handle); } protected virtual void PrepareMethod(MethodBase method, RuntimeMethodHandle handle, RuntimeTypeHandle[] instantiation) { RuntimeHelpers.PrepareMethod(handle, instantiation); } protected virtual void DisableInlining(MethodBase method, RuntimeMethodHandle handle) { } public virtual MethodBase GetIdentifiable(MethodBase method) { if (!PinnedHandles.TryGetValue(GetMethodHandle(method), out var value)) { return method; } return value.Pin.Method; } public virtual MethodPinInfo GetPin(MethodBase method) { if (!PinnedMethods.TryGetValue(method, out var value)) { return default(MethodPinInfo); } return value.Pin; } public virtual MethodPinInfo GetPin(RuntimeMethodHandle handle) { if (!PinnedHandles.TryGetValue(handle, out var value)) { return default(MethodPinInfo); } return value.Pin; } public virtual MethodPinInfo[] GetPins() { return (from p in PinnedHandles.Values.ToArray() select p.Pin).ToArray(); } public virtual IntPtr GetNativeStart(MethodBase method) { method = GetIdentifiable(method); if (PinnedMethods.TryGetValue(method, out var value)) { return GetFunctionPointer(method, value.Pin.Handle); } return GetFunctionPointer(method, GetMethodHandle(method)); } public virtual void Pin(MethodBase method) { method = GetIdentifiable(method); Interlocked.Increment(ref PinnedMethods.GetOrAdd(method, delegate(MethodBase m) { PrivateMethodPin privateMethodPin = new PrivateMethodPin { Pin = { Method = m } }; RuntimeMethodHandle runtimeMethodHandle = (privateMethodPin.Pin.Handle = GetMethodHandle(m)); PinnedHandles[runtimeMethodHandle] = privateMethodPin; DisableInlining(method, runtimeMethodHandle); Type? declaringType = method.DeclaringType; if ((object)declaringType != null && declaringType.IsGenericType) { PrepareMethod(method, runtimeMethodHandle, (from type in method.DeclaringType.GetGenericArguments() select type.TypeHandle).ToArray()); } else { PrepareMethod(method, runtimeMethodHandle); } return privateMethodPin; }).Pin.Count); } public virtual void Unpin(MethodBase method) { method = GetIdentifiable(method); if (PinnedMethods.TryGetValue(method, out var value) && Interlocked.Decrement(ref value.Pin.Count) <= 0) { PinnedMethods.TryRemove(method, out var value2); PinnedHandles.TryRemove(value.Pin.Handle, out value2); } } public MethodInfo CreateCopy(MethodBase method) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Expected O, but got Unknown method = GetIdentifiable(method); if (method == null || (method.GetMethodImplementationFlags() & MethodImplAttributes.CodeTypeMask) != 0) { throw new InvalidOperationException("Uncopyable method: " + (method?.ToString() ?? "NULL")); } DynamicMethodDefinition val = new DynamicMethodDefinition(method); try { return val.Generate(); } finally { ((IDisposable)val)?.Dispose(); } } public bool TryCreateCopy(MethodBase method, out MethodInfo dm) { method = GetIdentifiable(method); if (method == null || (method.GetMethodImplementationFlags() & MethodImplAttributes.CodeTypeMask) != 0) { dm = null; return false; } try { dm = CreateCopy(method); return true; } catch { dm = null; return false; } } public MethodBase GetDetourTarget(MethodBase from, MethodBase to) { //IL_0177: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Expected O, but got Unknown //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Unknown result type (might be due to invalid IL or missing references) //IL_01aa: Unknown result type (might be due to invalid IL or missing references) //IL_01c9: Unknown result type (might be due to invalid IL or missing references) //IL_01ec: Unknown result type (might be due to invalid IL or missing references) //IL_0214: Unknown result type (might be due to invalid IL or missing references) to = GetIdentifiable(to); MethodInfo methodInfo = null; if (from is MethodInfo methodInfo2 && !from.IsStatic && to is MethodInfo methodInfo3 && to.IsStatic && methodInfo2.ReturnType == methodInfo3.ReturnType && methodInfo2.ReturnType.IsValueType) { Type? declaringType = from.DeclaringType; GlueThiscallStructRetPtrOrder glueThiscallStructRetPtrOrder; if ((glueThiscallStructRetPtrOrder = (((object)declaringType != null && declaringType.IsValueType) ? GlueThiscallInStructRetPtr : GlueThiscallStructRetPtr)) != 0) { int managedSize = Extensions.GetManagedSize(methodInfo2.ReturnType); if (managedSize == 3 || managedSize == 5 || managedSize == 6 || managedSize == 7 || managedSize > IntPtr.Size) { Type thisParamType = Extensions.GetThisParamType(from); Type item = methodInfo2.ReturnType.MakeByRefType(); int num = 0; int num2 = 1; if (glueThiscallStructRetPtrOrder == GlueThiscallStructRetPtrOrder.RetThisArgs) { num = 1; num2 = 0; } List list = new List { thisParamType }; list.Insert(num2, item); list.AddRange(from p in @from.GetParameters() select p.ParameterType); DynamicMethodDefinition val = new DynamicMethodDefinition("Glue:ThiscallStructRetPtr<" + Extensions.GetID(from, (string)null, (string)null, true, false, true) + "," + Extensions.GetID(to, (string)null, (string)null, true, false, true) + ">", typeof(void), list.ToArray()); try { ILProcessor iLProcessor = val.GetILProcessor(); iLProcessor.Emit(OpCodes.Ldarg, num2); iLProcessor.Emit(OpCodes.Ldarg, num); for (int i = 2; i < list.Count; i++) { iLProcessor.Emit(OpCodes.Ldarg, i); } iLProcessor.Emit(OpCodes.Call, ((MemberReference)iLProcessor.Body.Method).Module.ImportReference(to)); iLProcessor.Emit(OpCodes.Stobj, ((MemberReference)iLProcessor.Body.Method).Module.ImportReference(methodInfo2.ReturnType)); iLProcessor.Emit(OpCodes.Ret); methodInfo = val.Generate(); } finally { ((IDisposable)val)?.Dispose(); } } } } return methodInfo ?? to; } public uint TryMemAllocScratchCloseTo(IntPtr target, out IntPtr ptr, int size) { //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Expected O, but got Unknown if (size == 0 || size > _MemAllocScratchDummySafeSize) { ptr = IntPtr.Zero; return 0u; } bool num = Math.Abs((long)target - (long)ReferenceNonDynamicPoolPtr) < 1073741824; bool flag = DynamicMethodDefinition.IsDynamicILAvailable && Math.Abs((long)target - (long)ReferenceDynamicPoolPtr) < 1073741824; if (!num && !flag) { ptr = IntPtr.Zero; return 0u; } DynamicMethodDefinition val = new DynamicMethodDefinition((MethodBase)_MemAllocScratchDummy); MethodBase method; try { val.Name = $"MemAllocScratch<{(long)target:X16}>"; method = ((!flag) ? DMDGenerator.Generate(val, (object)null) : DMDGenerator.Generate(val, (object)null)); } finally { ((IDisposable)val)?.Dispose(); } Pin(method); ptr = GetNativeStart(method); DetourHelper.Native.MakeReadWriteExecutable(ptr, _MemAllocScratchDummySafeSize); return _MemAllocScratchDummySafeSize; } public static int MemAllocScratchDummy(int a, int b) { if (a >= 1024 && b >= 1024) { return a + b; } return MemAllocScratchDummy(a + b, b + 1); } } public class DetourRuntimeMonoPlatform : DetourRuntimeILPlatform { private static readonly object[] _NoArgs = new object[0]; private static readonly MethodInfo _DynamicMethod_CreateDynMethod = typeof(DynamicMethod).GetMethod("CreateDynMethod", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly FieldInfo _DynamicMethod_mhandle = typeof(DynamicMethod).GetField("mhandle", BindingFlags.Instance | BindingFlags.NonPublic); public override bool OnMethodCompiledWillBeCalled => false; public override event OnMethodCompiledEvent OnMethodCompiled; protected override RuntimeMethodHandle GetMethodHandle(MethodBase method) { if (method is DynamicMethod) { _DynamicMethod_CreateDynMethod?.Invoke(method, _NoArgs); if (_DynamicMethod_mhandle != null) { return (RuntimeMethodHandle)_DynamicMethod_mhandle.GetValue(method); } } return method.MethodHandle; } protected unsafe override void DisableInlining(MethodBase method, RuntimeMethodHandle handle) { ushort* ptr = (ushort*)((long)handle.Value + 2); *ptr = (ushort)(*ptr | 8u); } } public class DetourRuntimeNET50Platform : DetourRuntimeNETCore30Platform { public new static readonly Guid JitVersionGuid = new Guid("a5eec3a4-4176-43a7-8c2b-a05b551d4f49"); } public class DetourRuntimeNET60Platform : DetourRuntimeNETCore30Platform { [UnmanagedFunctionPointer(CallingConvention.ThisCall)] private unsafe delegate CorJitResult d_compileMethod_thiscall(IntPtr thisPtr, IntPtr corJitInfo, in CORINFO_METHOD_INFO methodInfo, uint flags, out byte* nativeEntry, out uint nativeSizeOfCode); public new static readonly Guid JitVersionGuid = new Guid("5ed35c58-857b-48dd-a818-7c0136dc9f73"); private d_compileMethod_thiscall our_compileMethod; private d_compileMethod_thiscall real_compileMethod; protected unsafe override CorJitResult InvokeRealCompileMethod(IntPtr thisPtr, IntPtr corJitInfo, in CORINFO_METHOD_INFO methodInfo, uint flags, out byte* nativeEntry, out uint nativeSizeOfCode) { if (real_compileMethod == null) { return base.InvokeRealCompileMethod(thisPtr, corJitInfo, in methodInfo, flags, out nativeEntry, out nativeSizeOfCode); } return real_compileMethod(thisPtr, corJitInfo, in methodInfo, flags, out nativeEntry, out nativeSizeOfCode); } protected unsafe override IntPtr GetCompileMethodHook(IntPtr real) { if (PlatformHelper.Is((Platform)37) && IntPtr.Size == 4) { real_compileMethod = DynDll.AsDelegate(real); our_compileMethod = base.CompileMethodHook; IntPtr functionPointerForDelegate = Marshal.GetFunctionPointerForDelegate(our_compileMethod); NativeDetourData data = DetourRuntimeNETCore30Platform.CreateNativeTrampolineTo(functionPointerForDelegate); d_compileMethod_thiscall d_compileMethod_thiscall = DynDll.AsDelegate(data.Method); IntPtr zero = IntPtr.Zero; IntPtr zero2 = IntPtr.Zero; CORINFO_METHOD_INFO methodInfo = default(CORINFO_METHOD_INFO); d_compileMethod_thiscall(zero, zero2, in methodInfo, 0u, out var _, out var _); DetourRuntimeNETCore30Platform.FreeNativeTrampoline(data); return functionPointerForDelegate; } return base.GetCompileMethodHook(real); } } public class DetourRuntimeNETCore30Platform : DetourRuntimeNETCorePlatform { protected enum CorJitResult { CORJIT_OK } protected struct CORINFO_SIG_INST { public uint classInstCount; public unsafe IntPtr* classInst; public uint methInstCount; public unsafe IntPtr* methInst; } protected struct CORINFO_SIG_INFO { public int callConv; public IntPtr retTypeClass; public IntPtr retTypeSigClass; public byte retType; public byte flags; public ushort numArgs; public CORINFO_SIG_INST sigInst; public IntPtr args; public IntPtr pSig; public uint sbSig; public IntPtr scope; public uint token; } protected struct CORINFO_METHOD_INFO { public IntPtr ftn; public IntPtr scope; public unsafe byte* ILCode; public uint ILCodeSize; public uint maxStack; public uint EHcount; public int options; public int regionKind; public CORINFO_SIG_INFO args; public CORINFO_SIG_INFO locals; } [UnmanagedFunctionPointer(CallingConvention.StdCall)] private unsafe delegate CorJitResult d_compileMethod(IntPtr thisPtr, IntPtr corJitInfo, in CORINFO_METHOD_INFO methodInfo, uint flags, out byte* nativeEntry, out uint nativeSizeOfCode); protected delegate object d_MethodHandle_GetLoaderAllocator(IntPtr methodHandle); protected delegate object d_CreateRuntimeMethodInfoStub(IntPtr methodHandle, object loaderAllocator); protected delegate RuntimeMethodHandle d_CreateRuntimeMethodHandle(object runtimeMethodInfo); protected delegate Type d_GetDeclaringTypeOfMethodHandle(IntPtr methodHandle); protected delegate Type d_GetTypeFromNativeHandle(IntPtr handle); public static readonly Guid JitVersionGuid = new Guid("d609bed1-7831-49fc-bd49-b6f054dd4d46"); private d_compileMethod our_compileMethod; private IntPtr real_compileMethodPtr; private d_compileMethod real_compileMethod; [ThreadStatic] private static int hookEntrancy = 0; protected d_MethodHandle_GetLoaderAllocator MethodHandle_GetLoaderAllocator; protected d_CreateRuntimeMethodInfoStub CreateRuntimeMethodInfoStub; protected d_CreateRuntimeMethodHandle CreateRuntimeMethodHandle; protected d_GetDeclaringTypeOfMethodHandle GetDeclaringTypeOfMethodHandle; protected d_GetTypeFromNativeHandle GetTypeFromNativeHandle; private MethodInfo _getTypeFromHandleUnsafeMethod; private static FieldInfo _runtimeAssemblyPtrField = Type.GetType("System.Reflection.RuntimeAssembly").GetField("m_assembly", BindingFlags.Instance | BindingFlags.NonPublic); public override bool OnMethodCompiledWillBeCalled => true; protected unsafe override void DisableInlining(MethodBase method, RuntimeMethodHandle handle) { ushort* ptr = (ushort*)((byte*)(void*)handle.Value + 6); *ptr = (ushort)(*ptr | 0x2000u); } private IntPtr GetCompileMethod(IntPtr jit) { return DetourRuntimeNETCorePlatform.ReadObjectVTable(jit, VTableIndex_ICorJitCompiler_compileMethod); } protected unsafe virtual CorJitResult InvokeRealCompileMethod(IntPtr thisPtr, IntPtr corJitInfo, in CORINFO_METHOD_INFO methodInfo, uint flags, out byte* nativeEntry, out uint nativeSizeOfCode) { nativeEntry = null; nativeSizeOfCode = 0u; if (real_compileMethod == null) { return CorJitResult.CORJIT_OK; } return real_compileMethod(thisPtr, corJitInfo, in methodInfo, flags, out nativeEntry, out nativeSizeOfCode); } protected unsafe virtual IntPtr GetCompileMethodHook(IntPtr real) { real_compileMethod = DynDll.AsDelegate(real); our_compileMethod = CompileMethodHook; IntPtr functionPointerForDelegate = Marshal.GetFunctionPointerForDelegate(our_compileMethod); NativeDetourData data = CreateNativeTrampolineTo(functionPointerForDelegate); d_compileMethod d_compileMethod = DynDll.AsDelegate(data.Method); IntPtr zero = IntPtr.Zero; IntPtr zero2 = IntPtr.Zero; CORINFO_METHOD_INFO methodInfo = default(CORINFO_METHOD_INFO); d_compileMethod(zero, zero2, in methodInfo, 0u, out var _, out var _); FreeNativeTrampoline(data); return functionPointerForDelegate; } protected unsafe override void InstallJitHooks(IntPtr jit) { SetupJitHookHelpers(); IntPtr zero = IntPtr.Zero; IntPtr zero2 = IntPtr.Zero; CORINFO_METHOD_INFO methodInfo = default(CORINFO_METHOD_INFO); InvokeRealCompileMethod(zero, zero2, in methodInfo, 0u, out var _, out var _); IntPtr compileMethodHook = GetCompileMethodHook(GetCompileMethod(jit)); _ = hookEntrancy; IntPtr* vTableEntry = DetourRuntimeNETCorePlatform.GetVTableEntry(jit, VTableIndex_ICorJitCompiler_compileMethod); DetourHelper.Native.MakeWritable((IntPtr)vTableEntry, (uint)IntPtr.Size); real_compileMethodPtr = *vTableEntry; *vTableEntry = compileMethodHook; } protected static NativeDetourData CreateNativeTrampolineTo(IntPtr target) { IntPtr from = DetourHelper.Native.MemAlloc(64u); NativeDetourData nativeDetourData = DetourHelper.Native.Create(from, target); DetourHelper.Native.MakeWritable(nativeDetourData); DetourHelper.Native.Apply(nativeDetourData); DetourHelper.Native.MakeExecutable(nativeDetourData); DetourHelper.Native.FlushICache(nativeDetourData); return nativeDetourData; } protected static void FreeNativeTrampoline(NativeDetourData data) { DetourHelper.Native.MakeWritable(data); DetourHelper.Native.MemFree(data.Method); DetourHelper.Native.Free(data); } protected unsafe CorJitResult CompileMethodHook(IntPtr jit, IntPtr corJitInfo, in CORINFO_METHOD_INFO methodInfo, uint flags, out byte* nativeEntry, out uint nativeSizeOfCode) { nativeEntry = null; nativeSizeOfCode = 0u; if (jit == IntPtr.Zero) { return CorJitResult.CORJIT_OK; } hookEntrancy++; try { CorJitResult result = InvokeRealCompileMethod(jit, corJitInfo, in methodInfo, flags, out nativeEntry, out nativeSizeOfCode); if (hookEntrancy == 1) { try { RuntimeTypeHandle[] array = null; RuntimeTypeHandle[] array2 = null; if (methodInfo.args.sigInst.classInst != null) { array = new RuntimeTypeHandle[methodInfo.args.sigInst.classInstCount]; for (int i = 0; i < array.Length; i++) { array[i] = GetTypeFromNativeHandle(methodInfo.args.sigInst.classInst[i]).TypeHandle; } } if (methodInfo.args.sigInst.methInst != null) { array2 = new RuntimeTypeHandle[methodInfo.args.sigInst.methInstCount]; for (int j = 0; j < array2.Length; j++) { array2[j] = GetTypeFromNativeHandle(methodInfo.args.sigInst.methInst[j]).TypeHandle; } } RuntimeTypeHandle typeHandle = GetDeclaringTypeOfMethodHandle(methodInfo.ftn).TypeHandle; RuntimeMethodHandle methodHandle = CreateHandleForHandlePointer(methodInfo.ftn); JitHookCore(typeHandle, methodHandle, (IntPtr)nativeEntry, nativeSizeOfCode, array, array2); } catch { } } return result; } finally { hookEntrancy--; } } protected RuntimeMethodHandle CreateHandleForHandlePointer(IntPtr handle) { return CreateRuntimeMethodHandle(CreateRuntimeMethodInfoStub(handle, MethodHandle_GetLoaderAllocator(handle))); } protected virtual void SetupJitHookHelpers() { //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Expected O, but got Unknown //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_01ce: Unknown result type (might be due to invalid IL or missing references) //IL_01d5: Expected O, but got Unknown //IL_01f3: Unknown result type (might be due to invalid IL or missing references) //IL_0216: Unknown result type (might be due to invalid IL or missing references) //IL_0247: Unknown result type (might be due to invalid IL or missing references) //IL_025c: Unknown result type (might be due to invalid IL or missing references) //IL_0271: Unknown result type (might be due to invalid IL or missing references) //IL_02f0: Unknown result type (might be due to invalid IL or missing references) //IL_02f7: Expected O, but got Unknown //IL_0311: Unknown result type (might be due to invalid IL or missing references) //IL_031c: Unknown result type (might be due to invalid IL or missing references) //IL_0327: Unknown result type (might be due to invalid IL or missing references) //IL_033a: Unknown result type (might be due to invalid IL or missing references) //IL_03a2: Unknown result type (might be due to invalid IL or missing references) //IL_03a9: Expected O, but got Unknown //IL_03c3: Unknown result type (might be due to invalid IL or missing references) //IL_03ce: Unknown result type (might be due to invalid IL or missing references) //IL_03e1: Unknown result type (might be due to invalid IL or missing references) MethodInfo methodInfo = typeof(object).Assembly.GetType("Internal.Runtime.CompilerServices.Unsafe").GetMethods().First((MethodInfo m) => m.Name == "As" && m.ReturnType.IsByRef); MethodInfo method = typeof(RuntimeMethodHandle).GetMethod("GetLoaderAllocator", BindingFlags.Static | BindingFlags.NonPublic); DynamicMethodDefinition val = new DynamicMethodDefinition("MethodHandle_GetLoaderAllocator", typeof(object), new Type[1] { typeof(IntPtr) }); MethodInfo methodInfo2; try { ILProcessor iLProcessor = val.GetILProcessor(); ModuleDefinition module = ((MemberReference)iLProcessor.Body.Method).Module; Type parameterType = method.GetParameters().First().ParameterType; iLProcessor.Emit(OpCodes.Ldarga_S, ((MethodReference)iLProcessor.Body.Method).Parameters[0]); iLProcessor.Emit(OpCodes.Call, module.ImportReference((MethodBase)methodInfo.MakeGenericMethod(typeof(IntPtr), parameterType))); iLProcessor.Emit(OpCodes.Ldobj, module.ImportReference(parameterType)); iLProcessor.Emit(OpCodes.Call, module.ImportReference((MethodBase)method)); iLProcessor.Emit(OpCodes.Ret); methodInfo2 = val.Generate(); } finally { ((IDisposable)val)?.Dispose(); } MethodHandle_GetLoaderAllocator = methodInfo2.CreateDelegate(); MethodInfo orCreateGetTypeFromHandleUnsafe = GetOrCreateGetTypeFromHandleUnsafe(); GetTypeFromNativeHandle = orCreateGetTypeFromHandleUnsafe.CreateDelegate(); Type type = typeof(RuntimeMethodHandle).Assembly.GetType("System.RuntimeMethodHandleInternal"); MethodInfo method2 = typeof(RuntimeMethodHandle).GetMethod("GetDeclaringType", BindingFlags.Static | BindingFlags.NonPublic, null, new Type[1] { type }, null); DynamicMethodDefinition val2 = new DynamicMethodDefinition("GetDeclaringTypeOfMethodHandle", typeof(Type), new Type[1] { typeof(IntPtr) }); MethodInfo methodInfo3; try { ILProcessor iLProcessor2 = val2.GetILProcessor(); ModuleDefinition module2 = ((MemberReference)iLProcessor2.Body.Method).Module; iLProcessor2.Emit(OpCodes.Ldarga_S, ((MethodReference)iLProcessor2.Body.Method).Parameters[0]); iLProcessor2.Emit(OpCodes.Call, module2.ImportReference((MethodBase)methodInfo.MakeGenericMethod(typeof(IntPtr), type))); iLProcessor2.Emit(OpCodes.Ldobj, module2.ImportReference(type)); iLProcessor2.Emit(OpCodes.Call, module2.ImportReference((MethodBase)method2)); iLProcessor2.Emit(OpCodes.Ret); methodInfo3 = val2.Generate(); } finally { ((IDisposable)val2)?.Dispose(); } GetDeclaringTypeOfMethodHandle = methodInfo3.CreateDelegate(); Type[] array = new Type[2] { typeof(IntPtr), typeof(object) }; Type type2 = typeof(RuntimeMethodHandle).Assembly.GetType("System.RuntimeMethodInfoStub"); ConstructorInfo constructor = type2.GetConstructor(array); DynamicMethodDefinition val3 = new DynamicMethodDefinition("new RuntimeMethodInfoStub", type2, array); MethodInfo methodInfo4; try { ILProcessor iLProcessor3 = val3.GetILProcessor(); ModuleDefinition module3 = ((MemberReference)iLProcessor3.Body.Method).Module; iLProcessor3.Emit(OpCodes.Ldarg_0); iLProcessor3.Emit(OpCodes.Ldarg_1); iLProcessor3.Emit(OpCodes.Newobj, module3.ImportReference((MethodBase)constructor)); iLProcessor3.Emit(OpCodes.Ret); methodInfo4 = val3.Generate(); } finally { ((IDisposable)val3)?.Dispose(); } CreateRuntimeMethodInfoStub = methodInfo4.CreateDelegate(); ConstructorInfo constructorInfo = typeof(RuntimeMethodHandle).GetConstructors(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).First(); DynamicMethodDefinition val4 = new DynamicMethodDefinition("new RuntimeMethodHandle", typeof(RuntimeMethodHandle), new Type[1] { typeof(object) }); MethodInfo methodInfo5; try { ILProcessor iLProcessor4 = val4.GetILProcessor(); ModuleDefinition module4 = ((MemberReference)iLProcessor4.Body.Method).Module; iLProcessor4.Emit(OpCodes.Ldarg_0); iLProcessor4.Emit(OpCodes.Newobj, module4.ImportReference((MethodBase)constructorInfo)); iLProcessor4.Emit(OpCodes.Ret); methodInfo5 = val4.Generate(); } finally { ((IDisposable)val4)?.Dispose(); } CreateRuntimeMethodHandle = methodInfo5.CreateDelegate(); } private MethodInfo GetOrCreateGetTypeFromHandleUnsafe() { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Expected O, but got Unknown //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Expected O, but got Unknown //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Expected O, but got Unknown if (_getTypeFromHandleUnsafeMethod != null) { return _getTypeFromHandleUnsafeMethod; } ModuleDefinition val = ModuleDefinition.CreateModule("MonoMod.RuntimeDetour.Runtime.NETCore3+Helpers", new ModuleParameters { Kind = (ModuleKind)0 }); Assembly assembly; try { TypeDefinition val2 = new TypeDefinition("System", "Type", (TypeAttributes)129) { BaseType = val.TypeSystem.Object }; val.Types.Add(val2); MethodDefinition val3 = new MethodDefinition("GetTypeFromHandleUnsafe", (MethodAttributes)22, val.ImportReference(typeof(Type))) { IsInternalCall = true }; ((MethodReference)val3).Parameters.Add(new ParameterDefinition(val.ImportReference(typeof(IntPtr)))); val2.Methods.Add(val3); assembly = ReflectionHelper.Load(val); } finally { ((IDisposable)val)?.Dispose(); } MakeAssemblySystemAssembly(assembly); return _getTypeFromHandleUnsafeMethod = assembly.GetType("System.Type").GetMethod("GetTypeFromHandleUnsafe"); } protected unsafe virtual void MakeAssemblySystemAssembly(Assembly assembly) { IntPtr intPtr = (IntPtr)_runtimeAssemblyPtrField.GetValue(assembly); int num = IntPtr.Size + IntPtr.Size + IntPtr.Size + IntPtr.Size + IntPtr.Size + 4 + IntPtr.Size + IntPtr.Size + 4 + 4 + IntPtr.Size + IntPtr.Size + 4 + 4 + IntPtr.Size; if (IntPtr.Size == 8) { num += 4; } IntPtr intPtr2 = *(IntPtr*)((byte*)(void*)intPtr + num); int num2 = IntPtr.Size + IntPtr.Size + IntPtr.Size + IntPtr.Size; IntPtr intPtr3 = *(IntPtr*)((byte*)(void*)intPtr2 + num2); int num3 = IntPtr.Size + IntPtr.Size + IntPtr.Size + 4 + 4 + IntPtr.Size + IntPtr.Size + IntPtr.Size + IntPtr.Size + 4; int* ptr = (int*)((byte*)(void*)intPtr3 + num3); *ptr |= 1; } protected void HookPermanent(MethodBase from, MethodBase to) { Pin(from); Pin(to); HookPermanent(GetNativeStart(from), GetNativeStart(to)); } protected void HookPermanent(IntPtr from, IntPtr to) { NativeDetourData detour = DetourHelper.Native.Create(from, to); DetourHelper.Native.MakeWritable(detour); DetourHelper.Native.Apply(detour); DetourHelper.Native.MakeExecutable(detour); DetourHelper.Native.FlushICache(detour); DetourHelper.Native.Free(detour); } } public class DetourRuntimeNETCorePlatform : DetourRuntimeNETPlatform { [UnmanagedFunctionPointer(CallingConvention.StdCall)] private delegate IntPtr d_getJit(); [UnmanagedFunctionPointer(CallingConvention.ThisCall)] private delegate void d_getVersionIdentifier(IntPtr thisPtr, out Guid versionIdentifier); private static d_getJit getJit; private static bool isNet5Jit; private const int vtableIndex_ICorJitCompiler_getVersionIdentifier = 4; private const int vtableIndex_ICorJitCompiler_getVersionIdentifier_net5 = 2; protected virtual int VTableIndex_ICorJitCompiler_compileMethod => 0; public override bool OnMethodCompiledWillBeCalled => false; public override event OnMethodCompiledEvent OnMethodCompiled; public DetourRuntimeNETCorePlatform() { GlueThiscallInStructRetPtr = GlueThiscallStructRetPtr; } protected static IntPtr GetJitObject() { if (getJit == null) { ProcessModule processModule = ((IEnumerable)Process.GetCurrentProcess().Modules).Cast().FirstOrDefault((ProcessModule m) => Path.GetFileNameWithoutExtension(m.FileName).EndsWith("clrjit", StringComparison.Ordinal)); if (processModule == null) { throw new PlatformNotSupportedException(); } IntPtr intPtr = default(IntPtr); if (!DynDll.TryOpenLibrary(processModule.FileName, ref intPtr, false, (int?)null)) { throw new PlatformNotSupportedException(); } if (PlatformHelper.Is((Platform)37)) { isNet5Jit = processModule.FileVersionInfo.ProductMajorPart >= 5; } else { isNet5Jit = typeof(object).Assembly.GetName().Version.Major >= 5; } try { getJit = DynDll.AsDelegate(DynDll.GetFunction(intPtr, "getJit")); } catch { DynDll.CloseLibrary(intPtr); throw; } } return getJit(); } protected static Guid GetJitGuid(IntPtr jit) { int index = (isNet5Jit ? 2 : 4); DynDll.AsDelegate(ReadObjectVTable(jit, index))(jit, out var versionIdentifier); return versionIdentifier; } protected unsafe static IntPtr* GetVTableEntry(IntPtr @object, int index) { return (IntPtr*)((nint)(*(IntPtr*)(void*)@object) + (nint)index * (nint)sizeof(IntPtr)); } protected unsafe static IntPtr ReadObjectVTable(IntPtr @object, int index) { return *GetVTableEntry(@object, index); } protected override void DisableInlining(MethodBase method, RuntimeMethodHandle handle) { } protected virtual void InstallJitHooks(IntPtr jitObject) { throw new PlatformNotSupportedException(); } protected virtual void JitHookCore(RuntimeTypeHandle declaringType, RuntimeMethodHandle methodHandle, IntPtr methodBodyStart, ulong methodBodySize, RuntimeTypeHandle[] genericClassArguments, RuntimeTypeHandle[] genericMethodArguments) { try { Type type = Type.GetTypeFromHandle(declaringType); if (genericClassArguments != null && type.IsGenericTypeDefinition) { type = type.MakeGenericType(genericClassArguments.Select(Type.GetTypeFromHandle).ToArray()); } MethodBase methodBase = MethodBase.GetMethodFromHandle(methodHandle, type.TypeHandle); if (methodBase == null) { methodBase = GetPin(methodHandle).Method; } try { OnMethodCompiled?.Invoke(methodBase, methodBodyStart, methodBodySize); } catch (Exception arg) { MMDbgLog.Log($"Error executing OnMethodCompiled event: {arg}"); } } catch (Exception arg2) { MMDbgLog.Log($"Error in JitHookCore: {arg2}"); } } public static DetourRuntimeNETCorePlatform Create() { try { IntPtr jitObject = GetJitObject(); Guid jitGuid = GetJitGuid(jitObject); DetourRuntimeNETCorePlatform detourRuntimeNETCorePlatform = null; if (jitGuid == DetourRuntimeNET60Platform.JitVersionGuid) { detourRuntimeNETCorePlatform = new DetourRuntimeNET60Platform(); } else if (jitGuid == DetourRuntimeNET50Platform.JitVersionGuid) { detourRuntimeNETCorePlatform = new DetourRuntimeNET50Platform(); } else if (jitGuid == DetourRuntimeNETCore30Platform.JitVersionGuid) { detourRuntimeNETCorePlatform = new DetourRuntimeNETCore30Platform(); } if (detourRuntimeNETCorePlatform == null) { return new DetourRuntimeNETCorePlatform(); } detourRuntimeNETCorePlatform?.InstallJitHooks(jitObject); return detourRuntimeNETCorePlatform; } catch (Exception arg) { MMDbgLog.Log("Could not get JIT information for the runtime, falling out to the version without JIT hooks"); MMDbgLog.Log($"Error: {arg}"); } return new DetourRuntimeNETCorePlatform(); } } public class DetourRuntimeNETPlatform : DetourRuntimeILPlatform { private static readonly object[] _NoArgs = new object[0]; private static readonly Type _RTDynamicMethod = typeof(DynamicMethod).GetNestedType("RTDynamicMethod", BindingFlags.NonPublic); private static readonly FieldInfo _RTDynamicMethod_m_owner = _RTDynamicMethod?.GetField("m_owner", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo _DynamicMethod_m_method = typeof(DynamicMethod).GetField("m_method", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly MethodInfo _DynamicMethod_GetMethodDescriptor = typeof(DynamicMethod).GetMethod("GetMethodDescriptor", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly FieldInfo _RuntimeMethodHandle_m_value = typeof(RuntimeMethodHandle).GetField("m_value", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly MethodInfo _IRuntimeMethodInfo_get_Value = typeof(RuntimeMethodHandle).Assembly.GetType("System.IRuntimeMethodInfo")?.GetMethod("get_Value"); private static readonly MethodInfo _RuntimeHelpers__CompileMethod = typeof(RuntimeHelpers).GetMethod("_CompileMethod", BindingFlags.Static | BindingFlags.NonPublic); private static readonly bool _RuntimeHelpers__CompileMethod_TakesIntPtr; private static readonly bool _RuntimeHelpers__CompileMethod_TakesIRuntimeMethodInfo; private static readonly bool _RuntimeHelpers__CompileMethod_TakesRuntimeMethodHandleInternal; private static IntPtr ThePreStub; public override bool OnMethodCompiledWillBeCalled => false; public override event OnMethodCompiledEvent OnMethodCompiled; public override MethodBase GetIdentifiable(MethodBase method) { if (_RTDynamicMethod_m_owner != null && method.GetType() == _RTDynamicMethod) { return (MethodBase)_RTDynamicMethod_m_owner.GetValue(method); } return base.GetIdentifiable(method); } protected override RuntimeMethodHandle GetMethodHandle(MethodBase method) { if (method is DynamicMethod dynamicMethod) { if (_RuntimeHelpers__CompileMethod_TakesIntPtr) { _RuntimeHelpers__CompileMethod.Invoke(null, new object[1] { ((RuntimeMethodHandle)_DynamicMethod_GetMethodDescriptor.Invoke(dynamicMethod, _NoArgs)).Value }); } else if (_RuntimeHelpers__CompileMethod_TakesIRuntimeMethodInfo) { _RuntimeHelpers__CompileMethod.Invoke(null, new object[1] { _RuntimeMethodHandle_m_value.GetValue((RuntimeMethodHandle)_DynamicMethod_GetMethodDescriptor.Invoke(dynamicMethod, _NoArgs)) }); } else if (_RuntimeHelpers__CompileMethod_TakesRuntimeMethodHandleInternal) { _RuntimeHelpers__CompileMethod.Invoke(null, new object[1] { _IRuntimeMethodInfo_get_Value.Invoke(_RuntimeMethodHandle_m_value.GetValue((RuntimeMethodHandle)_DynamicMethod_GetMethodDescriptor.Invoke(dynamicMethod, _NoArgs)), null) }); } else { try { dynamicMethod.CreateDelegate(typeof(MulticastDelegate)); } catch { } } if (_DynamicMethod_m_method != null) { return (RuntimeMethodHandle)_DynamicMethod_m_method.GetValue(method); } if (_DynamicMethod_GetMethodDescriptor != null) { return (RuntimeMethodHandle)_DynamicMethod_GetMethodDescriptor.Invoke(method, _NoArgs); } } return method.MethodHandle; } protected override void DisableInlining(MethodBase method, RuntimeMethodHandle handle) { } protected unsafe override IntPtr GetFunctionPointer(MethodBase method, RuntimeMethodHandle handle) { MMDbgLog.Log("mets: " + Extensions.GetID(method, (string)null, (string)null, true, false, false)); MMDbgLog.Log($"meth: 0x{(long)handle.Value:X16}"); MMDbgLog.Log($"getf: 0x{(long)handle.GetFunctionPointer():X16}"); bool flag = false; IntPtr intPtr; while (true) { if (method.IsVirtual) { Type? declaringType = method.DeclaringType; if ((object)declaringType != null && declaringType.IsValueType) { MMDbgLog.Log($"ldfn: 0x{(long)Extensions.GetLdftnPointer(method):X16}"); bool flag2 = false; Type[] interfaces = method.DeclaringType.GetInterfaces(); foreach (Type interfaceType in interfaces) { if (method.DeclaringType.GetInterfaceMap(interfaceType).TargetMethods.Contains(method)) { flag2 = true; break; } } intPtr = Extensions.GetLdftnPointer(method); if (!flag2) { return intPtr; } goto IL_00f3; } } intPtr = base.GetFunctionPointer(method, handle); goto IL_00f3; IL_00f3: if (PlatformHelper.Is((Platform)65536)) { if (IntPtr.Size != 4) { int num = 0; IntPtr intPtr2 = WalkPrecode(intPtr); while (intPtr2 != intPtr && num < 16) { num++; intPtr = intPtr2; intPtr2 = WalkPrecode(intPtr); } } break; } if (IntPtr.Size == 4) { int num2 = (int)intPtr; if (*(byte*)num2 == 184 && *(byte*)(num2 + 5) == 144 && *(byte*)(num2 + 6) == 232 && *(byte*)(num2 + 11) == 233) { int num3 = num2 + 11; int num4 = *(int*)(num3 + 1) + (num3 + 1 + 4); intPtr = NotThePreStub(intPtr, (IntPtr)num4); MMDbgLog.Log($"ngen: 0x{(long)intPtr:X8}"); } num2 = (int)intPtr; if (*(byte*)num2 == 233 && *(byte*)(num2 + 5) == 95) { int num5 = num2; int num6 = *(int*)(num5 + 1) + (num5 + 1 + 4); intPtr = NotThePreStub(intPtr, (IntPtr)num6); MMDbgLog.Log($"ngen: 0x{(int)intPtr:X8}"); } break; } long num7 = (long)intPtr; if (*(uint*)num7 == 1959363912 && *(uint*)(num7 + 5) == 1224837960 && *(uint*)(num7 + 18) == 1958886217 && *(ushort*)(num7 + 23) == 47176) { intPtr = NotThePreStub(intPtr, (IntPtr)(*(long*)(num7 + 25))); MMDbgLog.Log($"ngen: 0x{(long)intPtr:X16}"); return intPtr; } if (*(byte*)num7 == 233 && *(byte*)(num7 + 5) == 95) { long num8 = num7; long num9 = *(int*)(num8 + 1) + (num8 + 1 + 4); intPtr = NotThePreStub(intPtr, (IntPtr)num9); for (int j = 0; j < 16; j++) { num7 = (long)intPtr + j; if (*(ushort*)num7 == 47176 && *(ushort*)(num7 + 10) == 57599) { num9 = *(long*)(num7 + 2); intPtr = NotThePreStub(intPtr, (IntPtr)num9); j = -1; } else if ((*(ushort*)num7 & 0xFFF0) == 47168 && (*(uint*)(num7 + 10) & 0xF0FFFF) == 65382 && *(ushort*)(num7 + 13) == 34063 && (*(byte*)num7 & 0xF) == (*(byte*)(num7 + 12) & 0xF)) { num8 = num7; num9 = *(int*)(num8 + 13 + 2) + (num8 + 13 + 2 + 4); intPtr = NotThePreStub(intPtr, (IntPtr)num9); j = -1; } } MMDbgLog.Log($"ngen: 0x{(long)intPtr:X16}"); return intPtr; } if (*(byte*)num7 != 232 || flag) { break; } MMDbgLog.Log("Method thunk reset; regenerating"); flag = true; long num10 = *(int*)(num7 + 1) + (num7 + 1 + 4); MMDbgLog.Log($"PrecodeFixupThunk: 0x{num10:X16}"); PrepareMethod(method, handle); } return intPtr; unsafe IntPtr WalkPrecode(IntPtr curr) { long num11 = (long)curr; if (*(uint*)num11 == 268435593 && *(uint*)(num11 + 4) == 2839556394u && *(uint*)(num11 + 8) == 3592356160u) { IntPtr ptrParsed = *(IntPtr*)(num11 + 16); return NotThePreStub(curr, ptrParsed); } if (*(uint*)num11 == 268435595 && *(uint*)(num11 + 4) == 2839556458u && *(uint*)(num11 + 8) == 3592356160u) { IntPtr ptrParsed2 = *(IntPtr*)(num11 + 16); return NotThePreStub(curr, ptrParsed2); } if (*(uint*)num11 == 268435468 && *(uint*)(num11 + 4) == 1476395115 && *(uint*)(num11 + 8) == 3592356192u) { IntPtr ptrParsed3 = *(IntPtr*)(num11 + 16); return NotThePreStub(curr, ptrParsed3); } if (*(uint*)num11 == 2432696336u && *(uint*)(num11 + 4) == 2432696352u && *(uint*)(num11 + 8) == 2432696833u && *(uint*)(num11 + 12) == 1476395120 && *(uint*)(num11 + 16) == 3592356352u) { IntPtr ptrParsed4 = *(IntPtr*)(num11 + 24); return NotThePreStub(curr, ptrParsed4); } return curr; } } private IntPtr NotThePreStub(IntPtr ptrGot, IntPtr ptrParsed) { if (ThePreStub == IntPtr.Zero) { ThePreStub = (IntPtr)(-2); MethodInfo methodInfo = typeof(HttpWebRequest).Assembly.GetType("System.Net.Connection")?.GetMethod("SubmitRequest", BindingFlags.Instance | BindingFlags.NonPublic); if (methodInfo != null) { ThePreStub = GetNativeStart(methodInfo); MMDbgLog.Log($"ThePreStub: 0x{(long)ThePreStub:X16}"); } else if (PlatformHelper.Is((Platform)37)) { ThePreStub = (IntPtr)(-1); } } if (!(ptrParsed == ThePreStub)) { return ptrParsed; } return ptrGot; } static DetourRuntimeNETPlatform() { MethodInfo runtimeHelpers__CompileMethod = _RuntimeHelpers__CompileMethod; _RuntimeHelpers__CompileMethod_TakesIntPtr = (((object)runtimeHelpers__CompileMethod != null) ? runtimeHelpers__CompileMethod.GetParameters()[0].ParameterType.FullName : null) == "System.IntPtr"; MethodInfo runtimeHelpers__CompileMethod2 = _RuntimeHelpers__CompileMethod; _RuntimeHelpers__CompileMethod_TakesIRuntimeMethodInfo = (((object)runtimeHelpers__CompileMethod2 != null) ? runtimeHelpers__CompileMethod2.GetParameters()[0].ParameterType.FullName : null) == "System.IRuntimeMethodInfo"; MethodInfo runtimeHelpers__CompileMethod3 = _RuntimeHelpers__CompileMethod; _RuntimeHelpers__CompileMethod_TakesRuntimeMethodHandleInternal = (((object)runtimeHelpers__CompileMethod3 != null) ? runtimeHelpers__CompileMethod3.GetParameters()[0].ParameterType.FullName : null) == "System.RuntimeMethodHandleInternal"; ThePreStub = IntPtr.Zero; } } } namespace MonoMod.RuntimeDetour.HookGen { internal sealed class HookEndpoint { internal readonly MethodBase Method; private readonly Dictionary> HookMap = new Dictionary>(); private readonly List HookList = new List(); internal HookEndpoint(MethodBase method) { Method = method; } private static IDetour _NewHook(MethodBase from, Delegate to) { return new Hook(from, to); } private static IDetour _NewILHook(MethodBase from, Manipulator to) { return new ILHook(from, to); } private void _Add(Func gen, TDelegate hookDelegate) where TDelegate : Delegate { if (!((Delegate?)hookDelegate == (Delegate?)null)) { if (!HookMap.TryGetValue(hookDelegate, out var value)) { value = (HookMap[hookDelegate] = new Stack()); } IDetour item = gen(Method, hookDelegate); value.Push(item); HookList.Add(item); } } public void _Remove(Delegate hookDelegate) { if ((object)hookDelegate != null && HookMap.TryGetValue(hookDelegate, out var value)) { IDetour detour = value.Pop(); detour.Dispose(); if (value.Count == 0) { HookMap.Remove(hookDelegate); } HookList.Remove(detour); } } public void Add(Delegate hookDelegate) { _Add(_NewHook, hookDelegate); } public void Remove(Delegate hookDelegate) { _Remove(hookDelegate); } public void Modify(Delegate hookDelegate) { _Add((Func)_NewILHook, Extensions.CastDelegate(hookDelegate)); } public void Unmodify(Delegate hookDelegate) { _Remove(hookDelegate); } } public static class HookEndpointManager { private class HookEntry { public HookEntryType Type; public MethodBase Method; public Delegate Hook; public override bool Equals(object obj) { if (obj is HookEntry hookEntry && hookEntry.Type.Equals(Type) && hookEntry.Method.Equals(Method)) { return hookEntry.Hook.Equals(Hook); } return false; } public override int GetHashCode() { return Type.GetHashCode() ^ Method.GetHashCode() ^ Hook.GetHashCode(); } } private enum HookEntryType { Hook, Modification } private static readonly Dictionary HookEndpointMap = new Dictionary(); private static readonly Dictionary> OwnedHookLists = new Dictionary>(); public static event Func OnGetOwner; public static event Func OnRemoveAllOwnedBy; public static event Func OnAdd; public static event Func OnRemove; public static event Func OnModify; public static event Func OnUnmodify; public static object GetOwner(Delegate hook) { return Extensions.InvokeWhileNull((MulticastDelegate)(HookEndpointManager.OnGetOwner ?? new Func(DefaultOnGetOwner)), new object[1] { hook }); } private static object DefaultOnGetOwner(Delegate hook) { return hook.Method.DeclaringType.Assembly; } private static HookEndpoint GetEndpoint(MethodBase method) { object value; return (HookEndpoint)(HookEndpointMap.TryGetValue(method, out value) ? (value as HookEndpoint) : (HookEndpointMap[method] = new HookEndpoint(method))); } private static void AddEntry(HookEntryType type, MethodBase method, Delegate hook) { object owner = GetOwner(hook); if (owner != null) { if (!OwnedHookLists.TryGetValue(owner, out var value)) { value = (OwnedHookLists[owner] = new List()); } value.Add(new HookEntry { Type = type, Method = method, Hook = hook }); } } private static void RemoveEntry(HookEntryType type, MethodBase method, Delegate hook) { object owner = GetOwner(hook); if (owner != null && OwnedHookLists.TryGetValue(owner, out var value)) { int num = value.FindLastIndex((HookEntry entry) => entry.Type.Equals(type) && entry.Method.Equals(method) && entry.Hook.Equals(hook)); if (num != -1) { value.RemoveAt(num); } } } public static void RemoveAllOwnedBy(object owner) { Func onRemoveAllOwnedBy = HookEndpointManager.OnRemoveAllOwnedBy; if ((onRemoveAllOwnedBy != null && !Extensions.InvokeWhileTrue((MulticastDelegate)onRemoveAllOwnedBy, new object[1] { owner })) || owner == null || !OwnedHookLists.TryGetValue(owner, out var value) || value == null) { return; } OwnedHookLists.Remove(owner); foreach (HookEntry item in value) { switch (item.Type) { case HookEntryType.Hook: GetEndpoint(item.Method).Remove(item.Hook); break; case HookEntryType.Modification: GetEndpoint(item.Method).Unmodify(item.Hook); break; } } } public static void Add(MethodBase method, Delegate hookDelegate) where T : Delegate { Add(method, hookDelegate); } public static void Add(MethodBase method, Delegate hookDelegate) { Func onAdd = HookEndpointManager.OnAdd; if (onAdd == null || Extensions.InvokeWhileTrue((MulticastDelegate)onAdd, new object[2] { method, hookDelegate })) { GetEndpoint(method).Add(hookDelegate); AddEntry(HookEntryType.Hook, method, hookDelegate); } } public static void Remove(MethodBase method, Delegate hookDelegate) where T : Delegate { Remove(method, hookDelegate); } public static void Remove(MethodBase method, Delegate hookDelegate) { Func onRemove = HookEndpointManager.OnRemove; if (onRemove == null || Extensions.InvokeWhileTrue((MulticastDelegate)onRemove, new object[2] { method, hookDelegate })) { GetEndpoint(method).Remove(hookDelegate); RemoveEntry(HookEntryType.Hook, method, hookDelegate); } } public static void Modify(MethodBase method, Delegate callback) where T : Delegate { Modify(method, callback); } public static void Modify(MethodBase method, Delegate callback) { Func onModify = HookEndpointManager.OnModify; if (onModify == null || Extensions.InvokeWhileTrue((MulticastDelegate)onModify, new object[2] { method, callback })) { GetEndpoint(method).Modify(callback); AddEntry(HookEntryType.Modification, method, callback); } } public static void Unmodify(MethodBase method, Delegate callback) { Unmodify(method, callback); } public static void Unmodify(MethodBase method, Delegate callback) { Func onUnmodify = HookEndpointManager.OnUnmodify; if (onUnmodify == null || Extensions.InvokeWhileTrue((MulticastDelegate)onUnmodify, new object[2] { method, callback })) { GetEndpoint(method).Unmodify(callback); RemoveEntry(HookEntryType.Modification, method, callback); } } } }