Presenter: Jai Verma
Abstract: Game hacking is a major contributor to the hacking economy. People shell out a lot of money for hacks which help them cheat at multiplayer games. These hacks can range from enhancing in-game abilities for gaining an upper hand in the game, to bypassing in-app purchases. With the advent of anti-cheat such as VAC (Valve Anti Cheat), the techniques employed by hackers to cheat at games have become increasingly refined. These techniques are comparable to those utilised in sophisticated malware and rootkits. These techniques include runtime process injection, function interposition, and proxying network traffic to name a few. To avoid detection, hackers even resort to custom kernel drivers to interact with the game process. This talk will detail a few of the techniques utilised by game hackers and malware developers alike, through the hacking of an open-source game. I will showcase some hacks for an open-source FPS game by describing the method in detail for the macOS platform. While the demo will be macOS specific, the approach applies to all modern operating systems.
3. What?
• Make a basic trainer for an open-source FPS
game (Assault Cube)
• https://assault.cubers.net
• Aimbot, ESP, Unlimited Ammo…
• How to approach this problem?
4. Why?
• Little guided documentation online about
game hacking on macOS
• Lots of tutorials for Windows
• To win at PUBG :P
5. Things we need
• We need to read and modify game process memory
• We possibly need to run our code in context of the game
process
• Tools:
• Disassembler: IDA Pro, radare2, …
• Debugger: lldb, gdb
• DBI: Frida
• OS API: Mach API (Mach is part of the XNU kernel)
6. Things we need
•First of all we need to find our health and
ammo in memory so that we can change it
•How do we do this?
•Debugger? - too tedious, have to stop process
execution
•Cheat Engine? - very powerful and easy to use
•Frida! - fast and easy to use, lower level of
abstraction
7. Needle in the Haystack
• Memory.scanSync(address, size, pattern)
• Memory.readByteArray(address, length)
• Memory.writeByteArray(address, bytes)
8.
9.
10.
11.
12. What just happened!?
• That was Frida’s API being used for
modifying process memory
• This was highly abstracted and works on
multiple platforms - Windows, Linux, macOS,
iOS, Android
• Now you might be wondering? So how does
this actually work internally? SHOW ME MAC
SPECIFIC CODE! ANYONE CAN DO THIS WITH
FRIDA!
14. Now what?
•We’ve just found the address of our ammo for a
particular instance of the game
•This address might change from match to match
and will definitely change when we restart the
game since it is a heap address and we have ASLR
•We need to find our player object address on the
heap and then a pointer to our player object
which might be stored somewhere in a ‘rw-‘
segment of our game binary like the ‘.data’
segment
15. • Frida has a MemoryAccessMonitor API as well
which we could leverage if we were on
Windows (doesn’t support macOS yet)
• We’ll just take help from lldb instead
• We can use a watchpoint to monitor
instructions which write to our ammo
address which might be calculated by adding
an offset to our player object address
16.
17. What was all that?
• Our health was at 100 to begin with
• Then we set a watchpoint which would be triggered
whenever any instruction writes to our health
address and the new value isn’t 100
• When the watchpoint is hit, we see that our health
has reduced to 84 (eax) which happens when you get
shot
• So clearly our health is at [esi+0xf8]
• So our player object should be at [esi]. Bingo!
18. • We can scan the ‘rw-‘ segments of the
address space our binary is mapped into for
our player object pointer
19. Making an aimbot
• What’s that?
• It automatically locks your aim on to your opponents
head so that you can easily kill them and show off your
mad skills
• Need to calculate yaw and pitch angles
• All this info is stored in our player object. Find the
offset just like we found health and ammo
• Similar to our player, all the enemy player object
pointers are stored in memory adjacent to our player
pointer
24. •Pitch = tan-1((z2-z1) / dist)
•Yaw = tan-1((y2-y1) / (x2-x1))
•Dist = Euclidean distance =
√((x2-x1)2 + (y2-y1)2)
•This is a good start, but we also need to check
whether the enemy we’re locking on to is visible or
not
•Games define a function generally called TraceLine
which gives us coordinates and a boolean which
signifies whether the a line drawn from A to B
collides with anything
25. • So A here is us and B is the bad guy we want to
kill
• Since this function is defined in the game
binary and is present in the game process memory
while running, we need to find a way to call
this with our parameters
• Mach API to the rescue again
• kern_return_t thread_create_running(task_t
parent_task, thread_state_flavor_t flavor,
thread_state_t new_state, mach_msg_type_number_t
new_stateCnt, thread_act_t *child_act);
26. •thread_create_running creates and starts a
new thread with a state that we specify
•This state includes the processor registers
so we can execute our own code in the context
of the remote process by setting eip state
•For this we need to allocate a region of
memory to hold our code (r-x) and a region
for the function stack (rw-)
•This game is a 32-bit process so function
arguments are passed on the stack (x86)
29. • We can also use Frida’s NativeFunction API
to call process functions if they follow a
standard calling convention
• Or you can use x86Writer for more fine
tuned use cases
32. What else?
• Alright so now we have a functional aimbot
which doesn’t blindly aim at walls
• ESP! - Extra Sensory Perception
• Draw bounding boxes on all enemies so that
we can easily find them, even through
walls!
34. • Assault Cube uses OpenGL for rendering
• We can therefore call OpenGL functions for
our own use
• OpenGL rendering has to be done in the main
thread though!
• Or we could use Apple’s Cocoa API too
35. OpenGL Rendering Pipeline
• I won’t be going into the details of the
various transforms that one has to go through
to display an object on the screen
• You can read about them on this very helpful
website: http://www.songho.ca/opengl/
gl_transform.html
• All I’ll say is that we need to find a model-
view-projection matrix in process memory and
multiply enemy position coordinates with it to
get on-screen pixel coordinates
36. Local Space
• A generic rendering pipeline looks like
this:
View Space
Clip Space
Model Matrix World Space View Matrix
Projection
Matrix
Perspective Division
& Viewport Transform
Screen
Coordinates
37. • The only hard part is locating the mvp
matrix in memory
• After that it’s just some matrix
multiplication and calling OpenGL API
38.
39.
40. • But how do we actually call these functions
• We can use Frida’s Interceptor API to
attach to a function that is executed on
the main thread or completely replace a
function’s implementation with our own!
• This can easily be done using Mach API as
well. All we need are calls to vm_allocate,
vm_protect, vm_write to make a ‘code cave’
for our code
45. • The place where I’ve attached and inserted
my code is not ideal as it causes the
bounding boxes to flicker
• This is probably due to double buffering
used by OpenGL and I’m drawing my stuff on
the wrong buffer
46. A little bit of Cocoa
•Use Cocoa API?
•Create NSWindow as an overlay
•Create a transparent NSView and set that as
contentView of overlay NSWindow
•Draw bounding boxes in NSView by overriding
NSView’s [- drawRect:] function
•Set needsDisplay to 1 to tell NSView to redraw
bounding boxes
47.
48.
49. • Remember to call drawing functions for
Cocoa in main thread!
51. • Flickering is gone :)
• But it’s too slow :(
• But all that’s my problem
• Both these issues can be fixed by proper
usage of the APIs
52. What else?
• Other possible methods for doing this are
dylib injection and method swizzling
• These techniques also work well for iOS
apps (both jailbroken and non-jailbroken)
• I wrote about hacking a minesweeper game
for iOS using these techniques and all the
details are present at https://
jaiverma.github.io/blog/ios-game-hacking if
you want to read about it
53. Resources and Thanks!
• Frida (https://frida.re/)
• https://github.com/rentzsch/mach_inject/ - big
help in understanding thread_create_running
• Rake from https://guidedhacking.com/ on
helping me understand OpenGL rendering
pipeline
• Apple Docs (https://developer.apple.com/
documentation/kernel/)
54. Conclusion
• All I did in this presentation was describe
how we can hack a game using Mach API, but
a lot of malicious things are possible when
you have control of a process
• This is usually the case when malware has
infected a system
• Malware can easily siphon off sensitive
information from applications to a remote
server
55. • I will post all code to GitHub soon
• https://github.com/jaiverma/
• Twitter: _jaiverma