コード例 #1
0
        /// <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>
        private static IntPtr CreateWrapperFunction <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 = MemoryBuffer.Add(functionAddress);

            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(MemoryBuffer.Add(assembledMnemonics));
        }
コード例 #2
0
        /// <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);
        }