Example #1
0
        private JumpDetails RewriteRelativeJump(Instruction instruction, FunctionPatch patch)
        {
            long originalJmpTarget = GetRelativeJumpTarget(instruction);

            patch.NewFunction.AddRange(Utilities.AssembleAbsoluteJump((IntPtr)originalJmpTarget, Is64Bit()));
            return(new JumpDetails((long)instruction.PC, originalJmpTarget));
        }
Example #2
0
        /* == Patch Function ==
         *
         * First a function stub is generated containing:
         * 1. Opcodes between Instruction.PC and "newAddress"
         * 2. a jmp to "newAddress".
         *
         * Then these functions look at the original JMP target and look for jumps back to
         * the end of the instruction.
         *
         * Patches are then generated that convert those jumps back to jumps to the location of
         * function stub.
         *
         * Patches are added to patch.Patches.
         */

        private void PatchReturnAddresses(JumpDetails jumpDetails, FunctionPatch patch, long newAddress)
        {
            long originalJmpTarget = jumpDetails.JumpOpcodeTarget;

            GetSearchRange(ref originalJmpTarget, out long searchLength);

            /* Get original opcodes after original JMP instruction. */

            IntPtr startRemainingOpcodes  = (IntPtr)jumpDetails.JumpOpcodeEnd;
            int    lengthRemainingOpcodes = (int)(newAddress - (long)startRemainingOpcodes);

            CurrentProcess.ReadRaw(startRemainingOpcodes, out byte[] remainingInstructions, lengthRemainingOpcodes);

            /* Build function stub + patches. */

            // Must guarantee relative jumps to be patches can reach our new prologue
            // as such must get range of search first before creating stub.
            long   maxDisplacement     = Int32.MaxValue - searchLength;
            IntPtr newOriginalPrologue = Utilities.InsertJump(remainingInstructions, Is64Bit(), newAddress, originalJmpTarget, maxDisplacement);

            var patches = PatchJumpTargets(new AddressRange(originalJmpTarget, originalJmpTarget + searchLength),
                                           new AddressRange((long)startRemainingOpcodes, newAddress), (long)newOriginalPrologue);

            patch.Patches.AddRange(patches);
        }
Example #3
0
        private JumpDetails RewriteRIPRelativeJump(Instruction instruction, FunctionPatch patch)
        {
            IntPtr targetAddress = GetRewriteRIPRelativeJumpTarget(instruction);

            patch.NewFunction.AddRange(Utilities.AssembleAbsoluteJump(targetAddress, Is64Bit()));
            return(new JumpDetails((long)instruction.PC, (long)targetAddress));
        }
Example #4
0
        /* == Patch Function ==
         *
         * First a function stub is generated containing:
         * 1. Opcodes between Instruction.PC and "newAddress"
         * 2. a jmp to "newAddress".
         *
         * Then these functions look at the original JMP target and look for jumps back to
         * the end of the instruction.
         *
         * Patches are then generated that convert those jumps back to jumps to the location of
         * function stub.
         *
         * Patches are added to patch.Patches.
         */

        private void PatchReturnAddresses(JumpDetails jumpDetails, FunctionPatch patch, long newAddress)
        {
            long originalJmpTarget    = jumpDetails.JumpOpcodeTarget;
            long initialSearchPointer = originalJmpTarget;

            GetSearchRange(ref initialSearchPointer, out long searchLength);

            /* Get original opcodes after original JMP instruction. */

            IntPtr startRemainingOpcodes  = (IntPtr)jumpDetails.JumpOpcodeEnd;
            int    lengthRemainingOpcodes = (int)(newAddress - (long)startRemainingOpcodes);
            var    remainingInstructions  = TryReadFromMemory(startRemainingOpcodes, lengthRemainingOpcodes);

            /* Build function stub + patches. */

            // Must guarantee relative jumps to be patches can reach our new prologue
            // as such must get range of search first before creating stub.
            long   maxDisplacement     = Int32.MaxValue - searchLength;
            IntPtr newOriginalPrologue = Utilities.InsertJump(remainingInstructions, Is64Bit(), newAddress, originalJmpTarget, maxDisplacement);

            // Catch all return addresses in page range.
            var pageRange       = new AddressRange(initialSearchPointer, initialSearchPointer + searchLength);
            var jumpTargetRange = new AddressRange((long)startRemainingOpcodes, newAddress);

            patch.Patches = PatchJumpTargets(pageRange, originalJmpTarget, jumpTargetRange, newOriginalPrologue);
        }
Example #5
0
        private JumpDetails RewriteRIPRelativeJump(Instruction instruction, FunctionPatch patch)
        {
            nuint targetAddress = GetRewriteRIPRelativeJumpTarget(instruction);

            patch.NewFunction.AddRange(Utilities.AssembleAbsoluteJump(targetAddress, _is64Bit));
            return(new JumpDetails(PC(instruction), targetAddress));
        }
Example #6
0
        private JumpDetails RewriteRelativeJump(Instruction instruction, FunctionPatch patch)
        {
            nuint originalJmpTarget = GetRelativeJumpTarget(instruction);

            patch.NewFunction.AddRange(Utilities.AssembleAbsoluteJump(originalJmpTarget, _is64Bit));
            return(new JumpDetails(PC(instruction), originalJmpTarget));
        }
        private JumpDetails RewriteRIPRelativeJump(Instruction instruction, FunctionPatch patch)
        {
            IntPtr pointerAddress = (IntPtr)((long)instruction.PC + GetOperandOffset(instruction.Operands[0]));

            CurrentProcess.Read(pointerAddress, out IntPtr targetAddress);

            patch.NewFunction.AddRange(Utilities.AssembleAbsoluteJump(targetAddress, Is64Bit()));
            return(new JumpDetails((long)instruction.PC, (long)targetAddress));
        }
Example #8
0
        /// <summary>
        /// Rewrites existing functions (supplied as list of bytes) converting relative to absolute jumps as well
        /// as the return addresses from given jumps.
        /// See Source Code for more details.
        /// </summary>
        /// <param name="oldFunction">The function to rewrite.</param>
        /// <param name="baseAddress">The original address of the function.</param>
        /// <returns></returns>
        public FunctionPatch Patch(List <byte> oldFunction, IntPtr baseAddress)
        {
            /*
             *  === What this is ===
             *
             *  This function attempts to find and patch other non-Reloaded API hooks.
             *
             *  It does this by rewriting a given function `oldfunction` converting relative to absolute jumps.
             *  In addition, it follows the target of all of the original relative jumps, and tries to detect
             *  and correct existing jumps back to game code to a new address.
             *
             *  The purpose is to allow to stack Reloaded hooks on non-Reloaded hooks;
             *  e.g. Steam hooks DirectX EndScene; we want to hook the Steam hooked scene.
             */

            /*
             *  === Cases Handled ===
             *
             *  Where '*' indicates 0 or more.
             *
             *  This function patches:
             *      1. Relative immediate jumps.
             *          nop*
             *          jmp 0x123456
             *          nop*
             *
             *      2. Push + Return
             *          nop*
             *          push 0x612403
             *          ret
             *          nop*
             *
             *      3. RIP Relative Addressing (X64)
             *          nop*
             *          JMP [RIP+0]
             *          nop*
             *
             *  This function ignores:
             *      Indirect memory operand pointer jumps.
             *          jmp [0x123456]
             */

            FunctionPatch functionPatch          = new FunctionPatch();
            long          reloadedHookEndAddress = oldFunction.Count + (long)baseAddress; // End of our own hook.

            Disassembler disassembler = new Disassembler(oldFunction.ToArray(), _architecture, (ulong)baseAddress, true);

            Instruction[] instructions = disassembler.Disassemble().ToArray();

            for (int x = 0; x < instructions.Length; x++)
            {
                Instruction instruction     = instructions[x];
                Instruction nextInstruction = (x + 1 < instructions.Length) ? instructions[x + 1] : null;
                JumpDetails jumpDetails;

                if (IsRelativeJump(instruction) && IsJumpTargetInDifferentModule((long)instruction.PC, GetRelativeJumpTarget(instruction)))
                {
                    jumpDetails = RewriteRelativeJump(instruction, functionPatch);
                }
                else if (IsRIPRelativeJump(instruction) && IsJumpTargetInDifferentModule((long)instruction.PC, (long)GetRewriteRIPRelativeJumpTarget(instruction)))
                {
                    jumpDetails = RewriteRIPRelativeJump(instruction, functionPatch);
                }
                else if (nextInstruction != null && IsPushReturn(instruction, nextInstruction) && IsJumpTargetInDifferentModule((long)instruction.PC, GetPushReturnTarget(instruction)))
                {
                    jumpDetails = RewritePushReturn(instruction, nextInstruction, functionPatch);
                }
                else
                {
                    functionPatch.NewFunction.AddRange(instruction.Bytes);
                    continue; // No patching on no addresses to patch.
                }

                PatchReturnAddresses(jumpDetails, functionPatch, reloadedHookEndAddress);
            }

            return(functionPatch);
        }
Example #9
0
        private JumpDetails RewritePushReturn(Instruction pushInstruction, Instruction retInstruction, FunctionPatch patch)
        {
            // Push does not support 64bit immediates. This makes our life considerably easier.
            long originalJmpTarget = GetPushReturnTarget(pushInstruction);

            patch.NewFunction.AddRange(Utilities.AssembleAbsoluteJump((IntPtr)originalJmpTarget, Is64Bit()));
            return(new JumpDetails((long)retInstruction.PC, originalJmpTarget));
        }
Example #10
0
        /// <summary>
        /// Rewrites existing functions (supplied as list of bytes) converting relative to absolute jumps as well
        /// as the return addresses from given jumps.
        /// See Source Code for more details.
        /// </summary>
        /// <param name="oldFunction">The function to rewrite.</param>
        /// <param name="baseAddress">The original address of the function.</param>
        /// <returns></returns>
        public unsafe FunctionPatch Patch(List <byte> oldFunction, nuint baseAddress)
        {
            /*
             *  === What this is ===
             *
             *  This function attempts to find and patch other non-Reloaded API hooks.
             *
             *  It does this by rewriting a given function `oldfunction` converting relative to absolute jumps.
             *  In addition, it follows the target of all of the original relative jumps, and tries to detect
             *  and correct existing jumps back to game code to a new address.
             *
             *  The purpose is to allow to stack Reloaded hooks on non-Reloaded hooks;
             *  e.g. Steam hooks DirectX EndScene; we want to hook the Steam hooked scene.
             */

            /*
             *  === Cases Handled ===
             *
             *  Where '*' indicates 0 or more.
             *
             *  This function patches:
             *      1. Relative immediate jumps.
             *          nop*
             *          jmp 0x123456
             *          nop*
             *
             *      2. Push + Return
             *          nop*
             *          push 0x612403
             *          ret
             *          nop*
             *
             *      3. RIP Relative Addressing (X64)
             *          nop*
             *          JMP [RIP+0]
             *          nop*
             *
             *  This function ignores:
             *      Indirect memory operand pointer jumps.
             *          jmp [0x123456]
             */

            FunctionPatch functionPatch          = new FunctionPatch();
            nuint         reloadedHookEndAddress = baseAddress + (nuint)oldFunction.Count; // End of our own hook.

            var oldFunctionArr = oldFunction.ToArray();
            var decoder        = Decoder.Create(_is64Bit ? 64 : 32, new ByteArrayCodeReader(oldFunctionArr));

            decoder.IP = baseAddress;
            var endRip = (nuint)oldFunction.Count + baseAddress;

            var instructions = new List <Instruction>();

            while (decoder.IP < endRip)
            {
                instructions.Add(decoder.Decode());
            }

            for (int x = 0; x < instructions.Count; x++)
            {
                Instruction instruction     = instructions[x];
                Instruction nextInstruction = (x + 1 < instructions.Count) ? instructions[x + 1] : default;
                JumpDetails jumpDetails;

                if (IsRelativeJump(instruction) && !IsJumpTargetInAModule(PC(instruction), GetRelativeJumpTarget(instruction)))
                {
                    jumpDetails = RewriteRelativeJump(instruction, functionPatch);
                }
                else if (IsRIPRelativeJump(instruction) && !IsJumpTargetInAModule(PC(instruction), (nuint)GetRewriteRIPRelativeJumpTarget(instruction)))
                {
                    jumpDetails = RewriteRIPRelativeJump(instruction, functionPatch);
                }
                else if (nextInstruction != default && IsPushReturn(instruction, nextInstruction) && !IsJumpTargetInAModule(PC(instruction), GetPushReturnTarget(instruction)))
                {
                    jumpDetails = RewritePushReturn(instruction, nextInstruction, functionPatch);
                }
                else
                {
                    var offset  = instruction.IP - baseAddress;
                    var oldInsn = oldFunctionArr.AsSpan().Slice((int)offset, instruction.Length);
                    functionPatch.NewFunction.AddRange(oldInsn.ToArray());
                    continue; // No patching on no addresses to patch.
                }

                PatchReturnAddresses(jumpDetails, functionPatch, reloadedHookEndAddress);
            }

            return(functionPatch);
        }