| 
			
				|  | Cheat Engine The Official Site of Cheat Engine
 
 
 |  
 
	
		| View previous topic :: View next topic |  
		| Author | Message |  
		| tuxutat How do I cheat?
 
 ![]() Reputation: 0 
 Joined: 15 Mar 2013
 Posts: 6
 
 
 | 
			
				|  Posted: Wed May 01, 2013 2:46 pm    Post subject: How to write scripts that work with multiple game versions |   |  
				| 
 |  
				| 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 |  |  
		|  |  
		| Doctor Death Cheater
 
 ![]() Reputation: 1 
 Joined: 26 Apr 2014
 Posts: 42
 Location: Breaking Code
 
 | 
			
				|  Posted: Thu Feb 19, 2015 5:35 pm    Post subject: Re: How to write scripts that work with multiple game versio |   |  
				| 
 |  
				|  	  | 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 |  |  
		|  |  
		| Zanzer I post too much
 
 ![]() Reputation: 126 
 Joined: 09 Jun 2013
 Posts: 3278
 
 
 | 
			
				|  Posted: Sun Mar 15, 2015 11:48 pm    Post subject: |   |  
				| 
 |  
				| 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 |  |  
		|  |  
		| TheByteSize Advanced Cheater
 
 ![]() Reputation: 0 
 Joined: 06 Aug 2015
 Posts: 62
 
 
 | 
			
				|  Posted: Fri Sep 04, 2015 5:41 pm    Post subject: |   |  
				| 
 |  
				|  	  | 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 |  |  
		|  |  
		| Zanzer I post too much
 
 ![]() Reputation: 126 
 Joined: 09 Jun 2013
 Posts: 3278
 
 
 | 
			
				|  Posted: Fri Sep 04, 2015 6:01 pm    Post subject: |   |  
				| 
 |  
				| 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 |  |  
		|  |  
		| TheByteSize Advanced Cheater
 
 ![]() Reputation: 0 
 Joined: 06 Aug 2015
 Posts: 62
 
 
 | 
			
				|  Posted: Fri Sep 04, 2015 6:11 pm    Post subject: |   |  
				| 
 |  
				| 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 |  |  
		|  |  
		| Zanzer I post too much
 
 ![]() Reputation: 126 
 Joined: 09 Jun 2013
 Posts: 3278
 
 
 | 
			
				|  Posted: Fri Sep 04, 2015 6:22 pm    Post subject: |   |  
				| 
 |  
				| 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 |  |  
		|  |  
		| TheByteSize Advanced Cheater
 
 ![]() Reputation: 0 
 Joined: 06 Aug 2015
 Posts: 62
 
 
 | 
			
				|  Posted: Fri Sep 04, 2015 6:57 pm    Post subject: |   |  
				| 
 |  
				| Thank your for your clarifications.  Time for me to generalize my FF Type Zero code. |  |  
		| 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
 
 |  |