/// <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 FunctionHook(long gameFunctionAddress, TFunction functionDelegate, int hookLength) { FunctionHook <TFunction> localFunctionHook = CreateFunctionHook(gameFunctionAddress, functionDelegate, hookLength); this.OriginalFunction = localFunctionHook.OriginalFunction; this._originalDelegate = localFunctionHook._originalDelegate; }
/// <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); }