 |
Cheat Engine The Official Site of Cheat Engine
|
| View previous topic :: View next topic |
| Author |
Message |
Aeolius How do I cheat?
Reputation: 0
Joined: 04 Aug 2024 Posts: 2
|
Posted: Sun Aug 04, 2024 4:47 pm Post subject: Address string math & dereferencing |
|
|
Hello all. I was reading the wiki on CEAddressStrings:
wiki.cheatengine.org/index.php?title=CEAddressString
| Quote: | | These addresses do not start with a 0x in front but do support basic math. Example: "00400500+12" |
Just wondering what the limits of the "basic math" are. From tests, I can see that addition, subtraction, multiplication, and division are supported.
But is there any way to perform modulo math, bitwise operations, etc? Is there a way to dereference another address and use that value? Or grab the value of another MR?
In my example case, I have 2d collision map data arranged in a grid; this purely controls where things can or can't go, but has no inherent data re: what the specific things bumping around are. Said data lives elsewhere in the memory, where you can find stored coordinates linking them to the map representations.
So, I can easily make two byte-size memory records that store the X and Y values for a given entity. But what I'd like to do is have a third MR just to point to the specific address in the array that the current coords point to.
There's a simple formula to get there:
| Code: | | PROG.EXE+MAP_ARRAY_OFFSET+X_val+(Y_val*0x60) |
Now, building this third entry is simple enough with Lua:
| Code: |
function LookUpMapAddress(num)
local addresslist = getAddressList()
local x_monster = addresslist.getMemoryRecordByDescription(string.format("Monster %02s X",num))
local y_monster = addresslist.getMemoryRecordByDescription(string.format("Monster %02s Y",num))
local z_monster = addresslist.getMemoryRecordByDescription(string.format("Monster %02s Address",num))
z_monster.address = string.format("PROG.EXE+%08x",0x32F468+(tonumber(y_monster.value,16)*0x60)+tonumber(x_monster.value,16))
end
|
The wiki also mentions I can use $ to set the entry to a global Lua value, which is neat!
But I'm just wondering if there's a way to do this purely within MRs, to keep it up to date without having to get a script on a timer involved.
--
(Since it's my first post, I just want to include a remark on what an incredible educational tool Cheat Engine is; I've learned so much just from experimenting with it in the last week. I never thought I'd directly interact with assembly, and yet here I am, having just created my first working injection without crashing the process. Thrilling stuff!)
|
|
| Back to top |
|
 |
panraven Grandmaster Cheater
Reputation: 62
Joined: 01 Oct 2008 Posts: 959
|
Posted: Sun Aug 04, 2024 8:30 pm Post subject: |
|
|
You may try Pointer Type Cast.
Normally an Address like
[Base_Addr]
is to "dereference the Base_Addr" to a 8-byte integer of the value at address Base_Addr, usually used as an Address.
then it can be Pointer Type Cast the 8-byte integer as 1/2/4 byte integer, usually used as offset (RIP-addressing) or its TYPED value.
unsigned-casting keyword : BYTE/WORD/DWORD
signed-casting keyword : CHAR/SHORT/LONG
so if
| Code: |
Base_Addr:
dq 102030485068788 /// 15 digits
[Base_Addr] => 0x102030485068788
(BYTE)[Base_Addr] => (unsigned)0x88 => 136
(CHAR)[Base_Addr] => (signed)0x88 => -120
(WORD)[Base_Addr] => (unsigned)0x8788 => 34696
(SHORT)[Base_Addr] => (signed)0x8788 => -30840
(DWORD)[Base_Addr] => (unsigned)0x85068788 => 2231797640
(LONG)[Base_Addr] => (signed)0x85068788 => -2063169656
|
This is a testing Lua script to see how it work:
| Code: |
--- attach a process
autoAssemble[[
globalalloc(___,$1000)
___:
dq 2
___+10:
dq 4
___+20: /// base
db 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
db 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f
]]
print(string.format('Array Base : %X',getAddressSafe'___+20' or -1))
print('X:\n',readBytes[[
___+00
]],getAddressSafe[[
(CHAR)[___+00]
]])
print('Y:\n',readBytes[[
___+10
]],getAddressSafe[[
(CHAR)[___+10]
]])
print('(CHAR)[ base+(CHAR)[X]+3*(CHAR)[Y] ] :\n',readBytes[[
(CHAR)[___+10]*3+(CHAR)[___+00]+___+20
]],getAddressSafe[[
(CHAR)[(CHAR)[___+10]*3+(CHAR)[___+00]+___+20]
]])
print('Address:\n',string.format('%X vs %X',getAddressSafe[[
(CHAR)[___+10]*3+(CHAR)[___+00]+___+20
]] or -1, (getAddressSafe'___+20' or -1)+(2)+3*(4)))
--[[ sample output
Array Base : 214B0D00020
X:
2 2
Y:
4 4
(CHAR)[ base+(CHAR)[X]+3*(CHAR)[Y] ] :
14 14
Address:
214B0D0002E vs 214B0D0002E
--]]
|
Need a recent CE and may not work on too complex the expression.
_________________
- Retarded. |
|
| Back to top |
|
 |
ParkourPenguin I post too much
Reputation: 152
Joined: 06 Jul 2014 Posts: 4718
|
Posted: Sun Aug 04, 2024 10:07 pm Post subject: |
|
|
The full story lies in the function TSymhandler.getAddressFromName in the file "Cheat Engine/symbolhandler.pas"
First the symbol handler tries to convert what it can to integers. This includes
- Module names - game.exe
- In debug contexts, registers - e.g. RAX, RIP,...
- User-defined symbols
- Other symbols- e.g. kernel symbols (DBK),dotnet stuff, THREADSTACK, ...
Memory can be read from by using square brackets. This is most often used with pointers:
| Code: | | getAddressSafe'[[game.exe+1234]+5C]+8' |
The default value type used in symbol lookup is a pointer type (64-bit or 32-bit integer). Another integer type can be specified by prefixing the square brackets with a case-insensitive string embedded in parenthesis:
- BYTE - 1-byte unsigned
- WORD - 2-byte unsigned
- DWORD - 4-byte unsigned
- QWORD or UINT64 - 8-byte unsigned
- CHAR - 1-byte signed
- SHORT - 2-byte signed
- LONG - 4-byte signed
- LONGLONG or INT64- 8-byte signed
| Code: | -- FFFF0000 foo - 00 00 00 00 01 23 AB CD
assert(getAddressSafe'foo+(CHAR)[foo+4]' == 0xFFFF0001) |
This casting can also be applied to other kinds of values, including registers and user-defined symbols:
| Code: | -- RAX == 0x1BF50B0
assert(getAddressSafe'(WORD)RAX' == 0x50B0) |
| Code: | registerSymbol('bar',0x123FFFFFFFF)
assert(getAddressSafe'(LONG)bar' == -1)
unregisterSymbol'bar' |
Parenthesis can be used to get the address of another memory record via its description.
| Code: | | assert(AddressList['player health'].CurrentAddress == getAddressSafe'(player health)') |
Note that this shares the same syntax as when setting the value of a memory record but with different semantics. When setting a value, you can use `(another memory record)` to set it to the value of another memory record. In symbol lookup, `(memrec description)` gets the address of the memory record, not the value. In order to get the value of a memory record in symbol lookup, explicitly dereference the address:
| Code: | -- memory record is an unsigned 4-byte integer
assert(tonumber(AddressList['player index'].Value) == getAddressSafe'(DWORD)[(player index)]') |
This should be enough to do what you want.
If you want something more powerful, you can use Lua in symbol lookup. `$` can get a global variable:
| Code: | foo = 5
assert(getAddressSafe'$foo' == 5) | `$` can also execute arbitrary Lua code (kind of)
There's a symbol lookup callback defined in the file "autorun/luasymbols.lua" that will execute an arbitrary string as Lua code if it fails to be looked up any other way. This is why division works for you- it's actually Lua interpreting the string and returning the result.
This can be... weird at times and should probably never be used in a serious manner.
| Code: | assert(getAddressSafe'100 / 5' == 20)
assert(getAddressSafe'10 + 100 / 5' == 36) -- 0x10 + 20
registerSymbol('foo', 0x1000)
function bar()
return 4
end
assert(getAddressSafe'foo + bar()' == 0x1004)
assert(getAddressSafe'bar() + foo' == 0x1004)
assert(getAddressSafe'bar() / 2 + foo' == nil) -- fails ¯\_(ツ)_/¯
unregisterSymbol'foo' |
If you think you need to execute arbitrary Lua code in symbol lookup, just use pure Lua instead and don't rely on jank behaviour in the symbol handler.
Either work around the issue (e.g. manually set the address of a memory record in Lua) or make your own symbol lookup syntax that's not terrible (see registerSymbolLookupCallback in celua.txt)
_________________
I don't know where I'm going, but I'll figure it out when I get there. |
|
| Back to top |
|
 |
Aeolius How do I cheat?
Reputation: 0
Joined: 04 Aug 2024 Posts: 2
|
Posted: Sun Aug 04, 2024 10:26 pm Post subject: |
|
|
| panraven wrote: | | You may try Pointer Type Cast. |
You nailed it in one — (BYTE) did the trick exactly.
So the final formula looked roughly like this:
| Code: |
PROG.EXE+MAP_ARRAY_OFFSET+(BYTE)[X_val_addr]+(BYTE)[Y_val_addr]*60
|
And it perfectly replicated the output of that script.
Thanks a ton!
EDIT:
| ParkourPenguin wrote: | | The full story lies in the function TSymhandler.getAddressFromName in the file "Cheat Engine/symbolhandler.pas"... |
Spectacularly interesting stuff, thanks!
EDIT 2: I'm really liking that MR reference approach; it's loads more readable:
| Code: |
foo+(BYTE)[(Monster 17 Y)]*60+(BYTE)[(Monster 17 X)]
|
|
|
| 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
|
|