/// <summary> /// Initializes a new instance of the <see cref="AsmHook"/> class. /// This is an assembly hook and should not be used for except under unique circumstances. /// Hook is not activated until Enable() method is called. /// </summary> /// <param name="address">A memory address to install a hook.</param> /// <param name="assembly">FASM syntax assembly code representing your hook. The first line should be use64.</param> /// <param name="name">The name of what you are hooking, since a delegate is not required.</param> /// <param name="asmHookBehaviour">How the hook is inserted into the execution flow.</param> public AsmHook(IntPtr address, string[] assembly, string name, AsmHookBehaviour asmHookBehaviour = AsmHookBehaviour.ExecuteFirst) { address = HookManager.FollowJmp(address); var hasOtherHooks = HookManager.Originals.ContainsKey(address); if (!hasOtherHooks) { MemoryHelper.ReadRaw(address, 0x32, out var original); HookManager.Originals[address] = original; } this.address = address; this.hookImpl = ReloadedHooks.Instance.CreateAsmHook(assembly, address.ToInt64(), (Reloaded.Hooks.Definitions.Enums.AsmHookBehaviour)asmHookBehaviour); this.statsMethod = new DynamicMethod(name, null, null); this.statsMethod.GetILGenerator().Emit(OpCodes.Ret); var dele = this.statsMethod.CreateDelegate(typeof(Action)); HookManager.TrackedHooks.Add(new HookInfo(this, dele, Assembly.GetCallingAssembly())); }
/// <summary> /// Creates a cheat engine style hook, replacing instruction(s) with a JMP to a user provided set of ASM instructions (and optionally the original ones). /// </summary> /// <param name="asmCode">The assembly code to execute, precompiled.</param> /// <param name="functionAddress">The address of the function or mid-function to hook.</param> /// <param name="behaviour">Defines what should be done with the original code that was replaced with the JMP instruction.</param> /// <param name="hookLength">Optional explicit length of hook. Use only in rare cases where auto-length check overflows a jmp/call opcode.</param> public AsmHook(byte[] asmCode, long functionAddress, AsmHookBehaviour behaviour = AsmHookBehaviour.ExecuteFirst, int hookLength = -1) : this() { /* * === Hook Summary === * * A. Backup Original Code & Generate Function Disable Stub * A1. Find amount of bytes required to insert JMP opcode, backup original bytes. * B2. Function disable stub will contain original code and jump back to end of hook. * * B. Generate Function Stub. * B1. For stub, generate code combining asmCode and original instructions, depending on AsmHookBehaviour * B2. Add jmp back to end of original hook. * * Graph: * Original => Stub Entry (JMP To Hook or Original Copy) => Hook/Original Copy with JMP back. * * Notes: For hook disable, replace jmp address to function disable stub. * For hook re-enable, replace jmp address to function hook stub. */ if (hookLength == -1) { hookLength = Utilities.GetHookLength((IntPtr)functionAddress, MaxJmpSize, _is64Bit); } CurrentProcess.SafeReadRaw((IntPtr)functionAddress, out byte[] originalFunction, hookLength); long jumpBackAddress = functionAddress + hookLength; /* Size calculations for buffer, must have sufficient space. */ // Stubs: // Stub Entry => Stub Hook. // Stub Hook => Caller. // Disable.Original Stub => Caller. int codeAlignment = 4; // Alignment of code in memory. int numberOfStubs = 3; // Also number of allocations. int alignmentRequiredBytes = (codeAlignment * numberOfStubs); int stubEntrySize = MaxJmpSize; int stubHookSize = asmCode.Length + hookLength + MaxJmpSize; int stubOriginalSize = hookLength + MaxJmpSize; int requiredSizeOfBuffer = stubEntrySize + stubHookSize + stubOriginalSize + alignmentRequiredBytes; var buffer = Utilities.FindOrCreateBufferInRange(requiredSizeOfBuffer); buffer.ExecuteWithLock(() => { var patcher = new IcedPatcher(_is64Bit, originalFunction, (IntPtr)functionAddress); // Make Hook and Original Stub buffer.SetAlignment(codeAlignment); IntPtr hookStubAddr = MakeHookStub(buffer, patcher, asmCode, originalFunction, jumpBackAddress, behaviour); buffer.SetAlignment(codeAlignment); IntPtr originalStubAddr = MakeOriginalStub(buffer, patcher, originalFunction, jumpBackAddress); // Make Jump to Entry, Original Stub byte[] jmpToOriginal = Utilities.AssembleAbsoluteJump(originalStubAddr, _is64Bit); byte[] jmpToHook = Utilities.AssembleAbsoluteJump(hookStubAddr, _is64Bit); // Make Entry Stub IntPtr entryStubAddr = buffer.Add(jmpToHook, codeAlignment); // Make Disable/Enable _disableHookPatch = new Patch(entryStubAddr, jmpToOriginal); _enableHookPatch = new Patch(entryStubAddr, jmpToHook); // Make Hook Enabler var jumpOpcodes = Utilities.AssembleAbsoluteJump(entryStubAddr, _is64Bit).ToList(); Utilities.FillArrayUntilSize <byte>(jumpOpcodes, 0x90, hookLength); _activateHookPatch = new Patch((IntPtr)functionAddress, jumpOpcodes.ToArray()); return(true); }); }
/// <summary> /// Creates a cheat engine style hook, replacing instruction(s) with a JMP to a user provided set of ASM instructions (and optionally the original ones). /// </summary> /// <param name="asmCode"> /// The assembly code to execute, in FASM syntax. /// (Should start with use32/use64) /// </param> /// <param name="functionAddress">The address of the function or mid-function to hook.</param> /// <param name="behaviour">Defines what should be done with the original code that was replaced with the JMP instruction.</param> /// <param name="hookLength">Optional explicit length of hook. Use only in rare cases where auto-length check overflows a jmp/call opcode.</param> public AsmHook(string[] asmCode, long functionAddress, AsmHookBehaviour behaviour = AsmHookBehaviour.ExecuteFirst, int hookLength = -1) : this(Utilities.Assembler.Assemble(asmCode), functionAddress, behaviour, hookLength) { }
private IntPtr MakeHookStub(MemoryBuffer buffer, IcedPatcher patcher, byte[] asmCode, byte[] originalCode, long jumpBackAddress, AsmHookBehaviour behaviour) { var bytes = new List <byte>(asmCode.Length + originalCode.Length); var jmpBackBytes = Utilities.AssembleAbsoluteJump((IntPtr)jumpBackAddress, _is64Bit); switch (behaviour) { case AsmHookBehaviour.ExecuteFirst: bytes.AddRange(asmCode); bytes.AddRange(patcher.EncodeForNewAddress(buffer.Properties.WritePointer + bytes.Count)); break; case AsmHookBehaviour.ExecuteAfter: bytes.AddRange(patcher.EncodeForNewAddress(buffer.Properties.WritePointer + bytes.Count)); bytes.AddRange(asmCode); break; case AsmHookBehaviour.DoNotExecuteOriginal: bytes.AddRange(asmCode); break; default: throw new ArgumentOutOfRangeException(nameof(behaviour), behaviour, null); } bytes.AddRange(jmpBackBytes); return(buffer.Add(bytes.ToArray(), 1)); // Buffer is pre-aligned }
public IAsmHook CreateAsmHook(byte[] asmCode, long functionAddress, AsmHookBehaviour behaviour = AsmHookBehaviour.ExecuteFirst, int hookLength = -1) => new AsmHook(asmCode, functionAddress, behaviour, hookLength);
/// <inheritdoc /> public IAsmHook MakeAsmHook(byte[] asmCode, AsmHookBehaviour behaviour = AsmHookBehaviour.ExecuteFirst, int hookLength = -1) { return(Hooks.CreateAsmHook(asmCode, Address, behaviour, hookLength)); }
public IAsmHook CreateAsmHook(byte[] asmCode, long functionAddress, AsmHookBehaviour behaviour, int hookLength) => new AsmHook(asmCode, functionAddress, behaviour, hookLength);
public IAsmHook CreateAsmHook(byte[] asmCode, long functionAddress, AsmHookBehaviour behaviour) => CreateAsmHook(asmCode, functionAddress, behaviour, -1);
public IAsmHook MakeAsmHook(byte[] asmCode, AsmHookBehaviour behaviour = AsmHookBehaviour.ExecuteFirst, int hookLength = -1) { return(mFunction.MakeAsmHook(asmCode, behaviour, hookLength)); }
/// <summary> /// Creates a cheat engine style hook, replacing instruction(s) with a JMP to a user provided set of ASM instructions (and optionally the original ones). /// </summary> /// <param name="asmCode">The assembly code to execute, precompiled.</param> /// <param name="functionAddress">The address of the function or mid-function to hook.</param> /// <param name="behaviour">Defines what should be done with the original code that was replaced with the JMP instruction.</param> /// <param name="hookLength">Optional explicit length of hook. Use only in rare cases where auto-length check overflows a jmp/call opcode.</param> public AsmHook(byte[] asmCode, long functionAddress, AsmHookBehaviour behaviour = AsmHookBehaviour.ExecuteFirst, int hookLength = -1) : this(asmCode, functionAddress, new AsmHookOptions() { Behaviour = behaviour, hookLength = hookLength }) { }
/// <summary> /// Creates a cheat engine style hook, replacing instruction(s) with a JMP to a user provided set of ASM instructions (and optionally the original ones). /// </summary> /// <param name="asmCode"> /// The assembly code to execute, in FASM syntax. /// (Should start with use32/use64) /// </param> /// <param name="functionAddress">The address of the function or mid-function to hook.</param> /// <param name="behaviour">Defines what should be done with the original code that was replaced with the JMP instruction.</param> /// <param name="hookLength">Optional explicit length of hook. Use only in rare cases where auto-length check overflows a jmp/call opcode.</param> public AsmHook(string[] asmCode, nuint functionAddress, AsmHookBehaviour behaviour = AsmHookBehaviour.ExecuteFirst, int hookLength = -1) : this(Utilities.Assembler.Assemble(asmCode), functionAddress, new AsmHookOptions() { Behaviour = behaviour, hookLength = hookLength }) { }
public IAsmHook CreateAsmHook(byte[] asmCode, long functionAddress, AsmHookBehaviour behaviour, int hookLength) => new AsmHook(asmCode, (nuint)functionAddress.ToUnsigned(), behaviour, hookLength);