A patching system that supports modifying, extending (detouring, hooking), and patching code in memory at runtime. Uses Lua. Only supports x86.
Compile to a dll. The dll can then be loaded from inside lua
rps = require("RPS")
rps.hookCode(...) -- etc
When function A has been hooked and it is called by the program, the lua function is called instead. The lua function can then optionally call the original function. The returned value of the hooked function is passed back to the program.
hookCode(...)
Parameters:
luaHookCallback - the Lua function to call
hookAtAddress - the location in memory to start the hook
argumentCount - the amount of arguments passed to the function (including this parameter)
callingConvention - specifies whether the hooked function is a (0 = cdecl, 1 = thiscall, 2 = stdcall)
hookSize - the size of the hook (total size of all overwritten instructions)
Return value:
the original function
-- This function will be called instead of the original function A
-- This hook will make function A always result 0
-- when its original result would have been 1
function functionA_hook(this, param_1, param_2)
local result = functionA_original(this, param_1, param_2)
if result == 1 then
result = 0
end
return result
end
-- we hook function A at address 0xABCDEF12, it is a thiscall
-- and has three arguments. We need to overwrite 2 instructions (6 bytes total)
-- we expose function A as "functionA_original" so we can call the original
functionA_original = hookCode(functionA_hook, 0xABCDEF12, 3, 1, 6)
In order to be able to call a program function, for example function B, from Lua, we have to expose it to Lua.
exposeCode(...)
Parameters:
address - the location in memory to start the hook
argumentCount - the amount of arguments passed to the function (including 'this' parameter)
callingConvention - specifies whether the hooked function is a (0 = cdecl, 1 = thiscall, 2 = stdcall)
Return value:
the function to call the game code
-- we expose function A (a thiscall) as "functionA" so we can call it
functionA = exposeCode(0xABCDEF12, 3, 1)
function yourFunction()
-- the first parameter to the function is
-- the "this" parameter because it is a thiscall
local result = functionA(0x12345ABC, -1, 99)
if result == 1 then
result = 0
end
return result
end
Redirects program flow to a Lua function. Note that the memory that is overwritten to jump to Lua is executed after the Lua callback function. The function that is called receives one parameter which is a table of all 8 x86 registers. These registers can be written to, except for ESP. This table should always be returned by the Lua function.
detourCode(...)
Parameters:
luaCallback - the Lua function to call, receives one argument
address - location to put the detour at
size - total size of the instructions to overwrite
function onDetour(registers)
registers.EAX = 1
return registers
end
detourCode(onDetour, 0xABCDEF, 7)
scanForAOB(searchPattern[, min, max])
searchPattern scans the memory for this pattern
min address to start searching
max address to stop searching
searchPattern: hexadecimal array with question marks for wildcards.
example: "FF A1 E? B? ?? 00"
allocate(size)
returns a pointer to a memory block of 'size' length
readBytes(address, amount)
returns a table of bytes of size 'amount'
writeBytes(address, data)
writes the data to the address in memory
readString(address)
reads a 0-terminated ascii string from memory
There is also: readInteger, writeInteger, readSmallInteger, writeSmallInteger
allocateCode(size)
allocates a memory block with execution rights
writeCode(address, bytes)
writes the (code) bytes at the address