Counter-Strike Is Broken
NOTICE: #
This blog post is about VAC signature detection. NOTHING else. A lot of people are having panic attacks about other technical features that this does not cover. Also, Assualt Cube is used as a demonstration with VAMemory which was the memory c# class used while doing CS:GO research. My reason for posting is why easy, old and simple memory modification cheats using known memory modifications DLLs are not being detected after months of use. I have not and will not reverse Warden, VAC or care about other anti-cheat technics being used in them. I simply don’t care and it’s out of scope for this post.
TL;DR The Counter-Strike [VAC] anti-cheat engine is signature based (among common other things) and people selling cheats know this. They could flood the community with so many different signatures that Valve could never keep up with detection. They need to evolve by either monitoring kernel system calls, hooking ReadProcessMemory/WriteProcessMemory or preventing processes from attaching to their games or ASLR. (Address space layout randomization) One last thing before we get started. Stop calling people who download or buy cheats “hackers”. The correct term is “script kiddie”. Now, if the person using said cheats wrote them then the term “hacker” is acceptable. Nonetheless, in the case of VAC, it never is acceptable because it’s too easy and you’ll learn why by reading more :P
What is Counter-Strike, Glicko-2 rating and why people cheat. #
For those of you who don’t know what Counter-Strike is, it’s a very popular online FPS (first-person shooter) by Valve software. So popular that yearly completions are held and prize money in the millions is awarded to the victors. But guess what? It’s totally and hilariously fucking broken.
Now, most players of the game are not skilled enough to join these professional teams or obtain the “Global Elite” rank. Counter-Strike like chess uses the Glicko-2 rating system (https://en.wikipedia.org/wiki/Glicko_rating_system). A player’s Glicko-2 score then correlates to a Counter-Strike rank (see image below).
Again, like with Chess, the fun part of Counter-Strike is playing against people from all over the world and continuing to climb and better your Glicko-2 rating. It’s a competition-driven game where practice, skill, and teamwork pay off. I’m not a psychologist but cheating in games like Counter-Strike, Chess and others are likely… well. w/e just watch this. https://www.youtube.com/watch?v=CwzL61SpTM4
“Anti-Cheat Engine” doth butter no parsnips. #
A few weeks ago I decided to sit down and research wall-hacks for counter-strike. Wall-hacks allow a player to see everyone through any wall in the game. It’s one of the most common and popular cheats people use daily in Counter-Strike.
My experiment mostly started as a fun way to learn c# but quickly turned into a fascination with how incredibly awful and amateur the Valve anti-cheat engine is (VAC). It’s important to note that getting a VAC ban is permanent and every game you have purchased using VAC via Steam (http://store.steampowered.com/) will also be permanently banned from online play.
Now, I may be biased because my entire career has focused on computer security but I would put VAC, in regards to its effectiveness, right next to Norton Anti-Virus from the early 90’s. It’s a signature, community report, cat and mouse design (among other things). https://en.wikipedia.org/wiki/Valve_Anti-Cheat#Design
All of the lessons learned from malware detection tools over the last few decades seems to have been ignored by Valve. Now, to be fair they build games, not detection systems but like Windows/Defender users don’t care and will expect them to solve it. Blizzard Entertainment is a great example of this and their Warden Client (https://en.wikipedia.org/wiki/Blizzard_Entertainment#Technology) which is incredibly advanced compared to VAC.
Basically, take the oldest cheat source code you can find for Counter-Strike that has been “detected” for years and pack (https://github.com/yck1509/ConfuserEx) it and it’s now undetectable. Done. You just defeated Valve’s amazing anti-cheat engine.
Making money from the same 100 lines of code. #
The fun part is that the Counter-Strike “private” cheap market is huge. A lot of people sell their cheats for monthly subscription fees ranging from $20.00 USD to $45.00 USD. The authors are well aware that they could pack each binary uniquely at download but how do you continue a subscription model when everyone’s cheats are undetectable with different signatures. The authors want the cheats to be detected and even offer discounts and/or reimbursements for purchasing Counter-Strike if/when you do get VAC banned. The game’s price as fallen to a point that having to repurchase it is not really a deterrent. One possible thing Valve could do to prevent this is to track credit card usage and also ban credit cards from accounts that have a history of cheating.
So, how easy is it to actually write cheats for Counter-Strike? #
Well, as it turns out, incredibly easy. Unfortunately, due to the terms of service and past lawsuits in this area, I’m not going to post source code or explain how to reverse engineer the static addresses for game manipulation for Counter-Strike. However, I will for an open source game called Assault Cube. You can download Assault Cube for free at http://assault.cubers.net/
If you’re not familiar with how computer memory works this may be a little extreme, but I’ll try to keep it high level. To make this easier we will be using a program called Cheat Engine (http://www.cheatengine.org/).
Once you have Assault Cube and Cheat Engine installed, open Assault Cube and create a single player game with no bots. I like to use the ac_aqueous map because you spawn next to grenades that make it easier to damage yourself so you can track your health change.
Now we can open the Cheat Engine application and attach to the Assault Cube process (click the computer icon at the top left of the application under the menu). You will see a lot of processes but just keep scrolling until you see “ac_client.exe”.
Click “Open” and we can now search for values in memory for this process. This is where things get interesting because we can simply search for the health value and return all address in memory that holds this value. Go ahead and type 100 to the “Value:” search box at the top of the right sidebar. You should see something like this:
What you see on the screen now hold memory address that leads to a value of 100 running in memory for Assault Cube. One of these addresses is the place in memory that holds our player’s health count. Now, this list is huge, so like I said before we are going to hurt ourselves to get the list to change. By doing this we are filtering the list so it will become smaller and smaller so it’s easier to work with. Pick up some grenades and throw them next to you so we can lower our health. Be sure to now get too close because if you die you will need to start the whole process over.
It’s important to note that the green address signifies static addresses. So, in the case the value you want to change is green, you have way less work to do because you can use that address directly to change values.
After damaging myself with the grenade, my health is now 54. So, going back to the Cheat Engine application I will now enter 54 as the value and search again.
With any luck, your screen will look like mine and only have two address. Now, neither of these address are green, so they are dynamic – which means we can modify the game’s health as it’s running, but this address won’t be the same next time you start the game. It’s easy to trace a pointer for its static location and apply offsets using Cheat Engine, but we will save that for another time.
Right click both of the addresses (or as many as you have) and they will be added to the Memory View section of the application as seen below.
Now, by double clicking the value of one of the rows in the memory view section, a window will open that allows you to change the value in memory for this address. Select the first one and change the value to 1000000.
Now if you go back to Assault Cube, with any luck your health should be 1000000. Pretty easy, right? This concept can also be applied to armor and ammunition.
It’s important to note that changing values like this for online multiplayer games is fruitless. The server you connect to will store state so every time your client changes your health, the server will sync back its last recorded value. However, this is very effective for single-player games.
Let Me See Them Scripts Codes #
So, the heart of automating this process is writing some simple code in c# or whatever language you chose. Since I was using this as a way to learn c# I will be using that for the examples.
To read and write to another processes memory in c# is super easy. We just need to import three functions to c# from kernel32.dll which allows us to open a process and read or write to it.
[DllImport("kernel32.dll")]
public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);
[DllImport("kernel32.dll")]
public static extern bool ReadProcessMemory(int hProcess,
int lpBaseAddress, byte[] lpBuffer, int dwSize, ref int lpNumberOfBytesRead);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool WriteProcessMemory(int hProcess, int lpBaseAddress,
byte[] lpBuffer, int dwSize, ref int lpNumberOfBytesWritten);
Using these three functions we can then write a simple application that changes the value of something written in notepad.
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
public class MemoryRead
{
const int PROCESS_VM_WRITE = 0x0020;
const int PROCESS_VM_OPERATION = 0x0008;
[DllImport("kernel32.dll")]
public static extern IntPtr OpenProcess(int dwDesiredAccess,
bool bInheritHandle, int dwProcessId);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool WriteProcessMemory(int hProcess, int lpBaseAddress,
byte[] lpBuffer, int dwSize, ref int lpNumberOfBytesWritten);
public static void Main()
{
Process process = Process.GetProcessesByName("notepad")[0];
IntPtr processHandle = OpenProcess(0x1F0FFF, false, process.Id);
int bytesWritten = 0;
byte[] buffer = Encoding.Unicode.GetBytes("Mephux!\0");
// '\0' marks the end of string
// replace 0x0046A3B8 with your address
WriteProcessMemory((int)processHandle, 0x0046A3B8, buffer, buffer.Length, ref bytesWritten);
Console.ReadLine();
}
}
It gets even easier. Someone already did all the work. #
There is a great memory class for c# called VAMemory (http://www.vivid-abstractions.net/logical/programming/vamemory-c-memory-class-net-3-5/). A lot of people on Counter-Strike hacking forums will tell you not to use it because it will be detected but that not an issue when you use a packer/obfuscator (remember VAC is signature based - amoung other things but focusing on signatures for now).
The Packer (ConfuserEx) And Assault Cube Health/Ammo Hack. #
The blow code is a hack for Assault Cube to keep your ammo and health at 10000 at all times. Remember this won’t work for online play but will for single player mode. The below code uses the VAMemory class to make the process of reading and writing to memory easier.
using System;
using System.Threading;
namespace AC
{
class Program
{
// 0x0291A418
public static int Base = 0x00509B74;
public static int Health = 0xF8;
public static int Ammo = 0x150;
static void Main(string[] args)
{
VAMemory vam = new VAMemory("ac_client");
int LocalPlayer = vam.ReadInt32((IntPtr)Base);
while (true)
{
int address = LocalPlayer + Health;
vam.WriteInt32((IntPtr)address, 10000);
address = LocalPlayer + Ammo;
vam.WriteInt32((IntPtr)address, 10000);
}
}
}
}
So now that we have our cheat put together, let’s make it unique, compressed and obfuscated (among other things i.e preventing attachment). ConfuserEx (https://github.com/yck1509/ConfuserEx) is a great little open source packer that supports:
* Supports .NET Framework 2.0/3.0/3.5/4.0/4.5
* Symbol renaming (Support WPF/BAML)
* Protection against debuggers/profilers
* Protection against memory dumping
* Protection against tampering (method encryption)
* Control flow obfuscation
* Constant/resources encryption
* Reference hiding proxies
* Disable decompilers
* Embedding dependency
* Compressing output
* Extensible plugin API
Using ConfuserEx we will take out .exe cheats for Assault Cube, add the VAMemory.dll and pack it. Open ConfuserEx and it should look something like this.
Go ahead and select the base directory and use the plus button to add both the VAMemory.dll and the compiled c# executable.
Once you have everything added we will change tabs to the settings page and select the base packer. Make sure that tell the packer to pack both the executable and VAMemory.dll.
After that change tabs again to Protect! and click “Protect!”. You should see output similar to the below. Take note of the output.
Conclusion #
The overwatch program is a joke and will never scale. Overwatch is a user-sourced cheat detection program Valve introduced to help combat the issue (http://blog.counter-strike.net/index.php/overwatch/). It was also a clear sign of desperation.
Now that Counter-Strike is cross-platform I’m sure this adds a lot of other issues into the mix. In Windows, you can only attach to a process once. A lot of malware authors will attach to themselves to prevent detection tools from debugging. My best guess to why they don’t do this is for debugging and crash reporting. This could also cause issues with Address space layout randomization, PIE and other methods when debugging remotely or sending crash reports. (Speculation but I’m guessing there is some reason they don’t use it.)