Boring prologue
In this tutorial I will walk you through the complete process of making an infinite ammo cheat. In my experience the easiest so far.
Starting out with Cheat Engine is –super- easy. Find the instructions here: viewtopic.php?f=2&t=831
Buying the game you want to ‘crack’ is also, very, easy.
I’m going to do this in chunks, so bare with me.
With your game on the left and your Cheat Engine on the right, the first thing you want to do is stare at the screen and identify the value for ammo.
If you look, you will see the number 25 three times. So, the first thing we’ll do is look for this number in the memory. I do think this is a 4-byte value as it is fairly standard. But you can go through each of the types if the value doesn’t show.
Input the value 25 and press ‘First scan’. The first scan is always your base-line. From this point onwards, until you start a new scan, any search for values will be done in the list of addresses in the previous scan; trimming things you exclude.
We shoot once more, and look for 24 by changing the number and pressing the next scan button. Surprise surprise, 4 addresses remain. Earlier I pointed out that there was 3x25 in the image.
1) The label on the gun;
2) The label in the HUD;
3) The actual value from which these labels are derived.
(Unless you are a smartypants and think the bullet indicating 25-dots view is a number.....)
The next step is the add all 3 addresses to the address list by double-clicking. The addresses are now stored in the bottom of your Cheat Engine.
Now try and change the value one address at a time. You will notice that changing two of the values will not have any effect, and are set back to the value they were once a frame of the game has loaded in. That must be the labels. Changing the correct one will also change the other 2. That one is the one you want.
Well done. Freeze the value by checking the box in front of it and you have a temporary infinite ammo cheat.
If you’re reading this you are probably not satisfied that you have to do this every single time you want to Strafe through STRAFE at record pace. You want to make a script that does this for you, and sets the value accordingly.
We start doing this by right-clicking on the correct value and choosing “Find out what writes to this address”. Why write? The number is changing! You can’t change anything by reading, can you?
After firing a couple of rounds and reloading once you can see that 2 instructions have written to this address. Obviously we want the first one as we want to update the value when shooting, not when reloading. Select the top address and choose ‘Dissassemble this memory region’.
CE will now center and highlight the instruction that wrote to the address.
I won’t go into too much detail here, but on my first time through, no-one told me that [something] points to an actual adress. (in this case, the address where the ammo count resides). [rax+30] therefore means that something is stored in ‘Street, in the house with number 30’.
This can literally be translated into MOVE [HERE], SOMETHING (opcode destination,source)
Want to find out if my theory is correct? Let’s double click the line so we can edit it. We already know the value is a 4-byte value, so instead of eax, lets put in a raw value. 00 00 00 FF = 255
Well, now you definatly know this is the place to be. Shoot as much as you’d like, you will see it stays put at 255. (in address list and in game!)
Now, right-click and press “restore original code” (if it doesn’t work, restart ) and look at the top
for the tools menu -> Auto assemble -> Template -> AOB injection.
Say yes to the (ad)dress and CHANGE the name of INJECT. I called it ‘INF_AMMO’
The auto assembler will now be filled with a template. Believe me you are half-way.
For now all you need to know is that everything under [ENABLE] is injected into memory. At the location of the address where we edited the instruction previously. Cheat Engine makes a jump for us to a new location where we can do our own thing. You can see the original code under “code:”, right below INF_AMMO:
For now, lets change this
Code: Select all
INF_AMMO_MEM:
//mov [rax+30],ecx //we have established 'rax+30' contains our current ammo count
mov [rax+30],(int)25 //set it to 25, because it looks nicer than 255
jmp return
full-script
Code: Select all
{ Game : STRAFE.exe
Version:
Date : 2017-07-19
Author : Bakfiets
This script does blah blah blah
}
[ENABLE]
aobscan(INF_AMMO,C7 40 30 FF 00 00 00) // should be unique
alloc(INF_AMMO_MEM,64,STRAFE.exe)
label(return)
INF_AMMO_MEM:
//mov [rax+30],ecx //we have established 'rax+30' contains our current ammo count
mov [rax+30],(int)25 //set it to 25, because it looks nicer than 255
jmp return
INF_AMMO:
jmp INF_AMMO_MEM
nop
nop
return:
registersymbol(INF_AMMO)
[DISABLE]
INF_AMMO:
db C7 40 30 FF 00 00 00
unregistersymbol(INF_AMMO)
dealloc(INF_AMMO_MEM)
{
// ORIGINAL CODE - INJECTION POINT: 90442AA2
""+90442A82: EC - in al,dx
""+90442A83: 18 48 89 - sbb [rax-77],cl
""+90442A86: 3C 24 - cmp al,24
""+90442A88: 48 8B F9 - mov rdi,rcx
""+90442A8B: 48 89 54 24 08 - mov [rsp+08],rdx
""+90442A90: 48 8B 47 40 - mov rax,[rdi+40]
""+90442A94: 48 85 C0 - test rax,rax
""+90442A97: 74 0C - je 90442AA5
""+90442A99: 48 8B 47 40 - mov rax,[rdi+40]
""+90442A9D: 48 63 4C 24 08 - movsxd rcx,dword ptr [rsp+08]
// ---------- INJECTING HERE ----------
""+90442AA2: C7 40 30 FF 00 00 00 - mov [rax+30],ecx
// ---------- DONE INJECTING ----------
""+90442AA9: 48 83 C4 18 - add rsp,18
""+90442AAD: C3 - ret
""+90442AAE: 00 00 - add [rax],al
""+90442AB0: 00 00 - add [rax],al
""+90442AB2: 00 00 - add [rax],al
""+90442AB4: 2E 00 00 - add cs:[rax],al
""+90442AB7: 00 3C 00 - add [rax+rax],bh
""+90442ABA: 00 00 - add [rax],al
""+90442ABC: 01 04 01 - add [rcx+rax],eax
""+90442ABF: 00 04 22 - add [rdx],al
}
Template code with explaination
Code: Select all
{ Game : STRAFE.exe
Version:
Date : 2017-07-20
Author : Bakfiets
//Dont forget to update the header
}
[ENABLE]
aobscan(INF_AMMO,89 48 30 48 8B 3C 24) // should be unique
alloc(newmem,$1000,9C090592) //the name newmem will clash with other scripts. Change the name! (INF_AMMO_MEM for example)
label(code) //I usually delete this label
label(return) //The point of return created by Cheat engine
newmem: //Change to the name in the alloc() statement
code: //remove the label here too
mov [rax+30],ecx
mov rdi,[rsp]
jmp return
INF_AMMO:
jmp newmem //Change to the name in alloc() statement
nop
nop
return:
registersymbol(INF_AMMO)
[DISABLE]
INF_AMMO:
db 89 48 30 48 8B 3C 24
unregistersymbol(INF_AMMO)
dealloc(newmem) //Change to the name in alloc() statement
{
// ORIGINAL CODE - INJECTION POINT: 9C090592
""+9C090572: EC - in al,dx
""+9C090573: 18 48 89 - sbb [rax-77],cl
""+9C090576: 3C 24 - cmp al,24
""+9C090578: 48 8B F9 - mov rdi,rcx
""+9C09057B: 48 89 54 24 08 - mov [rsp+08],rdx
""+9C090580: 48 8B 47 40 - mov rax,[rdi+40]
""+9C090584: 48 85 C0 - test rax,rax
""+9C090587: 74 0C - je 9C090595
""+9C090589: 48 8B 47 40 - mov rax,[rdi+40]
""+9C09058D: 48 63 4C 24 08 - movsxd rcx,dword ptr [rsp+08]
// ---------- INJECTING HERE ----------
""+9C090592: 89 48 30 - mov [rax+30],ecx
""+9C090595: 48 8B 3C 24 - mov rdi,[rsp]
// ---------- DONE INJECTING ----------
""+9C090599: 48 83 C4 18 - add rsp,18
""+9C09059D: C3 - ret
""+9C09059E: 00 00 - add [rax],al
""+9C0905A0: 00 00 - add [rax],al
""+9C0905A2: 00 00 - add [rax],al
""+9C0905A4: 2E 00 00 - add cs:[rax],al
""+9C0905A7: 00 3C 00 - add [rax+rax],bh
""+9C0905AA: 00 00 - add [rax],al
""+9C0905AC: 01 04 01 - add [rcx+rax],eax
""+9C0905AF: 00 04 22 - add [rdx],al
}
Why did we do this again? We didn’t want to look for the value but instead simply activate a script whenever we want. How does CE find the correct stuff? Well, the aobscan(..) function scans for an array of bytes. You probably figured out already that ‘aobscan(INF_AMMO,C7 40 30 FF 00 00 00)’ containts the exact same bytes as the highlighted row in the disassembler. I advise you read up on this thing, because it will be your best friend.
If you are anything like me; you are not fullfilled yet. You dislike having a railgun or shotgun with 25 ammo and really need to do something about it. Luckily this is often very easy. In my experience so far, the current and MAX ammo count are VERY close friends.
So, lets change the current ammo count to the MAX ammo count!
There is literally a million ways to do find this out, but it really comes down to luck and ingenuity.
Lets get back to the disassembler. (Actually, my game crashed here because i forgot to restore the original code and activated the script; addressed are slightly off now)
We want to know where in memory RAX is located. We do this by setting a breakpoint in our new script or above it. In the image the cheat is activated so the proper instruction is now replaced by a jmp <address>. If you highlight it and press SPACE you will go to the new place in memory where our injection resides.
Set a breakpoint and hit fire. If the game halts, you know you did it right. To the right you will see the registers. In the code you see that the rax is getting the value that resides in rdi+40. And we know that rax is the thing we want. Remember from the script, that [rax+30] is where the ammo resides
Open the data structure dissector by pressing ctrl+D or using the tools menu. Put in the address that is now contained in RAX. Do not continue the program yet as the registers will change when you do so.
Now create a new structure with Structure -> define new structure. Press YES for all questions asked.
Navigate to offset 30 and voila, theres a 19! Our current ammo count. See that suspicious 25 underneath it? Told you, current and MAX are often best-friends.
So now we know that rax+34 contains the max amount of ammo for 1 magazine/clip/whatever.
Lets adjust the script accordingly
Code: Select all
[b]Replace[/b]
mov [rax+30],(int)25 //set it to 25, because it looks nicer than 255
[b]With[/b]
push rbx //Save the value in RBX for restoring it later, use a random register here
mov rbx,[rax+34] //Move the Max ammo into our Register
mov [rax+30],rbx //Move the value in our register into out Current ammo count
pop rbx //Restore original value
(read up on stacks if you want to know what push and pop does)
____________________________________________________________________________
Usefull info
Right before your eyes : viewforum.php?f=11
Youtube, for example Stephan Chapmans series on Cheat Engine tuts
The "the f' is this opcode" manual : [Link]
The thing that the manual above lacks : [Link]
This helped me quite a bit : [Link]
And last but not least. Took me hours to find, you see it in every table : [Link]
Changelog:
20-7-2017 : Added the template code with some explaination, as to not cause confusion