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 write scripts that work with multiple game versions

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

Joined: 15 Mar 2013
Posts: 6

PostPosted: Wed May 01, 2013 2:46 pm    Post subject: How to write scripts that work with multiple game versions Reply with quote

Normally, when a new patch is released, you have to update your AA scripts even if you use aobscan to find your hacking points. There is, however, a way to generalize your scripts so they will still work for new versions as long as the code at your hacking points remains the same (if only addresses and offsets change).

The cleanest way to do this is to have one (or more) initialization script that everything (other scripts, memory values) depends on. The init script could look something like this:

Code:

[enable]
// Find hacking point 1
aobscan(aob_hp1,AB CD EF 01 02 03 04 05 06 07 08 09 0A)
globalalloc(org_hp1,6)
label(r_hp1)
registersymbol(r_hp1)

// Find hacking point 2
// Always use ?? for things representing addresses or fixed offsets
aobscan(aob_hp2,C7 40 ?? 00 00 00 00 FF FE ?? ?? ?? ?? F3 F4)
globalalloc(org_hp2,8)
label(r_hp2)
registersymbol(r_hp2)

aob_hp1+2:
r_hp1:

aob_hp2:
r_hp2:

// Use createthread to make a backup of the original game code
// You can't use readmem for this
alloc(backupCode,128)
createthread(backupCode)

backupCode:
cld

mov esi,r_hp1
mov edi,org_hp1
mov ecx,#6
rep movsb // copy 6 bytes from esi to edi

mov esi,r_hp2
mov edi,org_hp2
mov ecx,#8
rep movsb

ret

[disable]
dealloc(org_hp1)
unregistersymbol(org_hp1)
dealloc(org_hp2)
unregistersymbol(org_hp2)
unregistersymbol(r_hp1)
unregistersymbol(r_hp2)

Having an init script like this is mostly necessary because readmem() can't be used otherwise in dependent scripts.

A dependent script that actually does something could look like this:
Code:

// Note: The init script must be active to be able to save this dependent script to your table.
[enable]
alloc(myCode1, 256)
label(ret_myCode1)
label(myCode2)
label(ret_myCode2)


r_hp1:
jmp myCode1
nop
ret_myCode1:

r_hp2:
jmp myCode2
nop
nop
nop
ret_myCode2:

myCode1:
// do whatever we like
xor eax,eax
xor ebx,ebx

// use the original code from our backup:
readmem(org_hp1,6)
jmp ret_myCode1

myCode2:
// Let's assume this is the original code:
// test.exe+2898C7 - C7 40 0C 00000000     - mov [eax+0C],00000000
// and we want to change it to this instead:
// test.exe+2898C7 - C7 40 0C 01000000     - mov [eax+0C],00000001
// We could do it like this:
readmem(org_hp2,3) // the offset +0C can change in a new game version, but no problem with readmem!
dd 1

{ // Alternatively, we could do this:
push eax
xor eax,eax // eax=0
mov al,byte ptr [org_hp2+2]
add eax,[esp]
mov [eax],1
pop eax
} //------------------------

readmem(org_hp2+7,1) // copy the last missing byte
// Note: The last NOP at the HP and this readmem are redundant and only here for demonstration purposes

jmp ret_myCode2

[disable]
r_hp1:
readmem(org_hp1,6)
r_hp2:
readmem(org_hp2,8)

dealloc(myCode1)

One thing to watch out for is if the code you overwrite with your injection contains a jump short instruction. When it's something like "je +02" (Bytes: 74 02), that's of course short enough to still point inside your code. If it's too long, then you'll have to split your readmem() code and insert a long jump instead (e.g. something like "je r_hp1+2C").

That's it, basically. If you are careful with absolute addresses, offsets and short jumps, your scripts have a good chance to survive game updates. It's still a good idea to save the original assembly code at the hacking points somewhere, so you can quickly check if the update breaks anything.
Back to top
View user's profile Send private message
Doctor Death
Cheater
Reputation: 1

Joined: 26 Apr 2014
Posts: 42
Location: Breaking Code

PostPosted: Thu Feb 19, 2015 5:35 pm    Post subject: Re: How to write scripts that work with multiple game versio Reply with quote

tuxutat wrote:
Normally, when a new patch is released, you have to update your AA scripts even if you use aobscan to find your hacking points. There is, however, a way to generalize your scripts so they will still work for new versions as long as the code at your hacking points remains the same (if only addresses and offsets change).

The cleanest way to do this is to have one (or more) initialization script that everything (other scripts, memory values) depends on. The init script could look something like this:

Code:

[enable]
// Find hacking point 1
aobscan(aob_hp1,AB CD EF 01 02 03 04 05 06 07 08 09 0A)
globalalloc(org_hp1,6)
label(r_hp1)
registersymbol(r_hp1)

// Find hacking point 2
// Always use ?? for things representing addresses or fixed offsets
aobscan(aob_hp2,C7 40 ?? 00 00 00 00 FF FE ?? ?? ?? ?? F3 F4)
globalalloc(org_hp2,8)
label(r_hp2)
registersymbol(r_hp2)

aob_hp1+2:
r_hp1:

aob_hp2:
r_hp2:

// Use createthread to make a backup of the original game code
// You can't use readmem for this
alloc(backupCode,128)
createthread(backupCode)

backupCode:
cld

mov esi,r_hp1
mov edi,org_hp1
mov ecx,#6
rep movsb // copy 6 bytes from esi to edi

mov esi,r_hp2
mov edi,org_hp2
mov ecx,#8
rep movsb

ret

[disable]
dealloc(org_hp1)
unregistersymbol(org_hp1)
dealloc(org_hp2)
unregistersymbol(org_hp2)
unregistersymbol(r_hp1)
unregistersymbol(r_hp2)

Having an init script like this is mostly necessary because readmem() can't be used otherwise in dependent scripts.

A dependent script that actually does something could look like this:
Code:

// Note: The init script must be active to be able to save this dependent script to your table.
[enable]
alloc(myCode1, 256)
label(ret_myCode1)
label(myCode2)
label(ret_myCode2)


r_hp1:
jmp myCode1
nop
ret_myCode1:

r_hp2:
jmp myCode2
nop
nop
nop
ret_myCode2:

myCode1:
// do whatever we like
xor eax,eax
xor ebx,ebx

// use the original code from our backup:
readmem(org_hp1,6)
jmp ret_myCode1

myCode2:
// Let's assume this is the original code:
// test.exe+2898C7 - C7 40 0C 00000000     - mov [eax+0C],00000000
// and we want to change it to this instead:
// test.exe+2898C7 - C7 40 0C 01000000     - mov [eax+0C],00000001
// We could do it like this:
readmem(org_hp2,3) // the offset +0C can change in a new game version, but no problem with readmem!
dd 1

{ // Alternatively, we could do this:
push eax
xor eax,eax // eax=0
mov al,byte ptr [org_hp2+2]
add eax,[esp]
mov [eax],1
pop eax
} //------------------------

readmem(org_hp2+7,1) // copy the last missing byte
// Note: The last NOP at the HP and this readmem are redundant and only here for demonstration purposes

jmp ret_myCode2

[disable]
r_hp1:
readmem(org_hp1,6)
r_hp2:
readmem(org_hp2,8)

dealloc(myCode1)

One thing to watch out for is if the code you overwrite with your injection contains a jump short instruction. When it's something like "je +02" (Bytes: 74 02), that's of course short enough to still point inside your code. If it's too long, then you'll have to split your readmem() code and insert a long jump instead (e.g. something like "je r_hp1+2C").

That's it, basically. If you are careful with absolute addresses, offsets and short jumps, your scripts have a good chance to survive game updates. It's still a good idea to save the original assembly code at the hacking points somewhere, so you can quickly check if the update breaks anything.


I'm seeing a lot of new functions that I've never seen before...

What is "globalalloc"?

What is going on here:

Code:

aob_hp1+2:
r_hp1:

aob_hp2:
r_hp2:


And finally, what is "readmem"? I've never heard of that before.
Back to top
View user's profile Send private message
Zanzer
I post too much
Reputation: 119

Joined: 09 Jun 2013
Posts: 3231

PostPosted: Sun Mar 15, 2015 11:48 pm    Post subject: Reply with quote

During script execution, READMEM directly copies the bytes at the specified location into the script.

tuxutat, you can use READMEM within the same script that contains the AOB.
You just need to make sure you use it before the code which changes the found AOB.

Code:
[ENABLE]
aobscan(aob_hp1,AB CD EF 01 02 03 04 05 06 07 08 09 0A)
alloc(myCode1,256)
alloc(org_hp1,6)
registersymbol(aob_hp1)
registersymbol(org_hp1)

label(ret_myCode1)

org_hp1:
readmem(aob_hp1,6)

myCode1:
// blah blah blah
jmp ret_myCode1

aob_hp1:
jmp myCode1
nop
ret_myCode1:

[DISABLE]
aob_hp1:
readmem(org_hp1,6)

unregistersymbol(aob_hp1)
unregistersymbol(org_hp1)
dealloc(myCode1)
dealloc(org_hp1)
Back to top
View user's profile Send private message
TheByteSize
Advanced Cheater
Reputation: 0

Joined: 06 Aug 2015
Posts: 62

PostPosted: Fri Sep 04, 2015 5:41 pm    Post subject: Reply with quote

Zanzer wrote:
During script execution, READMEM directly copies the bytes at the specified location into the script.

tuxutat, you can use READMEM within the same script that contains the AOB.
You just need to make sure you use it before the code which changes the found AOB.



I just want to revive this old post for a quick question. I'm still new and I would like a clarification.

OP said we need to do a backup but Zanzer said READMEM can be use without backup. So, when do we need to backup?
Back to top
View user's profile Send private message
Zanzer
I post too much
Reputation: 119

Joined: 09 Jun 2013
Posts: 3231

PostPosted: Fri Sep 04, 2015 6:01 pm    Post subject: Reply with quote

I simply mentioned that the backup can occur within the same script.
You just need to use READMEM before the code which overwrites the original bytes.
To try to keep things simple, I'll start with a default AOB Injection template.
I added comments to denote any additions.

Code:
[ENABLE]
aobscanmodule(INJECT,calc.exe,48 89 74 24 08 48 89 7C 24 10 41)
alloc(newmem,$1000,"calc.exe"+1B9D0)

label(code)
label(return)
label(backup) // add a label where you will store a backup

newmem:

code:
  mov [rsp+08],rsi
  jmp return

backup: // define the label >BEFORE< the overwrite at the 'INJECT' label
  readmem(INJECT,5) // backup the 5 bytes that the code overwrites
// also note that this code is below the final 'jmp return' and will never execute

INJECT:
  jmp code
return:
registersymbol(INJECT)
registersymbol(backup) // register the backup label so we can find it on disable

[DISABLE]
INJECT:
  //db 48 89 74 24 08 // commented out the bytes to use our backup instead
  readmem(backup,5) // read the 5 bytes we backed up
unregistersymbol(INJECT)
unregistersymbol(backup) // unregister our backup
dealloc(newmem)
Back to top
View user's profile Send private message
TheByteSize
Advanced Cheater
Reputation: 0

Joined: 06 Aug 2015
Posts: 62

PostPosted: Fri Sep 04, 2015 6:11 pm    Post subject: Reply with quote

What purpose do these lines serve?
Is it for the backup code into Global instead of Local Allocation?

Code:
alloc(backupCode,128)
createthread(backupCode)

backupCode:
cld
Back to top
View user's profile Send private message
Zanzer
I post too much
Reputation: 119

Joined: 09 Jun 2013
Posts: 3231

PostPosted: Fri Sep 04, 2015 6:22 pm    Post subject: Reply with quote

In essence, that code is just replicating the READMEM function.
I do not believe it is necessary or makes the backup easier to write or understand.

ALLOC creates a new block of memory
CREATETHREAD executes that block of memory one time
All of the code below 'backupCode' up to the RET performs a similar function as READMEM.
Back to top
View user's profile Send private message
TheByteSize
Advanced Cheater
Reputation: 0

Joined: 06 Aug 2015
Posts: 62

PostPosted: Fri Sep 04, 2015 6:57 pm    Post subject: Reply with quote

Thank your for your clarifications. Time for me to generalize my FF Type Zero code.
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 Tutorials -> Auto Assembler tutorials 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