[Lua] Find static pointers via AOB search in executable code

Upload *YOUR* gamehacking tools/helpers here
Post Reply
HenryEx
Expert Cheater
Expert Cheater
Posts: 53
Joined: Thu Aug 02, 2018 10:31 am
Reputation: 69

[Lua] Find static pointers via AOB search in executable code

Post by HenryEx »

getPointerFromCodeAOB( ins_offset, aobList, OPTIONAL aobNames, OPTIONAL aob_offset, OPTIONAL isSilent, OPTIONAL registerSymbol, OPTIONAL modulename )

This LUA function will fetch you the address to a static pointer from an AOB search for code that reads from it.
You can pass it a string of bytes to AOB search, or you can pass it a whole table of multiple AOB strings to search for. You can optionally provide a table of names for the pointers, for example to automatically register a symbol for each address found. This way, you don't need to tediously update all base pointers for your table by hand when an update changes those; you only need to check the AOB searches once.
But what does this do exactly?


Let's say a game fetches the base pointer for the player from a static location with code like this:

Code: Select all

...
48 8B 05 57E2A90D     - mov rax,[Game.exe+F6FA348]
48 8B 48 68           - mov rcx,[rax+68]
8B 46 20              - mov eax,[rsi+20]
39 41 18              - cmp [rcx+18],eax
...
You'll want the address from that mov rax,[Game.exe+F6FA348] instruction. The first three bytes are the instruction, the last four bytes is the address you want.

Tell the function that the address comes at three bytes after the start of the instruction, and pass an AOB string to search for like this:
playerPointer = getPointerFromCodeAOB( 3, "48 8B 05 ?? ?? ?? ?? 48 8B 48 68 8B 46 20" )

The function will return the address Game.exe+F6FA348 as a string and also print it to LUA console.


Or maybe an AOB search is easier when the instruction with the pointer isn't the first one? Consider a case like this:

Code: Select all

...
66 0F5A C8            - cvtpd2ps xmm1,xmm0
0F57 C0               - xorps xmm0,xmm0
F3 0F5D 0D BC6E8005   - minss xmm1,[Game.exe+75DCC44]
...
local aob = "66 0F 5A C8 0F 57 C0 F3 0F 5D 0D ?? ?? ?? ??"
getPointerFromCodeAOB( 4, aob, "Health Cap", 7, true, true )


The minss instruction is 4 bytes before the pointer this time, then we search for an AOB with the name "Health Cap" and tell the function to start looking for the pointer instruction 7 bytes from the start of the search to skip the first two instructions.
We set isSilent to true and registerSymbol to true, so the result isn't posted to the console but is immediately registered as a symbol called "Health Cap" in the table.

You can even search for multiple pointers in one go by passing it tables like this:

Code: Select all

local aobList = {
"48 8B 15 ?? ?? ?? ?? 48 85 D2 74",
"48 8B 05 ?? ?? ?? ?? 80 78 62 00 75",
"48 8B 05 ?? ?? ?? ?? 48 85 C0 74",
"48 8B 15 ?? ?? ?? ?? E9 ?? ?? ?? ?? 48 8B 15"
}
local aobNames = {
"Player",
"Inventory",
"Weapons",
"Quests"
}
It will then return a table of strings with the adresses like that, too, or return an empty string if no address was found.

I tried to make it as robust as possible, so the function throws errors if something goes wrong. It will by default scan the currently attached process module, but you can specify a different one too as the processname argument.

How to use or install

Either:

  1. Create a .lua file in the ..\Cheat Engine\autorun\ folder, then copy & paste the function into it for local use
  2. Copy & paste the function into the LUA script of a certain table if you want to distribute a table that uses the function for pointer updates.

Code: Select all

--- Get a static pointer from an executable code AOB, 
-- for example a "mov rax, [pointer]" instruction
-- can take single AOB strings or an array of AOBs
-- getPointerFromCodeAOB( ins_offset, aobList, OPTIONAL aobNames, OPTIONAL aob_offset, OPTIONAL isSilent, OPTIONAL registerSymbol, OPTIONAL modulename )
-- @param ins_offset: number of bytes from the start of the instruction to the pointer, e.g. for mov rax,[pointer] it's 3 bytes
-- @param aobList: one string of AOB bytes or an array of multiple strings
-- @param aobNames: string or array of names for the AOB pointers
-- @param aob_offset: number of bytes from start of AOB to start of pointer instruction, default 0
-- @param isSilent: suppresses non-error prints to console if true, default false
-- @param registerSymbol: registers a symbol for all AOBs found if true, default false
-- @param modulename: name of the module to search in, formatted like "process.exe"; default is currently attached process module
-- @return string or array of strings with module pointers, returns empty string when not found and false on error

function getPointerFromCodeAOB(...)
  if getOpenedProcessID() == 0 then print( "ERROR: Not attached to process!\n" ) return false end
  local args = {...}
  if #args < 2 then print( "ERROR: getPointerFromCodeAOB needs at least 2 arguments!\n" ) return false end
  local aobList, result = {}, {}
  if (type(args[2]) == "table") then
    aobList = args[2]
  else
    aobList = {args[2]}
  end
  local aobNames = args[3]
  if (type(args[3]) ~= "table") then
    aobNames = {aobNames}
  end
  for i=1,#aobList do
    aobNames[i] = aobNames[i] or 'Pointer '..i
  end

  local offins = args[1]
  local offaob = args[4] or 0
  local isSilent = args[5] or false
  local doSymbol = args[6] or false
  local procname = ( type(args[7])~='string' or (args[7] or '')=='' ) and process or args[7]
  if args[7] then
    local moduleList = enumModules()
    local moduleFound
    for _, v in pairs(moduleList) do
      if v.Name == procname then moduleFound = true break end
    end
    if not moduleFound then print(string.format( "ERROR: Specified modulename %s not found!\n",procname )) return false end
  end
  local pbase = readInteger(procname) and getAddress(procname)
  local pend = pbase + getModuleSize(procname)
  if type(offins) ~= "number" or type(offaob) ~= "number" then print ( "ERROR: offsets for getPointerFromCodeAOB must be a number! \n" ) return false end

  if not isSilent then print(string.format( "Fetching %d pointer(s) for %s ...\n",#aobList,procname )) end

  for i = 1, #aobList do
    local aob = AOBScan(aobList[i],"+X*C*W")
    if aob then
      local j = 0
      while j < aob.Count do
        local adr = getAddressSafe(aob[j])
        if adr < pbase or adr >= pend then aob.delete(j) else j = j + 1 end
      end
    end
    if not aob or aob.Count < 1 then
      result[i] = ""
      if not isSilent then print(string.format( "AOB #%d (%s) not found!",i,aobNames[i] )) end
    else
      if (aob.Count > 1) and not isSilent then print(string.format("WARNING: %d matches for AOB #%d (%s)! Using first result.",aob.Count,i,aobNames[i])) end
      local instruct = getAddressSafe(aob[0])+offaob
      local instructSize = getInstructionSize(instruct)
      local distance = readInteger( getAddressSafe(instruct)+offins )

      local address = ( instruct + instructSize + distance ) - pbase
      result[i] = string.format("%s+%X",procname,address)
      if not isSilent then print(string.format( "%s: %s+%X",aobNames[i],procname,address) ) end
      if doSymbol then registerSymbol( aobNames[i], string.format("%s+%X",procname,address) ) end
    end
  end
  if #aobList == 1 then result = result[1] end
  return result
end
I hope someone might find it useful.


edit 20240920: renamed processname parameter to modulename for clarification, added some sanity checking to that parameter to make sure you pass a valid module name.

edit 20240920-2: Fixed an error in module scanning: changed for-loop to a while-loop, since LUA has static for-loops that don't handle shrinking tables well.
Last edited by HenryEx on Fri Sep 20, 2024 5:15 pm, edited 3 times in total.

Paul44
RCE Fanatics
RCE Fanatics
Posts: 888
Joined: Thu Jul 27, 2017 9:02 am
Reputation: 566

Re: [Lua] Find static pointers via AOB search in executable code

Post by Paul44 »

^ I haven't tried your function (yet), but i thought it would be good to mention another (CE standard) option here:
goto [Memory View ~ Search ~ Find assembly code]: now fill in your (partial) static address, and start the search...
=> it will produce the opcodes, holding that address.
(note that it can take some time to find the 1st and/or more)

Important tip: if it does NOT return any result on a particular static address, then just fill in a "partial" address (cut off 2-3 bytes of them low_bytes). You can/could always use any of those results, and then just add the proper offset to get your final static address...

HenryEx
Expert Cheater
Expert Cheater
Posts: 53
Joined: Thu Aug 02, 2018 10:31 am
Reputation: 69

Re: [Lua] Find static pointers via AOB search in executable code

Post by HenryEx »

Paul44 wrote:
Fri Sep 13, 2024 5:26 pm
^ I haven't tried your function (yet), but i thought it would be good to mention another (CE standard) option here:
goto [Memory View ~ Search ~ Find assembly code]: now fill in your (partial) static address, and start the search...
=> it will produce the opcodes, holding that address.
(note that it can take some time to find the 1st and/or more)

Important tip: if it does NOT return any result on a particular static address, then just fill in a "partial" address (cut off 2-3 bytes of them low_bytes). You can/could always use any of those results, and then just add the proper offset to get your final static address...
As far as i know, you can't really use the assembly search in LUA scripts and it kind of does something different to this function anyways. You use the assembly search when you want to find out where a certain set of instructions is. You'd use this function if you already know where your desired code is and just want to update changing pointers.

Anyways, i updated the function a bit.
I renamed the processname parameter to modulename, since it doesn't change which process CE is attached to, just which module from the current porcess is searched.
I also fixed a bug in the module search when you find multiple out-of-module addresses - had to change a for-loop to a while-loop, since LUA for-loops are static and don't handle shrinking tables well.

Post Reply

Who is online

Users browsing this forum: gardof29