/// <summary> /// Generate a DynamicMethod to easily call the given native function from another DynamicMethod. /// </summary> /// <param name="target">The pointer to the native function to call.</param> /// <param name="signature">A MethodBase with the target function's signature.</param> /// <returns>The detoured DynamicMethod.</returns> public static DynamicMethod GenerateNativeProxy(IntPtr target, MethodBase signature) { Type returnType = (signature as MethodInfo)?.ReturnType ?? typeof(void); ParameterInfo[] args = signature.GetParameters(); Type[] argTypes = new Type[args.Length]; for (int i = 0; i < args.Length; i++) { argTypes[i] = args[i].ParameterType; } DynamicMethod dm = new DynamicMethod( $"native_{((long) target).ToString("X16")}", returnType, argTypes, true ).StubCriticalDetour(); // Detour the new DynamicMethod into the target. NativeDetourData detour = Native.Create(dm.GetNativeStart(), target); Native.MakeWritable(detour); Native.Apply(detour); Native.MakeExecutable(detour); Native.Free(detour); return(dm.Pin()); }
public DynamicMethod CreateCopy(MethodBase method) { MethodBody body = method.GetMethodBody(); if (body == null) { throw new InvalidOperationException("P/Invoke methods cannot be copied!"); } ParameterInfo[] args = method.GetParameters(); Type[] argTypes; if (!method.IsStatic) { argTypes = new Type[args.Length + 1]; argTypes[0] = method.DeclaringType; for (int i = 0; i < args.Length; i++) { argTypes[i + 1] = args[i].ParameterType; } } else { argTypes = new Type[args.Length]; for (int i = 0; i < args.Length; i++) { argTypes[i] = args[i].ParameterType; } } DynamicMethod dm = new DynamicMethod( $"orig_{method.Name}", // method.Attributes, method.CallingConvention, // DynamicMethod only supports public, static and standard (method as MethodInfo)?.ReturnType ?? typeof(void), argTypes, method.DeclaringType, false ); ILGenerator il = dm.GetILGenerator(); // TODO: Move away from using Harmony's ILCopying code in MonoMod... List <Label> endLabels = new List <Label>(); List <Harmony.ILCopying.ExceptionBlock> endBlocks = new List <Harmony.ILCopying.ExceptionBlock>(); Harmony.ILCopying.MethodCopier copier = new Harmony.ILCopying.MethodCopier(method, il); copier.Finalize(endLabels, endBlocks); foreach (Label label in endLabels) { il.MarkLabel(label); } foreach (Harmony.ILCopying.ExceptionBlock block in endBlocks) { Harmony.ILCopying.Emitter.MarkBlockAfter(il, block); } return(dm.Pin()); }
public DynamicMethod CreateCopy(MethodBase method) { MethodBody body; try { body = method.GetMethodBody(); } catch (InvalidOperationException) { body = null; } if (body == null) { throw new InvalidOperationException("P/Invoke methods cannot be copied!"); } ParameterInfo[] args = method.GetParameters(); Type[] argTypes; if (!method.IsStatic) { argTypes = new Type[args.Length + 1]; argTypes[0] = method.DeclaringType; for (int i = 0; i < args.Length; i++) { argTypes[i + 1] = args[i].ParameterType; } } else { argTypes = new Type[args.Length]; for (int i = 0; i < args.Length; i++) { argTypes[i] = args[i].ParameterType; } } DynamicMethod dm = new DynamicMethod( $"orig_{method.Name}", // method.Attributes, method.CallingConvention, // DynamicMethod only supports public, static and standard (method as MethodInfo)?.ReturnType ?? typeof(void), argTypes, method.DeclaringType, false ); ILGenerator il = dm.GetILGenerator(); // TODO: Move away from using Harmony's ILCopying code in MonoMod... using (Harmony.ILCopying.MethodCopier copier = new Harmony.ILCopying.MethodCopier(method, il)) { copier.Copy(); } return(dm.Pin()); }
/// <summary> /// Generate a new DynamicMethod with which you can invoke the previous state. /// </summary> public MethodBase GenerateTrampoline(MethodBase signature = null) { MethodBase remoteTrampoline = OnGenerateTrampoline?.InvokeWhileNull <MethodBase>(this, signature); if (remoteTrampoline != null) { return(remoteTrampoline); } if (signature == null) { signature = Target; } // Note: It'd be more performant to skip this step and just return the "chained trampoline." // Unfortunately, it'd allow a third party to break the Detour trampoline chain, among other things. // Instead, we create and return a DynamicMethod calling the "chained trampoline." Type returnType = (signature as MethodInfo)?.ReturnType ?? typeof(void); ParameterInfo[] args = signature.GetParameters(); Type[] argTypes = new Type[args.Length]; for (int i = 0; i < args.Length; i++) { argTypes[i] = args[i].ParameterType; } DynamicMethod dm; ILGenerator il; dm = new DynamicMethod( $"trampoline_{Method.Name}_{GetHashCode()}", returnType, argTypes, Method.DeclaringType, true ); il = dm.GetILGenerator(); for (int i = 0; i < 10; i++) { // Prevent old Unity mono from inlining the DynamicMethod. il.Emit(OpCodes.Nop); } il.Emit(OpCodes.Jmp, _ChainedTrampoline); return(dm.Pin()); }
/// <summary> /// Emit an inline delegate reference and invocation. Note that the delegates "leak" unless you use HookILCursor.FreeReference(id). /// </summary> public int EmitDelegate <T>(T cb) where T : Delegate { Instruction instrPrev = Instr; int id = EmitDelegatePush(cb); // Create a DynamicMethod that shifts the stack around a little. Type delType = References[id].GetType(); MethodInfo delInvokeOrig = delType.GetMethod("Invoke"); ParameterInfo[] args = delInvokeOrig.GetParameters(); Type[] argTypes = new Type[args.Length + 1]; for (int i = 0; i < args.Length; i++) { argTypes[i] = args[i].ParameterType; } argTypes[args.Length] = delType; DynamicMethod dmInvoke = new DynamicMethod( "HookIL:Invoke:" + delInvokeOrig.DeclaringType.FullName, delInvokeOrig.ReturnType, argTypes, true // If any random errors pop up, try setting this to false first. ); ILGenerator il = dmInvoke.GetILGenerator(); // Load the delegate reference first. il.Emit(SREmit.OpCodes.Ldarg, args.Length); // Load any other arguments on top of that. for (int i = 0; i < args.Length; i++) { il.Emit(SREmit.OpCodes.Ldarg, i); } // Invoke the delegate and return its result. il.Emit(SREmit.OpCodes.Callvirt, delInvokeOrig); il.Emit(SREmit.OpCodes.Ret); dmInvoke = dmInvoke.Pin(); // Invoke the DynamicMethod. DelegateInvokers[id] = dmInvoke; Emit(OpCodes.Call, dmInvoke); // DynamicMethodDefinition should pass it through. return(id); }
/// <summary> /// Generate a new DynamicMethod with which you can invoke the previous state. /// </summary> public MethodBase GenerateTrampoline(MethodBase signature = null) { if (signature == null) { signature = Target; } // Note: It'd be more performant to skip this step and just return the "chained trampoline." // Unfortunately, it'd allow a third party to break the Detour trampoline chain, among other things. // Instead, we create and return a DynamicMethod calling the "chained trampoline." Type returnType = (signature as MethodInfo)?.ReturnType ?? typeof(void); ParameterInfo[] args = signature.GetParameters(); Type[] argTypes = new Type[args.Length]; for (int i = 0; i < args.Length; i++) { argTypes[i] = args[i].ParameterType; } DynamicMethod dm; ILGenerator il; dm = new DynamicMethod( $"trampoline_{Method.Name}_{GetHashCode()}", returnType, argTypes, Method.DeclaringType, true ); il = dm.GetILGenerator(); // TODO: Use specialized Ldarg.* if possible; What about ref types? for (int i = 0; i < argTypes.Length; i++) { il.Emit(OpCodes.Ldarg, i); } il.Emit(OpCodes.Call, _ChainedTrampoline); il.Emit(OpCodes.Ret); return(dm.Pin()); }
public Hook(MethodBase from, MethodInfo to, object target) { Method = from; _Hook = to; if (_Hook.ReturnType != ((from as MethodInfo)?.ReturnType ?? typeof(void))) { throw new InvalidOperationException($"Return type of hook for {from} doesn't match, must be {((from as MethodInfo)?.ReturnType ?? typeof(void)).FullName}"); } if (target == null && !to.IsStatic) { throw new InvalidOperationException($"Hook for method {from} must be static, or you must pass a target instance."); } ParameterInfo[] hookArgs = _Hook.GetParameters(); // Check if the parameters match. // If the delegate has got an extra first parameter that itself is a delegate, it's the orig trampoline passthrough. ParameterInfo[] args = Method.GetParameters(); Type[] argTypes; if (!Method.IsStatic) { argTypes = new Type[args.Length + 1]; argTypes[0] = Method.DeclaringType; for (int i = 0; i < args.Length; i++) { argTypes[i + 1] = args[i].ParameterType; } } else { argTypes = new Type[args.Length]; for (int i = 0; i < args.Length; i++) { argTypes[i] = args[i].ParameterType; } } Type origType = null; if (hookArgs.Length == argTypes.Length + 1 && typeof(Delegate).IsAssignableFrom(hookArgs[0].ParameterType)) { origType = hookArgs[0].ParameterType; } else if (hookArgs.Length != argTypes.Length) { throw new InvalidOperationException($"Parameter count of hook for {from} doesn't match, must be {argTypes.Length}"); } for (int i = 0; i < argTypes.Length; i++) { Type argMethod = argTypes[i]; Type argHook = hookArgs[i + (origType == null ? 0 : 1)].ParameterType; if (!argMethod.IsAssignableFrom(argHook) && !argHook.IsAssignableFrom(argMethod)) { throw new InvalidOperationException($"Parameter #{i} of hook for {from} doesn't match, must be {argMethod.FullName} or related"); } } MethodInfo origInvoke = origType?.GetMethod("Invoke"); // TODO: Check origType Invoke arguments. DynamicMethod dm; ILGenerator il; dm = new DynamicMethod( $"hook_{Method.Name}_{GetHashCode()}", (Method as MethodInfo)?.ReturnType ?? typeof(void), argTypes, Method.DeclaringType, true ); il = dm.GetILGenerator(); if (target != null) { _RefTarget = il.EmitReference(target); } if (origType != null) { _RefTrampoline = il.EmitReference <Delegate>(null); } // TODO: Use specialized Ldarg.* if possible; What about ref types? for (int i = 0; i < argTypes.Length; i++) { il.Emit(OpCodes.Ldarg, i); } il.Emit(OpCodes.Call, _Hook); il.Emit(OpCodes.Ret); Target = dm.Pin(); _Detour = new Detour(Method, Target); if (origType != null) { DynamicMethodHelper.SetReference(_RefTrampoline.Value, GenerateTrampoline(origInvoke).CreateDelegate(origType)); } }
public Hook(MethodBase from, MethodInfo to, object target) { Method = from; _Hook = to; if (!(OnDetour?.InvokeWhileTrue(this, from, to, target) ?? true)) { return; } // Check if hook ret -> method ret is valid. Don't check for method ret -> hook ret, as that's too strict. Type returnType = (from as MethodInfo)?.ReturnType ?? typeof(void); if (_Hook.ReturnType != returnType && !_Hook.ReturnType.IsCompatible(returnType)) { throw new InvalidOperationException($"Return type of hook for {from} doesn't match, must be {((from as MethodInfo)?.ReturnType ?? typeof(void)).FullName}"); } if (target == null && !to.IsStatic) { throw new InvalidOperationException($"Hook for method {from} must be static, or you must pass a target instance."); } ParameterInfo[] hookArgs = _Hook.GetParameters(); // Check if the parameters match. // If the delegate has got an extra first parameter that itself is a delegate, it's the orig trampoline passthrough. ParameterInfo[] args = Method.GetParameters(); Type[] argTypes; if (!Method.IsStatic) { argTypes = new Type[args.Length + 1]; argTypes[0] = Method.GetThisParamType(); for (int i = 0; i < args.Length; i++) { argTypes[i + 1] = args[i].ParameterType; } } else { argTypes = new Type[args.Length]; for (int i = 0; i < args.Length; i++) { argTypes[i] = args[i].ParameterType; } } Type origType = null; if (hookArgs.Length == argTypes.Length + 1 && typeof(Delegate).IsAssignableFrom(hookArgs[0].ParameterType)) { _OrigDelegateType = origType = hookArgs[0].ParameterType; } else if (hookArgs.Length != argTypes.Length) { throw new InvalidOperationException($"Parameter count of hook for {from} doesn't match, must be {argTypes.Length}"); } for (int i = 0; i < argTypes.Length; i++) { Type argMethod = argTypes[i]; Type argHook = hookArgs[i + (origType == null ? 0 : 1)].ParameterType; if (!argMethod.IsCompatible(argHook)) { throw new InvalidOperationException($"Parameter #{i} of hook for {from} doesn't match, must be {argMethod.FullName} or related"); } } MethodInfo origInvoke = _OrigDelegateInvoke = origType?.GetMethod("Invoke"); // TODO: Check origType Invoke arguments. DynamicMethod dm; ILGenerator il; dm = new DynamicMethod( $"Hook<{Method}>?{GetHashCode()}", (Method as MethodInfo)?.ReturnType ?? typeof(void), argTypes, Method.DeclaringType, true ); il = dm.GetILGenerator(); if (target != null) { _RefTarget = il.EmitReference(target); } if (origType != null) { _RefTrampoline = il.EmitReference <Delegate>(null); } // TODO: Use specialized Ldarg.* if possible; What about ref types? for (int i = 0; i < argTypes.Length; i++) { il.Emit(OpCodes.Ldarg, i); } il.Emit(OpCodes.Call, _Hook); il.Emit(OpCodes.Ret); Target = dm.Pin(); // Temporarily provide a trampoline that waits for the proper trampoline. if (origType != null) { ParameterInfo[] origArgs = origInvoke.GetParameters(); Type[] origArgTypes = new Type[origArgs.Length]; for (int i = 0; i < origArgs.Length; i++) { origArgTypes[i] = origArgs[i].ParameterType; } dm = new DynamicMethod( $"Chain:TMP<{Method}>?{GetHashCode()}", (origInvoke as MethodInfo)?.ReturnType ?? typeof(void), origArgTypes, Method.DeclaringType, true ); il = dm.GetILGenerator(); // while (ref == null) { } Label lblStart = il.DefineLabel(); il.MarkLabel(lblStart); _RefTrampolineTmp = il.EmitReference <Delegate>(null); il.Emit(OpCodes.Brfalse, lblStart); // Invoke the generated delegate. il.EmitGetReference <Delegate>(_RefTrampolineTmp.Value); // TODO: Use specialized Ldarg.* if possible; What about ref types? for (int i = 0; i < argTypes.Length; i++) { il.Emit(OpCodes.Ldarg, i); } il.Emit(OpCodes.Callvirt, origInvoke); il.Emit(OpCodes.Ret); DynamicMethodHelper.SetReference(_RefTrampoline.Value, dm.CreateDelegate(origType)); } _Detour = new Detour(Method, Target); UpdateOrig(null); }