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 


Handling arrays of complex objects

 
Post new topic   Reply to topic    Cheat Engine Forum Index -> Cheat Engine
View previous topic :: View next topic  
Author Message
endian
How do I cheat?
Reputation: 0

Joined: 25 Sep 2018
Posts: 4

PostPosted: Tue Sep 25, 2018 4:37 pm    Post subject: Handling arrays of complex objects Reply with quote

Hello all, new here, hopefully this goes well...

I'd like to know what's the recommended way of handling arrays or groups of relatively complex objects in memoryrecord form.

Say I create the following records describing an object in memory:

Code:

Character
- Weapon
- Armor
- Pointer A
- Pointer B
- Group A
  - Prop 1
  - Prop 2
- Item
- Etc


Now, given only one or two objects, it's easy to copy and paste the record, adjust the address and be done with it.
But let's say I have 400+ objects - copying the record manually becomes really tedious.
Also, if I'd like to add another subrecord to all objects, I'd have to do it manually for all 400+ objects.

Previously I wrote a script that copied a "template" record and adjusted child addresses as needed, but I had to copy each property individually and that was a pain...

However, I know that for instance, you can reference a "template" drop down list using (recordname).
I'd like to know if there's something similar for actual memory records.

Essentially, I want to define a record as a sort of "class/struct", and copy it x number of times to describe object instances.

So, how would you deal with situations such as these?
Back to top
View user's profile Send private message
ParkourPenguin
I post too much
Reputation: 138

Joined: 06 Jul 2014
Posts: 4275

PostPosted: Tue Sep 25, 2018 5:40 pm    Post subject: Reply with quote

There's probably a better way of doing whatever it is you're trying to do without adding everything to the address list.

Taking a little time up front to write Lua code can make recurring tasks easier in the long run.

Creating your own UI would be useful for a table/trainer.

If it's trivial tasks, try the structure dissect window.

_________________
I don't know where I'm going, but I'll figure it out when I get there.
Back to top
View user's profile Send private message
atom0s
Moderator
Reputation: 198

Joined: 25 Jan 2006
Posts: 8516
Location: 127.0.0.1

PostPosted: Tue Sep 25, 2018 5:49 pm    Post subject: Reply with quote

Use registered symbols if you want to use it from a CE table.

Create a script that creates a named symbol and holds the base address of the object such as:

Code:

[ENABLE]
alloc(mobptr, 4)
registersybmol(mobptr, 4)

mobptr:
// Add whatever extra code to set the mobptr base address of the object.

[/DISABLE]
dealloc(mobptr)
unregistersymbol(mobptr)


Then anything you do in the cheat table to lay out the object use mobptr as the main address and add whatever offset you need to it.

Then you can just edit mobptr's value to point to whichever object you want to view at the time and all the rest will auto-update based on mobptr's value.

_________________
- Retired.
Back to top
View user's profile Send private message Visit poster's website
endian
How do I cheat?
Reputation: 0

Joined: 25 Sep 2018
Posts: 4

PostPosted: Wed Sep 26, 2018 6:37 am    Post subject: Reply with quote

Thank you for your suggestions.

Since I'm just prototyping a table, I'm trying to avoid writing a custom UI as much as possible at this stage.
In general, I try to use the base CE UI elements as much as possible.

In this case, for instance, I have a record describing a basic structure. (Please refer to the attachment marked as "Example #1").
This basic structure repeats itself about 22 times.

As I've said, I have a script that takes a "base" record with relative addresses and simply duplicates the child records recursively by manually copying each relevant property.
To regenerate the array, you have to delete the records and run the script again.

Since I keep running into this "array" issue, I thought that maybe CE had a "memory record template" feature I might have missed...

Is there any way of accessing CE's built in "copy-paste" function via lua?
Copying properties manually is annoying to say the least...



example.png
 Description:
Example #1
 Filesize:  38.86 KB
 Viewed:  3740 Time(s)

example.png


Back to top
View user's profile Send private message
FreeER
Grandmaster Cheater Supreme
Reputation: 53

Joined: 09 Aug 2013
Posts: 1091

PostPosted: Wed Sep 26, 2018 1:15 pm    Post subject: Reply with quote

Quote:
To regenerate the array, you have to delete the records and run the script again.
Don't. Just update the addresses in the script rather than create everything.

Alternatively just use data dissect, at least with CE 6.8.1 you can auto open and populate it with something like
Code:
-- hard coded dynamic addresses for tutorial step 9 during testing.
local addresses = {players={'016C32DC','08FD007C'},enemies={'08FF686C','09010084'}}

local sf = createStructureForm(addresses.players[1],'players group','PlayerStruct')
local players = sf.Group[0]
for i=2, #addresses.players do -- add rest (other) players
  players.addColumn().AddressText = addresses.players[i]
end

local enemies = sf.addGroup()
enemies.name = 'enemies group'
for k,addr in ipairs(addresses.enemies) do -- add all enemies
  enemies.addColumn().AddressText = addr
end

_________________
https://github.com/FreeER/ has a few CE related repos
Back to top
View user's profile Send private message
endian
How do I cheat?
Reputation: 0

Joined: 25 Sep 2018
Posts: 4

PostPosted: Mon Oct 01, 2018 4:23 pm    Post subject: Reply with quote

Hey all, thanks again for the suggestions!

I've decided to generate the records I need on demand and update (when needed) the addresses for said records using symbols (i.e. each record is assigned an offset from a base symbol).

However, I have another small question, albeit unrelated to the original subject - hope that's ok...

IIRC, when I needed to find a base address while using CE some years ago, I used something like this:

Code:

aobscan(aob1,A1 ?? ?? ?? ??)

label(ptrBase)
registersymbol(ptrBase)

aob1+1:
ptrBase:


Essentially, I extracted the base address from the mov instruction and then ptrBase would contain the address I needed.

However, for an x64 process, when I use:

Code:

aobscan(aob1,48 8B 05 ?? ?? ?? ??)

label(ptrBase)
registersymbol(ptrBase)

aob1+3:
ptrBase:


Now to get the address I need, I'd have to add up the address of aob1, aob1+3 itself and the size of the mov opcode (7) to get the needed address, for example:

Code:

004DA540 48 8B 05 39 60 D3 00 --> ptrBase should be 004DA540 + 7 + 00D36039


My asm knowledge is very rusty, and the last time I used aa in ce was with x86 processes...

So what's the recommended way to achieve this?

(I think I can't even access the address of an aobscan result using aa, so I'm using lua instead but it seems rather hacky...)
Back to top
View user's profile Send private message
FreeER
Grandmaster Cheater Supreme
Reputation: 53

Joined: 09 Aug 2013
Posts: 1091

PostPosted: Mon Oct 01, 2018 7:09 pm    Post subject: Reply with quote

aob1 + 7 + [aob1+3]:
ptrbase:
?


the aobscan for aob1 is replaced with a define(aob1,whatever the found address is) so you can use aob1 as the address of the aobscan result (quick test gave error on saving but executed)...Though generally I'd just hook the code that has the address in a register and copy it into some memory, creating my own pointer (commonly known as an "injection copy").

_________________
https://github.com/FreeER/ has a few CE related repos
Back to top
View user's profile Send private message
TheyCallMeTim13
Wiki Contributor
Reputation: 50

Joined: 24 Feb 2017
Posts: 976
Location: Pluto

PostPosted: Mon Oct 01, 2018 8:12 pm    Post subject: Reply with quote

You could write a Lua function to do it, I've thought about that myself.
Say something like "getOpcodeAddress(AOB, offset, symbolName)" where it would check if the process is X64 and if so read the "offset" then get the end of the instruction's address, add them and register a symbol at that address; and if it's just X32 just read the address and register the symbol. Then you could create a custom Auto Assembler command for it as well.

_________________
Back to top
View user's profile Send private message Visit poster's website
endian
How do I cheat?
Reputation: 0

Joined: 25 Sep 2018
Posts: 4

PostPosted: Tue Oct 02, 2018 10:50 am    Post subject: Reply with quote

@TheyCallMeTim13 That's what I've been doing for a while - I have lua script that performs the aobscan and registers a symbol in the form of "program.exe"+offset (this is on purpose, easier for me to track offset changes this way).

Something like this:

Code:

local bytes = "48 8B 05 ???????? ..."
local results = AOBScan(bytes, "+X-C-W", 2, "0")

if results.Count == 0 then
  results.destroy()
  return
end

local addr = results[0]
results.destroy()

local opAddr = readInteger(addr .. "+3")
local base = getAddress(addr .. "+7-citra-qt.exe+" .. h(opAddr))


h(x) just converts to hex... so in the end the base address is at "citra-qt.exe"+h(base)...



@FreeER I was actually thrown off by the saving error, I thought the aa script failed to execute properly, however this actually worked:

Code:

aob+7+[aob+3]-841F0FC300000000:
ptrBase:


The "-841F0FC300000000" trims the 8 byte result to the relevant 4 bytes (I guess it's an x86 pointer in a x64 process)...

Also, I've actually tried injection copy before, but the process constantly crashes when I enable the following:

Code:

[ENABLE]

aobscanmodule(aob,"citra-qt.exe",48 8B 05 ???????? ...)

alloc(newmem,$1000)

label(next)
label(return)

label(orig)
registersymbol(orig)

orig:
  readmem(aob,7)

newmem:
next:
  readmem(aob,7)
  jmp return

aob:
  jmp newmem
  nop
  nop

return:

[DISABLE]

aob:
  readmem(orig,7)

unregistersymbol(aob)
unregistersymbol(orig)
dealloc(newmem)


Any idea why this fails? Sholdn't this just replace the original mov with a jmp, perform the mov in newmem and then return?

Never used readmem before, and as I've said my asm skills are super rusty, It's been a while Embarassed

Obviously if this would have worked I'd also have to add a symbol for the base address and mov [_base], r8 to actually copy the address from the r8 register in this case to the symbol.
Back to top
View user's profile Send private message
FreeER
Grandmaster Cheater Supreme
Reputation: 53

Joined: 09 Aug 2013
Posts: 1091

PostPosted: Tue Oct 02, 2018 3:39 pm    Post subject: Reply with quote

Hm, if all addresses/pointers are 4 bytes there's actually a way to make it treat them as such...I can only remember the lua function name off the top of my head though setPointerSize(size), presumably that'd affect AA scripts as well, though I don't think I've ever actually needed to use it myself. If it's just a 4 byte offset though that'd probably break any other pointers you had, though potentially you could set it before and after and as long as nothing was being done directly in the time that you were enabling the script it might not matter.

As for the injection copy crashing... hm, only 2 things that come to mind right now is
1. the instruction being affected by it's position so that the same bytes can mean different things depending on the address they're written at. Eg. jmp and call. In which case using reassemble rather than readmem may help, which iirc is intended specifically to copy an instruction not just the bytes.
2. If it's an x64 process the allocation may have happened so far from the injection point that it can't fit in a 32 bit offset the 5 byte jmp uses so CE has to use more instructions (14 byte psuedo-jump iirc) to accomplish the jump. That can be fixed by adding the aob name as a third parameter to alloc so that it always tries to allocate within a 32 bit offset.
3. this shouldn't cause a crash but orig isn't associated with any memory in that script, it isn't an allocated/aobscan/define and it's above any label that is so ce can't automatically assign it one based on the last label and whatever it wrote there. I wouldn't be surprised if this failed to enable or disable but I wouldn't actually expect it to crash.

Try pausing the game in the advanced options, then enabling the script and seeing what happens and whether it looks like you'd expect.

_________________
https://github.com/FreeER/ has a few CE related repos
Back to top
View user's profile Send private message
TheyCallMeTim13
Wiki Contributor
Reputation: 50

Joined: 24 Feb 2017
Posts: 976
Location: Pluto

PostPosted: Tue Oct 02, 2018 6:57 pm    Post subject: Reply with quote

I actually wrote this last night, and tested in on AC Origins. But it's a part of one of my Lua modules. The aob scan needs to return one result (mine take an index argument).

Code:

function I2CETableCEA.getOpcodeAddress(strSignature, offset, aobSignaturePrivileges, ceaFile, index)
   if offset == nil then
      offset = 0
   end
   if type(strSignature) ~= 'string' then
      local msg = 'Error "strSignature" must be a string.'
      Logger.error(msg)
      error(msg)
   end
   if type(offset) ~= 'number' then
      local msg = 'Error "offset" must be a number.'
      Logger.error(msg)
      error(msg)
   end
   local addr = I2CETableCEA.aobScan(strSignature, aobSignaturePrivileges or I2CETableCEA.AOBSignaturePrivileges, ceaFile, index)
   addr = addr + offset
   if targetIs64Bit() then
      local os = readInteger(addr, true)
      return (addr + 4) + os
   else
      return readInteger(addr)
   end
end


local function getOpcodeAddress(parameters, syntaxcheck)
   ---- getOpcodeAddress(symbol, aob, (optional) offset, (optional) privileges, (optional) index)
   local tbl = split(parameters, ',')
   local symbol = tbl[1]:gsub(' ', strE)
   local aob = tbl[2]:gsub(' ', strE)
   local offset = tbl[3]
   if offset ~= nil then
      offset = offset:gsub(' ', strE)
      offset = tonumber(offset, 16)
   else
      offset = 0
   end
   local privileges = tbl[4]
   if privileges ~= nil then
      privileges = privileges:gsub(' ', strE)
   end
   local index = tbl[5]
   if index ~= nil then
      index = index:gsub(' ', strE)
      index = tonumber(index, 16)
   else
      index = 0
   end
   if symbol == nil then
      local msg = t('Wrong number of parameters, no "symbol", for "getOpcodeAddress" I2CETableCEA AA command.')
      Logger.error(msg)
      error(msg)
      return nil, msg
   end
   if aob == nil then
      local msg = t('Wrong number of parameters, no "aob", for "getOpcodeAddress" I2CETableCEA AA command.')
      Logger.error(msg)
      error(msg)
      return nil, msg
   end
   if syntaxcheck then
      return format('define(%s,%016X)', symbol, 0)
   end
   local addr = I2CETableCEA.getOpcodeAddress(aob, offset, privileges or I2CETableCEA.AOBSignaturePrivileges, nil, index)
   return format('define(%s,%016X)', symbol, addr or 0)
end
registerAutoAssemblerCommand('getOpcodeAddress', getOpcodeAddress)



Here is an example usage in AA (AOB was trimmed):
Code:

[ENABLE]
getOpcodeAddress(ptrGetSrvPlayerHealthHook, 40574883EC..., 1D)
registerSymbol(ptrGetSrvPlayerHealthHook)
[DISABLE]
unregisterSymbol(ptrGetSrvPlayerHealthHook)

Right now it only takes Hex offsets for the AA command.


And in Lua (AOB was trimmed):
Code:
I2CETableCEA.getOpcodeAddress(ptrGetSrvPlayerHealthHook, 0x1D, 40574883EC...)



Here is the (current) full module:
https://pastebin.com/NCGgPdF7

_________________
Back to top
View user's profile Send private message Visit poster's website
Display posts from previous:   
Post new topic   Reply to topic    Cheat Engine Forum Index -> Cheat Engine 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