Beispiel #1
0
        /// <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>
        /// Allows you to insert a set of your own assembly instructions in the middle of a game function and have the game conditionally redirect
        /// to said function.
        ///
        /// The hook requires a minimum of 6 (X86) or 7 (X64) bytes and will therefore overwrite as many instructions as it needs until it can get enough space
        /// to place the hook. If you have ever used Cheat ENgine's assembly injection, you're already familliar on how to work with this class.
        /// </summary>
        /// <param name="hookAddress">
        ///     The memory address for which the hook is to be generated.
        /// </param>
        /// <param name="mnemonics">
        ///     X86-32 or X64 FASM compatible assembly code that starts with the line use16, use32 or use64 for identifying the architecture.
        /// </param>
        /// <param name="originalInstructionOptions">
        ///     Defines a switch for the assembly hook builder telling it what should be done with the original set of bytes that are going to be
        ///     replaced with the jump opcode.
        /// </param>
        /// <param name="hookLength">[Optional] The explicit amount of bytes to overwrite when inserting the actual hook.</param>
        public AssemblyHook(IntPtr hookAddress, string[] mnemonics, OriginalInstructionOptions originalInstructionOptions, int hookLength = -1)
        {
            // Set hook address.
            HookAddress = hookAddress;

            // Our 32bit and 64bit code paths will differ once we get the disassembler rolling.
            bool is64bit = mnemonics[0] == "use64";

            // Get the default for architecture, then true hook length.
            if (hookLength == -1)
            {
                hookLength = GetDefaultHookLength(is64bit);

                ArchitectureMode disassemblerMode = is64bit ? ArchitectureMode.x86_64 : ArchitectureMode.x86_32;
                hookLength = HookCommon.GetHookLength(HookAddress, hookLength, disassemblerMode);
            }

            // Assemble our custom ASM hook function to be jumped to and executed by the game, then write it to memory.
            IntPtr returnAddress = HookAddress + hookLength;

            byte[] assembledBytes = Assembler.Assembler.Assemble(mnemonics);
            assembledBytes = ProcessCustomInstructions(assembledBytes, OriginalBytes, originalInstructionOptions);
            assembledBytes = AppendJumpBack(assembledBytes, is64bit, returnAddress);
            AsmHookAddress = MemoryBufferManager.Add(assembledBytes);

            // Backup our bytes to hook.
            OriginalBytes = Bindings.TargetProcess.ReadMemory(HookAddress, hookLength);

            // Setup our new bytes to overwrite the old ones with (absolute jump).
            NewBytes = new byte[hookLength];
            Populate(NewBytes, (byte)0x90);                          // NOP
            byte[] hookJump = AssembleJump(is64bit, AsmHookAddress); // Assemble absolute jump and copy to array of NOPs.
            Array.Copy(hookJump, NewBytes, hookJump.Length);
        }
Beispiel #3
0
        /// <summary>
        /// Creates the wrapper function that wraps a native function with our own defined or custom calling convention,
        /// into a regular CDECL function that is natively supported by the C# programming language.
        ///
        /// The return value is a pointer to a C# compatible CDECL fcuntion that calls our game function.
        ///
        /// This allows us to call non-standard "usercall" game functions, such as for example functions that take values
        /// in registers as parameters instead of using the stack, functions which take paremeters in a mixture of both stack
        /// and registers as well as functions which varying return parameters, either caller or callee cleaned up.
        /// </summary>
        /// <param name="functionAddress">The address of the function to create a wrapper for.</param>
        /// <param name="reloadedFunction">Structure containing the details of the actual function in question.</param>
        /// <returns>Pointer to the new CDECL function address to call from C# code to invoke our game function.</returns>
        private static IntPtr CreateWrapperFunctionInternal <TFunction>(IntPtr functionAddress, ReloadedFunctionAttribute reloadedFunction)
        {
            // Retrieve number of parameters.
            int numberOfParameters    = FunctionCommon.GetNumberofParameters(typeof(TFunction));
            int nonRegisterParameters = numberOfParameters - reloadedFunction.SourceRegisters.Length;

            // List of ASM Instructions to be Compiled
            List <string> assemblyCode = new List <string> {
                "use32"
            };

            // Backup Stack Frame
            assemblyCode.Add("push ebp");       // Backup old call frame
            assemblyCode.Add("mov ebp, esp");   // Setup new call frame

            // Reserve Extra Stack Space
            if (reloadedFunction.ReservedStackSpace > 0)
            {
                assemblyCode.Add($"sub esp, {reloadedFunction.ReservedStackSpace}");
            }

            // Setup Function Parameters
            if (numberOfParameters > 0)
            {
                assemblyCode.AddRange(AssembleFunctionParameters(numberOfParameters, reloadedFunction.SourceRegisters));
            }

            // Call Game Function Pointer (gameFunctionPointer is address at which our function address is written)
            IntPtr gameFunctionPointer = MemoryBufferManager.Add(functionAddress, IntPtr.Zero);

            assemblyCode.Add("call dword [0x" + gameFunctionPointer.ToString("X") + "]");

            // Stack cleanup if necessary
            // Move back the stack pointer to before our pushed parameters
            if (nonRegisterParameters > 0 && reloadedFunction.Cleanup == ReloadedFunctionAttribute.StackCleanup.Caller)
            {
                int stackCleanupBytes = 4 * nonRegisterParameters;
                assemblyCode.Add($"add esp, {stackCleanupBytes}");
            }

            if (reloadedFunction.ReturnRegister != ReloadedFunctionAttribute.Register.eax)
            {
                // MOV Game's custom calling convention return register into our return register, EAX.
                assemblyCode.Add("mov eax, " + reloadedFunction.ReturnRegister);
            }

            // Unreserve Extra Stack Space
            if (reloadedFunction.ReservedStackSpace > 0)
            {
                assemblyCode.Add($"add esp, {reloadedFunction.ReservedStackSpace}");
            }

            // Restore Stack Frame and Return
            assemblyCode.Add("pop ebp");
            assemblyCode.Add("ret");

            // Write function to buffer and return pointer.
            byte[] assembledMnemonics = Assembler.Assembler.Assemble(assemblyCode.ToArray());
            return(MemoryBufferManager.Add(assembledMnemonics));
        }
        /// <summary>
        /// Creates the wrapper function that wraps a native function with our own defined or custom calling convention,
        /// into a regular X64 Microsoft Calling Convention function that is natively supported by the C# programming language.
        ///
        /// This allows us to call non-standard "usercall" game functions, such as for example functions that take values
        /// in registers as parameters instead of using the stack, functions which take paremeters in a mixture of both stack
        /// and registers as well as functions which varying return parameters, either caller or callee cleaned up.
        /// </summary>
        /// <param name="functionAddress">The address of the function to create a wrapper for.</param>
        /// <param name="reloadedFunction">Structure containing the details of the actual function in question.</param>
        /// <returns>Pointer to the new X64 Microsoft Calling Convention function address to call from C# code to invoke our game function.</returns>
        private static IntPtr CreateWrapperFunctionInternal <TFunction>(IntPtr functionAddress, X64ReloadedFunctionAttribute reloadedFunction)
        {
            // Retrieve number of parameters.
            int numberOfParameters    = FunctionCommon.GetNumberofParametersWithoutFloats(typeof(TFunction));
            int nonRegisterParameters = numberOfParameters - reloadedFunction.SourceRegisters.Length;

            // List of ASM Instructions to be Compiled
            List <string> assemblyCode = new List <string> {
                "use64"
            };

            // Backup Stack Frame
            assemblyCode.Add("push rbp");       // Backup old call frame
            assemblyCode.Add("mov rbp, rsp");   // Setup new call frame

            // Setup Function Parameters
            if (numberOfParameters > 0)
            {
                assemblyCode.AddRange(AssembleFunctionParameters(numberOfParameters, reloadedFunction.SourceRegisters));
            }

            // Make Shadow Space if necessary.
            if (reloadedFunction.ShadowSpace)
            {
                assemblyCode.Add("sub rsp, 32");   // Setup new call frame
            }
            // Assemble the call to the game function in question.
            assemblyCode.AddRange(HookCommon.X64AssembleAbsoluteCallMnemonics(functionAddress, reloadedFunction));

            // Move return register back if necessary.
            if (reloadedFunction.ReturnRegister != X64ReloadedFunctionAttribute.Register.rax)
            {
                assemblyCode.Add("mov rax, " + reloadedFunction.ReturnRegister);
            }

            // Restore the stack pointer from the Shadow Space
            // 8 = IntPtr.Size
            // 32 = Shadow Space
            if (reloadedFunction.ShadowSpace)
            {
                assemblyCode.Add("add rsp, " + ((nonRegisterParameters * 8) + 32));
            }
            else
            {
                assemblyCode.Add("add rsp, " + (nonRegisterParameters * 8));
            }

            // Restore Stack Frame and Return
            assemblyCode.Add("pop rbp");
            assemblyCode.Add("ret");

            // Write function to buffer and return pointer.
            byte[] assembledMnemonics = Assembler.Assembler.Assemble(assemblyCode.ToArray());
            return(MemoryBufferManager.Add(assembledMnemonics));
        }
Beispiel #5
0
        /// <summary>
        /// Assembles a wrapper for other function hooks' stray bytes that will be overwritten by
        /// Reloaded's own hooking mechanism. Gives back the old functions their stray bytes and jumps back
        /// immediately to the end of Reloaded's hook.
        /// </summary>
        /// <param name="strayBytes">Bytes from another hook that will be replaced by Reloaded's hooking mechanism.</param>
        /// <param name="reloadedHookEndAddress">The target address for the mini wrapper to jump back to.</param>
        /// <param name="reloadedFunction">Structure containing the details of the actual function in question.</param>
        /// <param name="wrapperAddress">[Optional] Target address within of which the wrapper should be placed in 2GB range.</param>
        /// <returns>The address of a new "mini-wrapper" class for </returns>
        public static IntPtr X64AssembleMiniWrapper(byte[] strayBytes, long reloadedHookEndAddress, X64ReloadedFunctionAttribute reloadedFunction, long wrapperAddress = 0)
        {
            // Get our stray bytes.
            List <byte> newBytes = strayBytes.ToList();

            // Append our absolute jump to the mix.
            newBytes.AddRange(X64AssembleAbsoluteJump((IntPtr)reloadedHookEndAddress, reloadedFunction));

            // Write to memory buffer and return
            return(MemoryBufferManager.Add(newBytes.ToArray(), (IntPtr)wrapperAddress));
        }
Beispiel #6
0
        /// <summary>
        /// Assembles a wrapper for other function hooks' stray bytes that will be overwritten by
        /// Reloaded's own hooking mechanism. Gives back the old functions their stray bytes and jumps back
        /// immediately to the end of Reloaded's hook.
        /// </summary>
        /// <param name="strayBytes">Bytes from another hook that will be replaced by Reloaded's hooking mechanism.</param>
        /// <param name="reloadedHookEndAddress">The target address for the mini wrapper to jump back to.</param>
        /// <returns>The address of a new "mini-wrapper" class for </returns>
        public static IntPtr X86AssembleMiniWrapper(byte[] strayBytes, long reloadedHookEndAddress)
        {
            // Get our stray bytes.
            List <byte> newBytes = strayBytes.ToList();

            // Append our absolute jump to the mix.
            newBytes.AddRange(X86AssembleAbsoluteJump((IntPtr)reloadedHookEndAddress));

            // Write to memory buffer and return
            return(MemoryBufferManager.Add(newBytes.ToArray()));
        }
Beispiel #7
0
        /// <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 jump to.</param>
        /// <returns>A set of X86 assembler bytes to absolute jump to a specified address.</returns>
        public static byte[] X86AssembleAbsoluteJump(IntPtr functionAddress)
        {
            // List of ASM Instructions to be Compiled
            List <string> assemblyCode = new List <string> {
                "use32"
            };

            // Jump to Game Function Pointer (gameFunctionPointer is address at which our function address is written)
            IntPtr gameFunctionPointer = MemoryBufferManager.Add(functionAddress, IntPtr.Zero);

            assemblyCode.Add("jmp dword [0x" + gameFunctionPointer.ToString("X") + "]");

            // Assemble the individual bytes.
            return(Assemble(assemblyCode.ToArray()));
        }
        /// <summary>
        /// Creates the wrapper function for redirecting program flow to our C# function.
        /// </summary>
        /// <param name="functionAddress">The address of the function to create a wrapper for.</param>
        /// <returns></returns>
        internal static IntPtr CreateReverseWrapperInternal <TFunction>(IntPtr functionAddress, ReloadedFunctionAttribute reloadedFunction)
        {
            // Retrieve number of parameters.
            int numberOfParameters    = FunctionCommon.GetNumberofParameters(typeof(TFunction));
            int nonRegisterParameters = numberOfParameters - reloadedFunction.SourceRegisters.Length;

            // List of ASM Instructions to be Compiled
            List <string> assemblyCode = new List <string> {
                "use32"
            };

            // Backup Stack Frame
            assemblyCode.Add("push ebp");       // Backup old call frame
            assemblyCode.Add("mov ebp, esp");   // Setup new call frame

            // Push registers for our C# method as necessary.
            assemblyCode.AddRange(AssembleFunctionParameters(numberOfParameters, reloadedFunction.SourceRegisters));

            // Call C# Function Pointer (cSharpFunctionPointer is address at which our C# function address is written)
            IntPtr cSharpFunctionPointer = MemoryBufferManager.Add(functionAddress, IntPtr.Zero);

            assemblyCode.Add("call dword [0x" + cSharpFunctionPointer.ToString("X") + "]");

            // Restore stack pointer + stack frame
            assemblyCode.Add($"add esp, {numberOfParameters * 4}");

            // MOV our own return register, EAX into the register expected by the calling convention
            assemblyCode.Add($"mov {reloadedFunction.ReturnRegister}, eax");

            // Restore Stack Frame and Return
            assemblyCode.Add("pop ebp");

            // Caller/Callee Cleanup
            if (reloadedFunction.Cleanup == ReloadedFunctionAttribute.StackCleanup.Callee)
            {
                assemblyCode.Add($"ret {nonRegisterParameters * 4}");
            }
            else
            {
                assemblyCode.Add("ret");
            }

            // Assemble and return pointer to code
            byte[] assembledMnemonics = Assembler.Assembler.Assemble(assemblyCode.ToArray());
            return(MemoryBufferManager.Add(assembledMnemonics));
        }
Beispiel #9
0
        /* Entry Point */
        public static unsafe void Init()
        {
#if DEBUG
            Debugger.Launch();
#endif

            // Setup controllers.
            var controllerManager = new ControllerManager();
            playerOneController   = new ReloadedController(Player.PlayerOne, controllerManager);
            playerTwoController   = new ReloadedController(Player.PlayerTwo, controllerManager);
            playerThreeController = new ReloadedController(Player.PlayerThree, controllerManager);
            playerFourController  = new ReloadedController(Player.PlayerFour, controllerManager);

            // Hook get controls function.
            psPADServerHook = FunctionHook <psPADServerPC> .Create(0x444F30, PSPADServerImpl).Activate();

            // Copy the old function to a new place and create a function from it.
            byte[] periMakeRepeatBytes = GameProcess.ReadMemory((IntPtr)0x00434FF0, 0xDD);
            IntPtr functionPtr         = MemoryBufferManager.Add(periMakeRepeatBytes);

            periMakeRepeatFunction  = FunctionWrapper.CreateWrapperFunction <sGamePeri__MakeRepeatCount>((long)functionPtr);
            periMakeRepeatCountHook = FunctionHook <sGamePeri__MakeRepeatCount> .Create(0x00434FF0, MakeRepeatCountImpl).Activate();
        }
Beispiel #10
0
        /// <summary>
        /// Your own user code starts here.
        /// If this is your first time, do consider reading the notice above.
        /// It contains some very useful information.
        /// </summary>
        public static void Init()
        {
            /*
             *  Reloaded Mod Loader Sample: Memory Manipulation
             *  Architectures supported: X86, X64
             *
             *  An example of memory manipulation in Reloaded, showing how easily it is
             *  possible to write or read structures to and from memory.
             */

            // Want to see this in with a debugger? Uncomment this line.
            // Debugger.Launch();

            /// //////////////////////////////////
            /// Example #1: Reading/Writing Memory
            /// //////////////////////////////////

            // First let's allocate some memory to write our data to.
            // See footnote at 1*, you should really instead use the MemoryBufferManager class for this.
            IntPtr addressOfAllocation = GameProcess.AllocateMemory(2048);

            Bindings.PrintInfo($"Memory allocated at {addressOfAllocation.ToString("X")}");

            // Now let's write some memory to the given address.
            int oneThreeThreeSeven = 1337;

            GameProcess.WriteMemory(addressOfAllocation, ref oneThreeThreeSeven);
            Bindings.PrintInfo($"Written {1337} to address {addressOfAllocation.ToString("X")}");

            // Wait what!? That simple??
            // Well, of course, under the hood we use some generics and fancy C# to automatically convert a type
            // into an array of bytes to write into memory. Feel free to verify the result with Cheat Engine.

            // Let's read this address back.
            int valueAtAddress = GameProcess.ReadMemory <int>(addressOfAllocation);

            Bindings.PrintInfo($"Read {valueAtAddress} from address {addressOfAllocation.ToString("X")}");

            /// //////////////////////////////////////
            /// Example #2: Reading/Writing Structures
            /// //////////////////////////////////////

            // Let's try something a bit more complex now.
            addressOfAllocation += sizeof(int);     // Keep our previously written integer for later so you can verify it's there.
            Bindings.PrintInfo($"Demo #2, Read/Write address now at {addressOfAllocation.ToString("X")}");

            // Let's create and write our own custom structure.
            PlayerCoordinates playerCoordinates = new PlayerCoordinates()
            {
                xPosition = 10,
                yPosition = 1368.62F,
                zPosition = -5324.677F
            };

            // Now let's write it to memory.
            GameProcess.WriteMemory(addressOfAllocation, ref playerCoordinates);
            Bindings.PrintInfo($"Written arbitrary player coordinates {playerCoordinates.xPosition} {playerCoordinates.yPosition} {playerCoordinates.zPosition} to address {addressOfAllocation.ToString("X")}");

            // Wait... Nothing changed?
            // Of course I did say something about generics and fancy C# didn't I?
            // Let's read it back.
            PlayerCoordinates newPlayerCoordinates = GameProcess.ReadMemory <PlayerCoordinates>(addressOfAllocation);

            Bindings.PrintInfo($"Read player coordinates back from {addressOfAllocation.ToString("X")}");

            // Check if we are equal.
            if (newPlayerCoordinates.xPosition == playerCoordinates.xPosition &&
                newPlayerCoordinates.yPosition == playerCoordinates.yPosition &&
                newPlayerCoordinates.zPosition == playerCoordinates.zPosition)
            {
                Bindings.PrintInfo($"Success: See? It's incredibly easy!");
            }
            else
            {
                Bindings.PrintInfo($"Failure: Read back player coordinates are not equal.");
            }

            /// /////////////////////////////////////////
            /// Example #3: Reading/Writing Struct Arrays
            /// /////////////////////////////////////////

            // But what about arrays?
            // Well, libReloaded has an utility for even that.
            // First let's read some arbitrary array from a file and write it to memory.
            Bindings.PrintInfo($"Demo #3, Array Read/Write Test Begin! (This one you should see in a debugger)");

            // First write our arbitrary physics data into memory.
            byte[] physicsData   = File.ReadAllBytes($"{ModDirectory}\\phys.bin"); // ModDirectory is from Reloaded Template
            IntPtr arrayLocation = MemoryBufferManager.Add(physicsData);           // A good example of using MemoryBufferManager (see note below),

            // normally no guarantee our data will fit into our allocated memory,
            // MemoryBufferManager also handles that case without extra code.
            Bindings.PrintInfo($"Character physics data written to {arrayLocation.ToString("X")}");

            // Now let's read back the memory.
            // Length of array for this sample is known as 40.
            FixedArrayPtr <AdventurePhysics> adventurePhysicsArray = new FixedArrayPtr <AdventurePhysics>(arrayLocation, 40);

            // Let's try to read one entry back from memory.
            AdventurePhysics firstEntry = adventurePhysicsArray[0];                     // Wow! It's almost like a native array... isn't it?

            // But we want to know everything! Give me it all!
            AdventurePhysics[] adventurePhysicses = adventurePhysicsArray.ToArray();    // Huh? That's it. Yup.

            // How about writing to one of the entries?
            firstEntry.HangTime      = 1337;
            adventurePhysicsArray[0] = firstEntry;                                      // You've gotta be kidding me...
            Bindings.PrintInfo($"Physics at {arrayLocation.ToString("X")} changed.");
            Bindings.PrintInfo($"Demo end.");
        }
        /// <summary>
        /// Creates the wrapper function for redirecting program flow to our C# function.
        /// </summary>
        /// <param name="reloadedFunction">Structure containing the details of the actual function in question.</param>
        /// <param name="functionAddress">The address of the function to create a wrapper for.</param>
        /// <returns></returns>
        internal static IntPtr CreateX64WrapperFunctionInternal <TFunction>(IntPtr functionAddress, X64ReloadedFunctionAttribute reloadedFunction)
        {
            // Retrieve number of parameters.
            int numberOfParameters    = FunctionCommon.GetNumberofParametersWithoutFloats(typeof(TFunction));
            int nonRegisterParameters = numberOfParameters - reloadedFunction.SourceRegisters.Length;

            // List of ASM Instructions to be Compiled
            List <string> assemblyCode = new List <string> {
                "use64"
            };

            // If the attribute is Microsoft Call Convention, take fast path!
            if (reloadedFunction.Equals(new X64ReloadedFunctionAttribute(X64CallingConventions.Microsoft)))
            {
                // Backup old call frame
                assemblyCode.AddRange(HookCommon.X64AssembleAbsoluteJumpMnemonics(functionAddress, reloadedFunction));
            }
            else
            {
                // Backup Stack Frame
                assemblyCode.Add("push rbp");       // Backup old call frame
                assemblyCode.Add("mov rbp, rsp");   // Setup new call frame

                // We assume that we are stack aligned in usercall/custom calling conventions, if we are not, game over for now.
                // Our stack frame alignment is off by 8 here, we must also consider non-register parameters.
                // Note to self: In the case you forget, dummy. Your stack needs to be 16 byte aligned.

                // Calculate the bytes our wrapper parameters take on the stack in total.
                // The stack frame backup, push rbp and CALL negate themselves so it's down to this.
                // Then our misalignment to the stack.
                int stackBytesTotal   = (nonRegisterParameters * 8);
                int stackMisalignment = (stackBytesTotal % 16);

                // Prealign stack
                assemblyCode.Add($"sub rbp, {stackMisalignment}");   // Setup new call frame

                // Setup the registers for our C# method.
                assemblyCode.AddRange(AssembleFunctionParameters(numberOfParameters, reloadedFunction, stackMisalignment));

                // And now we add the shadow space for C# method's Microsoft Call Convention.
                assemblyCode.Add("sub rsp, 32");   // Setup new call frame

                // Assemble the Call to C# Function Pointer.
                assemblyCode.AddRange(HookCommon.X64AssembleAbsoluteCallMnemonics(functionAddress, reloadedFunction));

                // MOV our own return register, EAX into the register expected by the calling convention
                assemblyCode.Add($"mov {reloadedFunction.ReturnRegister}, rax");

                // Restore stack pointer (this is always the same, our function is Microsoft Call Convention Compliant)
                assemblyCode.Add("add rsp, " + ((nonRegisterParameters * 8) + 32));

                // Restore stack alignment
                assemblyCode.Add($"add rbp, {stackMisalignment}");

                // Restore Stack Frame and Return
                assemblyCode.Add("pop rbp");
                assemblyCode.Add("ret");
            }

            // Assemble and return pointer to code
            byte[] assembledMnemonics = Assembler.Assembler.Assemble(assemblyCode.ToArray());
            return(MemoryBufferManager.Add(assembledMnemonics));
        }