 |
Cheat Engine The Official Site of Cheat Engine
|
View previous topic :: View next topic |
Author |
Message |
ChronosMrk1 How do I cheat?
Reputation: 0
Joined: 23 Dec 2024 Posts: 5
|
Posted: Mon Dec 23, 2024 7:06 am Post subject: Creating a cheat table for mono Unity roguelike game. |
|
|
The game is a single player rogue-like and I can find the values I want pretty easily after starting a run but it's a pain to have to do it every time so I want to make a table. I tried to use pointer scanning to make a table first but I couldn't find the pointers to the variables and found out that you can't pointer scan for unity games. This is the first table I've ever tried to make I apologize for my noobiness and I'd appreciate it if you can help me out with this. I do know how to code (mostly python, SQL, and R).
What I've done so far:
I've used ILSpy to find the public class "CharacterScript" where the private float variable called "stamina" is located. I go to cheat engine attach it to the game and activate mono features. I use the mono dissector to find the variable in the class "stamina". (It has an offset of 170 but the offset is useless in this case because for mono unity games you can't use offset, I think?) I find instances of class while the game is open and before a run has started and I find three (there are three characters in the game). But because the run hasn't started all their stamina values are 0. When I start a run There are now 7 instances. I find the 3 instances that correspond to the 3 characters. I add the addresses of those three instances to my address list as well as their stamina addresses.
But at this point I don't know what do do next. I tried to see what accesses the instance addresses and I got a list when I checked to see what writes to this address I get nothing. For the stamina values when I check to see what writes to this address for the stamina values then I get the address to the CharacterScript:ChangeStamina function with the code: # movss [rsi+00000170],xmm5. When I start a new run new instances are created and I don't know where they're coming from, pointer-wise. Every time it runs it calls the InitializeCharacter function and sets the stamina at max. So I know I can just edit the code there so that the characters always start with 9999 stamina. Or if I removed the ChangeStamina function the characters stamina wouldn't change but I want to be able to set and freeze the values at will, so I need a way to add the variable to the table on every run and on every start up.
I'd really appreciate any help anyone would be willing to give me. Or maybe a some hints at least.
Last edited by ChronosMrk1 on Mon Dec 23, 2024 10:04 pm; edited 1 time in total |
|
Back to top |
|
 |
ChronosMrk1 How do I cheat?
Reputation: 0
Joined: 23 Dec 2024 Posts: 5
|
Posted: Mon Dec 23, 2024 10:02 pm Post subject: |
|
|
Anyone willing to give me some pointers on this problem?
|
|
Back to top |
|
 |
ParkourPenguin I post too much
Reputation: 152
Joined: 06 Jul 2014 Posts: 4697
|
Posted: Mon Dec 23, 2024 10:20 pm Post subject: |
|
|
ChronosMrk1 wrote: | I want to be able to set and freeze the values at will | Use code injection to copy the address to a registered symbol. After the game eventually runs that code injection, the registered symbol will then be a pointer to the stamina address. Search "injection copy" and you should find some tutorials.
You could use CE's mono features to avoid the potential problems with code injection, but there's a learning cliff to mono. See monoscript.lua in the autorun directory within the main CE directory.
_________________
I don't know where I'm going, but I'll figure it out when I get there. |
|
Back to top |
|
 |
ChronosMrk1 How do I cheat?
Reputation: 0
Joined: 23 Dec 2024 Posts: 5
|
Posted: Tue Dec 24, 2024 1:21 pm Post subject: |
|
|
ParkourPenguin wrote: | ChronosMrk1 wrote: | I want to be able to set and freeze the values at will | Use code injection to copy the address to a registered symbol. After the game eventually runs that code injection, the registered symbol will then be a pointer to the stamina address. Search "injection copy" and you should find some tutorials.
You could use CE's mono features to avoid the potential problems with code injection, but there's a learning cliff to mono. See monoscript.lua in the autorun directory within the main CE directory. |
But to do a code injection don't I need a static pointer first? From the template I have it needs a static pointer. And when I try to find out what accesses this address for the stamina value I get instructions that say they that it thinks that pointer is at the address of the CharacterScript class instance. But a new instance is made every run so that can't be the pointer address. By registered symbol you mean my globalalloc but I need an static addres to put
Sorry if the solution is obvious. I hope you can stick with me for a bit and help me out.
Description: |
stamina value what accesses for character 1 |
|
Filesize: |
50.08 KB |
Viewed: |
3415 Time(s) |

|
|
|
Back to top |
|
 |
ParkourPenguin I post too much
Reputation: 152
Joined: 06 Jul 2014 Posts: 4697
|
Posted: Tue Dec 24, 2024 1:53 pm Post subject: |
|
|
Use Template -> AOB Injection. No need for any static pointers.
Open a search engine and search for Cheat Engine injection copy
_________________
I don't know where I'm going, but I'll figure it out when I get there. |
|
Back to top |
|
 |
ChronosMrk1 How do I cheat?
Reputation: 0
Joined: 23 Dec 2024 Posts: 5
|
Posted: Tue Dec 24, 2024 9:36 pm Post subject: |
|
|
So I unfortunately the write instruction for each of the values for each of the characters are the same and will be the same for everyone of the stats since they're just instances of the same class. I made this crappy script to hopefully do a breakpoint after the write code executes then find which character it is. But honestly I've never coded in lua or assembly in my life so I barely know what I'm doing. Does it look right?
Code: |
[ENABLE]
alloc(newmem,$1000)
label(character1)
label(character2)
label(character3)
label(return)
registersymbol(character1)
registersymbol(character2)
registersymbol(character3)
newmem:
cmp [rsi+unique_offset], value_for_character1
je character1_handler
cmp [rsi+unique_offset], value_for_character2
je character2_handler
jmp character3_handler
character1_handler:
mov [character1], xmm5
jmp code
character2_handler:
mov [character2], xmm5
jmp code
character3_handler:
mov [character3], xmm5
jmp code
code:
movss [rsi+00000170],xmm5
jmp return
character1:
dd 0
character2:
dd 0
character3:
dd 0
255AF75F915:
jmp newmem
nop
return:
[DISABLE]
255AF75F915:
db F3 0F 11 AE 70 01 00 00
unregistersymbol(character1)
unregistersymbol(character2)
unregistersymbol(character3)
dealloc(newmem)
|
The syntax for assembly is really weird. It just feels wrong.
|
|
Back to top |
|
 |
ParkourPenguin I post too much
Reputation: 152
Joined: 06 Jul 2014 Posts: 4697
|
Posted: Tue Dec 24, 2024 10:49 pm Post subject: |
|
|
Most of that looks good, especially for someone new to assembly
ChronosMrk1 wrote: | Code: | cmp [rsi+unique_offset],value_for_character1 |
| The data type being accessed is ambiguous here. If it's a 1-byte or 2-byte value, you'd need to put `byte ptr` or `word ptr` respectively before `[`. By default, it's a 4-byte value (i.e. `cmp dword ptr [rsi+unique_offset],value_for_character1`). 8-byte values are weird since the immediate "value_for_character1" can't be more than 4 bytes in a `cmp` instruction- you'd have to put it in an 8-byte register and compare against that. Or do 2 4-byte value comparisons.
ChronosMrk1 wrote: | Code: | mov [character1], xmm5
... |
| You're trying to store the value and not the address of the value. Instead of xmm5, move `rsi`. (rsi is an 8 byte register, so the data access is inferred to be 8 bytes; no need for qword ptr)
The `jmp code` immediately before the `code:` label is superfluous
ChronosMrk1 wrote: | Code: | character1:
dd 0
character2:
dd 0
character3:
dd 0 |
| These should be `dq 0`. `dd` = declare doubleword = 4-byte values; `dq` = declare quadword = 8-byte values. Addresses are 8 bytes in a 64-bit process.
Don't use the code injection template. Use Template -> AOB Injection. Both templates perform a code injection. The AOB injection template searches for the code instead of assuming it's at a static address. This is necessary because there's no guarantee the code will be at the same address the next time the game is restarted.
Also, the memory records you add to the address list should have the pointer checkbox checked, base addresses are character1 / character2 / character3, and the only offset is the one used by the original code (i.e. 170)
_________________
I don't know where I'm going, but I'll figure it out when I get there. |
|
Back to top |
|
 |
ChronosMrk1 How do I cheat?
Reputation: 0
Joined: 23 Dec 2024 Posts: 5
|
Posted: Wed Dec 25, 2024 8:47 pm Post subject: |
|
|
I think I did it. It took a long while and definitely wasn't worth it. assembly can eat my ass how tf did that mad cunt make rollercoster tycoon with this garbage
Code: |
[ENABLE]
alloc(newmem1,2048)
alloc(newmem2,2048)
// Declare
alloc(Athena,8)
alloc(Chiyome,8)
alloc(Diana,8)
registersymbol(Athena)
registersymbol(Chiyome)
registersymbol(Diana)
// Persistent flags
alloc(AthenaSet,1)
alloc(ChiyomeSet,1)
alloc(DianaSet,1)
AthenaSet:
db 0
ChiyomeSet:
db 0
DianaSet:
db 0
// First injection point
define(inj1,GirlsScript:ChangeStamina+A5)
define(inj2,GirlsScript:ChangeStamina+1D0)
newmem1:
// Preserve registers
pushf
push rax
mov eax, [rsi+16C]
cmp eax, 1
jne skip1
// Check girlType
mov eax, [rsi+168]
cmp eax, 0 // Athena
jne check_chiyome1
cmp byte ptr [AthenaSet], 1
je skip1 // Skip if Athena is already set
mov [Athena], rsi
mov byte ptr [AthenaSet], 1
jmp skip1
check_chiyome1:
cmp eax, 1
jne check_diana1
cmp byte ptr [ChiyomeSet], 1
je skip1
mov [Chiyome], rsi
mov byte ptr [ChiyomeSet], 1
jmp skip1
check_diana1:
cmp eax, 2
jne skip1
cmp byte ptr [DianaSet], 1
je skip1
mov [Diana], rsi
mov byte ptr [DianaSet], 1
skip1:
pop rax
popf
movss [rsi+00000170], xmm5
jmp return1
inj1:
jmp newmem1
nop
nop
nop
return1:
newmem2:
pushf
push rax
mov eax, [rsi+16C]
cmp eax, 1
jne skip2
// Debugging: Check RSI and step values
// Check girlType and only update if the symbol is unset ffs
mov eax, [rsi+168]
cmp eax, 0 // Athena
jne check_chiyome2
cmp byte ptr [AthenaSet], 1
je skip2 // Skip if Athena is already set
mov [Athena], rsi
mov byte ptr [AthenaSet], 1
jmp skip2
check_chiyome2:
cmp eax, 1 // Chiyome
jne check_diana2
cmp byte ptr [ChiyomeSet], 1
je skip2 // Skip if Chiyome is already set
mov [Chiyome], rsi
mov byte ptr [ChiyomeSet], 1
jmp skip2
check_diana2:
cmp eax, 3 // Diana
jne skip2
cmp byte ptr [DianaSet], 1
je skip2 // Skip if Diana is already set
mov [Diana], rsi
mov byte ptr [DianaSet], 1
skip2:
pop rax
popf
// Execute originol code
movss [rsi+00000170], xmm5
jmp return2
inj2:
jmp newmem2
nop
nop
nop
return2:
[DISABLE]
// Restore original code
inj1:
db F3 0F 11 AE 70 01 00 00
inj2:
db F3 0F 11 AE 70 01 00 00
unregistersymbol(Athena)
unregistersymbol(Chiyome)
unregistersymbol(Diana)
dealloc(newmem1)
dealloc(newmem2)
dealloc(Athena)
dealloc(Chiyome)
dealloc(Diana)
dealloc(AthenaSet)
dealloc(ChiyomeSet)
dealloc(DianaSet)
|
|
|
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
|
|