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 


structure compare across game reboots

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

Joined: 24 Sep 2023
Posts: 9

PostPosted: Mon Oct 13, 2025 7:58 pm    Post subject: structure compare across game reboots Reply with quote

hi!

So I was battling with shared instructions on my cheat and wanted to know if there was a better way.

So I have my instruction I want to change:
- i change it, of course the game crashes because shared instructions
- I do "find out whate adresses this instruction accesses". I have 530 results (that's a lot)
- I choose the correct adress in group one and 3 or 5 others as group 2, and do a "scan for commonalities"
- Of course they don't have obvious commonalities, so I do a structure compare on a register I know is used in the instruction. (ex: RDI in mov [rdi+04],rax )
- I don't really see much obvious commonalities between the 2 groups, but I find what appears to be static values in my "correct" RDI offsets, that are different than what can be found in my "invalid" group.
- so if I compare like 3 of theses RDI offsets values with these "magic static numbers" that should be enough (like RDI +4 == 42 && RDI + 12 == 1337 && ...)
- problem: when I relaunch the game I notice that these values were in fact not static. Well, some of them are, but not the majority.

So what do?
I'd like to compare the same structures across game relaunches.
Of course adresses will be different, but MaxLevel/StructSize will be the same.

As when you save Structure Compare scans results in .sptr , it also saves the results in .sptr.results.x , I was a bit sad that in the Structure Compare window, when you open a .sptr it only loads the addresses, and doesn't even keep the ones from the window's previous scan.
Maybe I'm missing something, but can't I just load the results of a previous scan from last game boot, and compare them with the live result of the new scan with the game running?

For now my solution has been:
- going to the correct RDI address in the memory viewer
- copying everything from RDI-64 to RDI+512 (or more),
- putting the result in a line of a text file
- rebooting the game, refinding the correct RDI and repeating steps
- create a python script comparing the 2 lines of text and finding common values, printing their offset and value.
- repeat this a 3rd time if necessary

and NO, i did not just take screenshots of the memory view to then try to align them in layers with Paint.NET, that would be so hacky and time wasteful Rolling Eyes

So is there an easier/smarter way?

Thanks again in advance for your help, I always got deep and insightful answers here ( but no pressure Very Happy )
Back to top
View user's profile Send private message
ParkourPenguin
I post too much
Reputation: 152

Joined: 06 Jul 2014
Posts: 4704

PostPosted: Tue Oct 14, 2025 12:12 pm    Post subject: Reply with quote

tuxlu wrote:
I'd like to compare the same structures across game relaunches.
That would be more difficult than you describe in your post. Pointers in the structure can point to other memory that can be used in the comparison. In general, you'd need a dump of the entire working memory of the process.

I'd try to work around the problem. Injecting somewhere else is often better, such as a caller. e.g. say you want to make the player invincible, and the callstack when damaging stuff looks like this:
Code:
player taking damage:
update -> damagePlayer -> subtractHealth

enemy taking damage:
update -> damageEnemy -> subtractHealth
Injecting code in the `subtractHealth` function is annoying since both the player and enemies use that function. It's better to replace the call to the `subtractHealth` function in the `damagePlayer` function with NOPs, or do an early return in the damagePlayer function.

Another alternative is to find or make a pointer to the player instance that you can use to compare against RDI directly. To find one, use the pointer scanner; to make one, do a code injection at a better injection point (search "injection copy"). You don't need to do a code injection that accesses the same address- get any address in the same struct, or even any address in a different struct that has a pointer to the relevant struct (e.g. a `gameState` class might have a pointer to a `playerData` instance).

_________________
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
tuxlu
How do I cheat?
Reputation: 0

Joined: 24 Sep 2023
Posts: 9

PostPosted: Sun Oct 19, 2025 10:10 am    Post subject: Reply with quote

thanks for the fast answer (and sorry for my slow one)

so, if I understand well, you advise me to either:

- inject where the data is read instead of written? Like, if it's my player health, find when the game over instruction is called when health is 0, to disable it, instead of changing my health directly?

- do an injection at an instruction modifying a value in my player instance, but that is an"easier" injection point I know is consistent and not accessed by other shared instructions. with this I find the player instance address and check "if (rdi == playerAddr) ..."
That's interesting, but I really don't know if I could find this kind of "better injection point"

- put my debugger on the instruction I patched , after my checks that rdi is the correct one, and then "step out" 2 times, to find where my call to "subtractHealth" is done, and replace it.
Problem in my case: the method substracting my health seems to do quite a lot more than that, and if I try to nop it 1 level above, I just can't move my character.


I litterally coded the game I try to hack, and I know the instruction I try to alter isn't *supposed* to be shared. but it's in Godot, so it's an interpreted language and i fear it does funky stuff.

I have only 1 result instruction in "find out what writes to this address" of my player's health, but by using "find out what adresses this instruction accesses", I have 530 results.

So it's not a simple "class code shared between the player and the enemy" it's "the engine does funky stuff to set all values of all classes in one method" problem (that's my guess at least)

for now my solution seem to work, because I guess my "rdi" points to the user class , with "[rdi+04]" being the player's health.
So I try to find unique and consistent values around this memory region, and it works well, but some are not persistent across reboots.
But in the end, my solution worked, as I found that even across game reboots,


Code:
if ([RDI +8] == 42 && [RDI +32] == FF && [RDI +40] == FF) {
isMyPlayerHealthInstruction = true
}

(pseudocode, obviously)
[/code]

sorry, maybe I misunderstood your answer, but I'm feeling my solution is easier to achieve than what you are proposing , in my case.
Back to top
View user's profile Send private message
ParkourPenguin
I post too much
Reputation: 152

Joined: 06 Jul 2014
Posts: 4704

PostPosted: Sun Oct 19, 2025 12:26 pm    Post subject: Reply with quote

tuxlu wrote:
- inject where the data is read instead of written? Like, if it's my player health, find when the game over instruction is called when health is 0, to disable it, instead of changing my health directly?
No. Simply disabling that would have indeterminate effects on the game when your health reaches 0. Maybe the game crashes or softlocks.

tuxlu wrote:
- do an injection at an instruction modifying a value in my player instance, but that is an"easier" injection point I know is consistent and not accessed by other shared instructions. with this I find the player instance address and check "if (rdi == playerAddr) ..."
Yes.

tuxlu wrote:
- put my debugger on the instruction I patched , after my checks that rdi is the correct one, and then "step out" 2 times, to find where my call to "subtractHealth" is done, and replace it.
Problem in my case: the method substracting my health seems to do quite a lot more than that, and if I try to nop it 1 level above, I just can't move my character.
There could be several functions between the one that subtracts your health and the one that handles damage to the player.
Code:
player taking damage:
update -> irrelevant_functions... -> damagePlayer -> irrelevant_functions... -> subtractHealth

enemy taking damage:
update -> irrelevant_functions... -> damageEnemy -> irrelevant_functions... -> subtractHealth

Make two traces (e.g. right click instruction, break-and-trace, "step over instead of single step"): one of your player taking damage and one of an enemy taking damage. Look around where the callstacks converge or diverge.

This isn't relevant in your case, because:
tuxlu wrote:
I litterally coded the game I try to hack, and I know the instruction I try to alter isn't *supposed* to be shared. but it's in Godot, so it's an interpreted language and i fear it does funky stuff.
You shouldn't be messing with assembly when dealing with interpreted languages. You should instead target the intermediate bytecode format being interpreted. CE doesn't have any relevant features I'm aware of (except for general AOB scanning and replacing), but I'm sure there are reverse engineering tools out there capable of decompiling Godot games. e.g. gdsdecomp
_________________
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
tuxlu
How do I cheat?
Reputation: 0

Joined: 24 Sep 2023
Posts: 9

PostPosted: Sun Oct 19, 2025 12:53 pm    Post subject: Reply with quote

oh ok, thanks for your insightful reply.

So in my case what I did seemed to be the best solution,
so just as a feature request if it is revelant for other people: being able to load an old "Structure Compare scans result" and compare it with new results would help in my specific case.
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 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