/// <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())); }
/// <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 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 } }
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>))); }
private DynamicMethodDefinition GenerateManagedOriginal() { // Here we generate the "managed" version of the native method // It simply calls the trampoline generated by MonoMod // As a result, we can pass the managed original to HarmonyManipulator like a normal method var orig = Original; var dmd = new DynamicMethodDefinition($"NativeDetour<{orig.GetID(simple: true)}>", _returnType, _argTypes); dmd.Definition.Name += $"?{dmd.GetHashCode()}"; var def = dmd.Definition; for (var i = 0; i < _argTypeNames.Length; i++) { def.Parameters[i].Name = _argTypeNames[i]; } var il = dmd.GetILGenerator(); il.Emit(OpCodes.Ldc_I4, dmd.GetHashCode()); il.Emit(OpCodes.Call, GetTrampolineMethod); for (var i = 0; i < _argTypes.Length; i++) { il.Emit(OpCodes.Ldarg, i); } il.Emit(OpCodes.Call, _invokeTrampolineMethod); il.Emit(OpCodes.Ret); return(dmd); }
public void RtDynamicMethod() { var module = ModuleDefinition.FromFile(typeof(TDynamicMethod).Assembly.Location); DynamicMethod generateDynamicMethod = TDynamicMethod.GenerateDynamicMethod(); var rtDynamicMethod = generateDynamicMethod.GetType().GetField("m_dynMethod", (BindingFlags)(-1))?.GetValue(generateDynamicMethod); var dynamicMethodDefinition = new DynamicMethodDefinition(module, rtDynamicMethod); Assert.NotNull(dynamicMethodDefinition); Assert.NotEmpty(dynamicMethodDefinition.CilMethodBody.Instructions); Assert.Equal(dynamicMethodDefinition.CilMethodBody.Instructions.Select(q => q.OpCode), new [] { CilOpCodes.Ldarg_0, CilOpCodes.Call, CilOpCodes.Ldarg_1, CilOpCodes.Ret }); Assert.Equal(dynamicMethodDefinition.Parameters.Select(q => q.ParameterType), new TypeSignature[] { module.CorLibTypeFactory.String, module.CorLibTypeFactory.Int32 }); }
/// <inheritdoc /> public override DynamicMethodDefinition CopyOriginal() { var dmd = new DynamicMethodDefinition(Original); dmd.Definition.Name = "UnhollowedWrapper_" + dmd.Definition.Name; var cursor = new ILCursor(new ILContext(dmd.Definition)); // Remove il2cpp_object_get_virtual_method if (cursor.TryGotoNext(x => x.MatchLdarg(0), x => x.MatchCall(typeof(UnhollowerBaseLib.IL2CPP), nameof(UnhollowerBaseLib.IL2CPP.Il2CppObjectBaseToPtr)), x => x.MatchLdsfld(out _), x => x.MatchCall(typeof(UnhollowerBaseLib.IL2CPP), nameof(UnhollowerBaseLib.IL2CPP.il2cpp_object_get_virtual_method)))) { cursor.RemoveRange(4); } else { cursor.Goto(0) .GotoNext(x => x.MatchLdsfld(UnhollowerUtils.GetIl2CppMethodInfoPointerFieldForGeneratedMethod(Original))) .Remove(); } // Replace original IL2CPPMethodInfo pointer with a modified one that points to the trampoline cursor .Emit(Mono.Cecil.Cil.OpCodes.Ldc_I8, ((IntPtr)modifiedNativeMethodInfo).ToInt64()) .Emit(Mono.Cecil.Cil.OpCodes.Conv_I); return(dmd); }
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> ) ); }
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()); }
/// <summary>A low level way to read the body of a method. Used for quick searching in methods</summary> /// <param name="method">The original method</param> /// <returns>All instructions as opcode/operand pairs</returns> /// public static IEnumerable <KeyValuePair <OpCode, object> > ReadMethodBody(MethodBase method) { var dummyMethod = new DynamicMethodDefinition($"{method.Name}_Dummy{Guid.NewGuid()}", typeof(void), new Type[0]); return(MethodBodyReader.GetInstructions(dummyMethod.GetILGenerator(), method) .Select(instr => new KeyValuePair <OpCode, object>(instr.opcode, instr.operand))); }
private void ApplyReversePatch(ILContext ctx) { // Make a cecil copy of the original method for convenience sake var dmd = new DynamicMethodDefinition(original); var manipulator = new ILManipulator(dmd.Definition.Body); // Copy over variables from the original code ctx.Body.Variables.Clear(); foreach (var variableDefinition in dmd.Definition.Body.Variables) { ctx.Body.Variables.Add(new VariableDefinition(ctx.Module.ImportReference(variableDefinition.VariableType))); } var transpiler = GetTranspiler(standin); if (transpiler != null) { manipulator.AddTranspiler(transpiler); } manipulator.WriteTo(ctx.Body, standin); // Write a ret in case it got removed (wrt. HarmonyManipulator) ctx.IL.Emit(OpCodes.Ret); }
/* * 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); }
/* * 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 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 override DynamicMethodDefinition CopyOriginal() { DynamicMethodDefinition method = Original.ToNewDynamicMethodDefinition(); method.Definition.Name += "_wrapper"; ILContext ilcontext = new ILContext(method.Definition); ILCursor ilcursor = new ILCursor(ilcontext); FieldReference tempfieldreference = null; if (ilcursor.TryGotoNext(x => x.MatchLdsfld(out tempfieldreference), x => x.MatchCall(UnhollowerSupport.IL2CPPType, "il2cpp_object_get_virtual_method"))) { // Virtual method: Replace the sequence // - ldarg.0 // - call native int[UnhollowerBaseLib] UnhollowerBaseLib.IL2CPP::Il2CppObjectBaseToPtr(class [UnhollowerBaseLib] UnhollowerBaseLib.Il2CppObjectBase) // - ldsfld native int SomeClass::NativeMethodInfoPtr_Etc // - call native int[UnhollowerBaseLib] UnhollowerBaseLib.IL2CPP::il2cpp_object_get_virtual_method(native int, native int) ilcursor.Index -= 2; ilcursor.RemoveRange(4); } else if (ilcursor.TryGotoNext(x => x.MatchLdsfld(UnhollowerSupport.MethodBaseToIl2CppFieldInfo(Original)))) { ilcursor.Remove(); } else { MelonLogger.Error("Harmony Patcher could not rewrite Il2Cpp Unhollowed Method. Expect a stack overflow."); return(method); } ilcursor.Emit(Mono.Cecil.Cil.OpCodes.Ldc_I8, copiedMethodInfoPointer.ToInt64()); ilcursor.Emit(Mono.Cecil.Cil.OpCodes.Conv_I); return(method); }
private static void DecryptMethods(MethodDefinition methodDefinition, MethodBase invokeMethod, object fieldInstance) { if (methodDefinition.CilMethodBody == null) { return; } var instructions = methodDefinition.CilMethodBody.Instructions; if (instructions.Count < 9) { return; } if (instructions[0].OpCode.Code != CilCode.Ldsfld) { return; } if (((FieldDefinition)instructions[0].Operand).FullName != "i <Module>::Invoke") { return; } ToRemove.Add(instructions[3].Operand); Hooks.MethodBase = _assembly.ManifestModule.ResolveMethod(methodDefinition.MetadataToken.ToInt32()); var index = instructions[1].GetLdcI4Constant(); var dynamicMethodDef = new DynamicMethodDefinition(_module, invokeMethod.Invoke(fieldInstance, new object[] { index })); methodDefinition.CilMethodBody = dynamicMethodDef.CilMethodBody; }
public void Load() { // Optional: Disable achievements, stats and terminal. // Before hooking Stats.Increment, check if the method is empty. // Hooking empty methods causes issues on Linux versions notably, and Stats.Increment is empty in non-Steam versions of the game. using (DynamicMethodDefinition statsDmd = new DynamicMethodDefinition(typeof(Stats).GetMethod("Increment"))) { int instructionCount = statsDmd.Definition.Body.Instructions.Count; if (instructionCount > 1) { // the method has more than a lonely "ret", so hook it. On.Celeste.Stats.Increment += Stats_Increment; } } // Before hooking Achievements.Register, check the size of the method. // If it is 4 instructions long, hooking it is unnecessary and even causes issues. using (DynamicMethodDefinition statsDmd = new DynamicMethodDefinition(typeof(Achievements).GetMethod("Register"))) { int instructionCount = statsDmd.Definition.Body.Instructions.Count; if (instructionCount > 4) { On.Celeste.Achievements.Register += Achievements_Register; } } }
internal MethodPatcher(MethodBase original, MethodBase source, string harmonyInstanceID, List <MethodInfo> prefixes, List <MethodInfo> postfixes, List <MethodInfo> transpilers, List <MethodInfo> finalizers) { if (original == null) { throw new ArgumentNullException(nameof(original)); } this.original = original; this.source = source; this.harmonyInstanceID = harmonyInstanceID; this.prefixes = prefixes; this.postfixes = postfixes; this.transpilers = transpilers; this.finalizers = finalizers; Memory.MarkForNoInlining(original); if (Harmony.DEBUG) { FileLog.LogBuffered($"### Patch {original.FullDescription()}"); FileLog.FlushBuffer(); } idx = prefixes.Count() + postfixes.Count() + finalizers.Count(); firstArgIsReturnBuffer = NativeThisPointer.NeedsNativeThisPointerFix(original); returnType = AccessTools.GetReturnedType(original); patch = CreateDynamicMethod(original, $"_Patch{idx}"); if (patch == null) { throw new Exception("Could not create dynamic method"); } il = patch.GetILGenerator(); emitter = new Emitter(il); }
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 void TestILDasmToString() { var testObject = typeof(ILDasmToStringObject); var testMethod = testObject.GetMethod(nameof(ILDasmToStringObject.Test)); var dmd = new DynamicMethodDefinition(testMethod); Assert.AreEqual(expected.Replace("\r\n", "\n"), dmd.Definition.Body.ToILDasmString().Replace("\r\n", "\n")); }
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(); }
/// <summary>Returns the methods unmodified list of CodeInstructions</summary> /// <param name="original">The original method</param> /// <param name="generator">A new generator that now contains all local variables and labels contained in the result</param> /// <returns>A list containing all the original CodeInstructions</returns> public static List<CodeInstruction> GetOriginalInstructions(MethodBase original, out ILGenerator generator) { // Create a copy var dmd = new DynamicMethodDefinition(original); // Create a manipulator to obtain the instructions var manipulator = new ILManipulator(dmd.Definition.Body); generator = new CecilILGenerator(dmd.GetILProcessor()).GetProxy(); return manipulator.GetInstructions(generator); }
/// <summary>Returns the methods unmodified list of code instructions</summary> /// <param name="original">The original method/constructor</param> /// <param name="generator">A new generator that now contains all local variables and labels contained in the result</param> /// <returns>A list containing all the original <see cref="CodeInstruction"/></returns> /// public static List <CodeInstruction> GetOriginalInstructions(MethodBase original, out ILGenerator generator) { var method = new DynamicMethodDefinition($"{original.Name}_Dummy{Guid.NewGuid()}", typeof(void), new Type[0]); generator = method.GetILGenerator(); var reader = MethodBodyReader.GetInstructions(generator, original); return(reader.Select(ins => ins.GetCodeInstruction()).ToList()); }
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; }
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>; }
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; } }
private bool RedirectDMD(MethodBase methodBase, Delegate callback) { var endPoint = GetHookEndPoint(methodBase); var dmd = new DynamicMethodDefinition(methodBase); dmd.Reload(null, true); ReplaceDMD(endPoint, dmd); return(true); }
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")); } }
internal static DynamicMethodDefinition CreateDynamicMethod(MethodBase original, string suffix) { if (original == null) { throw new ArgumentNullException(nameof(original)); } var patchName = original.Name + suffix; patchName = patchName.Replace("<>", ""); var parameters = original.GetParameters(); var parameterTypes = parameters.Types().ToList(); if (original.IsStatic == false) { if (AccessTools.IsStruct(original.DeclaringType)) { parameterTypes.Insert(0, original.DeclaringType.MakeByRefType()); } else { parameterTypes.Insert(0, original.DeclaringType); } } var firstArgIsReturnBuffer = NativeThisPointer.NeedsNativeThisPointerFix(original); if (firstArgIsReturnBuffer) { parameterTypes.Insert(0, typeof(IntPtr)); } var returnType = firstArgIsReturnBuffer ? typeof(void) : AccessTools.GetReturnedType(original); var method = new DynamicMethodDefinition( patchName, returnType, parameterTypes.ToArray() ); #if NETSTANDARD2_0 || NETCOREAPP2_0 #else var offset = (original.IsStatic ? 0 : 1) + (firstArgIsReturnBuffer ? 1 : 0); for (var i = 0; i < parameters.Length; i++) { var param = method.Definition.Parameters[i + offset]; param.Attributes = (Mono.Cecil.ParameterAttributes)parameters[i].Attributes; param.Name = parameters[i].Name; } #endif return(method); }