[In this reprinted #altdevblogaday in-depth piece, Uber Entertainment software engineer Forrest Smith examines how users cheat in games with DirectX interception, and ways to deal with it.]
Online PC gaming is known for being full of dirty cheaters. Cheats can be implemented through many methods from simple to impressively complex. Macros, hex editing, memory inspection, memory modification, DLL injection, network manipulation, packet modification, and lord knows how many more. These various methods are then used to implement cheats such as rapid fire, no clip, aimbots, wallhacks, etc.
Today I want to discuss a specific form of hacking and how it's done. I hesitate to do so, but it's usage is already widespread amongst hack creators and users. The damage is already dealt. By sharing knowledge of its inner workings hopefully that damage can be mitigated.
Direct X interception is a particularly naughty breed of hacking. It works by intercepting all calls from a game to Direct X . The intermediate process can then do whatever it wants with that information. Most of the time the call will be passed directly onto the real DirectX without modification.
However, in special cases you can do something clever. If the render call is for an enemy player then you can determine its screen position. In many cases that player may be behind world geometry and will either fail z-buffer checks or be covered by subsequent draw calls. Too late, the enemy screen location is known. At the end of the frame you can create a new draw call to DirectX to draw a box around that screen location. Boom, that's a wallhack.
This type of wallhack is rampant. Even for the most popular of games. Here's what it looks like for Battlefield 3
This exists for every popular PC shooter you can imagine. All Battlefields
, all Call of Duties
, all Source engine games, all Unreal Engine games, etc.
The next step is to simulate mouse input by sending input messages to the app. With a known crosshair location and known screenspace enemy location, this is easy. Bam, that's an aimbot.
Flagrant aimbots can instantly snap from enemy to enemy. To avoid trivial detection, the simulated input can be smoothed over time. Even more realistic aimbots overshoot the target intentionally before narrowing in. Other variations don't move the crosshairs but do auto-fire when they are over an enemy target.
Coolest AI ever
Wallhacks and aimbots are pretty wicked, but it's just the beginning. What would happen if someone turned it up to 11? Matthew Fisher, a grad student at Stanford, did exactly this, and it's the coolest thing ever.
Using the basic methods described above, he wrote an AI that can play Starcraft 2
. The camera jumps around so fast it's impossible to keep up with. Please watch a few seconds of the video just to get an idea of what's going on
Let's break it down.
- Intercept Direct X calls to determine exactly what units are visible and where they are.
- With full knowledge of screen units pick an AI action to perform.
- Send input commands to game to select units.
- Send input commands to game to issue orders to selected units.
- Move camera.
- Goto 1.
The automated player is driven by simulated mouse input which requires a single frame to process. This prevents it from running at ludicrous warp speed, but as the video demonstrates it can run the entire loop several times per second. Pro Starcraft
gamers play the game around 300 actions per minute (APM). The automated player could theoretically hit about 3,600 APM.
Coolest. AI. Ever.
Matt wrote a large article on this player and his version of a DirectX interceptor. I highly recommend reading it here
Processing the frame
Let's go back for a moment and explain away some hand wavey magic. How exactly do you determine what a DirectX draw calls are for an enemy unit? By careful examination of what's being rendered of course!
If you've ever used PIX, or something similar, then you've seen a view of the screen as it's being rendered one call after another. By examining the vertex count, textures, shaders, etc you can figure out a lot.
Matt shows a breakdown of how he does this for Starcraft 2
, so let's take a look here
. Scroll down a bit and check out step 18. Upon visual inspection it's obvious that a Void Ray unit is rendered. You can record what data was used in that call and use it to determine the screen location of all void rays in the future. Voila!
I've discussed intercepting calls to determine unit locations. You can also add calls to render wall hacks. Finally, you can modify calls. This could be done to change textures or models.
For example an advantage can be gained by replacing player textures with bright red making enemies stand out. In a shooter, you might as well go full wallhack so it's more useful in other genres. 
So how can developers prevent this sort of cheating? Unfortunately it's not easy and there are no silver bullets. That such cheats continue to exist for games such as Battlefield, Call of Duty,
and Team Fortress 2
should indicate the complexity of the situation. There are however a variety of strategies that can help.
Game data can be modified to break cheating tools. If the tool relies on vertex count then modifying the data could break the tool. Hackers are of course exceptionally fast at updating their tools and can do so faster than devs. Run-time dynamic modification may help, but high poly player models will always stand out. Breaking meshes into smaller pieces with random jitter could help hide them, but there are both logistical and performance issues to consider.
It's possible to retrieve the final frame buffer and analyze it for illegitimate additions. This is unreliable and is likely slow. I have heard tales of games doing this only for the cheating tools to detect the check and turn themselves off for a frame.
Anti-virus like process detection is another option. Punkbuster attempts this, but it does not appear to work well. Blizzard's proprietary Warden software performs similar functionality and seems to be effective.
Player data analysis is always useful. The Starcraft 2
AI is exceedingly cool but would never survive in the wild. It's not difficult to spot and ban a 3600 APM player. Pinpointing wall hackers in a shooter is more challenging but is extremely viable. You should definitely be gathering and analyzing many data points.
Some companies have attempted to utilize the legal system to shut down cheat creators. This may
work within the United States, but many cheats comes from Eastern Europe or Russia. There is no chance for lawsuits to halt their operations.
IP addresses change and MAC addresses can be spoofed, but they can still be useful. If you have a known cheater and those addresses match up to a second account, then it may be worth investigating. It's not quite cheating, but League of Legends
uses them to help detect smurf accounts. If there is a max level account followed by a level 1 account playing extremely well, then you can quickly increase the matchmaking skill rating for the level 1 account to protect the real newbs.
The fact of the matter is that wallhacks and aimbots will always exist on PC. You can't stop them. The cat and mouse game between developers and hackers will forever be played, but it's not the end game.
Preventing players from cheating is important. Detecting when players cheat is possibly even more important. A common strategy is once cheaters are detected to let them keep playing for a few weeks. Then you drop the banhammer on thousands of players at once. From that point on, players will hesitate before cheating because the player will never know if the dev knows.
There are companies that don't care about preventing cheats if they are rarely used. What they care about is detecting those rarely used cheats and immediately banning the users. It's a different and non-obvious mindset that changes how you attack the problem.
For example, Blizzard does not validate all player movement within World of Warcraft
. There are too many players and it requires too much work to check every single position update for every single player. With some bit twiddling, it's possible to fly, run super fast, no clip, and even teleport.
The position update itself isn't checked, but suspicious activity can still be detected. Entering unreachable regions, visiting multiple zones impossibly fast, positions inside geometry, player reports, etc. Once suspicious activity is detected, the account can be auto-flagged, and then expensive validation checks can be performed for only a handful of players.
Here's an amusing video
where a WoW
player teleport hacks for a mere two minutes before getting banned. In the comments, he claims the ban wasn't due to the teleport but from the mining of ore with a bad z-axis value. The teleport itself wasn't prevented, but other more subtle behavior led to an impressively quick ban.
Do you know what one of Microsoft's top anti-cheat measures on Xbox is? Achievements. Players are incredibly invested in their profiles and achievement scores. It takes years to build up and most players won't dare risk losing it all by cheating.
How many players are willing to risk max level characters, collections of hats, and precious achievement scores just to cheat a little? Not many. At least not after the first ban wave.
Wizardry for good
It's worth noting that DirectX interception isn't just for evil deeds. If you've played any games on Steam, then you are likely familiar with their in-game overlay accessed via Shift-Tab. This overlay works exactly like the wallhack! Steam detects the Shift-Tab keystroke, hooks into DirectX calls, and renders the overlay on top of the actual game automagically. That's pretty slick if you think about it.
This has been a brief summary on cheating by means of Direct X interception. It can be used in all sorts of nasty ways, and if you have a popular multiplayer game it's probably being used. It's hard to stop and it can be hard to detect, but it's important to know about and understand. Keep fighting the good fight my friends.
[This piece was reprinted from #AltDevBlogADay, a shared blog initiative started by @mike_acton devoted to giving game developers of all disciplines a place to motivate each other to write regularly about their personal game development passions.]
- This method works perfectly with OpenGL as well. I don't mean to call out Direct X specifically but do so for reader clarity. Sorry Microsoft!
- In some games, including the Counter-Strike example, this can be done more easily with a simple data file replacement.