public void TestDetoursExt() { lock (TestObject.Lock) { // The following use cases are not meant to be usage examples. // Please take a look at DetourTest and HookTest instead. using (NativeDetour d = new NativeDetour( // .GetNativeStart() to enforce a native detour. typeof(TestObject).GetMethod("TestStaticMethod").Pin().GetNativeStart(), typeof(DetourExtTest).GetMethod("TestStaticMethod_A") )) { int staticResult = d.GenerateTrampoline <Func <int, int, int> >()(2, 3); Console.WriteLine($"TestStaticMethod(2, 3): {staticResult}"); Assert.Equal(6, staticResult); staticResult = TestObject.TestStaticMethod(2, 3); Console.WriteLine($"TestStaticMethod(2, 3): {staticResult}"); Assert.Equal(12, staticResult); } // We can't create a backup for this. MethodBase dm; using (DynamicMethodDefinition dmd = new DynamicMethodDefinition(typeof(TestObject).GetMethod("TestStaticMethod"))) { dm = dmd.Generate(); } using (NativeDetour d = new NativeDetour( dm, typeof(DetourExtTest).GetMethod("TestStaticMethod_A") )) { int staticResult = d.GenerateTrampoline <Func <int, int, int> >()(2, 3); Console.WriteLine($"TestStaticMethod(2, 3): {staticResult}"); Assert.Equal(6, staticResult); // FIXME: dm.Invoke can fail with a release build in mono 5.X! // staticResult = (int) dm.Invoke(null, new object[] { 2, 3 }); staticResult = ((Func <int, int, int>)dm.CreateDelegate <Func <int, int, int> >())(2, 3); Console.WriteLine($"TestStaticMethod(2, 3): {staticResult}"); Assert.Equal(12, staticResult); } // This was provided by Chicken Bones (tModLoader). // GetEncoding behaves differently on .NET Core and even between .NET Framework versions, // which is why this test only applies to Mono, preferably on Linux to verify if flagging // regions of code as read-writable and then read-executable works for AOT'd code. #if false using (Hook h = new Hook( typeof(Encoding).GetMethod("GetEncoding", new Type[] { typeof(string) }), new Func <Func <string, Encoding>, string, Encoding>((orig, name) => { if (name == "IBM437") { return(null); } return(orig(name)); }) )) { Assert.Null(Encoding.GetEncoding("IBM437")); } #endif } }
/// <summary> /// Returns an instruction to call the specified delegate. /// </summary> /// <typeparam name="T">The delegate type to emit.</typeparam> /// <param name="action">The delegate to emit.</param> /// <returns>The instruction to </returns> public static CodeInstruction EmitDelegate <T>(T action) where T : Delegate { if (action.Method.IsStatic && action.Target == null) { return(new CodeInstruction(OpCodes.Call, action.Method)); } var paramTypes = action.Method.GetParameters().Select(x => x.ParameterType).ToArray(); var dynamicMethod = new DynamicMethodDefinition(action.Method.Name, action.Method.ReturnType, paramTypes); var il = dynamicMethod.GetILGenerator(); var targetType = action.Target.GetType(); var preserveContext = action.Target != null && targetType.GetFields().Any(x => !x.IsStatic); if (preserveContext) { var currentDelegateCounter = _delegateCounter++; _delegateCache[currentDelegateCounter] = action; var cacheField = AccessTools.Field(typeof(Transpilers), nameof(_delegateCache)); var getMethod = AccessTools.Method(typeof(Dictionary <int, Delegate>), "get_Item"); il.Emit(OpCodes.Ldsfld, cacheField); il.Emit(OpCodes.Ldc_I4, currentDelegateCounter); il.Emit(OpCodes.Callvirt, getMethod); } else { if (action.Target == null) { il.Emit(OpCodes.Ldnull); } else { il.Emit(OpCodes.Newobj, AccessTools.FirstConstructor(targetType, x => x.GetParameters().Length == 0 && !x.IsStatic)); } il.Emit(OpCodes.Ldftn, action.Method); il.Emit(OpCodes.Newobj, AccessTools.Constructor(typeof(T), new[] { typeof(object), typeof(IntPtr) })); } for (var i = 0; i < paramTypes.Length; i++) { il.Emit(OpCodes.Ldarg_S, (short)i); } il.Emit(OpCodes.Callvirt, typeof(T).GetMethod("Invoke")); il.Emit(OpCodes.Ret); return(new CodeInstruction(OpCodes.Call, dynamicMethod.Generate())); }
internal void DetourILDetourTarget(bool force = false) { ILProxyDetour?.Dispose(); ILProxyDetour = null; if (!force && ILList.Count == 0) { return; } try { ILProxyDetour = new NativeDetour(ILCopy, DMD.Generate()); } catch (Exception e) { StringBuilder builder = new StringBuilder(); if (DMD.Definition?.Body?.Instructions != null) { builder.AppendLine("IL hook failed for:"); foreach (Instruction i in DMD.Definition.Body.Instructions) { builder.AppendLine(i?.ToString() ?? "NULL!"); } } else { builder.AppendLine("IL hook failed, no instructions found"); } throw new InvalidProgramException(builder.ToString(), e); } }
/// <summary>Creates a field reference</summary> /// <typeparam name="T">The class the field is defined in or "object" if type cannot be accessed at compile time</typeparam> /// <typeparam name="U">The type of the field</typeparam> /// <param name="fieldInfo">FieldInfo for the field</param> /// <returns>A read and writable field reference</returns> /// public static FieldRef <T, U> FieldRefAccess <T, U>(FieldInfo fieldInfo) { if (fieldInfo == null) { throw new ArgumentNullException(nameof(fieldInfo)); } if (!typeof(U).IsAssignableFrom(fieldInfo.FieldType)) { throw new ArgumentException("FieldInfo type does not match FieldRefAccess return type."); } if (typeof(T) != typeof(object)) { if (fieldInfo.DeclaringType == null || !fieldInfo.DeclaringType.IsAssignableFrom(typeof(T))) { throw new MissingFieldException(typeof(T).Name, fieldInfo.Name); } } var dm = new DynamicMethodDefinition($"__refget_{typeof(T).Name}_fi_{fieldInfo.Name}", typeof(U).MakeByRefType(), new[] { typeof(T) }); var il = dm.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldflda, fieldInfo); il.Emit(OpCodes.Ret); return((FieldRef <T, U>)dm.Generate().CreateDelegate <FieldRef <T, U> >()); }
public override MethodBase DetourTo(MethodBase replacement) { DebugCheck(); DynamicMethodDefinition newreplacementdmd = CopyOriginal(); HarmonyManipulator.Manipulate(Original, Original.GetPatchInfo(), new ILContext(newreplacementdmd.Definition)); MethodInfo newreplacement = newreplacementdmd.Generate(); MethodInfo il2CppShim = CreateIl2CppShim(newreplacement).Generate(); Type il2CppShimDelegateType = DelegateTypeFactory.instance.CreateDelegateType(il2CppShim, CallingConvention.Cdecl); Delegate il2CppShimDelegate = il2CppShim.CreateDelegate(il2CppShimDelegateType); IntPtr il2CppShimDelegatePtr = il2CppShimDelegate.GetFunctionPointer(); if (methodDetourPointer != IntPtr.Zero) { MelonUtils.NativeHookDetach(copiedMethodInfoPointer, methodDetourPointer); } MelonUtils.NativeHookAttach(copiedMethodInfoPointer, il2CppShimDelegatePtr); methodDetourPointer = il2CppShimDelegatePtr; PatchTools_RememberObject(Original, new LemonTuple <MethodInfo, MethodInfo, Delegate> { Item1 = newreplacement, Item2 = il2CppShim, Item3 = il2CppShimDelegate }); return(newreplacement); }
public static MethodInfo Prefix(MethodBase method) { var dynamicMethod = new DynamicMethodDefinition(method.Name + "_Class11Patch_Prefix", typeof(bool), new[] { typeof(string).MakeByRefType(), typeof(int) }); dynamicMethod.Definition.Parameters[0].Name = "__result"; dynamicMethod.Definition.Parameters[1].Name = "dummy"; var il = dynamicMethod.GetILGenerator(); //load "patched" into __result il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldstr, "patched"); il.Emit(OpCodes.Stind_Ref); //set prefixed to true il.Emit(OpCodes.Ldnull); il.Emit(OpCodes.Ldc_I4_1); il.Emit(OpCodes.Stfld, typeof(Class11Patch).GetField(nameof(prefixed))); //return false il.Emit(OpCodes.Ldc_I4_0); il.Emit(OpCodes.Ret); return(dynamicMethod.Generate()); }
public static Func<T, F> CreateFastFieldGetter<T, F>( FieldInfo fieldInfo ) { if( fieldInfo == null ) throw new ArgumentNullException( nameof( fieldInfo ) ); if( !typeof( F ).IsAssignableFrom( fieldInfo.FieldType ) ) throw new ArgumentException( "FieldInfo type does not match return type." ); if( typeof( T ) != typeof( object ) ) if( fieldInfo.DeclaringType == null || !fieldInfo.DeclaringType.IsAssignableFrom( typeof( T ) ) ) throw new MissingFieldException( typeof( T ).Name, fieldInfo.Name ); var name = $"FastReflection<{typeof( T ).FullName}.Get_{fieldInfo.Name}>"; var dm = new DynamicMethodDefinition( name, typeof( F ), new[] { typeof( T ) } ); var il = dm.GetILProcessor(); if( !fieldInfo.IsStatic ) { il.Emit( OpCodes.Ldarg_0 ); il.Emit( OpCodes.Castclass, fieldInfo.DeclaringType ); } il.Emit( fieldInfo.IsStatic ? OpCodes.Ldsfld : OpCodes.Ldfld, fieldInfo ); if( fieldInfo.FieldType.IsValueType != typeof( F ).IsValueType ) { il.Emit( OpCodes.Box, fieldInfo.FieldType ); } il.Emit( OpCodes.Ret ); return (Func<T, F>)dm.Generate().CreateDelegate( typeof( Func<T, F> ) ); }
/* * Fix for detour jump overriding the method that's right next in memory * See https://github.com/MonoMod/MonoMod.Common/blob/58702d64645aba613ad16275c0b78278ff0d2055/RuntimeDetour/Platforms/Native/DetourNativeX86Platform.cs#L77 * * It happens for any very small method, not just virtual, but it just so happens that * virtual methods are usually the only empty ones. The problem is with the detour code * overriding the method that's right next in memory. * * The 64bit absolute jump detour requires 14 bytes but on Linux an empty method is just * 10 bytes. On Windows, due to prologue differences, an empty method is exactly 14 bytes * as required. * * Now, the small code size wouldn't be a problem if it wasn't for the way Mono compiles * trampolines. Usually methods on x64 are 16 bytes aligned, so no actual code could get * overriden by the detour, but trampolines don't follow this rule and a trampoline generated * for the dynamic method from the patch is placed right after the method being detoured. * * The detour code then overrides the beggining of the trampoline and that leads to a * segfault on execution. * * There's also the fact that Linux seems to allocate the detour code far away in memory * so it uses the 64 bit jump in the first place. On Windows with Mono usually the 32 bit * smaller jumps suffice. * * The fix changes the order in which methods are JITted so that no trampoline is placed * after the detoured method (or at least the trampoline that causes this specific crash) */ internal static void PadShortMethods(MethodBase method) { if (isWindows) { return; } var count = method.GetMethodBody()?.GetILAsByteArray()?.Length ?? 0; if (count == 0) { return; } // the 16 here is arbitrary but high enough to prevent the jitted code from getting under 14 bytes // and high enough to not generate too many fix methods if (count >= 16) { return; } var methodDef = new DynamicMethodDefinition($"PadMethod-{Guid.NewGuid()}", typeof(void), new Type[0]); methodDef.GetILGenerator().Emit(OpCodes.Ret); // invoke the method so that it generates a trampoline that will later get overridden by the detour code _ = methodDef.Generate().Invoke(null, null); }
internal static FieldRef <T, F> FieldRefAccess <T, F>(FieldInfo fieldInfo, bool needCastclass) { ValidateFieldType <F>(fieldInfo); var delegateInstanceType = typeof(T); var declaringType = fieldInfo.DeclaringType; var dm = new DynamicMethodDefinition($"__refget_{delegateInstanceType.Name}_fi_{fieldInfo.Name}", typeof(F).MakeByRefType(), new[] { delegateInstanceType }); var il = dm.GetILGenerator(); // Backwards compatibility: This supports static fields, even those defined in structs. if (fieldInfo.IsStatic) { // ldarg.0 + ldflda actually works for static fields, but the potential castclass (and InvalidCastException) below must be avoided // so might as well use the singular ldsflda for static fields. il.Emit(OpCodes.Ldsflda, fieldInfo); } else { il.Emit(OpCodes.Ldarg_0); // The castclass is needed when T is a parent class or interface of declaring type (e.g. if T is object), // since there's no guarantee the instance passed to the delegate is actually of the declaring type. // In such a situation, the castclass will throw an InvalidCastException and thus prevent undefined behavior. if (needCastclass) { il.Emit(OpCodes.Castclass, declaringType); } il.Emit(OpCodes.Ldflda, fieldInfo); } il.Emit(OpCodes.Ret); return((FieldRef <T, F>)dm.Generate().CreateDelegate(typeof(FieldRef <T, F>))); }
/* * Fix for detour jump overriding the method that's right next in memory * See https://github.com/MonoMod/MonoMod.Common/blob/58702d64645aba613ad16275c0b78278ff0d2055/RuntimeDetour/Platforms/Native/DetourNativeX86Platform.cs#L77 * * It happens for any very small method, not just virtual, but it just so happens that * virtual methods are usually the only empty ones. The problem is with the detour code * overriding the method that's right next in memory. * * The 64bit absolute jump detour requires 14 bytes but on Linux an empty method is just * 10 bytes. On Windows, due to prologue differences, an empty method is exactly 14 bytes * as required. * * Now, the small code size wouldn't be a problem if it wasn't for the way Mono compiles * trampolines. Usually methods on x64 are 16 bytes aligned, so no actual code could get * overriden by the detour, but trampolines don't follow this rule and a trampoline generated * for the dynamic method from the patch is placed right after the method being detoured. * * The detour code then overrides the beggining of the trampoline and that leads to a * segfault on execution. * * There's also the fact that Linux seems to allocate the detour code far away in memory * so it uses the 64 bit jump in the first place. On Windows with Mono usually the 32 bit * smaller jumps suffice. * * The fix changes the order in which methods are JITted so that no trampoline is placed * after the detoured method (or at least the trampoline that causes this specific crash) */ internal static void PadShortMethods(MethodBase method) { if (isWindows) { return; } var count = method.GetMethodBody()?.GetILAsByteArray()?.Length ?? 0; if (count == 0) { return; } // the 16 here is arbitrary but high enough to prevent the jitted code from getting under 14 bytes // and high enough to not generate too many fix methods if (count >= 16) { return; } var methodDef = new DynamicMethodDefinition($"PadMethod-{Guid.NewGuid()}", typeof(void), new Type[0]); methodDef.GetILGenerator().Emit(OpCodes.Ret); _ = GetMethodStart(methodDef.Generate(), out var _); // trigger allocation/generation of jitted assembler }
public MethodBase GetDetourTarget(MethodBase from, MethodBase to) { Type context = to.DeclaringType; MethodInfo dm = null; if (GlueThiscallStructRetPtr != GlueThiscallStructRetPtrOrder.Original && from is MethodInfo fromInfo && !from.IsStatic && to is MethodInfo toInfo && to.IsStatic && fromInfo.ReturnType == toInfo.ReturnType && fromInfo.ReturnType.IsValueType) { int size = fromInfo.ReturnType.GetManagedSize(); if (size == 3 || size == 5 || size == 6 || size == 7 || size >= 9) { Type thisType = from.GetThisParamType(); Type retType = fromInfo.ReturnType.MakeByRefType(); // Refs are shiny pointers. int thisPos = 0; int retPos = 1; if (GlueThiscallStructRetPtr == GlueThiscallStructRetPtrOrder.RetThisArgs) { thisPos = 1; retPos = 0; } List<Type> argTypes = new List<Type> { thisType }; argTypes.Insert(retPos, retType); argTypes.AddRange(from.GetParameters().Select(p => p.ParameterType)); using (DynamicMethodDefinition dmd = new DynamicMethodDefinition( $"Glue:ThiscallStructRetPtr<{from.GetID(simple: true)},{to.GetID(simple: true)}>", typeof(void), argTypes.ToArray() )) { ILProcessor il = dmd.GetILProcessor(); // Load the return buffer address. il.Emit(OpCodes.Ldarg, retPos); // Invoke the target method with all remaining arguments. { il.Emit(OpCodes.Ldarg, thisPos); for (int i = 2; i < argTypes.Count; i++) il.Emit(OpCodes.Ldarg, i); il.Emit(OpCodes.Call, il.Body.Method.Module.ImportReference(to)); } // Store the returned object to the return buffer. il.Emit(OpCodes.Stobj, il.Body.Method.Module.ImportReference(fromInfo.ReturnType)); il.Emit(OpCodes.Ret); dm = dmd.Generate(); } } } return dm ?? to; }
public MethodInfo CreateCopy(MethodBase method) { if (method == null || (method.GetMethodImplementationFlags() & (MethodImplAttributes.OPTIL | MethodImplAttributes.Native | MethodImplAttributes.Runtime)) != 0) { throw new InvalidOperationException($"Uncopyable method: {method?.ToString() ?? "NULL"}"); } using (DynamicMethodDefinition dmd = new DynamicMethodDefinition(method)) return dmd.Generate(); }
private static string DetourMethod(MethodBase original, MethodBase replacement) { if (replacement == null) { replacement = _LastWrapperDMD.Generate(); _LastWrapperDMD.Dispose(); _LastWrapperDMD = null; } _Detours.Add(new Detour(original, replacement, ref _DetourConfig)); return null; }
public static void RegisterDelegate(DynamicMethodDefinition dynamicMethod, string delegateFullName) { MethodBase method = dynamicMethod.Generate(); switch (method.Name) { case "UseItemEffect": UseItemEffect.TryAdd(delegateFullName, (Action <Player, Rectangle>)method.CreateDelegate <Action <Player, Rectangle> >()); break; } }
static ILHookExtensions() { var detourGetter = new DynamicMethodDefinition("GetDetour", typeof(Detour), new[] { typeof(ILHook) }); var il = detourGetter.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Call, AccessTools.PropertyGetter(typeof(ILHook), "_Ctx")); il.Emit(OpCodes.Ldfld, AccessTools.Field(AccessTools.Inner(typeof(ILHook), "Context"), "Detour")); il.Emit(OpCodes.Ret); GetAppliedDetour = detourGetter.Generate().CreateDelegate <Func <ILHook, Detour> >() as Func <ILHook, Detour>; }
internal static void Initialize() { foreach (var field in AccessTools.GetDeclaredFields(typeof(OS)).Where(x => x.FieldType == typeof(Color))) { var dynMethod = new DynamicMethodDefinition(field.Name, typeof(Color).MakeByRefType(), new Type[] { typeof(OS) }); var p = dynMethod.GetILProcessor(); p.Emit(OpCodes.Ldarg_0); p.Emit(OpCodes.Ldflda, field); p.Emit(OpCodes.Ret); OSColorFieldsFast.Add(field.Name, dynMethod.Generate().CreateDelegate <RefColorFieldDelegate>()); } }
public void TestDetours() { // The following use cases are not meant to be usage examples. // Please take a look at DetourTest and HookTest instead. using (NativeDetour d = new NativeDetour( // .GetNativeStart() to enforce a native detour. typeof(TestObject).GetMethod("TestStaticMethod").GetNativeStart(), typeof(DetourExtTest).GetMethod("TestStaticMethod_A") )) { int staticResult = d.GenerateTrampoline <Func <int, int, int> >()(2, 3); Console.WriteLine($"TestStaticMethod(2, 3): {staticResult}"); Assert.Equal(6, staticResult); staticResult = TestObject.TestStaticMethod(2, 3); Console.WriteLine($"TestStaticMethod(2, 3): {staticResult}"); Assert.Equal(12, staticResult); } // We can't create a backup for this. MethodBase dm; using (DynamicMethodDefinition dmd = new DynamicMethodDefinition(typeof(TestObject).GetMethod("TestStaticMethod"))) { dm = dmd.Generate(); } using (NativeDetour d = new NativeDetour( dm, typeof(DetourExtTest).GetMethod("TestStaticMethod_A") )) { int staticResult = d.GenerateTrampoline <Func <int, int, int> >()(2, 3); Console.WriteLine($"TestStaticMethod(2, 3): {staticResult}"); Assert.Equal(6, staticResult); staticResult = (int)dm.Invoke(null, new object[] { 2, 3 }); Console.WriteLine($"TestStaticMethod(2, 3): {staticResult}"); Assert.Equal(12, staticResult); } // This was provided by Chicken Bones (tModLoader). using (Hook h = new Hook( typeof(Encoding).GetMethod("GetEncoding", new Type[] { typeof(string) }), new Func <Func <string, Encoding>, string, Encoding>((orig, name) => { if (name == "IBM437") { return(null); } return(orig(name)); }) )) { Assert.Null(Encoding.GetEncoding("IBM437")); } }
public static InstantiationHandler CreateInstantiationHandler(Type type) { var constructorInfo = type.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[0], null); if (constructorInfo is null) { throw new ApplicationException(string.Format("The type {0} must declare an empty constructor (the constructor may be private, internal, protected, protected internal, or public).", type)); } var dynamicMethod = new DynamicMethodDefinition($"InstantiateObject_{type.Name}", type, null); var generator = dynamicMethod.GetILGenerator(); generator.Emit(OpCodes.Newobj, constructorInfo); generator.Emit(OpCodes.Ret); return((InstantiationHandler)dynamicMethod.Generate().CreateDelegate(typeof(InstantiationHandler))); }
internal static StructFieldRef <T, F> StructFieldRefAccess <T, F>(FieldInfo fieldInfo) where T : struct { ValidateFieldType <F>(fieldInfo); var dm = new DynamicMethodDefinition($"__refget_{typeof(T).Name}_struct_fi_{fieldInfo.Name}", typeof(F).MakeByRefType(), new[] { typeof(T).MakeByRefType() }); var il = dm.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldflda, fieldInfo); il.Emit(OpCodes.Ret); return((StructFieldRef <T, F>)dm.Generate().CreateDelegate(typeof(StructFieldRef <T, F>))); }
/// <summary>Returns an instruction to call the specified closure</summary> /// <typeparam name="T">The delegate type to emit</typeparam> /// <param name="closure">The closure that defines the method to call</param> /// <returns>A <see cref="CodeInstruction"/> that calls the closure as a method</returns> /// public static CodeInstruction CallClosure <T>(T closure) where T : Delegate { if (closure.Method.IsStatic && closure.Target == null) { return(new CodeInstruction(OpCodes.Call, closure.Method)); } var parameters = closure.Method.GetParameters().Select(x => x.ParameterType).ToArray(); var closureMethod = new DynamicMethodDefinition(closure.Method.Name, closure.Method.ReturnType, parameters); var il = closureMethod.GetILGenerator(); var targetType = closure.Target.GetType(); var preserveContext = closure.Target != null && targetType.GetFields().Any(x => !x.IsStatic); if (preserveContext) { var n = State.closureCache.Count; State.closureCache[n] = closure; il.Emit(OpCodes.Ldsfld, AccessTools.Field(typeof(Transpilers), nameof(State.closureCache))); il.Emit(OpCodes.Ldc_I4, n); il.Emit(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(Dictionary <int, Delegate>), "Item")); } else { if (closure.Target == null) { il.Emit(OpCodes.Ldnull); } else { il.Emit(OpCodes.Newobj, AccessTools.FirstConstructor(targetType, x => x.IsStatic == false && x.GetParameters().Length == 0)); } il.Emit(OpCodes.Ldftn, closure.Method); il.Emit(OpCodes.Newobj, AccessTools.Constructor(typeof(T), new[] { typeof(object), typeof(IntPtr) })); } for (var i = 0; i < parameters.Length; i++) { il.Emit(OpCodes.Ldarg, i); } il.Emit(OpCodes.Callvirt, AccessTools.Method(typeof(T), nameof(Action.Invoke))); il.Emit(OpCodes.Ret); return(new CodeInstruction(OpCodes.Call, closureMethod.Generate())); }
private static Delegate CreateGetStaticFieldRefDelegate <TField>(FieldInfo fi) { var dm = new DynamicMethodDefinition ( "__ReturnByRef" + fi.DeclaringType?.Name + fi.Name, fi.FieldType.MakeByRefType(), new Type[0] ); ILGenerator gen = dm.GetILGenerator(); gen.Emit(OpCodes.Ldsflda, fi); gen.Emit(OpCodes.Ret); return(dm.Generate().CreateDelegate(typeof(FuncByRef <TField>))); }
private static Delegate CreateGetStaticFieldDelegate <TField>(FieldInfo fi) { var dm = new DynamicMethodDefinition ( "__FieldAccess" + fi.DeclaringType?.Name + fi.Name, typeof(TField), new Type[0] ); ILGenerator gen = dm.GetILGenerator(); gen.Emit(OpCodes.Ldsfld, fi); gen.Emit(OpCodes.Ret); return(dm.Generate().CreateDelegate(typeof(Func <TField>))); }
private static Delegate CreateGetFieldRefDelegate <TClass, TField>(FieldInfo info) { var dm = new DynamicMethodDefinition ( "__ReturnByRef" + info.DeclaringType?.Name + info.Name, typeof(TField).MakeByRefType(), new[] { typeof(TClass) } ); ILGenerator gen = dm.GetILGenerator(); gen.Emit(OpCodes.Ldarg_0); gen.Emit(OpCodes.Ldflda, info); gen.Emit(OpCodes.Ret); return(dm.Generate().CreateDelegate(typeof(FuncByRef <TClass, TField>))); }
static int SizeOf(Type type) { if (sizes.TryGetValue(type, out var size)) { return(size); } var dm = new DynamicMethodDefinition("SizeOfType", typeof(int), new Type[0]); var il = dm.GetILGenerator(); il.Emit(OpCodes.Sizeof, type); il.Emit(OpCodes.Ret); size = (int)dm.Generate().Invoke(null, null); sizes.Add(type, size); return(size); }
private static Delegate CreateSetStaticFieldDelegate <TField>(FieldInfo fi) { var dm = new DynamicMethodDefinition ( "__FieldSet" + fi.DeclaringType?.Name + fi.Name, typeof(void), new[] { typeof(TField) } ); ILGenerator gen = dm.GetILGenerator(); gen.Emit(OpCodes.Ldarg_0); gen.Emit(OpCodes.Stsfld, fi); gen.Emit(OpCodes.Ret); return(dm.Generate().CreateDelegate(typeof(Action <TField>))); }
public override void Apply() { // The process to patch native methods is as follows: // 1. Create a managed proxy method that calls NativeDetour's trampoline (we need to cache it // because we don't know the trampoline method when generating the DMD). // 2. Pass the proxy to the normal Harmony manipulator to apply prefixes, postfixes, transpilers, etc. // 3. NativeDetour the method to the managed proxy // 4. Cache the NativeDetour's trampoline (technically we wouldn't need to, this is just a workaround // for MonoMod's API. if (IsRunningOnDotNetCore) { Logger.Log(Logger.LogChannel.Warn, () => $"Patch target {Original.GetID()} is marked as extern. " + "Extern methods may not be patched because of inlining behaviour of coreclr (refer to https://github.com/dotnet/coreclr/pull/8263)." + "If you need to patch externs, consider using pure NativeDetour instead."); } var prevDmd = _dmd; _nativeDetour?.Dispose(); _dmd = GenerateManagedOriginal(); var ctx = new ILContext(_dmd.Definition); HarmonyManipulator.Manipulate(Original, Original.GetPatchInfo(), ctx); var target = _dmd.Generate(); _nativeDetour = new NativeDetour(Original, target, new NativeDetourConfig { ManualApply = true }); lock (TrampolineCache) { if (prevDmd != null) { TrampolineCache.Remove(prevDmd.GetHashCode()); } TrampolineCache[_dmd.GetHashCode()] = CreateDelegate(_trampolineDelegateType, _nativeDetour.GenerateTrampoline(_invokeTrampolineMethod)); } _nativeDetour.Apply(); }
/// <summary>Creates a static field reference delegate</summary> /// <typeparam name="F">The type of the field</typeparam> /// <param name="fieldInfo">FieldInfo for the field</param> /// <returns>A read and writable field reference delegate</returns> /// public static FieldRef <F> StaticFieldRefAccess <F>(FieldInfo fieldInfo) { if (fieldInfo == null) { throw new ArgumentNullException(nameof(fieldInfo)); } var t = fieldInfo.DeclaringType; var dm = new DynamicMethodDefinition($"__refget_{t.Name}_static_fi_{fieldInfo.Name}", typeof(F).MakeByRefType(), new Type[0]); var il = dm.GetILGenerator(); il.Emit(OpCodes.Ldsflda, fieldInfo); il.Emit(OpCodes.Ret); return((FieldRef <F>)dm.Generate().CreateDelegate <FieldRef <F> >()); }
internal static FieldRef <F> StaticFieldRefAccess <F>(FieldInfo fieldInfo) { if (fieldInfo.IsStatic is false) { throw new ArgumentException("Field must be static"); } Tools.ValidateFieldType <F>(fieldInfo); var dm = new DynamicMethodDefinition($"__refget_{fieldInfo.DeclaringType?.Name ?? "null"}_static_fi_{fieldInfo.Name}", typeof(F).MakeByRefType(), new Type[0]); var il = dm.GetILGenerator(); il.Emit(OpCodes.Ldsflda, fieldInfo); il.Emit(OpCodes.Ret); return((FieldRef <F>)dm.Generate().CreateDelegate(typeof(FieldRef <F>))); }
private static void HookSystemRuntimeTypeGetMethodBase() { var systemRuntimeTypeType = typeof(Type).Assembly.GetType("System.RuntimeType"); var getMethodBase1 = systemRuntimeTypeType.GetMethods(BindingFlags.Static | BindingFlags.NonPublic) .Where(m => m.Name == "GetMethodBase") .Where(m => m.GetParameters().Length == 2) .FirstOrDefault(m => m.GetParameters().First().ParameterType == systemRuntimeTypeType && m.GetParameters().Last().ParameterType.Name == "IRuntimeMethodInfo"); var getMethodBase2 = systemRuntimeTypeType.GetMethods(BindingFlags.Static | BindingFlags.NonPublic) .FirstOrDefault(m => m.Name == "GetMethodBase" && m.GetParameters().Length == 1); var myMethod = typeof(Program).GetMethod(nameof(Hook5), BindingFlags.Static | BindingFlags.Public); var replacementMethod = new DynamicMethodDefinition( getMethodBase2.Name, getMethodBase2.ReturnType, getMethodBase2.GetParameters().Select(x => x.ParameterType).ToArray() ) { OwnerType = getMethodBase1.DeclaringType }; var iLGenerator = replacementMethod.GetILGenerator(); iLGenerator.DeclareLocal(typeof(MethodBase), false); iLGenerator.Emit(System.Reflection.Emit.OpCodes.Ldnull); iLGenerator.Emit(System.Reflection.Emit.OpCodes.Stloc_0); iLGenerator.Emit(System.Reflection.Emit.OpCodes.Ldnull); iLGenerator.Emit(System.Reflection.Emit.OpCodes.Ldarg_0); iLGenerator.Emit(System.Reflection.Emit.OpCodes.Call, getMethodBase1); iLGenerator.Emit(System.Reflection.Emit.OpCodes.Stloc_0); iLGenerator.Emit(System.Reflection.Emit.OpCodes.Ldloca, 0); iLGenerator.Emit(System.Reflection.Emit.OpCodes.Call, myMethod); iLGenerator.Emit(System.Reflection.Emit.OpCodes.Ldloc_0); iLGenerator.Emit(System.Reflection.Emit.OpCodes.Ret); var replacementMethodInfo = replacementMethod.Generate(); Memory.SimpleHook(getMethodBase2, replacementMethodInfo); }
/// <summary>Creates an instantiation delegate</summary> /// <typeparam name="T">Type that constructor creates</typeparam> /// <returns>The new instantiation delegate</returns> /// public static InstantiationHandler <T> CreateInstantiationHandler <T>() { var constructorInfo = typeof(T).GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[0], null); if (constructorInfo == null) { throw new ApplicationException( $"The type {typeof(T)} must declare an empty constructor (the constructor may be private, internal, protected, protected internal, or public)."); } var dmd = new DynamicMethodDefinition($"InstantiateObject_{typeof(T).Name}", typeof(T), null); var il = dmd.GetILGenerator(); il.Emit(OpCodes.Newobj, constructorInfo); il.Emit(OpCodes.Ret); return((InstantiationHandler <T>)dmd.Generate().CreateDelegate <InstantiationHandler <T> >()); }