Absolute beginner: Your first ammo script

Section's for general approaches on hacking various options in games. No online-related discussions/posts OR warez!
Post Reply
User avatar
Bakfiets
Fearless Donors
Fearless Donors
Posts: 22
Joined: Mon Jul 03, 2017 6:56 pm
Reputation: 3

Absolute beginner: Your first ammo script

Post by Bakfiets » Wed Jul 19, 2017 10:47 pm

Boring prologueShow
Not so long ago I thought it would be usefull to learn a bit of assembly. Little did I know my ‘me-time’ was about to be consumed by looking at bytes and digging through assembly; let alone how much fun and fullfilment it brings. Today I want to share my first moments of discovering a new hobby.
This tutorial is for the absolute beginner, and should be seen as an 'hello world' equivelant. A teaser that provides a small taste into the Cheat Engine world. The game I'm using is called STRAFE, costs around 10 bucks and is actually pretty fun to play.

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.
Image

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.

Image

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.

Image

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.

Image

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?

Image

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’.

Image

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)

Image

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

Image

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-scriptShow

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 explainationShow

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
}
Now you have a script. Well played!
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)

Image

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

Image

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)
Now, whatever gun you use, the current ammo will be set to the max ammo!

____________________________________________________________________________

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 : http://www.mathemainzel.info/files/x86asmref.html
The thing that the manual above lacks : https://docs.oracle.com/cd/E18752_01/ht ... eoizy.html
This helped me quite a bit : https://stackoverflow.com/questions/254 ... r-contents
And last but not least. Took me hours to find, you see it in every table : http://www.cjump.com/CIS77/ASM/FlowCont ... labels.htm

Changelog:
20-7-2017 : Added the template code with some explaination, as to not cause confusion
Last edited by Bakfiets on Thu Jul 20, 2017 7:38 am, edited 2 times in total.

User avatar
SunBeam
Trouble Makers
Trouble Makers
Posts: 353
Joined: Thu Mar 02, 2017 10:15 pm
Reputation: 95

Re: Absolute beginner: Your first ammo script

Post by SunBeam » Wed Jul 19, 2017 11:47 pm

Congrats for the tutorial! One observation: in order to have normal-jump references to your code cave, try to allocate close to game's module. As in, use alloc(x, size, game.exe) rather than alloc(x, size).

Instead of this:

Image

(see the FF 25 then following bytes?)

You will see a normal jump: E9 xx xx xx xx.

Cheers,
Sun

User avatar
Bakfiets
Fearless Donors
Fearless Donors
Posts: 22
Joined: Mon Jul 03, 2017 6:56 pm
Reputation: 3

Re: Absolute beginner: Your first ammo script

Post by Bakfiets » Thu Jul 20, 2017 7:45 am

SunBeam wrote:
Wed Jul 19, 2017 11:47 pm
Congrats for the tutorial! One observation: in order to have normal-jump references to your code cave, try to allocate close to game's module. As in, use alloc(x, size, game.exe) rather than alloc(x, size).

Instead of this:

Image

(see the FF 25 then following bytes?)

You will see a normal jump: E9 xx xx xx xx.

Cheers,
Sun
Thanks, first 'blog' piece I ever wrote!
Regarding the observation, the screenshot has the jump with the correct alloc(..,STRAFE.exe). I just re-tried it and it does the exact same thing. Mono related maybe?

One question still remains regarding the template

Code: Select all

alloc(<name>,$1000)
Why is there a dollar-sign infront of the memory to allocate, and what would be the appropriate way to determine this value? At the moment I am just putting more in there than I would need. The -perfect- way would be to determine how much bytes the whole script puts in memory? (assuming 1 inject per script)
In non Mono-games it seems to paste the module name behind it, and it also uses aobscanmodule. I read somewhere it's about Mono not defining a module like normal?

User avatar
SunBeam
Trouble Makers
Trouble Makers
Posts: 353
Joined: Thu Mar 02, 2017 10:15 pm
Reputation: 95

Re: Absolute beginner: Your first ammo script

Post by SunBeam » Thu Jul 20, 2017 8:13 am

What I meant is there's a difference between alloc with or without the near-image. Although correct either way, the calculated JMP falls in the 'normal' ASM opcodes (as opposed to the long form, jumping to a pointer reference). The script compiler checks if allocation is near the game's image and calculates the difference between source and destination. If that difference exceeds a certain value, a long jump is compiled instead.

e.g.:

Code: Select all

alloc(x, 1000, game.exe)
game.exe+100:
jmp x
The above JMP has to occupy 5 bytes (E9 xx xx xx xx). And you also have to adjust the remaining code with NOPs (if the instruction you hook is > 5).

In your case, you can see the JMP you got is FF 25 then 8+ bytes. That's because you used alloc(x, size) without specifying the 3rd optional parameter:

Code: Select all

ALLOC(allocName, sizeInBytes, Optional: AllocateNearThisAddress)

Allocates a certain amount of memory and defines the specified name in the script. If AllocateNearThisAddress is
specified CE will try to allocate the memory near that address. This is useful for 64-bit targets where the jump
distance could be bigger than 2GB otherwise.
Since you're missing AllocateNearThisAddress and the jump distance to your allocation > 2GB, a long jump has been compiled instead. Not sure how familiar you are with hooking, but that long jump will break the instructions under it (whereas instead of 5 bytes, you get 8+ modified).

Just to emphasize this, let's take your code for example:

Code: Select all

90442A9D: 48 63 4C 24 08 | movsxd rcx,dword ptr [rsp+08]
90442AA2: 89 48 30 | mov [rax+30],ecx
90442AA5: 48 8B 3C 24 | mov rdi,[rsp]
If you allocate like this: alloc(Hook,0x1000) and the distance to allocation triggers a long jump compile, then your code becomes this:

Code: Select all

90442A9D: FF 25 00 00 00 00 xx xx xx xx | jmp Hook (referenced by the address)
You can already see that you go past the 5 bytes the original instruction has and have break the next 2 instructions (cuz your long jump uses 10 bytes). Sure, CE will probably copy the next 2 instructions in the cave, so you got nothing to worry about.

If you allocate like this alloc(Hook, 0x1000, game.exe), then this happens:

Code: Select all

90442A9D: E9 xx xx xx xx | jmp Hook (referenced by an address close to game's image)
Thus you don't need additional bytes and next 2 instructions won't be broken nor copied in the cave.

Optimization :)

That was what my observation's about :)

Cheers,
Sun

User avatar
Bakfiets
Fearless Donors
Fearless Donors
Posts: 22
Joined: Mon Jul 03, 2017 6:56 pm
Reputation: 3

Re: Absolute beginner: Your first ammo script

Post by Bakfiets » Thu Jul 20, 2017 8:47 am

SunBeam wrote:
Thu Jul 20, 2017 8:13 am
Not sure how familiar you are with hooking => not

Optimization :) => any programmer's wet-dream :oops:

If you allocate like this alloc(Hook, 0x1000, game.exe),
Thank you for the detailed description, now I actually know what I'm doing. However, any attempts to include the ,STRAFE.exe in the alloc may cause the game to crash (and does for each of my scripts at the moment). I have been struggling with it for a while and others had already pointed this out to me.
I can also see that before it crashes the injected code is a long jmp even with the modulename attached. (FF25 00000...)
Why it didn't crash while making the tutorial I can't explain, as it does crash now.
aobscanmodule doesn't work either.

Either the game doesn't want to do what you/we want to do, or I a'm doing something wrong. I suspect its the former. I'm gonna check with a non Mono game and see what happens in both scenario's.

User avatar
SunBeam
Trouble Makers
Trouble Makers
Posts: 353
Joined: Thu Mar 02, 2017 10:15 pm
Reputation: 95

Re: Absolute beginner: Your first ammo script

Post by SunBeam » Thu Jul 20, 2017 11:41 am

Game's called Strafe? Will give it a go and return with feedback, just so we iron things out ;)

User avatar
Bakfiets
Fearless Donors
Fearless Donors
Posts: 22
Joined: Mon Jul 03, 2017 6:56 pm
Reputation: 3

Re: Absolute beginner: Your first ammo script

Post by Bakfiets » Thu Jul 20, 2017 1:33 pm

Yes, STRAFE, 2016/2017. Appreciate it!
You can find my 'complete' table here. viewtopic.php?t=3339

User avatar
SunBeam
Trouble Makers
Trouble Makers
Posts: 353
Joined: Thu Mar 02, 2017 10:15 pm
Reputation: 95

Re: Absolute beginner: Your first ammo script

Post by SunBeam » Thu Jul 20, 2017 1:59 pm

Ahh. If this is an Unity game and you want to hook some functions you find by AOB, it's hard to predict where these are allocated in memory. So even if you use the 3rd parameter in alloc, chances are Unity's JIT is allocated past the 2GB threshold, thus your jump will still end-up being a long one :( Forgot this aspect, my bad. That explains what you said 3 posts above :)

Schnitzelmaker
Expert Cheater
Expert Cheater
Posts: 98
Joined: Fri Mar 03, 2017 6:18 pm
Reputation: 44

Re: Absolute beginner: Your first ammo script

Post by Schnitzelmaker » Thu Jul 20, 2017 2:17 pm

It could end up above the 2GB, but from my experience its really rare in unity games. And it's always better use the 3rd parameter.

And for lazy people like me i'm mostly using:
aobscan(INF_AMMO,C7 40 30 FF 00 00 00) // should be unique
alloc(INF_AMMO_MEM,64,INF_AMMO)

Then it tries to allocate near the found aob.

User avatar
SunBeam
Trouble Makers
Trouble Makers
Posts: 353
Joined: Thu Mar 02, 2017 10:15 pm
Reputation: 95

Re: Absolute beginner: Your first ammo script

Post by SunBeam » Thu Jul 20, 2017 3:15 pm

^ That should do it ;)

User avatar
Bakfiets
Fearless Donors
Fearless Donors
Posts: 22
Joined: Mon Jul 03, 2017 6:56 pm
Reputation: 3

Re: Absolute beginner: Your first ammo script

Post by Bakfiets » Thu Jul 20, 2017 9:09 pm

Schnitzelmaker wrote:
Thu Jul 20, 2017 2:17 pm
It could end up above the 2GB, but from my experience its really rare in unity games. And it's always better use the 3rd parameter.

And for lazy people like me i'm mostly using:
aobscan(INF_AMMO,C7 40 30 FF 00 00 00) // should be unique
alloc(INF_AMMO_MEM,64,INF_AMMO)

Then it tries to allocate near the found aob.
I always like how easy some things turn out to be, thank you! :lol:

Post Reply

Who is online

Users browsing this forum: No registered users