Cheat Engine Forum Index Cheat Engine
The Official Site of Cheat Engine
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 


Find static pointers via AOB search in executable code

 
Post new topic   Reply to topic    Cheat Engine Forum Index -> Cheat Engine Extensions
View previous topic :: View next topic  
Author Message
HenryEx
Advanced Cheater
Reputation: 1

Joined: 18 Dec 2011
Posts: 79

PostPosted: Fri Sep 13, 2024 10:03 am    Post subject: Find static pointers via AOB search in executable code Reply with quote

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:
...
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:
Code:
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:
...
66 0F5A C8            - cvtpd2ps xmm1,xmm0
0F57 C0               - xorps xmm0,xmm0
F3 0F5D 0D BC6E8005   - minss xmm1,[Game.exe+75DCC44]
...

Code:
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:
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:
--- 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-1: renamed processname parameter to modulename for clarification, added some sanity checking to that parameter to make sure you pass a valid module.

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 11:13 am; edited 4 times in total
Back to top
View user's profile Send private message
xxhehe
Expert Cheater
Reputation: 0

Joined: 11 Mar 2015
Posts: 147

PostPosted: Sat Sep 14, 2024 9:25 pm    Post subject: Reply with quote

They are helpful.
Back to top
View user's profile Send private message
Csimbi
I post too much
Reputation: 96

Joined: 14 Jul 2007
Posts: 3208

PostPosted: Wed Sep 18, 2024 3:37 pm    Post subject: Re: Find static pointers via AOB search in executable code Reply with quote

HenryEx wrote:

I hope someone might find it useful.

It is.
Any chance to add a new parameter that would limit the memory scan to the executable image?
E.g. only the EXE would be scanned (which is usually small compared to all the memory allocated to the process).
Maybe a module name a'la CE's own aobscanmodule - if provided, pass it to CE and do module scan instead of full aobscan?
Thank you!
Back to top
View user's profile Send private message
HenryEx
Advanced Cheater
Reputation: 1

Joined: 18 Dec 2011
Posts: 79

PostPosted: Fri Sep 20, 2024 6:22 am    Post subject: Re: Find static pointers via AOB search in executable code Reply with quote

Csimbi wrote:
It is.
Any chance to add a new parameter that would limit the memory scan to the executable image?
E.g. only the EXE would be scanned (which is usually small compared to all the memory allocated to the process).
Maybe a module name a'la CE's own aobscanmodule - if provided, pass it to CE and do module scan instead of full aobscan?
Thank you!


The function already does this by default. It only AOB scans executable code in the first place, and also filters out all addresses that are outside of the module region of the attached process. Or whatever process you specified in the optional processname parameter.

Now that i think about it, that would be a problem for games with dynamically initialized code like mono games.
Also, i guess the processname parameter is kind of a misnomer, since it doesn't change the process that CE is attached to and searches, it only changes the "module region" filter. So you can also look for executable AOBs in a loaded DLL and the like. I suppose calling it the "modulename" parameter would have been more accurate.


edit: actually changed the parameter name, should be clearer now. Also some additional sanity checking to make sure we actually get a name, user still needs to make sure it's valid though.
edit2: Scratch that, actually added check that the module you specify does exist.
edit3: Now that i'm looking over the module range scan, i found more problems. Changing the for loop to a while loop, since for loops are static and don't handle looping through a shrinking table very well.
Back to top
View user's profile Send private message
Csimbi
I post too much
Reputation: 96

Joined: 14 Jul 2007
Posts: 3208

PostPosted: Sat Sep 21, 2024 2:21 pm    Post subject: Re: Find static pointers via AOB search in executable code Reply with quote

HenryEx wrote:

The function already does this by default. It only AOB scans executable code in the first place, and also filters out all addresses that are outside of the module region of the attached process. Or whatever process you specified in the optional processname parameter.

Really? It felt really slow - as if it was scanning the whole memory.
Might be a performance issue then?

No worries, take your time - thanks for doing this!
Back to top
View user's profile Send private message
xxhehe
Expert Cheater
Reputation: 0

Joined: 11 Mar 2015
Posts: 147

PostPosted: Sun Sep 22, 2024 7:26 pm    Post subject: Re: Find static pointers via AOB search in executable code Reply with quote

HenryEx wrote:
Csimbi wrote:
It is.
Any chance to add a new parameter that would limit the memory scan to the executable image?
E.g. only the EXE would be scanned (which is usually small compared to all the memory allocated to the process).
Maybe a module name a'la CE's own aobscanmodule - if provided, pass it to CE and do module scan instead of full aobscan?
Thank you!


The function already does this by default. It only AOB scans executable code in the first place, and also filters out all addresses that are outside of the module region of the attached process. Or whatever process you specified in the optional processname parameter.

Now that i think about it, that would be a problem for games with dynamically initialized code like mono games.
Also, i guess the processname parameter is kind of a misnomer, since it doesn't change the process that CE is attached to and searches, it only changes the "module region" filter. So you can also look for executable AOBs in a loaded DLL and the like. I suppose calling it the "modulename" parameter would have been more accurate.


edit: actually changed the parameter name, should be clearer now. Also some additional sanity checking to make sure we actually get a name, user still needs to make sure it's valid though.
edit2: Scratch that, actually added check that the module you specify does exist.
edit3: Now that i'm looking over the module range scan, i found more problems. Changing the for loop to a while loop, since for loops are static and don't handle looping through a shrinking table very well.

If the data to be scanned is in a dll module......
Back to top
View user's profile Send private message
HenryEx
Advanced Cheater
Reputation: 1

Joined: 18 Dec 2011
Posts: 79

PostPosted: Mon Sep 23, 2024 11:28 am    Post subject: Re: Find static pointers via AOB search in executable code Reply with quote

xxhehe wrote:
If the data to be scanned is in a dll module......


Yes, that works. You can specify an AOB for hooking the renderer to add an overlay or whatever and use the modulename parameter to only return results from, for example, "d3d11.dll".

The module / .exe / .dll to scan is case sensitive though, so it MUST be specified exactly as it appears in CE's drop-down menu in Memory Scan Options.
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    Cheat Engine Forum Index -> Cheat Engine Extensions All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Powered by phpBB © 2001, 2005 phpBB Group

CE Wiki   IRC (#CEF)   Twitter
Third party websites