Ejemplo n.º 1
0
        private static void Create(Hook <TFunction> hook, ReverseWrapper <TFunction> reverseWrapper, long functionAddress, int minHookLength = -1)
        {
            /*
             *  === 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.
             */

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

            /* Calculate Hook Length (Unless Explicit) */

            if (minHookLength == -1)
            {
                minHookLength = Utilities.GetHookLength((IntPtr)functionAddress, jumpOpcodes.Count, ArchitectureMode.x86_32);
            }

            // 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.
            if (minHookLength > jumpOpcodes.Count)
            {
                int nopBytes = minHookLength - jumpOpcodes.Count;

                for (int x = 0; x < nopBytes; x++)
                {
                    jumpOpcodes.Add(0x90);
                }
            }

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

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

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

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

            /* Commit the original modified function to memory. */
            byte[] patchedFunction        = functionPatch.NewFunction.ToArray();
            var    buffer                 = Utilities.FindOrCreateBufferInRange(patchedFunction.Length);
            var    patchedFunctionAddress = buffer.Add(patchedFunction);

            /* Create Hook instance. */
            hook.OriginalFunctionAddress = patchedFunctionAddress;
            hook.OriginalFunction        = Wrapper.Create <TFunction>((long)patchedFunctionAddress);
            hook.ReverseWrapper          = reverseWrapper;
            hook._otherHookPatches       = functionPatch.Patches;
            hook._hookPatch = new Patch((IntPtr)functionAddress, jumpOpcodes.ToArray());
        }
Ejemplo n.º 2
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();
        }
Ejemplo n.º 3
0
        /// <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());
        }
        public void CallRewrittenPushReturn()
        {
            var functionPatcher = new FunctionPatcher(ArchitectureMode);

            CurrentProcess.ReadRaw(_pushReturnPtr, out byte[] originalBytes, _pushReturnLength);
            var patch = functionPatcher.Patch(originalBytes.ToList(), _pushReturnPtr);

            var buffer             = Utilities.FindOrCreateBufferInRange(patch.NewFunction.Count);
            var newFunctionAddress = buffer.Add(patch.NewFunction.ToArray());
            var wrapper            = Wrapper.Create <DummyFunctions.ReturnNumberDelegate>((long)newFunctionAddress);

            Assert.Equal(DummyFunctions.Five, wrapper());
        }
Ejemplo n.º 5
0
        public void CallRewrittenRelativeJmp()
        {
            var functionPatcher = new FunctionPatcher(ArchitectureMode);

            Memory.Sources.Memory.CurrentProcess.ReadRaw(_relativeJmpPtr, out byte[] originalBytes, _relativeJmpLength);
            var patch = functionPatcher.Patch(originalBytes.ToList(), _relativeJmpPtr);

            var buffer             = Utilities.FindOrCreateBufferInRange(patch.NewFunction.Count);
            var newFunctionAddress = buffer.Add(patch.NewFunction.ToArray());
            var wrapper            = ReloadedHooks.Instance.CreateWrapper <DummyFunctions.ReturnNumberDelegate>((long)newFunctionAddress, out _);

            Assert.Equal(DummyFunctions.Five, wrapper());
        }
Ejemplo n.º 6
0
        private static void Create(Hook <TFunction> hook, ReverseWrapper <TFunction> reverseWrapper, long functionAddress, int minHookLength = -1)
        {
            Mutex.MakeHookMutex.WaitOne();

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

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

            // 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.
            if (minHookLength > jumpOpcodes.Count)
            {
                int nopBytes = minHookLength - jumpOpcodes.Count;

                for (int x = 0; x < nopBytes; x++)
                {
                    jumpOpcodes.Add(0x90);
                }
            }

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

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

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

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

            /* Commit the original modified function to memory. */
            byte[] patchedFunction        = functionPatch.NewFunction.ToArray();
            var    buffer                 = Utilities.FindOrCreateBufferInRange(patchedFunction.Length, 1, long.MaxValue);
            var    patchedFunctionAddress = buffer.Add(patchedFunction);

            /* Create Hook instance. */
            hook.OriginalFunctionAddress = patchedFunctionAddress;
            hook.OriginalFunction        = Wrapper.Create <TFunction>((long)patchedFunctionAddress);
            hook.ReverseWrapper          = reverseWrapper;
            hook._otherHookPatches       = functionPatch.Patches;
            hook._hookPatch = new Patch((IntPtr)functionAddress, jumpOpcodes.ToArray());

            Mutex.MakeHookMutex.ReleaseMutex();
        }
        public void CallPatchedRIPRelativeReturnJump()
        {
            // Build RIP Relative Jump to ReturnSix
            var  buffer    = Utilities.FindOrCreateBufferInRange(100);
            long jmpTarget = (long)_dummyFunctions.ReturnSix;

            var ripRelativeJmp = new string[]
            {
                $"{_use32}",
                $"jmp qword [rip + 0]" // FASM offsets from end of instruction.
            };

            var asm         = _assembler.Assemble(ripRelativeJmp);
            var relativePtr = buffer.Add(asm);

            buffer.Add(ref jmpTarget, false, 1);

            // TODO: Hack! SharpDisasm does not correctly disasm multiple `jmp [rip + 0]` opcodes in sequence because of mixing code + data (pointer).
            // See other instance of this for notes.
            byte[] int3Bytes = { 0xCC, 0xCC, 0xCC, 0xCC };
            buffer.Add(int3Bytes);

            // Create a wrapper and call to confirm jump works.
            var wrapper = Wrapper.Create <DummyFunctions.ReturnNumberDelegate>((long)relativePtr);

            Assert.Equal(DummyFunctions.Six, wrapper());

            // Now try to retarget jump to ReturnFive
            var  patcher      = new FunctionPatcher(ArchitectureMode);
            long searchTarget = jmpTarget;

            FunctionPatcherTesting.GetSearchRange(patcher, ref searchTarget, out long searchLength);

            var patches = FunctionPatcherTesting.PatchJumpTargets(patcher,
                                                                  new AddressRange(searchTarget, searchTarget + searchLength),
                                                                  new AddressRange(jmpTarget, jmpTarget),
                                                                  (long)_dummyFunctions.ReturnFive);

            Assert.True(patches.Count > 0);

            foreach (var patch in patches)
            {
                patch.Apply();
            }

            Assert.Equal(DummyFunctions.Five, wrapper());
        }
        public void CallPatchedPushReturnReturnJump()
        {
            // Build RIP Relative Jump to ReturnSix
            var  buffer    = Utilities.FindOrCreateBufferInRange(100);
            long jmpTarget = (long)_dummyFunctions.ReturnSix;

            var pushReturnJmp = new string[]
            {
                $"{_use32}",
                $"push {jmpTarget}",
                "ret"
            };

            var asm           = _assembler.Assemble(pushReturnJmp);
            var pushReturnPtr = buffer.Add(asm);

            // Create a wrapper and call to confirm jump works.
            var wrapper = Wrapper.Create <DummyFunctions.ReturnNumberDelegate>((long)pushReturnPtr);

            Assert.Equal(DummyFunctions.Six, wrapper());

            // Now try to retarget jump to ReturnFive
            var  patcher      = new FunctionPatcher(ArchitectureMode);
            long searchTarget = jmpTarget;

            FunctionPatcherTesting.GetSearchRange(patcher, ref searchTarget, out long searchLength);

            var patches = FunctionPatcherTesting.PatchJumpTargets(patcher,
                                                                  new AddressRange(searchTarget, searchTarget + searchLength),
                                                                  new AddressRange(jmpTarget, jmpTarget),
                                                                  (long)_dummyFunctions.ReturnFive);

            Assert.True(patches.Count > 0);

            foreach (var patch in patches)
            {
                patch.Apply();
            }

            Assert.Equal(DummyFunctions.Five, wrapper());
        }
Ejemplo n.º 9
0
        public void CallPatchedPushReturnReturnJump()
        {
            // Build RIP Relative Jump to ReturnSix
            var   buffer    = Utilities.FindOrCreateBufferInRange(100);
            nuint jmpTarget = _dummyFunctions.ReturnSix;

            var pushReturnJmp = new string[]
            {
                $"{Macros._use32}",
                $"push {jmpTarget}",
                "ret"
            };

            var asm           = _assembler.Assemble(pushReturnJmp);
            var pushReturnPtr = buffer.Add(asm);

            // Create a wrapper and call to confirm jump works.
            var wrapper = ReloadedHooks.Instance.CreateWrapper <DummyFunctions.ReturnNumberDelegate>((long)pushReturnPtr, out _);

            Assert.Equal(DummyFunctions.Six, wrapper());

            // Now try to retarget jump to ReturnFive
            var   patcher      = new FunctionPatcher(Environment.Is64BitProcess);
            nuint searchTarget = jmpTarget;

            FunctionPatcherTesting.GetSearchRange(patcher, ref searchTarget, out nuint searchLength);

            var patches = FunctionPatcherTesting.PatchJumpTargets(patcher,
                                                                  new AddressRange(searchTarget, searchTarget + searchLength),
                                                                  new AddressRange(jmpTarget, jmpTarget),
                                                                  _dummyFunctions.ReturnFive);

            Assert.True(patches.Count > 0);

            foreach (var patch in patches)
            {
                patch.Apply();
            }

            Assert.Equal(DummyFunctions.Five, wrapper());
        }
Ejemplo n.º 10
0
        public void CallPatchedRelativeReturnJump()
        {
            // Build RIP Relative Jump to ReturnSix
            var  buffer    = Utilities.FindOrCreateBufferInRange(100);
            long jmpTarget = (long)_dummyFunctions.ReturnSix;

            var relativeJmp = new string[]
            {
                $"{Macros._use32}",
                $"jmp {(long)jmpTarget - (long)buffer.Properties.WritePointer}", // FASM relative offsets are relative to start of instruction.
            };

            var asm         = _assembler.Assemble(relativeJmp);
            var relativePtr = buffer.Add(asm, 1);

            // Create a wrapper and call to confirm jump works.
            var wrapper = ReloadedHooks.Instance.CreateWrapper <DummyFunctions.ReturnNumberDelegate>((long)relativePtr, out _);

            Assert.Equal(DummyFunctions.Six, wrapper());

            // Now try to retarget jump to ReturnFive
            var  patcher      = new FunctionPatcher(ArchitectureMode);
            long searchTarget = jmpTarget;

            FunctionPatcherTesting.GetSearchRange(patcher, ref searchTarget, out long searchLength);

            var patches = FunctionPatcherTesting.PatchJumpTargets(patcher,
                                                                  new AddressRange(searchTarget, searchTarget + searchLength),
                                                                  new AddressRange(jmpTarget, jmpTarget),
                                                                  (long)_dummyFunctions.ReturnFive);

            Assert.True(patches.Count > 0);

            foreach (var patch in patches)
            {
                patch.Apply();
            }

            Assert.Equal(DummyFunctions.Five, wrapper());
        }
 /// <summary>
 /// [TESTING USE ONLY]
 /// </summary>
 public static void GetSearchRange(FunctionPatcher patcher, ref nuint searchPointer, out nuint searchLength)
 {
     patcher.GetSearchRange(ref searchPointer, out searchLength);
 }
 /// <summary>
 /// [TESTING USE ONLY]
 /// </summary>
 public static List <Patch> PatchJumpTargets(FunctionPatcher patcher, AddressRange searchRange, AddressRange jumpTargetRange, nuint newJmpTarget)
 {
     return(patcher.PatchJumpTargets(searchRange, jumpTargetRange.StartPointer, jumpTargetRange, newJmpTarget));
 }
 /// <summary>
 /// [TESTING USE ONLY]
 /// </summary>
 public static List <Patch> PatchJumpTargets(FunctionPatcher patcher, AddressRange searchRange, AddressRange originalJmpTarget, long newJmpTarget)
 {
     return(patcher.PatchJumpTargets(searchRange, originalJmpTarget, newJmpTarget));
 }