Exemple #1
0
        /// <summary>
        /// Creates a hook for a function at a given address.
        /// </summary>
        /// <param name="function">The function to detour the original function to.</param>
        /// <param name="functionAddress">The address of the function to hook.</param>
        /// <param name="minHookLength">Optional explicit length of hook. Use only in rare cases where auto-length check overflows a jmp/call opcode.</param>
        public Hook(TFunction function, long functionAddress, int minHookLength = -1)
        {
            _is64Bit       = IntPtr.Size == 8;
            ReverseWrapper = CreateReverseWrapper(function);

            /*
             * === Hook Summary ===
             *
             * A. Insert Absolute Jump to ReverseWrapper (Convention => CDECL Marshaller)
             *     A1. Backup original bytes and patch between start and end of JMP for (B).
             *
             * B. Setup Wrapper to call original function (CDECL => Convention Marshaller)
             *     B1. Take bytes backed up from A, and create stub function with those
             *         bytes and JMP to end of hook.
             *     B2. Assign OriginalFunction to that function stub.
             *
             * Note: For X64 the same principles apply, just replace CDECL with Microsoft calling convention.
             */

            Mutex.MakeHookMutex.WaitOne();

            /* Create Convention => CDECL Wrapper. */
            List <byte> jumpOpcodes = Utilities.AssembleAbsoluteJump(ReverseWrapper.WrapperPointer, _is64Bit).ToList();

            /* Calculate Hook Length (Unless Explicit) */
            if (minHookLength == -1)
            {
                minHookLength = Utilities.GetHookLength((IntPtr)functionAddress, jumpOpcodes.Count, _is64Bit);
            }

            // Sometimes our hook can be larger than the amount of bytes taken by the jmp opcode.
            // We need to fill the remaining bytes with NOPs.
            Utilities.FillArrayUntilSize <byte>(jumpOpcodes, 0x90, minHookLength);

            /* Get bytes from original function prologue and patch them. */
            CurrentProcess.SafeReadRaw((IntPtr)functionAddress, out byte[] originalFunction, minHookLength);

            var functionPatcher = new FunctionPatcher(_is64Bit);
            var functionPatch   = functionPatcher.Patch(originalFunction.ToList(), (IntPtr)functionAddress);

            IntPtr hookEndAddress = (IntPtr)(functionAddress + minHookLength);

            functionPatch.NewFunction.AddRange(Utilities.AssembleAbsoluteJump(hookEndAddress, _is64Bit));

            /* Second wave of patching. */
            var icedPatcher = new IcedPatcher(_is64Bit, functionPatch.NewFunction.ToArray(), (IntPtr)functionAddress);

            /* Create Hook instance. */
            OriginalFunctionAddress        = icedPatcher.ToMemoryBuffer();
            OriginalFunction               = CreateWrapper((long)icedPatcher.ToMemoryBuffer(), out IntPtr originalFunctionWrapperAddress);
            OriginalFunctionWrapperAddress = originalFunctionWrapperAddress;

            _otherHookPatches = functionPatch.Patches;
            _hookPatch        = new Patch((IntPtr)functionAddress, jumpOpcodes.ToArray());

            Mutex.MakeHookMutex.ReleaseMutex();
        }
        /// <summary>
        /// Creates a hook for a function at a given address.
        /// </summary>
        /// <param name="functionAddress">The address of the function to hook.</param>
        /// <param name="minHookLength">Optional explicit length of hook. Use only in rare cases where auto-length check overflows a jmp/call opcode.</param>
        /// <param name="options">Options which control the hook generation procedure.</param>
        private void CreateHook(nuint functionAddress, int minHookLength = -1, FunctionHookOptions options = null)
        {
            // Set options if not passed in.
            if (options == null)
            {
                Misc.TryGetAttribute <TFunction, FunctionHookOptions>(out options);
                options ??= new FunctionHookOptions();
            }

            /*
             * === Hook Summary ===
             *
             * A. Insert Absolute Jump to ReverseWrapper (Convention => CDECL Marshaller)
             *     A1. Backup original bytes and patch between start and end of JMP for (B).
             *
             * B. Setup Wrapper to call original function (CDECL => Convention Marshaller)
             *     B1. Take bytes backed up from A, and create stub function with those
             *         bytes and JMP to end of hook.
             *     B2. Assign OriginalFunction to that function stub.
             *
             * Note: For X64 the same principles apply, just replace CDECL with Microsoft calling convention.
             */

            /* Create Target Convention => TFunction Wrapper. */
            var jumpOpcodes = options.PreferRelativeJump ?
                              Utilities.TryAssembleRelativeJump(functionAddress, ReverseWrapper.WrapperPointer.ToUnsigned(), _is64Bit, out _) :
                              Utilities.AssembleAbsoluteJump(ReverseWrapper.WrapperPointer.ToUnsigned(), _is64Bit).ToList();

            /* Calculate Hook Length (Unless Explicit) */
            if (minHookLength == -1)
            {
                minHookLength = Utilities.GetHookLength(functionAddress, jumpOpcodes.Count, _is64Bit);
            }

            // Sometimes our hook can be larger than the amount of bytes taken by the jmp opcode.
            // We need to fill the remaining bytes with NOPs.
            Utilities.FillArrayUntilSize <byte>(jumpOpcodes, 0x90, minHookLength);

            /* Get bytes from original function prologue and patch them. */
            CurrentProcess.SafeReadRaw(functionAddress, out byte[] originalFunction, minHookLength);

            var   functionPatcher = new FunctionPatcher(_is64Bit, options);
            var   functionPatch   = functionPatcher.Patch(originalFunction.ToList(), functionAddress);
            nuint hookEndAddress  = functionAddress + (nuint)minHookLength;

            /* Second wave of patching. */
            var icedPatcher = new IcedPatcher(_is64Bit, functionPatch.NewFunction.ToArray(), functionAddress);

            /* Create Hook instance. */
            OriginalFunctionAddress        = icedPatcher.ToMemoryBuffer(hookEndAddress).ToSigned();
            OriginalFunction               = CreateWrapper(icedPatcher.ToMemoryBuffer(null), out nuint originalFunctionWrapperAddress);
            OriginalFunctionWrapperAddress = originalFunctionWrapperAddress.ToSigned();

            _otherHookPatches = functionPatch.Patches;
            _hookPatch        = new Patch(functionAddress, jumpOpcodes.ToArray());
        }