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)); }
/* == 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); }
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)); }
/* == 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); }
private JumpDetails RewriteRIPRelativeJump(Instruction instruction, FunctionPatch patch) { nuint targetAddress = GetRewriteRIPRelativeJumpTarget(instruction); patch.NewFunction.AddRange(Utilities.AssembleAbsoluteJump(targetAddress, _is64Bit)); return(new JumpDetails(PC(instruction), targetAddress)); }
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)); }
/// <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); }
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)); }
/// <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); }