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 


How to reference changing address from AOBScan and wildcards

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

Joined: 02 Mar 2025
Posts: 2

PostPosted: Sun Mar 02, 2025 5:52 am    Post subject: How to reference changing address from AOBScan and wildcards Reply with quote

Hey all,
I'm relatively new to working with AA but I have some experience with CE.
In my latest project, I tried to create some cheats for the Journey of the Prairie King minigame within Stardew Valley.
Some problems arose, when I wanted to instantly pass a timer based level.
I quickly found a set of instructions that seemed promising, namely
Code:
44 2B F0                            - sub r14d,eax
44 89 35 20 42 1A FE          - mov [7FF84F28717C],r14d

If i could write 0 to [7FF84F28717C], the level would instantly pass as there are no enemies spawned and the timer is 0.
However, the specific address of 7FF84F28717C kept changing with each start of Stardew Valley.
So i adapted the AOBScan to use wildcards and used the signature
Code:
44 2B F0 44 89 35 ** ** ** FE

This worked fine, but it created two new problems.
The first was, that I had to restore the original bytes when deactivating.
I searched this forum and I found a solution in here: /viewtopic.php?p=5510717 (I apparently cannot post URLs yet, so this is a workaround, it's a thread in this forum)
using readmem().
The second problem however was that I somehow had to reference the address in order to write 0 to it.
Just like Dark Byte wrote in the thread, I saved the original Bytes at a new location called 'originalbytes' and I thought I could just change the second instruction to
Code:

mov [originalbytes+6],0

however, when I checked the instruction in CE, the address that was inserted there was a completely different one from what I expected.
Now to my question(s): Can someone explain to me why this was the case? I assume that the address itself was somewhat relative to the area the instruction was made at, but again i'm not sure. And second, how can I reference the correct address here?

In this specific case, I was able to come up with a much more elegant solution, by not jumping at the injectionpoint but rather replacing the
Code:
44 2B F0 - sub r14d,eax
with
45 33 F6 - xor r14d,r14d

in order to have r14d be 0 and so I didn't need to know the address at all, but I'd still like to know how I can handle such a situation when it comes up again Smile
Thanks for any help in advance, this forum has already been a great source of information.
Back to top
View user's profile Send private message
Csimbi
I post too much
Reputation: 97

Joined: 14 Jul 2007
Posts: 3320

PostPosted: Sun Mar 02, 2025 1:23 pm    Post subject: Reply with quote

Anything that might change should be replaced with wildcards in the AOB scan - this you did.
Next step is to store original bytes (before replacing them) somewhere - use readmem(...) for this.
Then, to execute the original code, use reassemble().
Lastly, in the disable section restore the original bytes from the location you stored using readmem(...) again.

Replacing instructions works, but it is less flexible. If you can, hook the code before or after these instructions; that way, you avoid the problem as a whole.
Back to top
View user's profile Send private message
ParkourPenguin
I post too much
Reputation: 152

Joined: 06 Jul 2014
Posts: 4695

PostPosted: Sun Mar 02, 2025 1:57 pm    Post subject: Reply with quote

zetsubo wrote:
So i adapted the AOBScan to use wildcards and used the signature
Code:
44 2B F0 44 89 35 ** ** ** FE
Why not replace the last address byte with a wildcard?
zetsubo wrote:
however, when I checked the instruction in CE, the address that was inserted there was a completely different one from what I expected.
Now to my question(s): Can someone explain to me why this was the case? I assume that the address itself was somewhat relative to the area the instruction was made at...
Yes. It's called RIP-relative addressing. The register RIP is the instruction pointer: it contains the address of the next instruction to execute. The actual address accessed is calculated using the value of RIP and the offset specified by the machine code (bytes of instruction).
e.g.:
Code:
00007FF7510E2F55  -  44 89 35 20 42 1A FE  -  mov [7FF84F28717C],r14d
address of instruction + size of instruction + offset = accessed address
0x7FF7510E2F55 + 7 + 0xFE1A4220 = 0x7FF84F28717C

As you've discovered, the easiest way in this case is to simply change the original code. There's no need for you to allocate memory for a code injection if you can do everything you want near the injection point itself.

If that wasn't available and you needed to write a code injection, `reassemble` is the next easiest method:
Code:
aobscan(INJECT,44 2B F0 44 89 35 ** ** ** FE)
...

newmem:
  xor r14d,r14d

  // mov [address],r14d
  reassemble(INJECT+3)

  jmp return
  ...

If that isn't enough, you can use some math expressions in AA:
Code:
aobscan(INJECT,44 2B F0 44 89 35 ** ** ** FE)
...

label(specialAddress)

INJECT+7+(LONG)[INJECT+6]:
specialAddress:

...

newmem:
  mov [specialAddress],0
  ...
The token `(LONG)` before square brackets indicates CE should read a signed 32-byte integer at this address. The default is a pointer value (64-bit unsigned int in x64).

If that doesn't work out for whatever reason, use a {$lua} block. You'll need to do the aobscan in Lua, however, as {$lua} blocks are basically preprocessor functions and run before just about anything else. Good news is you can scan through only executable memory and save some time.
Code:
[ENABLE]
{$lua}
local function def_values(inject, specialAddress)
  return ([[
define(INJECT,%X)
define(specialAddress,%X)
]]):format(inject, specialAddress)
end

if syntaxcheck then
  return def_values(0,0)
end

local inject = AOBScanUnique('44 2B F0 44 89 35 ** ** ** FE', '+X-C')

local offset = readInteger(inject+6, true)  -- true -> signed value

return def_values(inject, inject+7+offset)
{$asm}

alloc(newmem,2048,INJECT)
...

newmem:
  mov [specialAddress],0
  ...


Edit: some offsets were ambiguous or incorrect. Don't write code at 3am

_________________
I don't know where I'm going, but I'll figure it out when I get there.


Last edited by ParkourPenguin on Tue Mar 04, 2025 1:47 am; edited 1 time in total
Back to top
View user's profile Send private message
zetsubo
How do I cheat?
Reputation: 0

Joined: 02 Mar 2025
Posts: 2

PostPosted: Mon Mar 03, 2025 5:02 am    Post subject: Reply with quote

Csimbi wrote:

Replacing instructions works, but it is less flexible. If you can, hook the code before or after these instructions; that way, you avoid the problem as a whole.

usually that is exactly my approach Smile However, in this case there was a Call instruction right before my mentioned instructions and after those there was a comparison. I never replaced a call, so i wasn't sure what was going to happen if i did, and i couldn't replace the comparison afterwards, because I couldn't think of a way to jump back into the original spot after my custom code executed while still preserving the logic for what happens after the comparison.

ParkourPenguin wrote:
Why not replace the last address byte with a wildcard?

I did about 50 tests and the last byte always stayed the same Smile So i didn't wildcard it so the signature would be a bit more rigid and would not yield false positives Smile

ParkourPenguin wrote:

Yes. It's called RIP-relative addressing. The register RIP is the instruction pointer: it contains the address of the next instruction to execute. The actual address accessed is calculated using the value of RIP and the offset specified by the machine code (bytes of instruction).
e.g.:
Code:
00007FF7510E2F55  -  44 89 35 20 42 1A FE  -  mov [7FF84F28717C],r14d
address of instruction + size of instruction + offset = accessed address
0x7FF7510E2F55 + 7 + 0xFE1A4220 = 0x7FF84F28717C

As you've discovered, the easiest way in this case is to simply change the original code. There's no need for you to allocate memory for a code injection if you can do everything you want near the injection point itself.

If that wasn't available and you needed to write a code injection, `reassemble` is the next easiest method:
Code:
aobscan(INJECT,44 2B F0 44 89 35 ** ** ** FE)
...

newmem:
  xor r14d,r14d

  // mov [address],r14d
  reassemble(INJECT+3)

  jmp return
  ...


That is super helpful to know, thanks for explaining it so clearly Smile I found some threads in this forum where people said to use reassemble() but I just couldn't figure out the correct way to do so. As for the LUA-Code, I actually got a working solution with LUA, but I just thought that the lua code wasn't elegant enough and I wanted to see if i could get the same result just using AA. But it's super helpful to confirm that the lua code is indeed executed before everything else, that threw me off on my first attempts.
Thanks you two for taking the time to help me out here, glad to see this forum is still active after such a long time Very Happy
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    Cheat Engine Forum Index -> General Gamehacking 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