/// <summary> /// Generates a wrapper function for an individual virtual function table entry /// in a virtual function table. /// </summary> /// <typeparam name="TFunction">Delegate type marked with complete ReloadedFunction Attribute/X64 Reloaded Function Attribute that defines the individual function properties.</typeparam> /// <param name="tableEntry">The individual Virtual function table entry to create a wrapper function for.</param> /// <returns>Delegate to assign back to ReloadedFunction marked game function.</returns> public static TFunction CreateWrapperFunction <TFunction>(this VirtualFunctionTable.TableEntry tableEntry) { // Create X86/X64 wrapper conditionally. if (IntPtr.Size == 4) { return(FunctionWrapper.CreateWrapperFunction <TFunction>((long)tableEntry.FunctionPointer)); } if (IntPtr.Size == 8) { return(X64FunctionWrapper.CreateWrapperFunction <TFunction>((long)tableEntry.FunctionPointer)); } // Null case return(default(TFunction)); }
/// <summary> /// Retrieves an instance of the delegate which can be used to call the function behind the function pointer. /// </summary> /// <returns>False if the pointer to call is invalid, else true.</returns> public TDelegate GetDelegate() { // Return false if pointer points to invalid address. if (FunctionAddress == IntPtr.Zero) { return(null); } // Get the pointer to the function. // Our pointer is dereferenced here, see "Pointer" Property. IntPtr functionPointer = FunctionAddress; // [Performance] Fast return if the pointer is the same as the last value. if (functionPointer == _lastFunctionPointer) { return(_delegate); } // Try to get the cached function wrapper. if (_methodCache.TryGetValue(functionPointer, out var cachedDelegate)) { return(cachedDelegate); } // Cached delegate not found. else { // Create wrapper if nonexisting. if (IntPtr.Size == 4) { _delegate = FunctionWrapper.CreateWrapperFunction <TDelegate>((long)functionPointer); } else { _delegate = X64FunctionWrapper.CreateWrapperFunction <TDelegate>((long)functionPointer); } // Cache the function wrapper. _methodCache[functionPointer] = _delegate; // Cache last function pointer. _lastFunctionPointer = functionPointer; // Return new Delegate return(_delegate); } }
/* 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(); }
/// <summary> /// Creates a function hook for a function at a user specified address. /// This class provides Windows API (and general process) hooking functionality for standard cdecl, stdcall, as well as custom /// ReloadedFunction Attribute declared functions. For more details, see the description of the <see cref="FunctionHook{TDelegate}"/> class. /// </summary> /// <param name="gameFunctionAddress">The address of the game function to create the wrapper for.</param> /// <param name="functionDelegate"> /// A delegate instance of the supplied generic delegate type which calls/invokes /// the C# method that will be used to handle the hook. /// </param> /// <param name="hookLength"> /// Optional explicit length of the hook to perform used in the impossibly rare /// cases whereby auto-length checking overflows the default into a jmp/call. /// </param> /// <returns> /// An instance of <see cref="FunctionHook{TFunction}"/>, which may be used /// to call the original function. /// </returns> /// <remarks> /// Due to safety and depth concerns regarding the use of multiple hooks on a singular address, /// the class does not provide an implementation allowing you to unhook. Please instead implement /// a flag and just call + return value from the original method without changing any of the input /// parameters if you wish to achieve the same effect. /// </remarks> public static FunctionHook <TFunction> CreateFunctionHook(long gameFunctionAddress, TFunction functionDelegate, int hookLength) { /* * Retrieve C# function details. */ // Retrieve the function address from the supplied user delegate. // Our ReloadedFunction attribute. IntPtr cSharpFunctionAddress = Marshal.GetFunctionPointerForDelegate(functionDelegate); ReloadedFunctionAttribute reloadedFunction = GetReloadedFunctionAttribute <TFunction>(); /* * [Hook Part I] Create Custom => CDECL Wrapper and Assemble */ // Assemble the wrapper function. // Assemble a jump to our wrapper function. IntPtr wrapperFunctionAddress = CreateWrapperFunction <TFunction>(cSharpFunctionAddress, reloadedFunction); List <byte> jumpBytes = HookCommon.AssembleAbsoluteJump(wrapperFunctionAddress).ToList(); /* * [Hook Part II] Calculate Hook Length (Unless Explicit) */ // Retrieve hook length explicitly if (hookLength == -1) { hookLength = HookCommon.GetHookLength((IntPtr)gameFunctionAddress, jumpBytes.Count); } // Assemble JMP + NOPs for stolen/stray bytes. if (hookLength > jumpBytes.Count) { // Append NOPs after JMP to fill remaining bytes. int nopBytes = hookLength - jumpBytes.Count; for (int x = 0; x < nopBytes; x++) { jumpBytes.Add(0x90); } } /* * [Call Original Function Part I] Read stolen bytes and assemble function wrapper to call original function. */ // Backup game's hook bytes. // Check if stolen bytes contains a jmp as first (other hooks) // Calculate jump back address for original function. // Append absolute JMP instruction to return to original function for calling the original function in hook. List <byte> stolenBytes = Bindings.TargetProcess.ReadMemoryExternal((IntPtr)gameFunctionAddress, hookLength).ToList(); stolenBytes = HookCommon.ProcessStolenBytes(stolenBytes, (IntPtr)gameFunctionAddress); IntPtr jumpBackAddress = (IntPtr)(gameFunctionAddress + hookLength); stolenBytes.AddRange(HookCommon.AssembleAbsoluteJump(jumpBackAddress)); /* * [Call Original Function part II] Instantiate and return functionHook with the original game function address. */ // Assign original function. FunctionHook <TFunction> functionHook = new FunctionHook <TFunction>(); // Write original bytes and jump to memory, and return address. IntPtr gameFunctionWrapperAddress = MemoryBuffer.Add(stolenBytes.ToArray()); // Create wrapper for calling the original function. functionHook.OriginalFunction = FunctionWrapper.CreateWrapperFunction <TFunction>((long)gameFunctionWrapperAddress); // Store a copy of the original function. functionHook._originalDelegate = functionDelegate; /* * [Apply Hook] Write hook bytes. */ Bindings.TargetProcess.WriteMemoryExternal((IntPtr)gameFunctionAddress, jumpBytes.ToArray()); return(functionHook); }
/// <summary> /// Generates a wrapper function for an individual virtual function table entry /// in a virtual function table. /// </summary> /// <typeparam name="TFunction">Delegate type marked with complete ReloadedFunction Attribute that defines the individual function properties.</typeparam> /// <param name="tableEntry">The individual Virtual function table entry to create a wrapper function for.</param> /// <returns>Delegate to assign back to ReloadedFunction marked game function.</returns> public static TFunction CreateX86WrapperFunction <TFunction>(this VirtualFunctionTable.TableEntry tableEntry) { return(FunctionWrapper.CreateWrapperFunction <TFunction>((long)tableEntry.FunctionPointer)); }