/// <summary> /// Assembles an absolute jump to a user specified address and returns /// the resultant bytes of the assembly process. /// </summary> /// <param name="functionAddress">The address to assemble the absolute call to.</param> /// <param name="reloadedFunction">Structure containing the details of the actual function in question.</param> /// <returns>A set of X64 assembler bytes to absolute jump to a specified address.</returns> public static List <string> X64AssembleAbsoluteCallMnemonics(IntPtr functionAddress, X64ReloadedFunctionAttribute reloadedFunction) { // List of ASM Instructions to be Compiled List <string> assemblyCode = new List <string>(); // Assemble the call to the game function in question. // With the rewrite of MemoryBuffer, this piece of code has been greatly simplified. IntPtr gameFunctionPointer; if ((gameFunctionPointer = MemoryBufferManager.Add(functionAddress, IntPtr.Zero)) != IntPtr.Zero) { // Jump to Game Function Pointer (gameFunctionPointer is address at which our function address is written) assemblyCode.Add("call qword [qword 0x" + gameFunctionPointer.ToString("X") + "]"); } // Cannot get buffer in 2GB range. else { // Get register to delegate function calling to. X64ReloadedFunctionAttribute.Register jmpRegister = FunctionCommon.GetCallRegister(reloadedFunction); // Call Game Function Pointer (gameFunctionPointer is address at which our function address is written) assemblyCode.Add($"mov {jmpRegister}, 0x{functionAddress.ToString("X")}"); assemblyCode.Add($"call {jmpRegister}"); } // Assemble the individual bytes. return(assemblyCode); }
/// <summary> /// Returns a register to be used for calling of the target function. /// </summary> /// <param name="reloadedFunction"> /// Structure containing the details of the actual function in question. /// The source registers (parameters) are considered to be a blacklist of registers that cannot be used. /// The return register is considered by default as the starting candidate. /// </param> public static X64ReloadedFunctionAttribute.Register GetCallRegister(X64ReloadedFunctionAttribute reloadedFunction) { // X86-64 doesn't support 64bit immediates in most instructions, meaning that we must, unfortunately // make use of a register to call a game function in question. // Here we find an unused register and delegate it to calling the function. // Default value | nonvolatile register X64ReloadedFunctionAttribute.Register callRegister = reloadedFunction.ReturnRegister; // Use return register if it's not a parameter (safe). if (!reloadedFunction.SourceRegisters.Contains(callRegister)) { return(callRegister); } // Use R11 (volatile) if it's not a parameter. if (!reloadedFunction.SourceRegisters.Contains(X64ReloadedFunctionAttribute.Register.r11)) { return(X64ReloadedFunctionAttribute.Register.r11); } // Use R10 (volatile) if it's not a parameter. if (!reloadedFunction.SourceRegisters.Contains(X64ReloadedFunctionAttribute.Register.r10)) { return(X64ReloadedFunctionAttribute.Register.r10); } // Otherwise brute force! foreach (X64ReloadedFunctionAttribute.Register foo in Enum.GetValues( typeof(X64ReloadedFunctionAttribute.Register))) { // Don't use a parameter register. if (reloadedFunction.SourceRegisters.Contains(foo)) { continue; } return(foo); } return(callRegister); }