Video Games Taught me Cybersecurity
One of my coworkers asked me a few months ago how I learned all that I know about assembly, binary executables, and reverse engineering. He was, as I suspect most people might be, suprised when I told him that most of these skills I had learned as a teenager hacking video games.
The trigger for me finally deciding to write about it was, while researching how Endgame and other advanced endpoint protection suites work, I stumbled across this article by Matt Spisak, where he details a method to bypass branch-prediction failure based detection, such as Endgame’s, using various gadgets operating on VTables.
The content of this article will address some of the most important skills I learned from video game hacking, why they were important to hacking, and how they continue to be of use.
Prior to my entry into the less ethical side of video game programming, I had a very small exposure to scripting via Gary’s Mod and it’s built-in LUA scripting engine. I made extremely basic tools and addons, typically by cobbling together snippets of other people’s code, adding small amounts of functionality and interfacing as needed.
From there, I began to venture into the slightly less ethical. This was the first time I had to apply creative thought to solve problems with programming, and write my own code to do so. I had to learn how to utilize arcs and three-dimensional vectors to make an aimbot, snapping my point of aim onto targets as they became visible. I learned the basics of event-driven programming, creating event handlers to allow my scripts to execute in near-real time. Perhaps most importantly, I learned the limitations of scripting languages versus native, compiled languages.
C++, Assembly, and code injection
Most of the multiplayer servers that offered competitive game modes disabled the loading of untrusted client-side scripts to prevent the very thing I was trying to do. So, I set about circumventing this restriction. In order to do so, I had to learn the basics of assembly, reverse engineering, C++, and code injection.
The mechanism of disabling clientside scripts was shockingly simple. When you joined a server, it told the client some values to serve as read-only settings. Of interest to my use was “sv_cheats” which, when set to any non-zero value, would disable clientside scripting. With the help of a mentor, I set about finding the code responsible for the enforcement of this policy and disabling it.
First up was to identify the function and its location in memory in the currently running process. While finding the function was initially very difficult, as it did not directly reference any strings, my mentor eventually guided me toward the recently released MacOS libraries, which contained symbol names. Once the function of interest was identified, I used a technique known as signature scanning to locate it in memory, as libraries are not always loaded in the same order or placed in the same location in memory. At its core, signature scanning is little more than a search for the opcodes of the function of interest, with wildcards in the place of bytes whose values may change depending on state.
With the function’s location in memory, there was still the issue of executing custom code. My initial solution to this was a trampoline hook, where the first few bytes of a function are overwritten with a jmp instruction pointing to a function of my design. This was my first exposure to APIs such as CreateRemoteThread to get my code into process memory, VirtualProtect to set the memory region to be writable, and the concept of writing data directly to locations in memory using pointers.
After getting this to work offline, I was then informed that this method was much too noisy, and would get detected and banned by Valve Anti-Cheat, as it implemented some form of integrity checking on the .text section of the running game, as well as listing the loaded libraries and comparing them to a blacklist by hash. To avoid tampering with the .text section, I instead overwrote the virtual function table, or vtable, entry for the method I was hooking, ensuring to store the address that the entry initially pointed to for later reference. One obstacle out of the way, I next sought to avoid ever getting caught by hash-based blacklisting. As it is a requirement of hashing algorithms to have high avalanching, where one changed byte should effect more than one byte of the produced hash, I created an unused local double word variable and changed it’s value on the hard drive to a random value every time the the injector closed, thereby changing the hash of the library.
The exposure to assembly, reverse engineering, direct memory access, and process injection/hijacking have been massively beneficial to my career in cybersecurity. Being able to crack open executables, read their compiled code, and ascertain the purpose of a given subroutine is a fundamental part of reverse engineering malware. Knowing the mechanics and requirements of code injection allows me to better detect it on endpoints I am assigned to protect. And, perhaps most important to my current role as a threat hunter, it was a powerful example of the idea that indicators of compromise are not, and never will be, foolproof. Something as small as changing one byte or the tool used to accomplish an objective can completely circumvent created indicators of compromise.
Inside of Gary’s Mod there is a roleplaying game known as Trouble in Terrorist Town, where a fundamental mechanic is the secrecy around the role that players hold, with the majority being “Innocent,” and a select few being “Traitors,” with access to special equipment. After downloading and reviewing the serverside code, there was no way to directly know who the server had picked to be the traitors, so I had to think abstractly.
As with any first-person shooter, every player is made aware of the current state of the world around them, including the positions of people, their current primary(visible) equipment, and any other dynamic entities that may effect gameplay. This same information is what enables some video game hacks to let the user see opposing players through objects. For my use case, though, I didn’t particularly care if I could see through walls, I just wanted to be able to identify the small number of Traitors. I achieved this by creating an event hook, a provided functionality of the scripting engine, on the creation of entities and changes in the primary equipment of other players. By comparing the name and type of the entity or equipment, I could, with some accuracy, determine if a player was a Traitor.
Seeking more accurate results, I thought about the problem from a more behavioural standpoint. Traitors are more likely than their innocent counterparts to fire a weapon or damage another player. Using all three of these behaviours together, I created a sort of “naughty board,” private to my screen, where a user accumulated points as they triggered one of the three alert conditions I had set. This proved to be considerably more accurate than just flagging anyone that was near a restricted resource as a Traitor.
The similarities between detecting Traitors in a game and cyber threat hunting are astounding. Initially, I created a rule on easily observable indicators of “badness,” identifying any player that had a restricted weapon or was close to restricted entities as a Traitor. The false positive rate on this rule was significantly above my acceptable rate, so I went back and revised my initial hypothesis, correlating other, behavioural data with the emperical data previously operated on. By combining all of this and comparing it to the baseline, it was fairly straightforward to identify the anomalous players. With this information in hand, I was able to interact with persons of interest differently to identify the true Traitors. This same flow is how I hunt cyber threats today: Collect data, aggregate and baseline, identify anomalies, investigate, then update the baseline with the result of the investigation.
I hope you found this post interesting, and possibly even learned something new! If you think I got something wrong or missed something entirely, feel free to message me on Twitter(@micrictor).
Thanks for reading!