using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Threading; using Mono.Cecil; using Mono.Cecil.Cil; using Mono.Collections.Generic; using MonoMod; using MonoMod.Cil; using MonoMod.Core; using MonoMod.Core.Platforms; using MonoMod.Logs; using MonoMod.Utils; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: CLSCompliant(true)] [assembly: TargetFramework(".NETCoreApp,Version=v8.0", FrameworkDisplayName = ".NET 8.0")] [assembly: AssemblyCompany("0x0ade, DaNike")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("Copyright 2026 0x0ade, DaNike")] [assembly: AssemblyDescription("Flexible and easily extensible runtime detouring library. Wrap, replace and manipulate (Mono.Cecil) methods at runtime.")] [assembly: AssemblyFileVersion("25.3.4.0")] [assembly: AssemblyInformationalVersion("25.3.4+69fdc9deb")] [assembly: AssemblyProduct("MonoMod.RuntimeDetour")] [assembly: AssemblyTitle("MonoMod.RuntimeDetour")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/MonoMod/MonoMod")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("25.3.4.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] internal class { static () { MMDbgLog.LogVersion(); } } namespace MonoMod { internal static class MMDbgLog { [InterpolatedStringHandler] internal ref struct DebugLogSpamStringHandler { internal DebugLogInterpolatedStringHandler handler; public DebugLogSpamStringHandler(int literalLen, int formattedCount, out bool isEnabled) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) handler = new DebugLogInterpolatedStringHandler(literalLen, formattedCount, (LogLevel)0, ref isEnabled); } public override string ToString() { return ((object)Unsafe.As(ref handler)/*cast due to .constrained prefix*/).ToString(); } public string ToStringAndClear() { return ((DebugLogInterpolatedStringHandler)(ref handler)).ToStringAndClear(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendLiteral(string s) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendLiteral(s); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(string? s) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(s); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(string? s, int alignment = 0, string? format = null) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(s, alignment, format); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(ReadOnlySpan s) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(s); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(ReadOnlySpan s, int alignment = 0, string? format = null) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(s, alignment, format); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(T value) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(T value, int alignment) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(value, alignment); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(T value, string? format) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(value, format); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(T value, int alignment, string? format) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(value, alignment, format); } } [InterpolatedStringHandler] internal ref struct DebugLogTraceStringHandler { internal DebugLogInterpolatedStringHandler handler; public DebugLogTraceStringHandler(int literalLen, int formattedCount, out bool isEnabled) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) handler = new DebugLogInterpolatedStringHandler(literalLen, formattedCount, (LogLevel)1, ref isEnabled); } public override string ToString() { return ((object)Unsafe.As(ref handler)/*cast due to .constrained prefix*/).ToString(); } public string ToStringAndClear() { return ((DebugLogInterpolatedStringHandler)(ref handler)).ToStringAndClear(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendLiteral(string s) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendLiteral(s); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(string? s) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(s); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(string? s, int alignment = 0, string? format = null) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(s, alignment, format); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(ReadOnlySpan s) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(s); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(ReadOnlySpan s, int alignment = 0, string? format = null) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(s, alignment, format); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(T value) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(T value, int alignment) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(value, alignment); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(T value, string? format) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(value, format); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(T value, int alignment, string? format) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(value, alignment, format); } } [InterpolatedStringHandler] internal ref struct DebugLogInfoStringHandler { internal DebugLogInterpolatedStringHandler handler; public DebugLogInfoStringHandler(int literalLen, int formattedCount, out bool isEnabled) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) handler = new DebugLogInterpolatedStringHandler(literalLen, formattedCount, (LogLevel)2, ref isEnabled); } public override string ToString() { return ((object)Unsafe.As(ref handler)/*cast due to .constrained prefix*/).ToString(); } public string ToStringAndClear() { return ((DebugLogInterpolatedStringHandler)(ref handler)).ToStringAndClear(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendLiteral(string s) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendLiteral(s); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(string? s) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(s); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(string? s, int alignment = 0, string? format = null) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(s, alignment, format); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(ReadOnlySpan s) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(s); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(ReadOnlySpan s, int alignment = 0, string? format = null) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(s, alignment, format); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(T value) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(T value, int alignment) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(value, alignment); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(T value, string? format) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(value, format); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(T value, int alignment, string? format) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(value, alignment, format); } } [InterpolatedStringHandler] internal ref struct DebugLogWarningStringHandler { internal DebugLogInterpolatedStringHandler handler; public DebugLogWarningStringHandler(int literalLen, int formattedCount, out bool isEnabled) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) handler = new DebugLogInterpolatedStringHandler(literalLen, formattedCount, (LogLevel)3, ref isEnabled); } public override string ToString() { return ((object)Unsafe.As(ref handler)/*cast due to .constrained prefix*/).ToString(); } public string ToStringAndClear() { return ((DebugLogInterpolatedStringHandler)(ref handler)).ToStringAndClear(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendLiteral(string s) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendLiteral(s); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(string? s) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(s); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(string? s, int alignment = 0, string? format = null) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(s, alignment, format); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(ReadOnlySpan s) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(s); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(ReadOnlySpan s, int alignment = 0, string? format = null) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(s, alignment, format); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(T value) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(T value, int alignment) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(value, alignment); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(T value, string? format) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(value, format); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(T value, int alignment, string? format) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(value, alignment, format); } } [InterpolatedStringHandler] internal ref struct DebugLogErrorStringHandler { internal DebugLogInterpolatedStringHandler handler; public DebugLogErrorStringHandler(int literalLen, int formattedCount, out bool isEnabled) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) handler = new DebugLogInterpolatedStringHandler(literalLen, formattedCount, (LogLevel)4, ref isEnabled); } public override string ToString() { return ((object)Unsafe.As(ref handler)/*cast due to .constrained prefix*/).ToString(); } public string ToStringAndClear() { return ((DebugLogInterpolatedStringHandler)(ref handler)).ToStringAndClear(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendLiteral(string s) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendLiteral(s); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(string? s) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(s); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(string? s, int alignment = 0, string? format = null) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(s, alignment, format); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(ReadOnlySpan s) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(s); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(ReadOnlySpan s, int alignment = 0, string? format = null) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(s, alignment, format); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(T value) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(T value, int alignment) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(value, alignment); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(T value, string? format) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(value, format); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(T value, int alignment, string? format) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(value, alignment, format); } } public static bool IsWritingLog => DebugLog.IsWritingLog; [ModuleInitializer] internal static void LogVersion() { Info("Version 25.3.4"); } public static void Log(LogLevel level, string message) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) DebugLog.Log("MonoMod.RuntimeDetour", level, message); } public static void Log(LogLevel level, [InterpolatedStringHandlerArgument("level")] ref DebugLogInterpolatedStringHandler message) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) DebugLog.Log("MonoMod.RuntimeDetour", level, ref message); } public static void Spam(string message) { Log((LogLevel)0, message); } public static void Spam(ref DebugLogSpamStringHandler message) { Log((LogLevel)0, ref message.handler); } public static void Trace(string message) { Log((LogLevel)1, message); } public static void Trace(ref DebugLogTraceStringHandler message) { Log((LogLevel)1, ref message.handler); } public static void Info(string message) { Log((LogLevel)2, message); } public static void Info(ref DebugLogInfoStringHandler message) { Log((LogLevel)2, ref message.handler); } public static void Warning(string message) { Log((LogLevel)3, message); } public static void Warning(ref DebugLogWarningStringHandler message) { Log((LogLevel)3, ref message.handler); } public static void Error(string message) { Log((LogLevel)4, message); } public static void Error(ref DebugLogErrorStringHandler message) { Log((LogLevel)4, ref message.handler); } } internal static class MultiTargetShims { public static TypeReference GetConstraintType(this GenericParameterConstraint constraint) { return constraint.ConstraintType; } } } namespace MonoMod.SourceGen.Attributes { [AttributeUsage(AttributeTargets.Class)] internal sealed class EmitILOverloadsAttribute : Attribute { public EmitILOverloadsAttribute(string filename, string kind) { } } internal static class ILOverloadKind { public const string Cursor = "ILCursor"; public const string Matcher = "ILMatcher"; } } namespace MonoMod.RuntimeDetour { public class DetourConfig { public string Id { get; } public int? Priority { get; } public IEnumerable Before { get; } public IEnumerable After { get; } [Browsable(false)] [EditorBrowsable(EditorBrowsableState.Never)] public int SubPriority { get; } public DetourConfig(string id, int? priority = null, IEnumerable? before = null, IEnumerable? after = null) : this(id, priority, before, after, 0) { } [Browsable(false)] [EditorBrowsable(EditorBrowsableState.Never)] public DetourConfig(string id, int? priority, IEnumerable? before, IEnumerable? after, int subPriority) { Id = id; Priority = priority; Before = AsFixedSize(before ?? Enumerable.Empty()); After = AsFixedSize(after ?? Enumerable.Empty()); SubPriority = subPriority; } private static IEnumerable AsFixedSize(IEnumerable enumerable) { if (enumerable == Enumerable.Empty()) { return enumerable; } if (enumerable is ICollection) { return enumerable; } return enumerable.ToArray(); } public DetourConfig WithPriority(int? priority) { return new DetourConfig(Id, priority, Before, After, SubPriority); } public DetourConfig WithBefore(IEnumerable before) { return new DetourConfig(Id, Priority, before, After, SubPriority); } public DetourConfig WithBefore(params string[] before) { return WithBefore(before.AsEnumerable()); } public DetourConfig WithAfter(IEnumerable after) { return new DetourConfig(Id, Priority, Before, after, SubPriority); } public DetourConfig WithAfter(params string[] after) { return WithAfter(after.AsEnumerable()); } public DetourConfig AddBefore(IEnumerable before) { return WithBefore(Before.Concat(before)); } public DetourConfig AddBefore(params string[] before) { return AddBefore(before.AsEnumerable()); } public DetourConfig AddAfter(IEnumerable after) { return WithAfter(After.Concat(after)); } public DetourConfig AddAfter(params string[] after) { return AddAfter(after.AsEnumerable()); } } public abstract class DetourContext { private sealed class Scope { public readonly DetourContext Context; public readonly Scope? Prev; public bool Active = true; public Scope(DetourContext context, Scope? prev) { Context = context; Prev = prev; } } private sealed class ContextScopeHandler : ScopeHandlerBase { public static readonly ContextScopeHandler Instance = new ContextScopeHandler(); public override void EndScope(object? data) { ((Scope)data).Active = false; while (true) { Scope current = DetourContext.current; if (current != null && !current.Active) { DetourContext.current = DetourContext.current.Prev; continue; } break; } } } private static DetourContext? globalCurrent; [ThreadStatic] private static Scope? current; public static DetourContext? Current { get { Scope prev = current; while (prev != null && !prev.Active) { prev = prev.Prev; } return prev?.Context ?? globalCurrent; } } public DetourConfig? Config { get { if (!TryGetConfig(out DetourConfig config)) { return null; } return config; } } public static DetourConfig? CurrentConfig { get { if (!TryGetCurrentConfig(out DetourConfig config)) { return null; } return config; } } public IDetourFactory? Factory { get { if (!TryGetFactory(out IDetourFactory detourFactory)) { return null; } return detourFactory; } } public static IDetourFactory? CurrentFactory { get { if (!TryGetCurrentFactory(out IDetourFactory detourFactory)) { return null; } return detourFactory; } } [EditorBrowsable(EditorBrowsableState.Advanced)] public static DetourContext? SetGlobalContext(DetourContext? context) { return Interlocked.Exchange(ref globalCurrent, context); } private static DataScope PushContext(DetourContext ctx) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) current = new Scope(ctx, current); return new DataScope((ScopeHandlerBase)(object)ContextScopeHandler.Instance, (object)current); } [CLSCompliant(false)] public DataScope Use() { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return PushContext(this); } public static DetourConfig? GetDefaultConfig() { if (!TryGetCurrentConfig(out DetourConfig config)) { return null; } return config; } public static IDetourFactory GetDefaultFactory() { if (!TryGetCurrentFactory(out IDetourFactory detourFactory)) { return DetourFactory.Current; } return detourFactory; } protected abstract bool TryGetConfig(out DetourConfig? config); public static bool TryGetCurrentConfig(out DetourConfig? config) { for (Scope prev = current; prev != null; prev = prev.Prev) { if (prev.Active && prev.Context.TryGetConfig(out config)) { return true; } } DetourContext detourContext = globalCurrent; if (detourContext != null && detourContext.TryGetConfig(out config)) { return true; } config = null; return false; } protected abstract bool TryGetFactory([MaybeNullWhen(false)] out IDetourFactory detourFactory); public static bool TryGetCurrentFactory([MaybeNullWhen(false)] out IDetourFactory detourFactory) { for (Scope prev = current; prev != null; prev = prev.Prev) { if (prev.Context.TryGetFactory(out detourFactory)) { return true; } } DetourContext detourContext = globalCurrent; if (detourContext != null && detourContext.TryGetFactory(out detourFactory)) { return true; } detourFactory = null; return false; } } public abstract class EmptyDetourContext : DetourContext { protected override bool TryGetConfig(out DetourConfig? config) { config = null; return false; } protected override bool TryGetFactory([MaybeNullWhen(false)] out IDetourFactory detourFactory) { detourFactory = null; return false; } } public class DetourConfigContext : EmptyDetourContext { private readonly DetourConfig? cfg; public DetourConfigContext(DetourConfig? cfg) { this.cfg = cfg; } protected override bool TryGetConfig(out DetourConfig? config) { config = cfg; return true; } } public class DetourFactoryContext : EmptyDetourContext { private readonly IDetourFactory fac; public DetourFactoryContext(IDetourFactory fac) { this.fac = fac; } protected override bool TryGetFactory([MaybeNullWhen(false)] out IDetourFactory detourFactory) { detourFactory = fac; return true; } } public static class DetourManager { internal abstract class ManagedChainNode { public ManagedChainNode? Next; private MethodBase? lastTarget; private ICoreDetour? trampolineDetour; private bool hasStolenTrampoline; public abstract MethodBase Entry { get; } public abstract MethodBase NextTrampoline { get; } public abstract DetourConfig? Config { get; } public virtual bool DetourToFallback => true; public bool IsApplied { get; private set; } private void UndoTrampolineDetour() { ICoreDetour val = Interlocked.Exchange(ref trampolineDetour, null); if (val != null) { ((ICoreDetourBase)val).Undo(); ((IDisposable)val).Dispose(); } } public virtual void UpdateDetour(IDetourFactory factory, MethodBase fallback) { MethodBase methodBase = Next?.Entry; if ((object)methodBase == null && DetourToFallback) { methodBase = fallback; } if (!(methodBase == lastTarget)) { UndoTrampolineDetour(); if ((object)methodBase != null) { trampolineDetour = DetourFactory.CreateDetour(factory, NextTrampoline, methodBase, true); } lastTarget = methodBase; IsApplied = true; } } public void Remove() { if (!hasStolenTrampoline) { UndoTrampolineDetour(); } lastTarget = null; Next = null; IsApplied = false; } public void StealTrampoline(IDetourFactory factory) { Helpers.Assert(!hasStolenTrampoline, (string)null, "!hasStolenTrampoline"); StealTrampolineInner(); hasStolenTrampoline = true; UndoTrampolineDetour(); trampolineDetour = DetourFactory.CreateDetour(factory, NextTrampoline, (MethodBase)GetRemovedStub(MethodSignature.ForMethod(NextTrampoline)), true); } protected virtual void StealTrampolineInner() { throw new NotSupportedException("Can't steal ManagedChainNode trampoline"); } public virtual void ReturnStolenTrampoline() { Helpers.Assert(hasStolenTrampoline, (string)null, "hasStolenTrampoline"); UndoTrampolineDetour(); ReturnStolenTrampolineInner(); hasStolenTrampoline = false; } protected virtual void ReturnStolenTrampolineInner() { throw new NotSupportedException("Can't steal ManagedChainNode trampoline"); } } internal sealed class ManagedDetourChainNode : ManagedChainNode { public readonly SingleManagedDetourState Detour; public override MethodBase Entry => Detour.InvokeTarget; public override MethodBase NextTrampoline => Detour.NextTrampoline.TrampolineMethod; public override DetourConfig? Config => Detour.Config; public IDetourFactory Factory => Detour.Factory; public ManagedDetourChainNode(SingleManagedDetourState detour) { Detour = detour; } protected override void StealTrampolineInner() { Detour.NextTrampoline.StealTrampolineOwnership(); } protected override void ReturnStolenTrampolineInner() { Detour.NextTrampoline.ReturnTrampolineOwnership(); } } internal sealed class ManagedDetourSyncInfo : DetourSyncInfo { public int HasStolenTrampolines; public readonly ConcurrentQueue TrampolineStealers = new ConcurrentQueue(); public void StealTrampoline(IDetourFactory factory, ManagedChainNode node) { node.StealTrampoline(factory); TrampolineStealers.Enqueue(node); Volatile.Write(ref HasStolenTrampolines, 1); } public void ReturnStolenTrampolines() { if (Interlocked.CompareExchange(ref HasStolenTrampolines, 0, 1) == 1) { ManagedChainNode result; while (TrampolineStealers.TryDequeue(out result)) { result.ReturnStolenTrampoline(); } } } } internal sealed class RootManagedChainNode : ManagedChainNode { public readonly MethodSignature Sig; public readonly ManagedDetourSyncInfo SyncInfo = new ManagedDetourSyncInfo(); public readonly ConcurrentQueue StolenTrampolineReturners = new ConcurrentQueue(); private readonly DataScope syncProxyRefScope; public bool HasILHook; private ICoreDetour? syncDetour; private MethodInfo? sourceClone; private DynamicMethodDefinition? sourceCloneIl; public override MethodBase Entry { get; } public override MethodBase NextTrampoline { get; } public override DetourConfig? Config => null; public override bool DetourToFallback => true; public RootManagedChainNode(MethodBase method) { //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) Sig = MethodSignature.ForMethod(method); Entry = method; NextTrampoline = TrampolinePool.Rent(Sig); DataScope refScope = default(DataScope); ManagedDetourSyncInfo syncInfo = SyncInfo; FormatInterpolatedStringHandler val = default(FormatInterpolatedStringHandler); ((FormatInterpolatedStringHandler)(ref val))..ctor(0, 1); ((FormatInterpolatedStringHandler)(ref val)).AppendFormatted(Entry); syncInfo.SyncProxy = GenerateSyncProxy(DebugFormatter.Format(ref val), Sig, delegate(MethodDefinition val3, ILProcessor il) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) DynamicReferenceCell val2 = default(DynamicReferenceCell); refScope = DynamicReferenceManager.EmitNewTypedReference(il, SyncInfo, ref val2); }, delegate(MethodDefinition val2, ILProcessor il, Action loadSyncInfo) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) Enumerator enumerator = ((MethodReference)val2).Parameters.GetEnumerator(); try { while (enumerator.MoveNext()) { ParameterDefinition current = enumerator.Current; il.Emit(OpCodes.Ldarg, current); } } finally { ((IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose(); } il.Emit(OpCodes.Call, ((MemberReference)val2).Module.ImportReference(NextTrampoline)); }, delegate(MethodDefinition val2, ILProcessor il, Action loadSyncInfo) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) loadSyncInfo(); Extensions.Emit(il, OpCodes.Call, (MethodBase)ManagedDetourSyncInfo_ReturnStolenTrampolines); }); syncProxyRefScope = refScope; } public override void UpdateDetour(IDetourFactory factory, MethodBase fallback) { base.UpdateDetour(factory, fallback); Helpers.Assert(syncDetour != null, (string)null, "syncDetour is not null"); if (!HasILHook && Next == null && ((ICoreDetourBase)syncDetour).IsApplied) { ((ICoreDetourBase)syncDetour).Undo(); ((IDisposable)syncDetour).Dispose(); syncDetour = null; } else if ((HasILHook || Next != null) && !((ICoreDetourBase)syncDetour).IsApplied) { ((ICoreDetourBase)syncDetour).Apply(); } } public void PrepareDetour(IDetourFactory factory, out MethodInfo sourceClone, out DynamicMethodDefinition? sourceCloneIl) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Expected O, but got Unknown //IL_00a8: Expected O, but got Unknown if (syncDetour == null) { CreateDetourRequest val = default(CreateDetourRequest); ((CreateDetourRequest)(ref val))..ctor(Entry, SyncInfo.SyncProxy); ((CreateDetourRequest)(ref val)).set_ApplyByDefault(false); ((CreateDetourRequest)(ref val)).set_CreateSourceCloneIfNotILClone(true); ICoreDetour? obj = (syncDetour = factory.CreateDetour(val)); ICoreDetourWithClone val2 = (ICoreDetourWithClone)(object)((obj is ICoreDetourWithClone) ? obj : null); if (val2 != null) { MethodInfo sourceMethodClone = val2.SourceMethodClone; if ((object)sourceMethodClone != null) { sourceClone = (this.sourceClone = sourceMethodClone); DynamicMethodDefinition? obj2 = this.sourceCloneIl; if (obj2 != null) { obj2.Dispose(); } sourceCloneIl = (this.sourceCloneIl = val2.SourceMethodCloneIL); return; } } DynamicMethodDefinition obj3 = this.sourceCloneIl; if (obj3 == null) { DynamicMethodDefinition val3 = new DynamicMethodDefinition(Entry); DynamicMethodDefinition val4 = val3; this.sourceCloneIl = val3; obj3 = val4; } sourceCloneIl = obj3; sourceClone = this.sourceClone ?? (this.sourceClone = sourceCloneIl.Generate()); } else { Helpers.Assert((object)this.sourceClone != null, (string)null, "this.sourceClone is not null"); sourceClone = this.sourceClone; sourceCloneIl = this.sourceCloneIl; } } } internal sealed class ILHookEntry { public readonly SingleILHookState Hook; public ILContext? CurrentContext; public ILContext? LastContext; public bool IsApplied; public IDetourFactory Factory => Hook.Factory; public DetourConfig? Config => Hook.Config; public Manipulator Manip => Hook.Manip; public ILHookEntry(SingleILHookState hook) { Hook = hook; } public void Remove() { IsApplied = false; ILContext? lastContext = LastContext; if (lastContext != null) { lastContext.Dispose(); } } } internal sealed class ManagedDetourState { public readonly MethodBase Source; public MethodInfo? SourceClone; public DynamicMethodDefinition? SourceCloneIl; public MethodInfo? EndOfChain; private MethodDetourInfo? info; private readonly DepGraph detourGraph = new DepGraph(); internal readonly RootManagedChainNode detourList; private ManagedChainNode? noConfigChain; internal SpinLock detourLock = new SpinLock(enableThreadOwnerTracking: true); internal int detourChainVersion; internal readonly DepGraph ilhookGraph = new DepGraph(); internal readonly List noConfigIlhooks = new List(); internal int ilhookVersion; public MethodDetourInfo Info => info ?? (info = new MethodDetourInfo(this)); public event Action? DetourApplied; public event Action? DetourUndone; public event Action? ILHookApplied; public event Action? ILHookUndone; public ManagedDetourState(MethodBase src) { Source = src; detourList = new RootManagedChainNode(src); } public void AddDetour(SingleManagedDetourState detour, bool takeLock = true) { bool lockTaken = false; try { if (takeLock) { detourLock.Enter(ref lockTaken); } if (detour.ManagerData != null) { throw new InvalidOperationException("Trying to add a detour which was already added"); } ManagedDetourChainNode managedDetourChainNode = new ManagedDetourChainNode(detour); detourChainVersion++; DetourConfig config = managedDetourChainNode.Config; if (config != null) { DepGraphNode depGraphNode = new DepGraphNode(new DepListNode(config, managedDetourChainNode)); detourGraph.Insert(depGraphNode); detour.ManagerData = depGraphNode; } else { managedDetourChainNode.Next = noConfigChain; noConfigChain = managedDetourChainNode; detour.ManagerData = managedDetourChainNode; } PrepareEndOfChain(detour.Factory); UpdateChain(detour.Factory, out var _); } finally { if (lockTaken) { detourLock.Exit(useMemoryBarrier: true); } } InvokeDetourEvent(DetourManager.DetourApplied, this.DetourApplied, detour); } public void RemoveDetour(SingleManagedDetourState detour, bool takeLock = true) { bool lockTaken = false; try { if (takeLock) { detourLock.Enter(ref lockTaken); } detourChainVersion++; object obj = Interlocked.Exchange(ref detour.ManagerData, null); if (obj == null) { throw new InvalidOperationException("Trying to remove detour which wasn't added"); } if (!(obj is DepGraphNode depGraphNode)) { if (!(obj is ManagedDetourChainNode node)) { throw new InvalidOperationException("Trying to remove detour with unknown manager data"); } RemoveNoConfigDetour(detour, node); } else { RemoveGraphDetour(detour, depGraphNode); _ = (ManagedDetourChainNode)depGraphNode.ListNode.ChainNode; } } finally { if (lockTaken) { detourLock.Exit(useMemoryBarrier: true); } } InvokeDetourEvent(DetourManager.DetourUndone, this.DetourUndone, detour); } private void RemoveGraphDetour(SingleManagedDetourState detour, DepGraphNode node) { detourGraph.Remove(node); PrepareEndOfChain(detour.Factory); UpdateChain(detour.Factory, out var stealTrampolines); if (stealTrampolines) { detourList.SyncInfo.StealTrampoline(detour.Factory, node.ListNode.ChainNode); } node.ListNode.ChainNode.Remove(); } private void RemoveNoConfigDetour(SingleManagedDetourState detour, ManagedDetourChainNode node) { for (ref ManagedChainNode next = ref noConfigChain; next != null; next = ref next.Next) { if (next == node) { next = node.Next; node.Next = null; break; } } PrepareEndOfChain(detour.Factory); UpdateChain(detour.Factory, out var stealTrampolines); if (stealTrampolines) { detourList.SyncInfo.StealTrampoline(detour.Factory, node); } node.Remove(); } public void AddILHook(SingleILHookState ilhook, bool takeLock = true) { bool lockTaken = false; try { if (takeLock) { detourLock.Enter(ref lockTaken); } if (ilhook.ManagerData != null) { throw new InvalidOperationException("Trying to add an IL hook which was already added"); } ILHookEntry iLHookEntry = new ILHookEntry(ilhook); ilhookVersion++; DetourConfig config = iLHookEntry.Config; if (config != null) { DepGraphNode depGraphNode = new DepGraphNode(new DepListNode(config, iLHookEntry)); ilhookGraph.Insert(depGraphNode); ilhook.ManagerData = depGraphNode; } else { noConfigIlhooks.Add(iLHookEntry); ilhook.ManagerData = iLHookEntry; } try { PrepareEndOfChain(ilhook.Factory); UpdateEndOfChain(); } catch { object obj2 = Interlocked.Exchange(ref ilhook.ManagerData, null); if (!(obj2 is DepGraphNode node)) { if (!(obj2 is ILHookEntry item)) { throw new NotSupportedException("bad managerdata?"); } noConfigIlhooks.Remove(item); } else { ilhookGraph.Remove(node); } UpdateEndOfChain(); throw; } UpdateChain(ilhook.Factory, out var _); CleanILContexts(); } finally { if (lockTaken) { detourLock.Exit(useMemoryBarrier: true); } } InvokeILHookEvent(DetourManager.ILHookApplied, this.ILHookApplied, ilhook); } public void RemoveILHook(SingleILHookState ilhook, bool takeLock = true) { bool lockTaken = false; try { if (takeLock) { detourLock.Enter(ref lockTaken); } ilhookVersion++; object obj = Interlocked.Exchange(ref ilhook.ManagerData, null); if (obj == null) { throw new InvalidOperationException("Trying to remove IL hook which wasn't added"); } if (!(obj is DepGraphNode depGraphNode)) { if (!(obj is ILHookEntry node)) { throw new InvalidOperationException("Trying to remove IL hook with unknown manager data"); } RemoveNoConfigILHook(ilhook, node); } else { RemoveGraphILHook(ilhook, depGraphNode); _ = depGraphNode.ListNode.ChainNode; } } finally { if (lockTaken) { detourLock.Exit(useMemoryBarrier: true); } } InvokeILHookEvent(DetourManager.ILHookUndone, this.ILHookUndone, ilhook); } private void RemoveGraphILHook(SingleILHookState ilhook, DepGraphNode node) { ilhookGraph.Remove(node); PrepareEndOfChain(ilhook.Factory); UpdateEndOfChain(); UpdateChain(ilhook.Factory, out var _); CleanILContexts(); node.ListNode.ChainNode.Remove(); } private void RemoveNoConfigILHook(SingleILHookState ilhook, ILHookEntry node) { noConfigIlhooks.Remove(node); PrepareEndOfChain(ilhook.Factory); UpdateEndOfChain(); UpdateChain(ilhook.Factory, out var _); CleanILContexts(); node.Remove(); } private void PrepareEndOfChain(IDetourFactory factory) { detourList.PrepareDetour(factory, out SourceClone, out SourceCloneIl); if ((object)EndOfChain == null) { EndOfChain = SourceClone; } } private void UpdateEndOfChain() { //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Expected O, but got Unknown Helpers.Assert((object)SourceClone != null, (string)null, "SourceClone is not null"); if (noConfigIlhooks.Count == 0 && ilhookGraph.ListHead == null) { detourList.HasILHook = false; EndOfChain = SourceClone; return; } if (SourceCloneIl == null) { throw new InvalidOperationException("Target method cannot be ILHooked"); } detourList.HasILHook = true; DynamicMethodDefinition val = new DynamicMethodDefinition(SourceCloneIl); try { MethodDefinition definition = val.Definition; for (DepListNode depListNode = ilhookGraph.ListHead; depListNode != null; depListNode = depListNode.Next) { InvokeManipulator(depListNode.ChainNode, definition); } foreach (ILHookEntry noConfigIlhook in noConfigIlhooks) { InvokeManipulator(noConfigIlhook, definition); } MethodInfo methodInfo = val.Generate(); PlatformTriple.Current.Compile((MethodBase)methodInfo); Thread.MemoryBarrier(); EndOfChain = methodInfo; } finally { ((IDisposable)val)?.Dispose(); } } private static void InvokeManipulator(ILHookEntry entry, MethodDefinition def) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Expected O, but got Unknown entry.IsApplied = true; ILContext val = (entry.CurrentContext = new ILContext(def)); val.Invoke(entry.Manip); if (val.IsReadOnly) { val.Dispose(); } else { val.MakeReadOnly(); } } private void CleanILContexts() { for (DepListNode depListNode = ilhookGraph.ListHead; depListNode != null; depListNode = depListNode.Next) { CleanContext(depListNode.ChainNode); } foreach (ILHookEntry noConfigIlhook in noConfigIlhooks) { CleanContext(noConfigIlhook); } static void CleanContext(ILHookEntry entry) { if (entry.CurrentContext != entry.LastContext) { ILContext? lastContext = entry.LastContext; entry.LastContext = entry.CurrentContext; if (lastContext != null) { lastContext.Dispose(); } } } } private void UpdateChain(IDetourFactory updatingFactory, out bool stealTrampolines) { Helpers.Assert((object)SourceClone != null, (string)null, "SourceClone is not null"); Helpers.Assert((object)EndOfChain != null, (string)null, "EndOfChain is not null"); DepListNode depListNode = detourGraph.ListHead; ManagedChainNode managedChainNode = null; ref ManagedChainNode reference = ref managedChainNode; while (depListNode != null) { reference = depListNode.ChainNode; reference = ref reference.Next; reference = null; depListNode = depListNode.Next; } reference = noConfigChain; detourList.Next = managedChainNode; Volatile.Write(ref detourList.SyncInfo.UpdatingThread, EnvironmentEx.CurrentManagedThreadId); detourList.SyncInfo.WaitForNoActiveCalls(out stealTrampolines); try { for (managedChainNode = detourList; managedChainNode != null; managedChainNode = managedChainNode.Next) { IDetourFactory val = (managedChainNode.Next as ManagedDetourChainNode)?.Factory; if (val == null) { val = (managedChainNode as ManagedDetourChainNode)?.Factory; } if (val == null) { val = updatingFactory; } managedChainNode.UpdateDetour(val, EndOfChain); } } finally { Volatile.Write(ref detourList.SyncInfo.UpdatingThread, -1); } } private void InvokeDetourEvent(Action? evt1, Action? evt2, SingleManagedDetourState node) { if (evt1 != null || evt2 != null) { DetourInfo detourInfo = Info.GetDetourInfo(node); evt1?.Invoke(detourInfo); evt2?.Invoke(detourInfo); } } private void InvokeILHookEvent(Action? evt1, Action? evt2, SingleILHookState entry) { if (evt1 != null || evt2 != null) { ILHookInfo iLHookInfo = Info.GetILHookInfo(entry); evt1?.Invoke(iLHookInfo); evt2?.Invoke(iLHookInfo); } } } internal sealed class SingleManagedDetourState : SingleDetourStateBase { public readonly MethodInfo PublicTarget; public readonly MethodInfo InvokeTarget; public readonly IDetourTrampoline NextTrampoline; public DetourInfo? DetourInfo; public SingleManagedDetourState(IHook dt) : base(dt) { PublicTarget = dt.PublicTarget; InvokeTarget = dt.InvokeTarget; NextTrampoline = dt.NextTrampoline; } } internal sealed class SingleILHookState : SingleDetourStateBase { public readonly Manipulator Manip; public ILHookInfo? HookInfo; public SingleILHookState(IILHook hk) : base(hk) { Manip = hk.Manip; } } internal abstract class NativeChainNode { public NativeChainNode? Next; public abstract Delegate EntryDelegate { get; } public abstract void UpdateChain(IDetourFactory factory, Delegate? fallback); public virtual void Remove() { } } internal sealed class NativeDetourChainNode : NativeChainNode { public readonly SingleNativeDetourState Detour; public readonly ChainDelegateState? ChainState; public DetourConfig? Config => Detour.Config; public override Delegate EntryDelegate => ChainState?.GetDelegate() ?? Detour.Invoker; public NativeDetourChainNode(SingleNativeDetourState detour) { Detour = detour; if (detour.HasOrigParam) { ChainState = new ChainDelegateState(detour.Invoker, detour.NativeDelegateType); } } public override void UpdateChain(IDetourFactory factory, Delegate? fallback) { Delegate obj = Next?.EntryDelegate ?? fallback; obj = (((object)obj != null) ? Extensions.CastDelegate(obj, Detour.NativeDelegateType) : null); if (ChainState != null) { ChainState.Next = obj; } } public override void Remove() { if (ChainState != null) { ChainState.Remove(); } } } internal sealed class NativeDetourSyncInfo : DetourSyncInfo { public Delegate? FirstDelegate; } internal sealed class RootNativeDetourChainNode : NativeChainNode { public readonly NativeDetourSyncInfo SyncInfo; public Type EntryType; public Delegate SyncProxyDelegate; public nint SyncProxyNativeEntry; public readonly nint Function; private Delegate? origDelegate; private ICoreNativeDetour? nativeDetour; private nint lastNativeEntry; public override Delegate EntryDelegate { get { throw new InvalidOperationException(); } } public Delegate? OrigDelegate { get { Delegate obj = origDelegate; if ((object)obj != null) { return obj; } ICoreNativeDetour val = nativeDetour; if (val == null) { return null; } if (!val.HasOrigEntrypoint) { return null; } return origDelegate = Marshal.GetDelegateForFunctionPointer(val.OrigEntrypoint, EntryType); } } public RootNativeDetourChainNode(nint function) { SyncInfo = new NativeDetourSyncInfo(); Function = function; } public void MaybeSetEntryType(Type type) { if ((object)EntryType != null) { return; } MethodInfo delInvoke = type.GetMethod("Invoke"); FormatInterpolatedStringHandler val = default(FormatInterpolatedStringHandler); ((FormatInterpolatedStringHandler)(ref val))..ctor(16, 1); ((FormatInterpolatedStringHandler)(ref val)).AppendLiteral("native->managed "); ((FormatInterpolatedStringHandler)(ref val)).AppendFormatted(type); MethodInfo methodInfo = GenerateSyncProxy(DebugFormatter.Format(ref val), MethodSignature.ForMethod((MethodBase)delInvoke, true), delegate(MethodDefinition method, ILProcessor il) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected O, but got Unknown //IL_0027: Unknown result type (might be due to invalid IL or missing references) ((MethodReference)method).Parameters.Insert(0, new ParameterDefinition(((MemberReference)method).Module.ImportReference(typeof(NativeDetourSyncInfo)))); il.Emit(OpCodes.Ldarg_0); }, delegate(MethodDefinition method, ILProcessor il, Action loadSyncInfo) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_002b: 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.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldfld, ((MemberReference)method).Module.ImportReference(NativeDetourSyncInfo_FirstDelegate)); for (int i = 1; i < ((MethodReference)method).Parameters.Count; i++) { il.Emit(OpCodes.Ldarg, i); } il.Emit(OpCodes.Callvirt, ((MemberReference)method).Module.ImportReference((MethodBase)delInvoke)); }); Delegate obj = methodInfo.CreateDelegate(type, SyncInfo); SyncProxyNativeEntry = Marshal.GetFunctionPointerForDelegate(obj); EntryType = type; SyncInfo.SyncProxy = methodInfo; SyncProxyDelegate = obj; } public override void UpdateChain(IDetourFactory factory, Delegate? fallback) { if (nativeDetour == null || lastNativeEntry != SyncProxyNativeEntry) { ((IDisposable)nativeDetour)?.Dispose(); origDelegate = null; nativeDetour = DetourFactory.CreateNativeDetour(factory, (IntPtr)Function, (IntPtr)SyncProxyNativeEntry, false); lastNativeEntry = SyncProxyNativeEntry; } Delegate obj = Next?.EntryDelegate; obj = (((object)obj != null) ? Extensions.CastDelegate(obj, EntryType) : null); if ((object)obj != null && !((ICoreDetourBase)nativeDetour).IsApplied) { ((ICoreDetourBase)nativeDetour).Apply(); origDelegate = null; } else if ((object)obj == null && ((ICoreDetourBase)nativeDetour).IsApplied) { ((ICoreDetourBase)nativeDetour).Undo(); origDelegate = null; } SyncInfo.FirstDelegate = obj; } } internal sealed class ChainDelegateState { public readonly Delegate Orig; public readonly Type NextType; public Delegate? Next; private static readonly FieldInfo ChainDelegateState_Orig = typeof(ChainDelegateState).GetField("Orig"); private static readonly FieldInfo ChainDelegateState_Next = typeof(ChainDelegateState).GetField("Next"); private static readonly ConditionalWeakTable chainMethodCache = new ConditionalWeakTable(); private Delegate? selfDelegate; public ChainDelegateState(Delegate orig, Type nextType) { Orig = orig; NextType = nextType; } private static MethodInfo GenerateChainMethod(Type origDelType, Type nextDelType) { //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Expected O, but got Unknown //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) MethodInfo method = origDelType.GetMethod("Invoke"); MethodInfo method2 = nextDelType.GetMethod("Invoke"); Helpers.Assert((object)method != null && (object)method2 != null, (string)null, "origInvoke is not null && nextInvoke is not null"); MethodSignature obj = MethodSignature.ForMethod((MethodBase)method2, true); FormatInterpolatedStringHandler val = default(FormatInterpolatedStringHandler); ((FormatInterpolatedStringHandler)(ref val))..ctor(7, 1); ((FormatInterpolatedStringHandler)(ref val)).AppendLiteral("Chain<"); ((FormatInterpolatedStringHandler)(ref val)).AppendFormatted(nextDelType); ((FormatInterpolatedStringHandler)(ref val)).AppendLiteral(">"); DynamicMethodDefinition val2 = obj.CreateDmd(DebugFormatter.Format(ref val)); try { Helpers.Assert(val2.Module != null && val2.Definition != null, (string)null, "dmd.Module is not null && dmd.Definition is not null"); ModuleDefinition module = val2.Module; ((MethodReference)val2.Definition).Parameters.Insert(0, new ParameterDefinition(module.ImportReference(typeof(ChainDelegateState)))); ILProcessor iLProcessor = val2.GetILProcessor(); iLProcessor.Emit(OpCodes.Ldarg_0); iLProcessor.Emit(OpCodes.Ldfld, module.ImportReference(ChainDelegateState_Orig)); iLProcessor.Emit(OpCodes.Ldarg_0); iLProcessor.Emit(OpCodes.Ldfld, module.ImportReference(ChainDelegateState_Next)); for (int i = 1; i < ((MethodReference)val2.Definition).Parameters.Count; i++) { iLProcessor.Emit(OpCodes.Ldarg, i); } iLProcessor.Emit(OpCodes.Tail); iLProcessor.Emit(OpCodes.Callvirt, module.ImportReference((MethodBase)method)); iLProcessor.Emit(OpCodes.Ret); return val2.Generate(); } finally { ((IDisposable)val2)?.Dispose(); } } private static MethodInfo GetChainMethod(Type origDelType, Type nextDelType) { return chainMethodCache.GetValue(origDelType, (Type orig) => GenerateChainMethod(orig, nextDelType)); } public Delegate GetDelegate() { return selfDelegate ?? (selfDelegate = GetChainMethod(Orig.GetType(), NextType).CreateDelegate(NextType, this)); } public void Remove() { MethodInfo method = NextType.GetMethod("Invoke"); Helpers.Assert((object)method != null, (string)null, "nextInvoke is not null"); Next = GetRemovedStub(MethodSignature.ForMethod((MethodBase)method)).CreateDelegate(NextType, null); } } internal sealed class NativeDetourState { public readonly nint Function; internal RootNativeDetourChainNode detourList; private readonly DepGraph detourGraph = new DepGraph(); private NativeChainNode? noConfigChain; internal SpinLock detourLock = new SpinLock(enableThreadOwnerTracking: true); internal int detourChainVersion; private FunctionDetourInfo? info; public FunctionDetourInfo Info => info ?? (info = new FunctionDetourInfo(this)); public event Action? NativeDetourApplied; public event Action? NativeDetourUndone; public NativeDetourState(nint function) { Function = function; detourList = new RootNativeDetourChainNode(function); } public void AddDetour(SingleNativeDetourState detour, bool takeLock = true) { bool lockTaken = false; try { if (takeLock) { detourLock.Enter(ref lockTaken); } if (detour.ManagerData != null) { throw new InvalidOperationException("Trying to add a detour which was already added"); } NativeDetourChainNode nativeDetourChainNode = new NativeDetourChainNode(detour); detourChainVersion++; DetourConfig config = nativeDetourChainNode.Config; if (config != null) { DepGraphNode depGraphNode = new DepGraphNode(new DepListNode(config, nativeDetourChainNode)); detourGraph.Insert(depGraphNode); detour.ManagerData = depGraphNode; } else { nativeDetourChainNode.Next = noConfigChain; noConfigChain = nativeDetourChainNode; detour.ManagerData = nativeDetourChainNode; } detourList.MaybeSetEntryType(detour.NativeDelegateType); UpdateChain(detour.Factory); } finally { if (lockTaken) { detourLock.Exit(useMemoryBarrier: true); } } InvokeDetourEvent(DetourManager.NativeDetourApplied, this.NativeDetourApplied, detour); } public void RemoveDetour(SingleNativeDetourState detour, bool takeLock = true) { bool lockTaken = false; try { if (takeLock) { detourLock.Enter(ref lockTaken); } detourChainVersion++; object obj = Interlocked.Exchange(ref detour.ManagerData, null); if (obj == null) { throw new InvalidOperationException("Trying to remove detour which wasn't added"); } if (!(obj is DepGraphNode depGraphNode)) { if (!(obj is NativeDetourChainNode node)) { throw new InvalidOperationException("Trying to remove detour with unknown manager data"); } RemoveNoConfigDetour(detour, node); } else { RemoveGraphDetour(detour, depGraphNode); _ = (NativeDetourChainNode)depGraphNode.ListNode.ChainNode; } } finally { if (lockTaken) { detourLock.Exit(useMemoryBarrier: true); } } InvokeDetourEvent(DetourManager.NativeDetourUndone, this.NativeDetourUndone, detour); } private void RemoveGraphDetour(SingleNativeDetourState detour, DepGraphNode node) { detourGraph.Remove(node); UpdateChain(detour.Factory); node.ListNode.ChainNode.Remove(); } private void RemoveNoConfigDetour(SingleNativeDetourState detour, NativeDetourChainNode node) { for (ref NativeChainNode next = ref noConfigChain; next != null; next = ref next.Next) { if (next == node) { next = node.Next; node.Next = null; break; } } UpdateChain(detour.Factory); node.Remove(); } private void UpdateChain(IDetourFactory updatingFactory) { DepListNode depListNode = detourGraph.ListHead; NativeChainNode nativeChainNode = null; ref NativeChainNode reference = ref nativeChainNode; while (depListNode != null) { reference = depListNode.ChainNode; reference = ref reference.Next; reference = null; depListNode = depListNode.Next; } reference = noConfigChain; detourList.Next = nativeChainNode; Volatile.Write(ref detourList.SyncInfo.UpdatingThread, EnvironmentEx.CurrentManagedThreadId); detourList.SyncInfo.WaitForNoActiveCalls(out var _); try { for (nativeChainNode = detourList; nativeChainNode != null; nativeChainNode = nativeChainNode.Next) { IDetourFactory val = (nativeChainNode.Next as NativeDetourChainNode)?.Detour.Factory; if (val == null) { val = (nativeChainNode as NativeDetourChainNode)?.Detour.Factory; } if (val == null) { val = updatingFactory; } nativeChainNode.UpdateChain(val, detourList.OrigDelegate); } } finally { Volatile.Write(ref detourList.SyncInfo.UpdatingThread, -1); } } private void InvokeDetourEvent(Action? evt1, Action? evt2, SingleNativeDetourState node) { if (evt1 != null || evt2 != null) { NativeDetourInfo detourInfo = Info.GetDetourInfo(node); evt1?.Invoke(detourInfo); evt2?.Invoke(detourInfo); } } } internal sealed class SingleNativeDetourState : SingleDetourStateBase { public readonly nint Function; public readonly Type NativeDelegateType; public readonly Delegate Invoker; public readonly bool HasOrigParam; public NativeDetourInfo? DetourInfo; public SingleNativeDetourState(INativeDetour detour) : base(detour) { Function = detour.Function; NativeDelegateType = detour.NativeDelegateType; Invoker = detour.Invoker; HasOrigParam = detour.HasOrigParam; } } internal sealed class DepListNode { public readonly DetourConfig Config; public readonly TNode ChainNode; public DepListNode? Next; public DepListNode(DetourConfig config, TNode chainNode) { Config = config; ChainNode = chainNode; } } internal sealed class DepGraphNode { public readonly DepListNode ListNode; public readonly List> BeforeThis = new List>(); public bool Visiting; public bool Visited; public DetourConfig Config => ListNode.Config; public DepGraphNode(DepListNode listNode) { ListNode = listNode; } } internal sealed class DepGraph { private readonly List> nodes = new List>(); public DepListNode? ListHead; private readonly DepListNode dummyListNode = new DepListNode(null, default(TNode)); private static void PrioInsert(List> list, DepGraphNode node) { int? priority = node.Config.Priority; if (priority.HasValue) { int valueOrDefault = priority.GetValueOrDefault(); int i = -1; for (int j = 0; j < list.Count; j++) { priority = list[j].Config.Priority; if (priority.HasValue) { int valueOrDefault2 = priority.GetValueOrDefault(); if (valueOrDefault >= valueOrDefault2) { i = j; break; } continue; } i = j; break; } if (i < 0) { i = list.Count; } else { for (; i < list.Count; i++) { DepGraphNode depGraphNode = list[i]; if (depGraphNode.Config.Priority != node.Config.Priority || depGraphNode.Config.SubPriority <= node.Config.SubPriority) { break; } } } list.Insert(i, node); } else { list.Add(node); } } public void Insert(DepGraphNode node) { node.ListNode.Next = null; node.BeforeThis.Clear(); node.Visited = false; node.Visiting = false; int i = -1; for (int j = 0; j < nodes.Count; j++) { DepGraphNode depGraphNode = nodes[j]; depGraphNode.Visited = false; if (i < 0) { int? priority = node.Config.Priority; if (priority.HasValue) { int valueOrDefault = priority.GetValueOrDefault(); priority = depGraphNode.Config.Priority; if (priority.HasValue) { int valueOrDefault2 = priority.GetValueOrDefault(); if (valueOrDefault >= valueOrDefault2) { i = j; } } else { i = j; } } } bool flag = false; bool flag2 = false; if (node.Config.Before.Contains(depGraphNode.Config.Id)) { PrioInsert(depGraphNode.BeforeThis, node); flag = true; } bool isEnabled; if (node.Config.After.Contains(depGraphNode.Config.Id)) { if (flag) { MMDbgLog.DebugLogWarningStringHandler message = new MMDbgLog.DebugLogWarningStringHandler(53, 2, out isEnabled); if (isEnabled) { message.AppendLiteral("Detour '"); message.AppendFormatted(node.Config.Id); message.AppendLiteral("' is marked as being both before and after '"); message.AppendFormatted(depGraphNode.Config.Id); message.AppendLiteral("'"); } MMDbgLog.Warning(ref message); } else { PrioInsert(node.BeforeThis, depGraphNode); flag2 = true; } } if (depGraphNode.Config.Before.Contains(node.Config.Id)) { if (flag) { MMDbgLog.DebugLogWarningStringHandler message2 = new MMDbgLog.DebugLogWarningStringHandler(53, 2, out isEnabled); if (isEnabled) { message2.AppendLiteral("Detour '"); message2.AppendFormatted(node.Config.Id); message2.AppendLiteral("' is marked as being both before and after '"); message2.AppendFormatted(depGraphNode.Config.Id); message2.AppendLiteral("'"); } MMDbgLog.Warning(ref message2); } else { PrioInsert(node.BeforeThis, depGraphNode); flag2 = true; } } if (!depGraphNode.Config.After.Contains(node.Config.Id)) { continue; } if (flag2) { MMDbgLog.DebugLogWarningStringHandler message3 = new MMDbgLog.DebugLogWarningStringHandler(53, 2, out isEnabled); if (isEnabled) { message3.AppendLiteral("Detour '"); message3.AppendFormatted(node.Config.Id); message3.AppendLiteral("' is marked as being both before and after '"); message3.AppendFormatted(depGraphNode.Config.Id); message3.AppendLiteral("'"); } MMDbgLog.Warning(ref message3); } else { PrioInsert(depGraphNode.BeforeThis, node); } } if (i < 0) { i = nodes.Count; } else { for (; i < nodes.Count; i++) { DepGraphNode depGraphNode2 = nodes[i]; if (depGraphNode2.Config.Priority != node.Config.Priority || depGraphNode2.Config.SubPriority <= node.Config.SubPriority) { break; } } } nodes.Insert(i, node); UpdateList(); } public void Remove(DepGraphNode node) { nodes.Remove(node); foreach (DepGraphNode node2 in nodes) { node2.BeforeThis.Remove(node); node2.Visited = false; } node.BeforeThis.Clear(); node.Visited = false; node.Visiting = false; node.ListNode.Next = null; UpdateList(); } private void UpdateList() { DepListNode depListNode = dummyListNode; depListNode.Next = null; DepListNode nextHolder = depListNode; foreach (DepGraphNode node in nodes) { InsertListNode(ref nextHolder, node); } ListHead = depListNode.Next; } private static void InsertListNode(ref DepListNode nextHolder, DepGraphNode node) { if (node.Visiting) { throw new InvalidOperationException("Cycle detected"); } if (node.Visited) { return; } node.Visiting = true; try { DepListNode listNode = node.ListNode; listNode.Next = null; foreach (DepGraphNode beforeThi in node.BeforeThis) { InsertListNode(ref nextHolder, beforeThi); } nextHolder.Next = listNode; nextHolder = listNode; node.Visited = true; } finally { node.Visiting = false; } } } internal class DetourSyncInfo { public MethodBase? SyncProxy; public int ActiveCalls; public int UpdatingThread; public bool WaitForChainUpdate() { if (Volatile.Read(in ActiveCalls) > 1 && DetermineThreadCallDepth() > 1) { return true; } Interlocked.Decrement(ref ActiveCalls); if (UpdatingThread == EnvironmentEx.CurrentManagedThreadId) { throw new InvalidOperationException("Method's detour chain is being updated by the current thread!"); } SpinWait spinWait = default(SpinWait); while (Volatile.Read(in UpdatingThread) != -1) { spinWait.SpinOnce(); } return false; } public void WaitForNoActiveCalls(out bool hasActiveCallsFromThread) { int num = DetermineThreadCallDepth(); hasActiveCallsFromThread = num > 0; SpinWait spinWait = default(SpinWait); while (Volatile.Read(in ActiveCalls) > num) { spinWait.SpinOnce(); } } private int DetermineThreadCallDepth() { if (Volatile.Read(in ActiveCalls) <= 0 || SyncProxy == null) { return 0; } StackFrame[] frames = new StackTrace().GetFrames(); MethodBase syncProxyIdentif = PlatformTriple.Current.GetIdentifiable(SyncProxy); return frames.Count(delegate(StackFrame f) { MethodBase method = f.GetMethod(); return (object)method != null && PlatformTriple.Current.GetIdentifiable(method) == syncProxyIdentif; }); } } internal abstract class SingleDetourStateBase { public readonly IDetourFactory Factory; public readonly DetourConfig? Config; public object? ManagerData; public bool IsValid; public bool IsApplied => Volatile.Read(in ManagerData) != null; protected SingleDetourStateBase(IDetourBase detour) { Factory = detour.Factory; Config = detour.Config; ManagerData = null; IsValid = true; } } private static readonly MethodInfo ManagedDetourSyncInfo_ReturnStolenTrampolines = typeof(ManagedDetourSyncInfo).GetMethod("ReturnStolenTrampolines"); private static readonly ConcurrentDictionary detourStates = new ConcurrentDictionary(); private static readonly FieldInfo NativeDetourSyncInfo_FirstDelegate = typeof(NativeDetourSyncInfo).GetField("FirstDelegate"); private static readonly ConcurrentDictionary nativeDetourStates = new ConcurrentDictionary(); private static readonly FieldInfo DetourSyncInfo_ActiveCalls = typeof(DetourSyncInfo).GetField("ActiveCalls"); private static readonly FieldInfo DetourSyncInfo_UpdatingThread = typeof(DetourSyncInfo).GetField("UpdatingThread"); private static readonly MethodInfo DetourSyncInfo_WaitForChainUpdate = typeof(DetourSyncInfo).GetMethod("WaitForChainUpdate"); private static readonly MethodInfo Interlocked_Increment = typeof(Interlocked).GetMethod("Increment", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(int).MakeByRefType() }, null); private static readonly MethodInfo Interlocked_Decrement = typeof(Interlocked).GetMethod("Decrement", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(int).MakeByRefType() }, null); private static readonly ConditionalWeakTable removedStubCache = new ConditionalWeakTable(); public static event Action? DetourApplied; public static event Action? DetourUndone; public static event Action? ILHookApplied; public static event Action? ILHookUndone; public static event Action? NativeDetourApplied; public static event Action? NativeDetourUndone; internal static ManagedDetourState GetDetourState(MethodBase method) { method = PlatformTriple.Current.GetIdentifiable(method); return detourStates.GetOrAdd(method, (MethodBase m) => new ManagedDetourState(m)); } public static MethodDetourInfo GetDetourInfo(MethodBase method) { return GetDetourState(method).Info; } internal static NativeDetourState GetNativeDetourState(nint function) { return nativeDetourStates.GetOrAdd(function, (nint f) => new NativeDetourState(f)); } public static FunctionDetourInfo GetNativeDetourInfo(nint function) { return GetNativeDetourState(function).Info; } private static MethodInfo GenerateSyncProxy(string innerName, MethodSignature Sig, Action emitLoadSyncInfo, Action emitInvoke, Action? emitLastCallReturn = null) { //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Expected O, but got Unknown //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Unknown result type (might be due to invalid IL or missing references) //IL_0187: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: Unknown result type (might be due to invalid IL or missing references) //IL_01c1: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: Unknown result type (might be due to invalid IL or missing references) //IL_01ee: Unknown result type (might be due to invalid IL or missing references) //IL_0246: Unknown result type (might be due to invalid IL or missing references) //IL_024d: Expected O, but got Unknown //IL_026a: Unknown result type (might be due to invalid IL or missing references) //IL_0227: Unknown result type (might be due to invalid IL or missing references) //IL_022e: Expected O, but got Unknown //IL_02c1: Unknown result type (might be due to invalid IL or missing references) //IL_02d3: Unknown result type (might be due to invalid IL or missing references) //IL_02e5: Unknown result type (might be due to invalid IL or missing references) //IL_031f: 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_02af: Unknown result type (might be due to invalid IL or missing references) //IL_036b: Unknown result type (might be due to invalid IL or missing references) //IL_037d: Unknown result type (might be due to invalid IL or missing references) //IL_0359: Unknown result type (might be due to invalid IL or missing references) //IL_03b6: Unknown result type (might be due to invalid IL or missing references) //IL_03f2: Unknown result type (might be due to invalid IL or missing references) //IL_03e0: Unknown result type (might be due to invalid IL or missing references) FormatInterpolatedStringHandler val = default(FormatInterpolatedStringHandler); ((FormatInterpolatedStringHandler)(ref val))..ctor(11, 1); ((FormatInterpolatedStringHandler)(ref val)).AppendLiteral("SyncProxy<"); ((FormatInterpolatedStringHandler)(ref val)).AppendFormatted(innerName); ((FormatInterpolatedStringHandler)(ref val)).AppendLiteral(">"); DynamicMethodDefinition val2 = Sig.CreateDmd(DebugFormatter.Format(ref val)); try { ILProcessor il = val2.GetILProcessor(); MethodDefinition definition = val2.Definition; ModuleDefinition module = val2.Module; TypeReference val3 = module.ImportReference(typeof(DetourSyncInfo)); VariableDefinition syncInfoVar = new VariableDefinition(val3); il.Body.Variables.Add(syncInfoVar); emitLoadSyncInfo(definition, il); il.Emit(OpCodes.Stloc, syncInfoVar); Instruction val4 = il.Create(OpCodes.Nop); il.Append(val4); il.Emit(OpCodes.Ldloc, syncInfoVar); il.Emit(OpCodes.Ldflda, module.ImportReference(DetourSyncInfo_ActiveCalls)); il.Emit(OpCodes.Call, module.ImportReference((MethodBase)Interlocked_Increment)); il.Emit(OpCodes.Pop); il.Emit(OpCodes.Ldloc, syncInfoVar); il.Emit(OpCodes.Volatile); il.Emit(OpCodes.Ldfld, module.ImportReference(DetourSyncInfo_UpdatingThread)); il.Emit(OpCodes.Ldc_I4_M1); Instruction val5 = il.Create(OpCodes.Nop); il.Emit(OpCodes.Beq_S, val5); il.Emit(OpCodes.Ldloc, syncInfoVar); il.Emit(OpCodes.Call, module.ImportReference((MethodBase)DetourSyncInfo_WaitForChainUpdate)); il.Emit(OpCodes.Brtrue_S, val5); il.Emit(OpCodes.Br_S, val4); il.Append(val5); VariableDefinition val6 = null; if (Sig.ReturnType != typeof(void)) { val6 = new VariableDefinition(((MethodReference)definition).ReturnType); il.Body.Variables.Add(val6); } ExceptionHandler val7 = new ExceptionHandler((ExceptionHandlerType)2); il.Body.ExceptionHandlers.Add(val7); Instruction val8 = il.Create(OpCodes.Nop); il.Append(val8); val7.TryStart = val8; emitInvoke(definition, il, delegate { //IL_0006: Unknown result type (might be due to invalid IL or missing references) il.Emit(OpCodes.Ldloc, syncInfoVar); }); if (val6 != null) { il.Emit(OpCodes.Stloc, val6); } Instruction val9 = il.Create(OpCodes.Nop); il.Emit(OpCodes.Leave_S, val9); Instruction val10 = il.Create(OpCodes.Ldloc, syncInfoVar); Instruction tryEnd = (val7.HandlerStart = val10); val7.TryEnd = tryEnd; il.Append(val10); il.Emit(OpCodes.Ldflda, module.ImportReference(DetourSyncInfo_ActiveCalls)); il.Emit(OpCodes.Call, module.ImportReference((MethodBase)Interlocked_Decrement)); if (emitLastCallReturn == null) { il.Emit(OpCodes.Pop); } else { Instruction val12 = il.Create(OpCodes.Nop); il.Emit(OpCodes.Brtrue_S, val12); emitLastCallReturn(definition, il, delegate { //IL_0006: Unknown result type (might be due to invalid IL or missing references) il.Emit(OpCodes.Ldloc, syncInfoVar); }); il.Append(val12); } il.Emit(OpCodes.Endfinally); val7.HandlerEnd = val9; il.Append(val9); if (val6 != null) { il.Emit(OpCodes.Ldloc, val6); } il.Emit(OpCodes.Ret); return val2.Generate(); } finally { ((IDisposable)val2)?.Dispose(); } } private static MethodInfo GenerateRemovedStub(MethodSignature trampolineSig) { //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) FormatInterpolatedStringHandler val = default(FormatInterpolatedStringHandler); ((FormatInterpolatedStringHandler)(ref val))..ctor(13, 1); ((FormatInterpolatedStringHandler)(ref val)).AppendLiteral("RemovedStub<"); ((FormatInterpolatedStringHandler)(ref val)).AppendFormatted(trampolineSig); ((FormatInterpolatedStringHandler)(ref val)).AppendLiteral(">"); DynamicMethodDefinition val2 = trampolineSig.CreateDmd(DebugFormatter.Format(ref val)); try { Helpers.Assert(val2.Module != null && val2.Definition != null, (string)null, "dmd.Module is not null && dmd.Definition is not null"); ModuleDefinition module = val2.Module; ILProcessor iLProcessor = val2.GetILProcessor(); iLProcessor.Emit(OpCodes.Ldstr, "Detour has been removed"); iLProcessor.Emit(OpCodes.Newobj, module.ImportReference((MethodBase)typeof(InvalidOperationException).GetConstructor(new Type[1] { typeof(string) }))); iLProcessor.Emit(OpCodes.Throw); return val2.Generate(); } finally { ((IDisposable)val2)?.Dispose(); } } private static MethodInfo GetRemovedStub(MethodSignature trampolineSig) { return removedStubCache.GetValue(trampolineSig, (MethodSignature orig) => GenerateRemovedStub(trampolineSig)); } } internal static class Extensions { public static MethodInfo CreateILCopy(this MethodBase method) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Expected O, but got Unknown DynamicMethodDefinition val = new DynamicMethodDefinition(method); try { return val.Generate(); } finally { ((IDisposable)val)?.Dispose(); } } } public sealed class Hook : IHook, IDetourBase, IDetour, IDisposable { private sealed class TrampolineData : IDetourTrampoline, IDisposable { private readonly MethodInfo trampoline; private bool alive; private bool hasOwnership; public MethodBase TrampolineMethod => trampoline; public TrampolineData(MethodSignature sig) { trampoline = TrampolinePool.Rent(sig); alive = (hasOwnership = true); } public void Dispose() { lock (this) { if (alive) { alive = false; if (hasOwnership) { TrampolinePool.Return(trampoline); } } } } public void StealTrampolineOwnership() { lock (this) { Helpers.Assert(alive && hasOwnership, (string)null, "alive && hasOwnership"); hasOwnership = false; } } public void ReturnTrampolineOwnership() { lock (this) { Helpers.Assert(!hasOwnership, (string)null, "!hasOwnership"); if (!alive) { TrampolinePool.Return(trampoline); } else { hasOwnership = true; } } } } private sealed class HookData { public readonly object? Target; public readonly Delegate? InvokeNext; public HookData(object? target, Delegate? invokeNext) { Target = target; InvokeNext = invokeNext; } } private const bool ApplyByDefault = true; private readonly IDetourFactory factory; private readonly MethodInfo realTarget; private readonly TrampolineData trampoline; private readonly DetourManager.ManagedDetourState state; private readonly DetourManager.SingleManagedDetourState detour; private readonly DataScope delegateObjectScope; private static readonly FieldInfo HookData_Target = typeof(HookData).GetField("Target"); private static readonly FieldInfo HookData_InvokeNext = typeof(HookData).GetField("InvokeNext"); private bool disposedValue; IDetourFactory IDetourBase.Factory => factory; public DetourConfig? Config { get; } public MethodBase Source { get; } public MethodInfo Target { get; } MethodInfo IHook.PublicTarget => Target; MethodInfo IHook.InvokeTarget => realTarget; IDetourTrampoline IHook.NextTrampoline => trampoline; public bool IsValid => !disposedValue; public bool IsApplied => detour.IsApplied; public DetourInfo DetourInfo => state.Info.GetDetourInfo(detour); public Hook(Expression source, Expression target) : this(Helpers.ThrowIfNull>(source, "source").Body, Helpers.ThrowIfNull>(target, "target").Body) { } public Hook(Expression source, Expression target) : this(((MethodCallExpression)Helpers.ThrowIfNull(source, "source")).Method, ((MethodCallExpression)Helpers.ThrowIfNull(target, "target")).Method) { } public Hook(MethodBase source, MethodInfo target) : this(source, target, DetourContext.GetDefaultConfig()) { } public Hook(Expression source, Expression target, bool applyByDefault) : this(Helpers.ThrowIfNull>(source, "source").Body, Helpers.ThrowIfNull>(target, "target").Body, applyByDefault) { } public Hook(Expression source, Expression target, bool applyByDefault) : this(((MethodCallExpression)Helpers.ThrowIfNull(source, "source")).Method, ((MethodCallExpression)Helpers.ThrowIfNull(target, "target")).Method, applyByDefault) { } public Hook(MethodBase source, MethodInfo target, bool applyByDefault) : this(source, target, DetourContext.GetDefaultConfig(), applyByDefault) { } public Hook(Expression source, Expression target, DetourConfig? config) : this(Helpers.ThrowIfNull>(source, "source").Body, Helpers.ThrowIfNull>(target, "target").Body, config) { } public Hook(Expression source, Expression target, DetourConfig? config) : this(((MethodCallExpression)Helpers.ThrowIfNull(source, "source")).Method, ((MethodCallExpression)Helpers.ThrowIfNull(target, "target")).Method, config) { } public Hook(MethodBase source, MethodInfo target, DetourConfig? config) : this(source, target, config, applyByDefault: true) { } public Hook(Expression source, Expression target, DetourConfig? config, bool applyByDefault) : this(Helpers.ThrowIfNull>(source, "source").Body, Helpers.ThrowIfNull>(target, "target").Body, config, applyByDefault) { } public Hook(Expression source, Expression target, DetourConfig? config, bool applyByDefault) : this(((MethodCallExpression)Helpers.ThrowIfNull(source, "source")).Method, ((MethodCallExpression)Helpers.ThrowIfNull(target, "target")).Method, config, applyByDefault) { } public Hook(MethodBase source, MethodInfo target, DetourConfig? config, bool applyByDefault) : this(source, target, DetourContext.GetDefaultFactory(), config, applyByDefault) { } public Hook(MethodBase source, MethodInfo target, IDetourFactory factory, DetourConfig? config, bool applyByDefault) : this(source, target, null, factory, config, applyByDefault) { } public Hook(Expression source, Expression target, object? targetObj) : this(Helpers.ThrowIfNull>(source, "source").Body, Helpers.ThrowIfNull>(target, "target").Body, targetObj) { } public Hook(Expression source, Expression target, object? targetObj) : this(((MethodCallExpression)Helpers.ThrowIfNull(source, "source")).Method, ((MethodCallExpression)Helpers.ThrowIfNull(target, "target")).Method, targetObj) { } public Hook(MethodBase source, MethodInfo target, object? targetObj) : this(source, target, targetObj, DetourContext.GetDefaultConfig()) { } public Hook(Expression source, Expression target, object? targetObj, bool applyByDefault) : this(Helpers.ThrowIfNull>(source, "source").Body, Helpers.ThrowIfNull>(target, "target").Body, targetObj, applyByDefault) { } public Hook(Expression source, Expression target, object? targetObj, bool applyByDefault) : this(((MethodCallExpression)Helpers.ThrowIfNull(source, "source")).Method, ((MethodCallExpression)Helpers.ThrowIfNull(target, "target")).Method, targetObj, applyByDefault) { } public Hook(MethodBase source, MethodInfo target, object? targetObj, bool applyByDefault) : this(source, target, targetObj, DetourContext.GetDefaultConfig(), applyByDefault) { } public Hook(Expression source, Expression target, object? targetObj, DetourConfig? config) : this(Helpers.ThrowIfNull>(source, "source").Body, Helpers.ThrowIfNull>(target, "target").Body, targetObj, config) { } public Hook(Expression source, Expression target, object? targetObj, DetourConfig? config) : this(((MethodCallExpression)Helpers.ThrowIfNull(source, "source")).Method, ((MethodCallExpression)Helpers.ThrowIfNull(target, "target")).Method, targetObj, config) { } public Hook(MethodBase source, MethodInfo target, object? targetObj, DetourConfig? config) : this(source, target, targetObj, config, applyByDefault: true) { } public Hook(Expression source, Expression target, object? targetObj, DetourConfig? config, bool applyByDefault) : this(Helpers.ThrowIfNull>(source, "source").Body, Helpers.ThrowIfNull>(target, "target").Body, targetObj, config, applyByDefault) { } public Hook(Expression source, Expression target, object? targetObj, DetourConfig? config, bool applyByDefault) : this(((MethodCallExpression)Helpers.ThrowIfNull(source, "source")).Method, ((MethodCallExpression)Helpers.ThrowIfNull(target, "target")).Method, targetObj, config, applyByDefault) { } public Hook(MethodBase source, MethodInfo target, object? targetObj, DetourConfig? config, bool applyByDefault) : this(source, target, targetObj, DetourContext.GetDefaultFactory(), config, applyByDefault) { } public Hook(Expression source, Delegate target) : this(Helpers.ThrowIfNull>(source, "source").Body, target) { } public Hook(Expression source, Delegate target) : this(((MethodCallExpression)Helpers.ThrowIfNull(source, "source")).Method, target) { } public Hook(MethodBase source, Delegate target) : this(source, target, DetourContext.GetDefaultConfig()) { } public Hook(Expression source, Delegate target, bool applyByDefault) : this(Helpers.ThrowIfNull>(source, "source").Body, target, applyByDefault) { } public Hook(Expression source, Delegate target, bool applyByDefault) : this(((MethodCallExpression)Helpers.ThrowIfNull(source, "source")).Method, target, applyByDefault) { } public Hook(MethodBase source, Delegate target, bool applyByDefault) : this(source, target, DetourContext.GetDefaultConfig(), applyByDefault) { } public Hook(Expression source, Delegate target, DetourConfig? config) : this(Helpers.ThrowIfNull>(source, "source").Body, target, config) { } public Hook(Expression source, Delegate target, DetourConfig? config) : this(((MethodCallExpression)Helpers.ThrowIfNull(source, "source")).Method, target, config) { } public Hook(MethodBase source, Delegate target, DetourConfig? config) : this(source, target, config, applyByDefault: true) { } public Hook(Expression source, Delegate target, DetourConfig? config, bool applyByDefault) : this(Helpers.ThrowIfNull>(source, "source").Body, target, config, applyByDefault) { } public Hook(Expression source, Delegate target, DetourConfig? config, bool applyByDefault) : this(((MethodCallExpression)Helpers.ThrowIfNull(source, "source")).Method, target, config, applyByDefault) { } public Hook(MethodBase source, Delegate target, DetourConfig? config, bool applyByDefault) : this(source, target, DetourContext.GetDefaultFactory(), config, applyByDefault) { } public Hook(MethodBase source, Delegate target, IDetourFactory factory, DetourConfig? config, bool applyByDefault) : this(source, GetDelegateHookInfo(Helpers.ThrowIfNull(target, "target"), out object target2), target2, factory, config, applyByDefault) { } private static MethodInfo GetDelegateHookInfo(Delegate del, out object? target) { if (del.GetInvocationList().Length == 1) { target = del.Target; return del.Method; } target = del; return del.GetType().GetMethod("Invoke") ?? throw new InvalidOperationException("Could not get Invoke method of delegate"); } public Hook(MethodBase source, MethodInfo target, object? targetObject, IDetourFactory factory, DetourConfig? config, bool applyByDefault) { Helpers.ThrowIfArgumentNull(source, "source"); Helpers.ThrowIfArgumentNull(target, "target"); Helpers.ThrowIfArgumentNull(factory, "factory"); this.factory = factory; Config = config; Source = PlatformTriple.Current.GetIdentifiable(source); Target = target; realTarget = PrepareRealTarget(targetObject, out trampoline, out delegateObjectScope); bool isEnabled; MMDbgLog.DebugLogTraceStringHandler message = new MMDbgLog.DebugLogTraceStringHandler(23, 2, out isEnabled); if (isEnabled) { message.AppendLiteral("Creating Hook from "); message.AppendFormatted(Source); message.AppendLiteral(" to "); message.AppendFormatted(Target); } MMDbgLog.Trace(ref message); state = DetourManager.GetDetourState(source); detour = new DetourManager.SingleManagedDetourState(this); if (applyByDefault) { Apply(); } } private MethodInfo PrepareRealTarget(object? target, out TrampolineData trampoline, out DataScope scope) { //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Expected O, but got Unknown //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Expected O, but got Unknown //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_01c0: 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_0241: Unknown result type (might be due to invalid IL or missing references) //IL_0246: Unknown result type (might be due to invalid IL or missing references) //IL_0218: Unknown result type (might be due to invalid IL or missing references) //IL_0226: Unknown result type (might be due to invalid IL or missing references) //IL_0200: 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_0281: Unknown result type (might be due to invalid IL or missing references) //IL_0293: Unknown result type (might be due to invalid IL or missing references) CheckSupported(); MethodSignature val = MethodSignature.ForMethod(Source); MethodSignature val2 = MethodSignature.ForMethod((MethodBase)Target, true); if (target == null && !Target.IsStatic) { throw new ArgumentException("Target method is nonstatic, but no target object was provided"); } if (target != null && Target.IsStatic) { throw new ArgumentException("Target method is static, but a target object was provided"); } Type type = null; if (val2.ParameterCount == val.ParameterCount + 1) { type = val2.FirstParameter; val2 = new MethodSignature(val2.ReturnType, val2.Parameters.Skip(1)); } if (!val.IsCompatibleWith(val2)) { throw new ArgumentException("Target method is not compatible with source method"); } MethodSignature val3 = val; MethodInfo methodInfo = type?.GetMethod("Invoke"); if ((object)methodInfo != null) { val3 = MethodSignature.ForMethod((MethodBase)methodInfo, true); } if (!val3.IsCompatibleWith(val)) { throw new ArgumentException("Target method's delegate parameter is not compatible with the source method"); } trampoline = new TrampolineData(val3); if (target == null && (object)type == null) { scope = default(DataScope); return Target; } HookData hookData = new HookData(target, ((object)type != null) ? Extensions.CreateDelegate(trampoline.TrampolineMethod, type) : null); FormatInterpolatedStringHandler val4 = default(FormatInterpolatedStringHandler); ((FormatInterpolatedStringHandler)(ref val4))..ctor(6, 1); ((FormatInterpolatedStringHandler)(ref val4)).AppendLiteral("Hook<"); ((FormatInterpolatedStringHandler)(ref val4)).AppendFormatted(Extensions.GetID((MethodBase)Target, (string)null, (string)null, true, false, false)); ((FormatInterpolatedStringHandler)(ref val4)).AppendLiteral(">"); DynamicMethodDefinition val5 = val.CreateDmd(DebugFormatter.Format(ref val4)); try { ILProcessor iLProcessor = val5.GetILProcessor(); ModuleDefinition module = val5.Module; MethodDefinition definition = val5.Definition; VariableDefinition val6 = new VariableDefinition(module.ImportReference(typeof(HookData))); iLProcessor.Body.Variables.Add(val6); DynamicReferenceCell val7 = default(DynamicReferenceCell); scope = DynamicReferenceManager.EmitNewTypedReference(iLProcessor, hookData, ref val7); iLProcessor.Emit(OpCodes.Stloc, val6); if (!Target.IsStatic) { iLProcessor.Emit(OpCodes.Ldloc, val6); iLProcessor.Emit(OpCodes.Ldfld, module.ImportReference(HookData_Target)); Type declaringType = Target.DeclaringType; if ((object)declaringType != null && declaringType.IsValueType) { iLProcessor.Emit(OpCodes.Unbox, module.ImportReference(declaringType)); } } if ((object)type != null) { iLProcessor.Emit(OpCodes.Ldloc, val6); iLProcessor.Emit(OpCodes.Ldfld, module.ImportReference(HookData_InvokeNext)); } Enumerator enumerator = ((MethodReference)definition).Parameters.GetEnumerator(); try { while (enumerator.MoveNext()) { ParameterDefinition current = enumerator.Current; iLProcessor.Emit(OpCodes.Ldarg, ((ParameterReference)current).Index); } } finally { ((IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose(); } Extensions.Emit(iLProcessor, OpCodes.Call, (MethodBase)Target); iLProcessor.Emit(OpCodes.Ret); return val5.Generate(); } finally { ((IDisposable)val5)?.Dispose(); } } private void CheckSupported() { if (!Source.IsGenericMethod) { Type declaringType = Source.DeclaringType; if ((object)declaringType == null || !declaringType.IsGenericType) { return; } } throw new ArgumentException("Source method is generic, generic hooks are not supported"); } private void CheckDisposed() { if (disposedValue) { throw new ObjectDisposedException(ToString()); } } public void Apply() { CheckDisposed(); bool lockTaken = false; try { state.detourLock.Enter(ref lockTaken); if (!IsApplied) { bool isEnabled; MMDbgLog.DebugLogTraceStringHandler message = new MMDbgLog.DebugLogTraceStringHandler(23, 2, out isEnabled); if (isEnabled) { message.AppendLiteral("Applying Hook from "); message.AppendFormatted(Source); message.AppendLiteral(" to "); message.AppendFormatted(Target); } MMDbgLog.Trace(ref message); state.AddDetour(detour, !lockTaken); } } finally { if (lockTaken) { state.detourLock.Exit(useMemoryBarrier: true); } } } public void Undo() { CheckDisposed(); bool lockTaken = false; try { state.detourLock.Enter(ref lockTaken); if (IsApplied) { bool isEnabled; MMDbgLog.DebugLogTraceStringHandler message = new MMDbgLog.DebugLogTraceStringHandler(22, 2, out isEnabled); if (isEnabled) { message.AppendLiteral("Undoing Hook from "); message.AppendFormatted(Source); message.AppendLiteral(" to "); message.AppendFormatted(Target); } MMDbgLog.Trace(ref message); state.RemoveDetour(detour, !lockTaken); } } finally { if (lockTaken) { state.detourLock.Exit(useMemoryBarrier: true); } } } private void Dispose(bool disposing) { if (!disposedValue && detour != null) { detour.IsValid = false; if (!AppDomain.CurrentDomain.IsFinalizingForUnload() && !Environment.HasShutdownStarted) { Undo(); } delegateObjectScope.Dispose(); if (disposing) { trampoline.Dispose(); } disposedValue = true; } } ~Hook() { Dispose(disposing: false); } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } } public interface IDetour : IDisposable { DetourConfig? Config { get; } bool IsValid { get; } bool IsApplied { get; } void Apply(); void Undo(); } internal interface IDetourBase { IDetourFactory Factory { get; } DetourConfig? Config { get; } } internal interface IDetourTrampoline { MethodBase TrampolineMethod { get; } void StealTrampolineOwnership(); void ReturnTrampolineOwnership(); } internal interface IHook : IDetourBase { MethodInfo PublicTarget { get; } MethodInfo InvokeTarget { get; } IDetourTrampoline NextTrampoline { get; } } internal interface IILHook : IDetourBase { Manipulator Manip { get; } } internal interface INativeDetour : IDetourBase { nint Function { get; } Type NativeDelegateType { get; } Delegate Invoker { get; } bool HasOrigParam { get; } } [CLSCompliant(false)] public sealed class ILHook : IILHook, IDetourBase, IDetour, IDisposable { private const bool ApplyByDefault = true; private readonly IDetourFactory factory; private readonly DetourManager.ManagedDetourState state; private readonly DetourManager.SingleILHookState hook; private bool disposedValue; IDetourFactory IDetourBase.Factory => factory; public MethodBase Method { get; } public Manipulator Manipulator { get; } public DetourConfig? Config { get; } Manipulator IILHook.Manip => Manipulator; public bool IsValid => !disposedValue; public bool IsApplied => hook.IsApplied; public ILHookInfo HookInfo => state.Info.GetILHookInfo(hook); public ILHook(Expression source, Manipulator manip) : this(Helpers.ThrowIfNull>(source, "source").Body, manip) { } public ILHook(Expression source, Manipulator manip) : this(((MethodCallExpression)Helpers.ThrowIfNull(source, "source")).Method, manip) { } public ILHook(MethodBase source, Manipulator manip) : this(source, manip, DetourContext.GetDefaultConfig()) { } public ILHook(Expression source, Manipulator manip, bool applyByDefault) : this(Helpers.ThrowIfNull>(source, "source").Body, manip, applyByDefault) { } public ILHook(Expression source, Manipulator manip, bool applyByDefault) : this(((MethodCallExpression)Helpers.ThrowIfNull(source, "source")).Method, manip, applyByDefault) { } public ILHook(MethodBase source, Manipulator manip, bool applyByDefault) : this(source, manip, DetourContext.GetDefaultConfig(), applyByDefault) { } public ILHook(Expression source, Manipulator manip, DetourConfig? config) : this(Helpers.ThrowIfNull>(source, "source").Body, manip, config) { } public ILHook(Expression source, Manipulator manip, DetourConfig? config) : this(((MethodCallExpression)Helpers.ThrowIfNull(source, "source")).Method, manip, config) { } public ILHook(MethodBase source, Manipulator manip, DetourConfig? config) : this(source, manip, config, applyByDefault: true) { } public ILHook(Expression source, Manipulator manip, DetourConfig? config, bool applyByDefault) : this(Helpers.ThrowIfNull>(source, "source").Body, manip, config, applyByDefault) { } public ILHook(Expression source, Manipulator manip, DetourConfig? config, bool applyByDefault) : this(((MethodCallExpression)Helpers.ThrowIfNull(source, "source")).Method, manip, config, applyByDefault) { } public ILHook(MethodBase source, Manipulator manip, DetourConfig? config, bool applyByDefault) : this(source, manip, DetourContext.GetDefaultFactory(), config, applyByDefault) { } public ILHook(MethodBase method, Manipulator manipulator, IDetourFactory factory, DetourConfig? config, bool applyByDefault) { Helpers.ThrowIfArgumentNull(method, "method"); Helpers.ThrowIfArgumentNull(manipulator, "manipulator"); Helpers.ThrowIfArgumentNull(factory, "factory"); Method = PlatformTriple.Current.GetIdentifiable(method); Manipulator = manipulator; Config = config; this.factory = factory; bool isEnabled; MMDbgLog.DebugLogTraceStringHandler message = new MMDbgLog.DebugLogTraceStringHandler(20, 1, out isEnabled); if (isEnabled) { message.AppendLiteral("Creating ILHook for "); message.AppendFormatted(Method); } MMDbgLog.Trace(ref message); state = DetourManager.GetDetourState(method); hook = new DetourManager.SingleILHookState(this); if (applyByDefault) { Apply(); } } private void CheckDisposed() { if (disposedValue) { throw new ObjectDisposedException(ToString()); } } public void Apply() { CheckDisposed(); bool lockTaken = false; try { state.detourLock.Enter(ref lockTaken); if (!IsApplied) { bool isEnabled; MMDbgLog.DebugLogTraceStringHandler message = new MMDbgLog.DebugLogTraceStringHandler(20, 1, out isEnabled); if (isEnabled) { message.AppendLiteral("Applying ILHook for "); message.AppendFormatted(Method); } MMDbgLog.Trace(ref message); state.AddILHook(hook, !lockTaken); } } finally { if (lockTaken) { state.detourLock.Exit(useMemoryBarrier: true); } } } public void Undo() { CheckDisposed(); bool lockTaken = false; try { state.detourLock.Enter(ref lockTaken); if (IsApplied) { bool isEnabled; MMDbgLog.DebugLogTraceStringHandler message = new MMDbgLog.DebugLogTraceStringHandler(19, 1, out isEnabled); if (isEnabled) { message.AppendLiteral("Undoing ILHook for "); message.AppendFormatted(Method); } MMDbgLog.Trace(ref message); state.RemoveILHook(hook, !lockTaken); } } finally { if (lockTaken) { state.detourLock.Exit(useMemoryBarrier: true); } } } private void Dispose(bool disposing) { if (!disposedValue && hook != null) { hook.IsValid = false; if (!AppDomain.CurrentDomain.IsFinalizingForUnload() && !Environment.HasShutdownStarted) { Undo(); } disposedValue = true; } } ~ILHook() { Dispose(disposing: false); } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } } public abstract class DetourBase { public MethodDetourInfo Method { get; } public bool IsApplied => IsAppliedCore(); public DetourConfig? Config => ConfigCore(); private protected DetourBase(MethodDetourInfo method) { Method = method; } private protected abstract bool IsAppliedCore(); private protected abstract DetourConfig? ConfigCore(); public void Apply() { ref SpinLock detourLock = ref Method.state.detourLock; bool lockTaken = detourLock.IsThreadOwnerTrackingEnabled && detourLock.IsHeldByCurrentThread; try { if (!lockTaken) { detourLock.Enter(ref lockTaken); } ApplyCore(); } finally { if (lockTaken) { detourLock.Exit(useMemoryBarrier: true); } } } public void Undo() { ref SpinLock detourLock = ref Method.state.detourLock; bool lockTaken = detourLock.IsThreadOwnerTrackingEnabled && detourLock.IsHeldByCurrentThread; try { if (!lockTaken) { detourLock.Enter(ref lockTaken); } UndoCore(); } finally { if (lockTaken) { detourLock.Exit(useMemoryBarrier: true); } } } private protected abstract void ApplyCore(); private protected abstract void UndoCore(); } public sealed class DetourCollection : IEnumerable, IEnumerable { public struct Enumerator : IEnumerator, IEnumerator, IDisposable { private readonly MethodDetourInfo mdi; private DetourManager.ManagedChainNode? curNode; private int version; public DetourInfo Current => mdi.GetDetourInfo(((DetourManager.ManagedDetourChainNode)curNode).Detour); object IEnumerator.Current => Current; internal Enumerator(MethodDetourInfo mdi) { this.mdi = mdi; version = mdi.state.detourChainVersion; curNode = mdi.state.detourList; } [MemberNotNullWhen(true, "curNode")] public bool MoveNext() { if (version != mdi.state.detourChainVersion) { throw new InvalidOperationException("The detour chain was modified while enumerating"); } curNode = curNode?.Next; return curNode != null; } public void Reset() { version = mdi.state.detourChainVersion; curNode = mdi.state.detourList; } public void Dispose() { curNode = null; } } private readonly MethodDetourInfo mdi; internal DetourCollection(MethodDetourInfo mdi) { this.mdi = mdi; } public Enumerator GetEnumerator() { return new Enumerator(mdi); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } public sealed class DetourInfo : DetourBase { private readonly DetourManager.SingleManagedDetourState detour; public MethodBase Entry => detour.PublicTarget; internal DetourManager.ManagedDetourChainNode? ChainNode { get { object managerData = detour.ManagerData; if (!(managerData is DetourManager.ManagedDetourChainNode result)) { if (managerData is DetourManager.DepGraphNode depGraphNode) { return (DetourManager.ManagedDetourChainNode)depGraphNode.ListNode.ChainNode; } return null; } return result; } } public DetourInfo? Next { get { if (!(ChainNode?.Next is DetourManager.ManagedDetourChainNode managedDetourChainNode)) { return null; } return base.Method.GetDetourInfo(managedDetourChainNode.Detour); } } internal DetourInfo(MethodDetourInfo method, DetourManager.SingleManagedDetourState detour) : base(method) { this.detour = detour; } private protected override bool IsAppliedCore() { return detour.IsApplied; } private protected override DetourConfig? ConfigCore() { return detour.Config; } private protected override void ApplyCore() { if (detour.IsApplied) { throw new InvalidOperationException("Detour is already applied"); } if (!detour.IsValid) { throw new InvalidOperationException("Detour is no longer valid"); } base.Method.state.AddDetour(detour, takeLock: false); } private protected override void UndoCore() { if (!detour.IsApplied) { throw new InvalidOperationException("Detour is not currently applied"); } base.Method.state.RemoveDetour(detour, takeLock: false); } } public sealed class FunctionDetourInfo { [EditorBrowsable(EditorBrowsableState.Never)] public readonly ref struct Lock { private readonly FunctionDetourInfo fdi; private readonly bool lockTaken; internal Lock(FunctionDetourInfo fdi) { this.fdi = fdi; lockTaken = false; try { fdi.EnterLock(ref lockTaken); } catch { if (lockTaken) { fdi.ExitLock(); } throw; } } public void Dispose() { if (lockTaken) { fdi.ExitLock(); } } } internal readonly DetourManager.NativeDetourState state; private NativeDetourCollection? lazyDetours; public nint Function => state.Function; public bool HasActiveCall => Volatile.Read(in state.detourList.SyncInfo.ActiveCalls) > 0; public NativeDetourCollection Detours => lazyDetours ?? (lazyDetours = new NativeDetourCollection(this)); public NativeDetourInfo? FirstDetour { get { if (!(state.detourList.Next is DetourManager.NativeDetourChainNode nativeDetourChainNode)) { return null; } return GetDetourInfo(nativeDetourChainNode.Detour); } } public bool IsDetoured => state.detourList.Next != null; public event Action? DetourApplied { add { state.NativeDetourApplied += value; } remove { state.NativeDetourApplied -= value; } } public event Action? DetourUndone { add { state.NativeDetourUndone += value; } remove { state.NativeDetourUndone -= value; } } internal FunctionDetourInfo(DetourManager.NativeDetourState state) { this.state = state; } internal NativeDetourInfo GetDetourInfo(DetourManager.SingleNativeDetourState node) { NativeDetourInfo detourInfo = node.DetourInfo; if (detourInfo == null || detourInfo.Function != this) { return node.DetourInfo = new NativeDetourInfo(this, node); } return detourInfo; } [EditorBrowsable(EditorBrowsableState.Advanced)] public void EnterLock(ref bool lockTaken) { state.detourLock.Enter(ref lockTaken); } [EditorBrowsable(EditorBrowsableState.Advanced)] public void ExitLock() { state.detourLock.Exit(useMemoryBarrier: true); } [EditorBrowsable(EditorBrowsableState.Advanced)] public Lock WithLock() { return new Lock(this); } } public sealed class ILHookCollection : IEnumerable, IEnumerable { public struct Enumerator : IEnumerator, IEnumerator, IDisposable { private readonly MethodDetourInfo mdi; private DetourManager.DepListNode? listEntry; private List.Enumerator listEnum; private int state; private int version; public ILHookInfo Current => state switch { 0 => throw new InvalidOperationException(), 1 => mdi.GetILHookInfo(listEntry.ChainNode.Hook), 2 => mdi.GetILHookInfo(listEnum.Current.Hook), _ => throw new InvalidOperationException(), }; object IEnumerator.Current => Current; internal Enumerator(MethodDetourInfo mdi) { this.mdi = mdi; version = mdi.state.ilhookVersion; listEntry = null; state = 0; listEnum = default(List.Enumerator); } public bool MoveNext() { if (version != mdi.state.ilhookVersion) { throw new InvalidOperationException("The detour chain was modified while enumerating"); } switch (state) { case 0: listEntry = mdi.state.ilhookGraph.ListHead; state = 1; goto IL_007a; case 1: listEntry = listEntry?.Next; goto IL_007a; case 2: return listEnum.MoveNext(); default: { throw new InvalidOperationException("Invalid state"); } IL_007a: if (listEntry != null) { return true; } listEnum = mdi.state.noConfigIlhooks.GetEnumerator(); state = 2; goto case 2; } } public void Reset() { version = mdi.state.ilhookVersion; listEntry = null; state = 0; listEnum = default(List.Enumerator); } public void Dispose() { listEnum.Dispose(); Reset(); } } private readonly MethodDetourInfo mdi; internal ILHookCollection(MethodDetourInfo mdi) { this.mdi = mdi; } public Enumerator GetEnumerator() { return new Enumerator(mdi); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } public sealed class ILHookInfo : DetourBase { private readonly DetourManager.SingleILHookState hook; public MethodInfo ManipulatorMethod => ((Delegate)(object)hook.Manip).Method; internal ILHookInfo(MethodDetourInfo method, DetourManager.SingleILHookState hook) : base(method) { this.hook = hook; } private protected override bool IsAppliedCore() { return hook.IsApplied; } private protected override DetourConfig? ConfigCore() { return hook.Config; } private protected override void ApplyCore() { if (hook.IsApplied) { throw new InvalidOperationException("ILHook is already applied"); } if (!hook.IsValid) { throw new InvalidOperationException("ILHook is no longer valid"); } base.Method.state.AddILHook(hook, takeLock: false); } private protected override void UndoCore() { if (!hook.IsApplied) { throw new InvalidOperationException("ILHook is not currently applied"); } base.Method.state.RemoveILHook(hook, takeLock: false); } } public sealed class MethodDetourInfo { [EditorBrowsable(EditorBrowsableState.Never)] public readonly ref struct Lock { private readonly MethodDetourInfo mdi; private readonly bool lockTaken; internal Lock(MethodDetourInfo mdi) { this.mdi = mdi; lockTaken = false; try { mdi.EnterLock(ref lockTaken); } catch { if (lockTaken) { mdi.ExitLock(); } throw; } } public void Dispose() { if (lockTaken) { mdi.ExitLock(); } } } internal readonly DetourManager.ManagedDetourState state; private DetourCollection? lazyDetours; private ILHookCollection? lazyILHooks; public MethodBase Method => state.Source; public bool HasActiveCall => Volatile.Read(in state.detourList.SyncInfo.ActiveCalls) > 0; public DetourCollection Detours => lazyDetours ?? (lazyDetours = new DetourCollection(this)); public ILHookCollection ILHooks => lazyILHooks ?? (lazyILHooks = new ILHookCollection(this)); public DetourInfo? FirstDetour { get { if (!(state.detourList.Next is DetourManager.ManagedDetourChainNode managedDetourChainNode)) { return null; } return GetDetourInfo(managedDetourChainNode.Detour); } } public bool IsDetoured { get { if (state.detourList.Next == null) { return state.detourList.HasILHook; } return true; } } public event Action? DetourApplied { add { state.DetourApplied += value; } remove { state.DetourApplied -= value; } } public event Action? DetourUndone { add { state.DetourUndone += value; } remove { state.DetourUndone -= value; } } public event Action? ILHookApplied { add { state.ILHookApplied += value; } remove { state.ILHookApplied -= value; } } public event Action? ILHookUndone { add { state.ILHookUndone += value; } remove { state.ILHookUndone -= value; } } internal MethodDetourInfo(DetourManager.ManagedDetourState state) { this.state = state; } internal DetourInfo GetDetourInfo(DetourManager.SingleManagedDetourState node) { DetourInfo detourInfo = node.DetourInfo; if (detourInfo == null || detourInfo.Method != this) { return node.DetourInfo = new DetourInfo(this, node); } return detourInfo; } internal ILHookInfo GetILHookInfo(DetourManager.SingleILHookState entry) { ILHookInfo hookInfo = entry.HookInfo; if (hookInfo == null || hookInfo.Method != this) { return entry.HookInfo = new ILHookInfo(this, entry); } return hookInfo; } [EditorBrowsable(EditorBrowsableState.Advanced)] public void EnterLock(ref bool lockTaken) { state.detourLock.Enter(ref lockTaken); } [EditorBrowsable(EditorBrowsableState.Advanced)] public void ExitLock() { state.detourLock.Exit(useMemoryBarrier: true); } [EditorBrowsable(EditorBrowsableState.Advanced)] public Lock WithLock() { return new Lock(this); } } public sealed class NativeDetourCollection : IEnumerable, IEnumerable { public struct Enumerator : IEnumerator, IEnumerator, IDisposable { private readonly FunctionDetourInfo mdi; private DetourManager.NativeChainNode? curNode; private int version; public NativeDetourInfo Current => mdi.GetDetourInfo(((DetourManager.NativeDetourChainNode)curNode).Detour); object IEnumerator.Current => Current; internal Enumerator(FunctionDetourInfo mdi) { this.mdi = mdi; version = mdi.state.detourChainVersion; curNode = mdi.state.detourList; } [MemberNotNullWhen(true, "curNode")] public bool MoveNext() { if (version != mdi.state.detourChainVersion) { throw new InvalidOperationException("The detour chain was modified while enumerating"); } curNode = curNode?.Next; return curNode != null; } public void Reset() { version = mdi.state.detourChainVersion; curNode = mdi.state.detourList; } public void Dispose() { curNode = null; } } private readonly FunctionDetourInfo mdi; internal NativeDetourCollection(FunctionDetourInfo mdi) { this.mdi = mdi; } public Enumerator GetEnumerator() { return new Enumerator(mdi); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } public sealed class NativeDetourInfo { private readonly DetourManager.SingleNativeDetourState detour; public FunctionDetourInfo Function { get; } public bool IsApplied => detour.IsApplied; public DetourConfig? Config => detour.Config; public MethodInfo Entry => detour.Invoker.Method; internal DetourManager.NativeDetourChainNode? ChainNode { get { object managerData = detour.ManagerData; if (!(managerData is DetourManager.NativeDetourChainNode result)) { if (managerData is DetourManager.DepGraphNode depGraphNode) { return (DetourManager.NativeDetourChainNode)depGraphNode.ListNode.ChainNode; } return null; } return result; } } public NativeDetourInfo? Next { get { if (!(ChainNode?.Next is DetourManager.NativeDetourChainNode nativeDetourChainNode)) { return null; } return Function.GetDetourInfo(nativeDetourChainNode.Detour); } } internal NativeDetourInfo(FunctionDetourInfo fdi, DetourManager.SingleNativeDetourState detour) { Function = fdi; this.detour = detour; } public void Apply() { ref SpinLock detourLock = ref Function.state.detourLock; bool lockTaken = detourLock.IsThreadOwnerTrackingEnabled && detourLock.IsHeldByCurrentThread; try { if (!lockTaken) { detourLock.Enter(ref lockTaken); } ApplyCore(); } finally { if (lockTaken) { detourLock.Exit(useMemoryBarrier: true); } } } public void Undo() { ref SpinLock detourLock = ref Function.state.detourLock; bool lockTaken = detourLock.IsThreadOwnerTrackingEnabled && detourLock.IsHeldByCurrentThread; try { if (!lockTaken) { detourLock.Enter(ref lockTaken); } UndoCore(); } finally { if (lockTaken) { detourLock.Exit(useMemoryBarrier: true); } } } private void ApplyCore() { if (detour.IsApplied) { throw new InvalidOperationException("NativeDetour is already applied"); } if (!detour.IsValid) { throw new InvalidOperationException("NativeDetour is no longer valid"); } Function.state.AddDetour(detour, takeLock: false); } private void UndoCore() { if (!detour.IsApplied) { throw new InvalidOperationException("NativeDetour is not currently applied"); } Function.state.RemoveDetour(detour, takeLock: false); } } public sealed class NativeHook : INativeDetour, IDetourBase, IDetour, IDisposable { private readonly IDetourFactory factory; private readonly Delegate hookDel; private readonly DetourManager.NativeDetourState state; private readonly DetourManager.SingleNativeDetourState detour; private readonly Type nativeDelType; private readonly bool hasOrigParam; private bool disposedValue; public static bool CanCallOriginal => DetourContext.GetDefaultFactory().SupportsNativeDetourOrigEntrypoint; IDetourFactory IDetourBase.Factory => factory; public DetourConfig? Config { get; } public nint Function { get; } Delegate INativeDetour.Invoker => hookDel; Type INativeDetour.NativeDelegateType => nativeDelType; bool INativeDetour.HasOrigParam => hasOrigParam; public bool IsValid => !disposedValue; public bool IsApplied => detour.IsApplied; public NativeDetourInfo DetourInfo => state.Info.GetDetourInfo(detour); public NativeHook(nint function, Delegate hook) : this(function, hook, applyByDefault: true) { } public NativeHook(nint function, Delegate hook, DetourConfig? config) : this(function, hook, config, applyByDefault: true) { } public NativeHook(nint function, Delegate hook, bool applyByDefault) : this(function, hook, DetourContext.GetDefaultConfig(), applyByDefault) { } public NativeHook(nint function, Delegate hook, DetourConfig? config, bool applyByDefault) : this(function, hook, DetourContext.GetDefaultFactory(), config, applyByDefault) { } public NativeHook(nint function, Delegate hook, IDetourFactory factory, DetourConfig? config, bool applyByDefault) { Helpers.ThrowIfArgumentNull(hook, "hook"); Helpers.ThrowIfArgumentNull(factory, "factory"); Function = function; hookDel = hook; this.factory = factory; Config = config; nativeDelType = GetNativeDelegateType(hook.GetType(), out hasOrigParam); state = DetourManager.GetNativeDetourState(function); detour = new DetourManager.SingleNativeDetourState(this); if (applyByDefault) { Apply(); } } private static Type GetNativeDelegateType(Type inDelType, out bool hasOrigParam) { MethodSignature val = MethodSignature.ForMethod((MethodBase)inDelType.GetMethod("Invoke"), true); Type firstParameter = val.FirstParameter; if ((object)firstParameter != null && typeof(Delegate).IsAssignableFrom(firstParameter)) { MethodSignature val2 = MethodSignature.ForMethod((MethodBase)firstParameter.GetMethod("Invoke"), true); if (val.Parameters.Skip(1).SequenceEqual(val2.Parameters)) { hasOrigParam = true; return firstParameter; } } hasOrigParam = false; return inDelType; } private void CheckDisposed() { if (disposedValue) { throw new ObjectDisposedException(ToString()); } } public void Apply() { CheckDisposed(); bool lockTaken = false; try { state.detourLock.Enter(ref lockTaken); if (!IsApplied) { bool isEnabled; MMDbgLog.DebugLogTraceStringHandler message = new MMDbgLog.DebugLogTraceStringHandler(25, 1, out isEnabled); if (isEnabled) { message.AppendLiteral("Applying NativeHook of 0x"); message.AppendFormatted(Function, "x16"); } MMDbgLog.Trace(ref message); state.AddDetour(detour, !lockTaken); } } finally { if (lockTaken) { state.detourLock.Exit(useMemoryBarrier: true); } } } public void Undo() { CheckDisposed(); bool lockTaken = false; try { state.detourLock.Enter(ref lockTaken); if (IsApplied) { bool isEnabled; MMDbgLog.DebugLogTraceStringHandler message = new MMDbgLog.DebugLogTraceStringHandler(26, 1, out isEnabled); if (isEnabled) { message.AppendLiteral("Undoing NativeHook from 0x"); message.AppendFormatted(Function, "x16"); } MMDbgLog.Trace(ref message); state.RemoveDetour(detour, !lockTaken); } } finally { if (lockTaken) { state.detourLock.Exit(useMemoryBarrier: true); } } } private void Dispose(bool disposing) { if (!disposedValue && detour != null) { detour.IsValid = false; if (!AppDomain.CurrentDomain.IsFinalizingForUnload() && !Environment.HasShutdownStarted) { Undo(); } disposedValue = true; } } ~NativeHook() { Dispose(disposing: false); } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } } internal static class TrampolinePool { private static readonly ConcurrentDictionary>> pool = new ConcurrentDictionary>>(); private static readonly ConstructorInfo Exception_ctor = typeof(Exception).GetConstructor(new Type[1] { typeof(string) }) ?? throw new InvalidOperationException(); private static ConcurrentBag> PoolForSig(MethodSignature sig) { return pool.GetOrAdd(sig, (MethodSignature _) => new ConcurrentBag>()); } public static MethodInfo Rent(MethodSignature sig) { ConcurrentBag> concurrentBag = PoolForSig(sig); WeakReference result; while (concurrentBag.TryTake(out result)) { if (result.TryGetTarget(out var target)) { return target; } } DynamicMethodDefinition val = sig.CreateDmd($"Trampoline<{sig}>"); try { return val.StubCriticalDetour().Generate(); } finally { ((IDisposable)val)?.Dispose(); } } public static void Return(MethodInfo trampoline) { PoolForSig(MethodSignature.ForMethod((MethodBase)trampoline)).Add(new WeakReference(trampoline)); } 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)Exception_ctor)); iLProcessor.Emit(OpCodes.Throw); return dm; } } internal static class AssemblyInfo { public const string AssemblyName = "MonoMod.RuntimeDetour"; public const string AssemblyVersion = "25.3.4"; } } namespace MonoMod.RuntimeDetour.HookGen { public static class HookEndpointManager { private const string ObsoleteMessage = "This member should never be used directly from user code. Use Hook or ILHook directly instead."; private const string HookAlreadyAppliedMsg = "Delegate has already been applied to this method as a hook!"; private static ConcurrentDictionary<(MethodBase, Delegate), Hook> Hooks = new ConcurrentDictionary<(MethodBase, Delegate), Hook>(); private static ConcurrentDictionary<(MethodBase, Delegate), ILHook> ILHooks = new ConcurrentDictionary<(MethodBase, Delegate), ILHook>(); [Obsolete("This member should never be used directly from user code. Use Hook or ILHook directly instead.", true)] [EditorBrowsable(EditorBrowsableState.Never)] public static void Add(MethodBase method, Delegate hookDelegate) where T : Delegate { Add(method, hookDelegate); } [Obsolete("This member should never be used directly from user code. Use Hook or ILHook directly instead.", true)] [EditorBrowsable(EditorBrowsableState.Never)] public static void Add(MethodBase method, Delegate hookDelegate) { if (!Hooks.TryAdd((method, hookDelegate), new Hook(method, hookDelegate))) { throw new ArgumentException("Delegate has already been applied to this method as a hook!"); } } [Obsolete("This member should never be used directly from user code. Use Hook or ILHook directly instead.", true)] [EditorBrowsable(EditorBrowsableState.Never)] public static void Remove(MethodBase method, Delegate hookDelegate) where T : Delegate { Remove(method, hookDelegate); } [Obsolete("This member should never be used directly from user code. Use Hook or ILHook directly instead.", true)] [EditorBrowsable(EditorBrowsableState.Never)] public static void Remove(MethodBase method, Delegate hookDelegate) { if (Hooks.TryRemove((method, hookDelegate), out Hook value)) { value.Dispose(); } } [Obsolete("This member should never be used directly from user code. Use Hook or ILHook directly instead.", true)] [EditorBrowsable(EditorBrowsableState.Never)] public static void Modify(MethodBase method, Delegate callback) where T : Delegate { Modify(method, callback); } [Obsolete("This member should never be used directly from user code. Use Hook or ILHook directly instead.", true)] [EditorBrowsable(EditorBrowsableState.Never)] public static void Modify(MethodBase method, Delegate callback) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown if (!ILHooks.TryAdd((method, callback), new ILHook(method, (Manipulator)callback))) { throw new ArgumentException("Delegate has already been applied to this method as a hook!"); } } [Obsolete("This member should never be used directly from user code. Use Hook or ILHook directly instead.", true)] [EditorBrowsable(EditorBrowsableState.Never)] public static void Unmodify(MethodBase method, Delegate callback) { Unmodify(method, callback); } [Obsolete("This member should never be used directly from user code. Use Hook or ILHook directly instead.", true)] [EditorBrowsable(EditorBrowsableState.Never)] public static void Unmodify(MethodBase method, Delegate callback) { if (ILHooks.TryRemove((method, callback), out ILHook value)) { value.Dispose(); } } public static void Clear() { foreach (Hook value in Hooks.Values) { value.Dispose(); } Hooks.Clear(); foreach (ILHook value2 in ILHooks.Values) { value2.Dispose(); } ILHooks.Clear(); } } } namespace System.Runtime.CompilerServices { [ExcludeFromCodeCoverage] [DebuggerNonUserCode] internal static class IsExternalInit { } }