Beispiel #1
0
        /// <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()));
        }
Beispiel #2
0
        /// <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);
            });
        }
Beispiel #3
0
 /// <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)
 {
 }
Beispiel #4
0
        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
        }
Beispiel #5
0
 public IAsmHook CreateAsmHook(byte[] asmCode, long functionAddress, AsmHookBehaviour behaviour   = AsmHookBehaviour.ExecuteFirst, int hookLength = -1) => new AsmHook(asmCode, functionAddress, behaviour, hookLength);
Beispiel #6
0
 /// <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));
 }
Beispiel #10
0
 /// <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 })
 {
 }
Beispiel #11
0
 /// <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 })
 {
 }
Beispiel #12
0
 public IAsmHook CreateAsmHook(byte[] asmCode, long functionAddress, AsmHookBehaviour behaviour, int hookLength) => new AsmHook(asmCode, (nuint)functionAddress.ToUnsigned(), behaviour, hookLength);