All right.
How's everyone doing tonight?
Today?
All right.
Cool.
Thank you for joining me for the first talk after the keynote.
I really appreciate it.
All right.
We'll just dive directly in now.
No need to wait.
So first, before we really get into the knee deep thick of it, we'll go a little bit about myself.
I'm Connor Kastner.
My four little acronyms that I got right now are GPEN and GSOC for Penetration Tester and Security Operations Center through SANS.
My day job, I'm a SOC analyst over at American Transmission Company working on protecting critical infrastructure.
And the main reason why I'm writing this talk and what I did in my childhood is I've always been a big fan of anything involving screens.
I grew up modding consoles.
I grew up plugging in USBs, downloading random apps for Minecraft or God mode for Black Ops or tricking my friends in middle school to hop in a private lobby where I hit a lag switch and pissed them off every single time.
Don't know how they kept falling for it, but here we go.
I had a good time with that.
So we'll treat this section as an overview, kind of introduce what we're talking about today.
First, we'll go over what an anti-cheat actually is, its purpose and what it hopes to accomplish.
We'll talk a little bit about how anti-cheat has developed over the past 25 years or so.
We'll go a little bit into what direct memory access is and how it acts as a bridge to hardware, to cheating software.
And then we'll build up that bridge and really get it some good integrity with firmware so that anti-cheat doesn't detect us when we start doing our dirty work.
And then we'll take a brief look at cheat development and how DMA library can help transmute gathered information for what our purpose is.
And then towards the end of this talk, we'll go a little bit about anti-cheat's methodology for detecting direct memory access.
Get started.
What even is an anti-cheat?
Anti-cheat's core functionality is about detecting and preventing software and hardware that seeks to give an unfair advantage to other players.
So we'll only be talking today about cheats and anti-cheats from a competitive multiplayer standpoint.
And the prime motivation for why companies even put any care into anti-cheat development is to maintain their consistent player base.
Players constantly encountering cheaters in a game decreases enjoyment.
If you log on to a Counter-Strike match and every single time you just get burned by a cheater that you can't see, you don't want to play anymore.
You don't care about that game.
And a decrease in profit correlates directly to a decrease in players.
Or a decrease in players correlates to a decrease in profit.
That takes a hit from a reputational and financial standpoint of the company since games are a business at the end of the day.
So there's three different types of detection methods that anti-cheats use.
There's signature, heuristic, and reputational based detection.
Signature is exactly what it sounds like.
It blocks known bad binaries or hardware.
If an actor posts a cheat to a form that they've developed, a bunch of people start downloading it and wreaking havoc within a game.
Anti-cheat developers can come in, look at everybody who's been cheating, start developing some signatures on what programs they were using, and put in a block for like that file hash, for example.
This is the exact same thing as like virus total.
If someone uploads something that's known bad and it gets analyzed and a bunch of anti-cheats are now aware of it, whenever you go online and download a random file that's bad, it immediately gets blocked by that anti-virus.
Now heuristic based detection identifies cheats through behaviors and the execution of software or even hardware and seeks to prevent weird anomalies or known vectors that cheats commonly use.
This usually begins by scanning whenever the game process itself starts.
It looks for a couple different things, such as process injections, suspicious memory modifications, API hooks, unusual system calls, and a lot more.
The list constantly goes on.
Going back to that anti-virus comparison, if we download a program and our AV watches it as it starts to add registry keys, scanning through every file in the system, changing extensions, the anti-virus should know that something very bad is happening and we need to cut that off.
Same thing goes on with an anti-cheat in that perspective.
Now we won't go over it too much today, but I still think it's important to touch on reputational based.
Reputational based is about accounts that are connected to the game itself.
That looks at a couple different factors, like account age, hours played versus standard skill progression, performance history, like did this person start performing incredibly well out of nowhere, if they've ever bought anything in the past, because that usually likes to increase trust,
anomalies in their accuracy with how they're hitting shots, ban history, blah, blah, blah.
There's so much that reputational looks at.
So now, with all of these three, let's take a look at a little historical overview of how anti-cheat has changed over the past 25 years or so.
So we started off with Punkbuster, which is usually the first big anti-cheat that came onto the market.
It started off in 2000.
As of 2015, it's kind of defunct.
I think the last game to use it was some Battlefield PC clone.
I don't know.
It was really simple, relied on signature based detection and only reputational based detection.
It didn't have a lot of heuristic capabilities that we come to know from some of the newer anti-cheats.
Then there's also VAC, which is Valve Anti-Cheat.
That's been on the market since 2002, and it primarily targets games that are using Steam's source engine.
So think Counter-Strike, Team Fortress 2, anything that is developed by Valve themselves.
They started off really simple back in 2002 with just signature based detection, but as time has grown on, it's really evolved to use more of a machine learning approach and what they call their Overwatch system, which flags people who have really bad reputation or have a risky history,
and they have trusted players come in, review gameplay, and if necessary, ban them.
Now, both of these anti-cheats live in what's called the user-land of the system.
User-land means restricted access to system resources, they can't really detect sophisticated memory modifications, and they're vulnerable to user-land API hooking and a lot more.
Especially with what we're talking about today too, cheats that are living in the kernel, whether that be drivers or hardware, are really difficult for these two anti-cheats to detect.
So now we'll move on to Easy Anti-Cheat and BattleEye, which we'll call the mid-tier anti-cheats of today.
Both of these were released in 2006 in our system , and they're still present in today's market.
Some of the big games that are using these nowadays are still Fortnite, Rainbow Six Siege, PUBG, GTA, you name it.
The player counts in these games peak as high as 3 to 5 million per day, so these anti-cheats need to be in pretty good shape to support a player base that big.
Now, these are both kernel-level anti-cheats with signature and heuristic-based detection.
Living in the kernel means they have a lot more control and visibility over the whole system overall.
This allows for monitoring and blocking unauthorized drivers, detecting modification in the game's memory regions, rogue system call analysis, and a lot more.
And with what we're talking about today, it allows us to access and analyze hardware interfaces directly.
Even with all that, with all I mentioned, these anti-cheats still are not that good with detecting direct memory access and preventing those kind of hardware attacks.
While they're in the kernel, their methods of hardware spoofing detection are still really bad, and they're behind other kernel-level anti-cheats.
Similar to... Vanguard.
VGK.
If you play Valorant League of Legends, you know what VGK is.
This was developed in 2020 alongside Valorant, which was their flagship game at the time.
It's a kernel-level anti-cheat with advanced signature and heuristic-based detection.
Its main appeal is being one of the strongest anti-cheats on the market.
What separates this two is that they enforce a lot of strict requirements on your system, like TPM 2.0 and Secure Boot.
Some of their methods are employing aggressive memory scanning techniques, encryption of what's called the Control Register 3 and PML 4 memory table, strict integrity checks on devices all connected to the motherboard, and a lot more.
When it comes down to DMA, our firmware must be as robust as possible.
Alright.
I've thrown around the word DMA a lot, but I haven't actually described what it is.
So what is DMA?
Direct memory access allows an actor to access memory independently of the CPU.
The way a normal computer functions, a process through a system wall will request the CPU to retrieve a value found at a certain memory address.
The difference with direct memory access is that we use an external FPGA board like we see on the screen over there to access physical memory, otherwise known as RAM, through the memory controller rather than touching the CPU itself.
That allows us to covertly transfer memory from that target computer to the attacker computer, bypassing a lot of virtual memory protections or CPU protections.
Using that custom firmware allows us to appear as a legal device.
We can bypass some stuff like IOMMU, VTD, read and write directly to physical memory, access memory without triggering CPU event logs, and a lot more.
We couldn't move on from the slide without talking about PCI-Leach.
PCI-Leach provides a toolkit to actually read and write memory on that target system.
PCI-Leach serves as a memory analysis framework and it gives us the ability to do memory pattern scanning, page table manipulation, and a lot more.
Now PCI-Leach FPGA is an open source repository on GitHub and that contains the actual hardware designs for the FPGA board itself.
And using this we can tweak our firmware with system Verilog and booting up Vivado to appear legal.
And I've been working on this talk for six months.
If any of you have had to open Vivado, I'm so sorry.
It's one of the most ridiculous tools I have ever worked with.
So now we'll get into the meat of this talk, which is actually building firmware with PCI-Leach FPGA.
The main component of direct memory access in this context is to build out that firmware to appear like a legal card.
We need to appear like a real legal card, like a network card or something, to actually bypass the entity by just blending in within our environment.
So we first start off by selecting a donor card, which can be whatever you want.
And then after that we will create a configuration space to present that card as legal and define who we are, what we do.
Then we'll step into the write mask, which defines writable bits within the configuration space.
Then we'll move forward to a bar controller, which actually manages the memory and defines register for this behavior, which actually allows us to emulate how we act.
Then we'll go over a couple additional implementations to really improve our emulation there.
So we'll start with the donor card.
To begin this process, you need to just go out and find any PCIe card.
Anything is really viable.
It's always recommended, if you are interested in this, to start with a Realtek Wi-Fi card for its ease of development, specifically relating to how we act, which is the bar controller, more on that soon.
In my experience, when I was working on proof of concept for this talk beginning last October, I just drove down Milwaukee PC, grabbed a random Wi-Fi card off the shelf, which led me down the path of developing what was called an RT-5392 firmware, which is a raw link NIC.
So now that we got our donor card, we actually got to start talking about defining who we are to appear as legit.
The configuration space at its core just defines who this card is and its purpose.
Whenever you plug something into the motherboard, on boot, the device will report out and says, hello, this is who I am and this is my purpose.
Any kernel-level anti-cheat worth its salt will scan through PCI devices that are connected to the motherboard to check for suspicious behaviors.
If we plug in a real DMA card and we've done nothing to it and it reports the anti-cheat that we're a DMA card, we get blocked.
It's a really bad decision.
So, in order to evade that, we need to start picking apart what are called donor values.
So that can fall under device and vendor ID, revision ID, and bar values.
We need to spend time logging this so that we can actually reflect that when we go to build out our firmware.
And then we can also touch on shadow configuration space too.
This allows us to provide higher resolution dump, which you can think about being stored on top of the configuration space as a mask, really to increase granularity since the main IP core is missing a lot of those extended capabilities.
And we can step forward to the write mask.
So the write mask allows us to create what's called a permission map.
We can define which bits in the configuration space are actually writable.
Might sound really weird to say that, but a configuration space does actually more than who I am and what I do.
It also houses an area to indicate certain events.
Most common being error reporting.
If our device on a donor card is having difficulty communicating on the system, we need to throw up that error, and that space needs to be writable.
Because if we throw up the error and we're stuck on a read-only bit, we're just constantly throwing up that error, and that's a really good detection method for anti-cheats that we're working with in a legal device that is probably not used for good.
PCI Leach FPGA doesn't have a write mask built in since it differs per device.
We need to actually spend time digging through and reflecting that behavior that is seen in the donor's configuration space.
As I said before, anti-cheats, this is a really easy way to check and see if a device is illegal.
We can look at known status registers like the link status register.
If that's not correctly marked as RW1C or RW for read-write, that's an incident indicator will probably get blocked like that.
Now we can step to a bar controller.
This is where you're going to spend a lot of your time banging your head.
We've defined who we are, we've defined what we do, now we need to start defining how we act with a base address register.
The bar is defined within our configuration space and it lets our operating system know where we're actually mapped to.
The bar controller sits in that memory region and this basically says this is where you can find me and interact with what I have to offer.
That's really difficult to do without some information, so we need to go into two vectors in our donor card using OS debug logs and driver initialization logs.
We can use a tool called MMIO trace which logs device behavior and allows us to see how a real donor card acts that we're trying to emulate.
What you go through with this is you just load the driver while you're working with the donor card and review a bunch of read and writes that are going on when it does its initial initialization or connected to a network depending on what you're using at.
That's not enough.
If you were able to look at this, you would have no idea what's going on, so we need to also point ourselves to what's the Linux driver code that's listed on Torvald's GitHub repo.
Within this driver code, we're able to see offsets within the C code that actually contextualizes those read and writes that we just dumped from MMIO trace.
Then we can use that paired alongside Windows TLP transactions when we pump up the driver using any debugger of your choice to ensure those consistency between operating systems because the requirements to load a driver on Windows and Linux are a little bit different,
so we need to make sure we have that aligned to appear legit.
Once you got all that, we can start writing our system Verilogs and have our card respond to the target driver, which is almost a bare necessary thing to do at this point to evade detection on things like easy anti-cheater battle line.
Then we can move forward and talk a little bit about additional implementations.
So I got two listed here.
There are a lot more you can do, but I think these are still important to go over.
First, we have interrupt handling.
This just lets the CPU know that an event has occurred.
Whenever you plug in a card and it initializes, it does a data transfer, it throws up an error, status buffer changes, anything you can think of, it always sends an interrupt.
Implementing this without this kind of handling higher tier anti-cheats like Vanguard, for example, will pick up that you're suspiciously quiet and just block you.
So if a device is not functioning normally, even if it is a completely legit card, it's not sending interrupts, it's not needed, and it just gets blocked.
So we really need to emulate that behavior.
One of the final stages of true emulation is called link state emulation, specifically relating to, like, network cards, for example.
Implementing a link state allows us to actually simulate an active network connection.
On higher tier anti-cheats like Vanguard, again, even if we're fully undetectable, if we don't have that net connection, we'll quietly be blocked, because there's no reason to have a Wi-Fi card active and running while you're playing a game if you're not even connected to Wi-Fi for it.
So with this, we've talked a lot about firmware, but what actually goes on to the cheating side of things?
I think it would first be beneficial if we talk a little bit and give an introduction about what a cheat actually is.
So we'll only be talking about external cheats today, because that's more related to DMA.
An external cheat refers to a separate executable which operates independently of the game process itself.
Doesn't modify any game code directly, it reads and writes memory externally, and it communicates through that memory manipulation.
What's nice is DMA lets us bypass a lot of those traditional memory protections through that direct level hardware access.
But all of this is a lot tougher without DMA library.
This is a publicly available C++ repository that actually unlocks our card's functionality by reading and processing memory from the game.
Utilizes a lot of those same components that we see in PCI leach, and it provides a lot of memory read and write operations, pattern scanning, region mapping, page table manipulation and more.
Those are a lot of complex operations when it comes down to DMA and that really helps us manage that device communication between the target and attacker computer.
And it fully solidifies that gap between hardware and software.
So we'll move forward onto what actually goes into developing a cheat.
What you're seeing right here, this is not an all-inclusive list by any means.
Attack paths will be different per game, but a lot of older games will go over this common attack path, which begins starting by finding the module base address.
This is the entry point into the game, where similar to like the bar region where we sit in that region, games also have that memory area that we need to find, so that's a module base address.
And once we're in that region, we need to start finding offsets.
Games are constantly shifting depending on what a user is doing or when they launch it or what's going on in the system, so we can't actually hard code any memory addresses, that's really bad practice, so we need to do what are called offsets.
And alongside of this, games also use what are called pointer chains to keep track of important objects.
Games use these pointer networks and mainly house information like player positions, weapon data, and a lot more.
When it comes down to newer games like Valorant, we actually need to write logic to bypass pointer encryption, but we'll touch more on that in a little bit.
So let's take a look at DMA library itself.
The way DMA library starts is by loading those DLLs from PCI leach to actually initialize and manage the card and provide that foundation for what we're working with.
Then afterwards, we got to create a device handle to establish communication with the hardware, setting up those command channels and initializing those pathways.
Afterwards, we got to do some cleanup and configure registers, which act as control points to managing that DMA transfer parameter and actually initialize the full process to map memory.
Now we got all that done, we're ready to actually communicate with our DMA card, we can circle back to pointer encryption.
Now Valorant specifically uses a sophisticated encryption to protect some of those critical pointers, such as UWorld, which is the base pointer for Unreal Engine games that we can think about it like that, ActorHealth, where other players are, and a lot more.
Encryption is done primarily to restrict that user land and kernel level access to sensitive information by guarding the process's memory table.
Again, as earlier, DMA bypasses a lot of those virtual protections, but the issue is it's still encrypted and we can't read anything through this.
But we can reverse engineer the Valorant binary itself.
We can extract and implement that decompiled decryption algorithm and apply that same logic that Valorant uses to actually decrypt those core pointers.
Once we got those target values, we can import and implement that functionality to draw boxes on the screen relative to where other actors are.
World of Screen function, you kind of can't see at the bottom there, but it's the mathematical heart of ESP.
ESP just means being able to see through walls and things like that.
And we can convert that 3D coordinates to 2D positions.
That uses the camera's position, player's location, field of view, and a couple different factors to actually get that to render on the screen.
Fully using all this, we gain our unfair advantage of actually being able to see through walls.
I've been rattling off a lot about firmware and cheat development, and it's a lot, but what does this actually look like in practice?
So I'll explain this a little more in a second, but we have our attacker computer right here, and we're connecting to our DMA through a cable, turning the screen through our cloud.
Usually you would not be able to see through that, but since we're running our cheat on that attacker computer and transferring that data over from the DMA card to that, we can actually manipulate and modify it there.
That's a lot there, and that's really confusing just to look at.
So the way this cheat works, we have the DMA card plugged into the target PC itself, it's pulling those key values from there, bringing it over to the attacker laptop where our cheat is actually running, and then we're using that decompiled algorithm to actually display everything,
and then we render those boxes on the screen, and then we use what's called a fuser, which actually overlays HDMI onto the main target PC monitor that we see there.
We've talked a lot, how do you even detect any of this?
Method is through errors we've made along the way.
Any good anti-cheat will scan through the device's configuration space, it'll look through its behaviors and cross-reference between known devices to ensure that everything is legal.
From a firmware view, human error is what will trip us up the most.
Incorrect configuration space values, improper bar implementations, suspicious memory scanning or pattern accesses will cause signature and heuristic based detection to flag us as illegal and block us rendering DMA defunct in that case.
From a software angle, we still need to make sure we're not doing any sort of unusual read and write frequencies, non-standard access sequences, any large scale memory scanning operations that will immediately trip you in detection.
The true goal of evasion when it comes down to direct memory access is to blend in as much as possible.
We need to have our hardware in a good state with emulation and our software needs to blend in.
Thank you guys so much.
I appreciate it.
I've got a couple minutes for questions.
Yep, exactly that.
We're trying to blend into our environment.
Sorry for holding my hand up, it's so bright right now.
So what you're saying is why don't we just use the donor card itself and then implement DMA functionality?
The main reason we use... because we have to use...
it has a certain chip, XLinux 7 something, I'm horrible at pronouncing it, we need to have that chip itself and have some of the data and JTAG functionalities on that so we can't actually repurpose the firmware on that because it's not really compatible with that.
Plus PCI leash FPGA itself has a lot of the backbone designs that we would actually need.
It would require reengineering the whole thing so it's a lot easier to just start out with the original hardware design itself and then just emulate it from there.
Yeah, exactly.
Possibly I haven't fully looked into it.
There might be some sort of incompatibility issues but I could get back to you on that.
Do we have to deal with any address space layout randomization ASLR correctly?
Not to my knowledge.
I could be wrong on that.
I've mainly focused on the firmware side of things.
I mainly use this as a kind of just example and then running the cheat off there.
So some of the more specific aspects of C++ design I'm not too familiar with.
At least when it comes down to like... I can still get back to you though.
I can do a source code analysis.
I did not.
I purposely used a completely separate system and didn't log into online games to test any of this because I didn't want to burn myself.
You're good.
Which slide?
Oh, okay.
Yeah, again I'm a little lackluster in the C++ cheat development space.
Yeah, no, that's completely viable.
I know who's credited here, Noah Tonegate is doing research using DMA to bypass like EDR or attack EDR itself.
Completely feasible.
I haven't fully looked into it too much because I was more focused on a cheating lens because I think it's a better introductory point because it does get a little heavy.
I can probably send you a bit more specialized resources on that though if you're curious.
Okay.
We are at 3.30.
I want to be respectful of people's time.
That's tough.
I'll say Black Ops 1 because I played that a lot as a kid.
It was really enjoyable.
Okay.
Thank you guys so much.
If you have any additional questions, please do not hesitate to reach out to me.