|
Cheat Engine The Official Site of Cheat Engine
|
View previous topic :: View next topic |
Author |
Message |
endian How do I cheat? Reputation: 0
Joined: 25 Sep 2018 Posts: 4
|
Posted: Tue Sep 25, 2018 4:37 pm Post subject: Handling arrays of complex objects |
|
|
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 |
|
|
ParkourPenguin I post too much Reputation: 138
Joined: 06 Jul 2014 Posts: 4275
|
Posted: Tue Sep 25, 2018 5:40 pm Post subject: |
|
|
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 |
|
|
atom0s Moderator Reputation: 198
Joined: 25 Jan 2006 Posts: 8516 Location: 127.0.0.1
|
Posted: Tue Sep 25, 2018 5:49 pm Post subject: |
|
|
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 |
|
|
endian How do I cheat? Reputation: 0
Joined: 25 Sep 2018 Posts: 4
|
Posted: Wed Sep 26, 2018 6:37 am Post subject: |
|
|
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...
Description: |
|
Filesize: |
38.86 KB |
Viewed: |
3741 Time(s) |
|
|
|
Back to top |
|
|
FreeER Grandmaster Cheater Supreme Reputation: 53
Joined: 09 Aug 2013 Posts: 1091
|
Posted: Wed Sep 26, 2018 1:15 pm Post subject: |
|
|
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 |
_________________
|
|
Back to top |
|
|
endian How do I cheat? Reputation: 0
Joined: 25 Sep 2018 Posts: 4
|
Posted: Mon Oct 01, 2018 4:23 pm Post subject: |
|
|
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 |
|
|
FreeER Grandmaster Cheater Supreme Reputation: 53
Joined: 09 Aug 2013 Posts: 1091
|
Posted: Mon Oct 01, 2018 7:09 pm Post subject: |
|
|
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").
_________________
|
|
Back to top |
|
|
TheyCallMeTim13 Wiki Contributor Reputation: 50
Joined: 24 Feb 2017 Posts: 976 Location: Pluto
|
Posted: Mon Oct 01, 2018 8:12 pm Post subject: |
|
|
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 |
|
|
endian How do I cheat? Reputation: 0
Joined: 25 Sep 2018 Posts: 4
|
Posted: Tue Oct 02, 2018 10:50 am Post subject: |
|
|
@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
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 |
|
|
FreeER Grandmaster Cheater Supreme Reputation: 53
Joined: 09 Aug 2013 Posts: 1091
|
Posted: Tue Oct 02, 2018 3:39 pm Post subject: |
|
|
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.
_________________
|
|
Back to top |
|
|
TheyCallMeTim13 Wiki Contributor Reputation: 50
Joined: 24 Feb 2017 Posts: 976 Location: Pluto
|
Posted: Tue Oct 02, 2018 6:57 pm Post subject: |
|
|
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 |
|
|
|
|
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
|
|