private void TestHookAddNoOriginal_Internal(AsmHookOptions options) { int wordSize = IntPtr.Size; string[] addFunction = { $"{Macros._use32}", $"push {Macros._ebp}", $"mov {Macros._ebp}, {Macros._esp}", $"mov {Macros._eax}, [{Macros._ebp} + {wordSize * 2}]", // Left Parameter $"mov {Macros._ecx}, [{Macros._ebp} + {wordSize * 3}]", // Right Parameter $"add {Macros._eax}, 1", // Left Parameter }; _addNoOriginalHook = ReloadedHooks.Instance.CreateAsmHook(addFunction, (long)_nativeCalculator.Add, options).Activate(); for (int x = 0; x < 100; x++) { for (int y = 1; y < 100;) { int expected = (x + y) + 1; int result = _addFunction(x, y); Assert.Equal(expected, result); y += 2; } } }
private void TestHookAddAfterOriginal_Internal(AsmHookOptions options) { string[] addFunction = { $"{Macros._use32}", $"add {Macros._eax}, 1", // Left Parameter - Should have already been copied from stack. }; _addAfterOriginalHook = ReloadedHooks.Instance.CreateAsmHook(addFunction, (long)_nativeCalculator.Add, options).Activate(); for (int x = 0; x < 100; x++) { for (int y = 1; y < 100;) { int expected = (x + y) + 1; int result = _addFunction(x, y); Assert.Equal(expected, result); y += 2; } } }
public IAsmHook CreateAsmHook(string[] asmCode, long functionAddress, AsmHookOptions options) => new AsmHook(asmCode, functionAddress, options);
/// <summary> /// Creates a cheat engine style hook, replacing instruction(s) with a JMP to a user provided set of ASM instructions (and optionally the original ones). /// </summary> /// <param name="asmCode">The assembly code to execute, precompiled.</param> /// <param name="functionAddress">The address of the function or mid-function to hook.</param> /// <param name="options">The options used for creating the assembly hook.</param> public AsmHook(byte[] asmCode, long functionAddress, AsmHookOptions options = default) : this() { options ??= new AsmHookOptions(); /* * === Hook Summary === * * A. Backup Original Code & Generate Function Disable Stub * A1. Find amount of bytes required to insert JMP opcode, backup original bytes. * B2. Function disable stub will contain original code and jump back to end of hook. * * B. Generate Function Stub. * B1. For stub, generate code combining asmCode and original instructions, depending on AsmHookBehaviour * B2. Add jmp back to end of original hook. * * Graph: * Original => Stub Entry (JMP To Hook or Original Copy) => Hook/Original Copy with JMP back. * * Notes: For hook disable, replace jmp address to function disable stub. * For hook re-enable, replace jmp address to function hook stub. */ if (options.hookLength == -1) { options.hookLength = Utilities.GetHookLength((IntPtr)functionAddress, options.MaxOpcodeSize, _is64Bit); } CurrentProcess.SafeReadRaw((IntPtr)functionAddress, out byte[] originalFunction, options.hookLength); long jumpBackAddress = functionAddress + options.hookLength; /* Size calculations for buffer, must have sufficient space. */ // Stubs: // Stub Entry => Stub Hook. // Stub Hook => Caller. // Disable.Original Stub => Caller. int codeAlignment = 16; // Alignment of code in memory. int numberOfStubs = 3; // Also number of allocations. int alignmentRequiredBytes = (codeAlignment * numberOfStubs); int pointerSize = (_is64Bit ? 8 : 4); int pointerRequiredBytes = pointerSize * 2; // 2 calls to AssembleAbsoluteJump int stubEntrySize = MaxAbsJmpSize; int stubHookSize = asmCode.Length + options.hookLength + MaxAbsJmpSize; int stubOriginalSize = options.hookLength + MaxAbsJmpSize + pointerSize; // 1 call to AssembleAbsoluteJump int requiredSizeOfBuffer = stubEntrySize + stubHookSize + stubOriginalSize + alignmentRequiredBytes + pointerRequiredBytes; var minMax = Utilities.GetRelativeJumpMinMax(functionAddress, Int32.MaxValue - requiredSizeOfBuffer); var buffer = Utilities.FindOrCreateBufferInRange(requiredSizeOfBuffer, minMax.min, minMax.max); buffer.ExecuteWithLock(() => { var patcher = new IcedPatcher(_is64Bit, originalFunction, (IntPtr)functionAddress); // Make Hook and Original Stub buffer.SetAlignment(codeAlignment); IntPtr hookStubAddr = MakeHookStub(buffer, patcher, asmCode, originalFunction, jumpBackAddress, options.Behaviour); buffer.SetAlignment(codeAlignment); IntPtr originalStubAddr = MakeOriginalStub(buffer, patcher, originalFunction, jumpBackAddress); // Make Jump to Entry, Original Stub buffer.SetAlignment(codeAlignment); var currAddress = buffer.Properties.WritePointer; byte[] jmpToOriginal = Utilities.AssembleRelativeJump(currAddress, originalStubAddr, _is64Bit); byte[] jmpToHook = Utilities.AssembleRelativeJump(currAddress, hookStubAddr, _is64Bit); // Make Entry Stub IntPtr entryStubAddr = buffer.Add(jmpToHook, codeAlignment); // Make Disable/Enable _disableHookPatch = new Patch(entryStubAddr, jmpToOriginal); _enableHookPatch = new Patch(entryStubAddr, jmpToHook); // Make Hook Enabler var jumpOpcodes = options.PreferRelativeJump ? Utilities.AssembleRelativeJump((IntPtr)functionAddress, entryStubAddr, _is64Bit).ToList() : Utilities.AssembleAbsoluteJump(entryStubAddr, _is64Bit).ToList(); Utilities.FillArrayUntilSize <byte>(jumpOpcodes, 0x90, options.hookLength); _activateHookPatch = new Patch((IntPtr)functionAddress, jumpOpcodes.ToArray()); return(true); }); }
/// <summary> /// Creates a cheat engine style hook, replacing instruction(s) with a JMP to a user provided set of ASM instructions (and optionally the original ones). /// </summary> /// <param name="asmCode"> /// The assembly code to execute, in FASM syntax. /// (Should start with use32/use64) /// </param> /// <param name="functionAddress">The address of the function or mid-function to hook.</param> /// <param name="options">The options used for creating the assembly hook.</param> public AsmHook(string[] asmCode, long functionAddress, AsmHookOptions options = default) : this(Utilities.Assembler.Assemble(asmCode), functionAddress, options) { }
public IAsmHook CreateAsmHook(byte[] asmCode, long functionAddress, AsmHookOptions options) => new AsmHook(asmCode, (nuint)functionAddress.ToUnsigned(), options);