BattlEye is a prevalent german third-party anti-cheat primarily developed by the 32-year-old founder Bastian Heiko Suter. It provides game publishers easy-to-use anti-cheat solutions, using generic protection mechanisms and game-specific detections to provide optimal security, or at least tries to. As their website states, they are always staying on top of state-of-the-art technologies and utilizing innovative methods of protection and detection, evidently due to their nationality: QUALITY MADE IN GERMANY. BattlEye consists of multiple organs that work together to catch and prevent cheaters in the respective games that pay them. The four main entities are:
Recently, a dump of BattlEye’s shellcode surfaced on the internet, and we decided to make a write-up of what exactly the current iteration of BattlEye is actively looking for. We have not worked on BattlEye for the past 6 months, so the last piece of shellcode we have dumped is most likely obsolete. Miscellaneous parts of code were recognized completely from memory in this recent dump, suggesting that BattlEye only appends to the shellcode and does not remove previous detection procedures.
BattlEye presumably streams its shellcode from their server to the windows service, known as BEService. This service communicates with the battleye module located inside of the game process, known as BEClient. The communication is done over the named pipe \\.\namedpipe\Battleye
and up until last year was unencrypted. Now, all communication is encrypted through a xor cipher with very small keys, making known plaintext attacks trivial. When the shellcode has been streamed to the client, it is allocated and executed outside of any known modules, making distinction easy. To dump the shellcode, you can either hook prevalent windows-api functions like CreateFile, ReadFile, et cetera, and dump any caller’s respective memory section (query memory information on the return address) that is outside of any known module, or periodically scan the game’s virtual memory space for executable memory outside of any known module, and dump it to disk. Make sure to keep track of which sections you have dumped so you do not end up with thousands of identical dumps.
The following pseudo-code snippets are heavily beautified. You will not be able to dump the BattlEye shellcode and instantly recognize some of these parts; the shellcode does not contain any function calls, and many algorithms are unrolled. That doesn’t really matter, as when you’re finished reading about this atrocious anticheat, you will have a field day bypassing it (:
The most common detection mechanism anti-cheat solutions utilize is memory enumeration and memory scanning, to detect known cheat images. It’s easy to implement and quite effective when done correctly, as long as you don’t forget basic assembly and blacklist a common function prologue, as we’ve seen in the past.
Battleye enumerates the entire address space of the game process (current process in the following context) and runs various checks whenever a page is executable and outside of the respective shellcode memory space.
This is their implementation:
BattlEye will flag any anomalies in the memory address space, primarily executable memory that does not correspond to a loaded image:
As we previously mentioned, BattlEye also scans memory of the local process for various hardcoded patterns, as the following implementation shows. What you might realize when reading this pseudo-code is that you can bypass these checks by overwriting any loaded module’s code section, as they will not run any pattern scans on known images. To prevent being hit by integrity checks, load any packed, whitelisted module and overwrite code sections marked as RWX, as you can’t run integrity checks without emulating the packer. The current iteration of BattlEye’s shellcode has these memory patterns hardcoded:
These memory patterns also contain a two-byte header, respectively an unknown static value 05 and an unique identifier. What you won’t see here is that BattlEye also dynamically streams patterns from BEServer and sends them to BEClient, but we won’t be covering those in this article.
These are iteratively scanned for by the following algorithm:
The module specific checks will report you for having specific modules loaded into the game process:
A very specific module check has been added that will report you to the server if your loaded module meets any of these criteria:
We do not know which modules meet these criteria, but suspect it is an attempt to detect very few, specific cheat modules.
Edit: @how02 alerted us that the module action_x64.dll
has the timestamp 0x5B12C900
, and contains a code section that is writeable, which could be exploitable as previously mentioned.
BattlEye has also incorporated a very questionable detection routine that we believe is seeking out memory with the flag PAGE_GUARD set, without actually checking if the PAGE_GUARD flag is set:
BattlEye’s shellcode enumerates every single window that is currently visible while the game is running, which it does by iterating windows from the top-down (z-value). Window handles inside of the game process are excluded from the aforementioned enumeration, as determined by a GetWindowThreadProcessId call. You can therefore hook the respective function to spoof ownership of the window and prevent BattlEye from enumerating your window.
If fewer than two windows were enumerated, the server gets notified. This is probably done to prevent someone from patching the respective functions, preventing any windows from being looked at by BattlEye’s shellcode:
BattlEye enumerates all running processes with a CreateToolhelp32Snapshot call, but does not handle any errors, making it very easy to patch and prevent any of the following detection routines:
If image is inside of at least two sub directories (from disk root), it will flag processes if the respective image path contains atleast one of these strings:
If your executable path matches one of these strings, the server will get notified of your executable path, as well as information on whether or not the parent process is one of the following (contains respective flag bit sent to server):
If the client cannot open a handle with the respective QueryLimitedInformation rights, it will set the flag bit 0x04 if error reason for the OpenProcess call fail does not equal ERROR_ACCESS_DENIED, which gives us the final enumeration container for the respective flag value:
If steam is the parent process, you will get instantly flagged and reported to the server with report id 0x40
If your process matches any of the miscellaneous criteria below, you will get instantly flagged and reported to the server with report id 0x38
BattlEye is keeping its eye out on the steam game overlay process, which is responsible for the in-game overlay most steam users know. The full image name of the steam game overlay host is gameoverlayui.exe and is known to be exploited for rendering purposes, as it is quite trivial to hijack and maliciously draw to the game window. The condition for the check is:
file size != 0 && image name contains (case insensitive) gameoverlayu
The following checks specific to the steam game overlay are almost identical to the routines being ran on the game process itself, therefore they have been omitted from the pseudo code.
The steam game overlay process will have its memory scanned for patterns and anomalies. We were unable to go further down the rabbit hole and find out what these patterns are for, as they are very generic and are probably cheat-module related.
The scan routine also looks for any anominalies in the form of executable memory outside of loaded images, suggesting intruders have injected code into the overlay process:
If the steam game overlay process has been protected using any windows process protection like Light (WinTcb), the server will get notified.
You will also get reported with report id 3B if the respective OpenProcess call to the aforementioned game overlay process returns ERROR_ACCESS_DENIED.
Modules of the steam game overlay process are also enumerated, specifically looking for vgui2_s.dll
and gameoverlayui.dll
Certain checks have been put in place for these respective modules, beginning with gameoverlayui.dll.
If this condition matches: [gameoverlayui.dll+6C779] == \00\8B\E5\5D\C3\CC\CC\B8\??\??\??\??\C3\CC\CC\CC
, the shellcode will scan a vtable at the address stored in the bytes \??\??\??\??.
If any of these vtable entries are outside of the original gameoverlayui.dll module or point to an int 3
instruction, you get reported with the report id 3B
.
The vgui2_s.dll
module also has a specific check routine set in place:
The previous routine checks for a modification at 48378, which is a location in the code section:
The routine then checks for a very specific and seemingly garbage modification:
We were unable to obtain a copy of vgui2_s.dll
that did not match the first of the two aforementioned checks, so we can’t discuss which vtable it is checking.
Threads in the steam game overlay process are also enumerated:
The memory address space of the windows process lsass.exe, also known as the Local Security Authority process, is enumerated and any anomalies will be reported to the server, just like we’ve seen in the two previous checks:
LSASS has previously been exploited to perform memory operations, as any process that would like an internet connection needs to let LSASS have access to it. BattlEye has currently mitigated this issue by manually stripping the process handle of read/write access and then hooking ReadProcessMemory/WriteProcessMemory
, redirecting the calls to their driver, BEDaisy. BEDaisy then decides whether or not the memory operation is a legit operation. If it determines that the operation is legitimate, it will continue it, else, they will deliberately blue-screen the machine.
BattlEye gathers miscellaneous information and sends it back to the server with the report id 3C. This information consists of:
Window text (Unicode) Window class name (Unicode) Window style Window extended style Window rectangle Owner process image path Owner process image size
Image name Image path Image size Handle access
…\Content\Paks\TslGame-WindowsNoEditor_assets_world.pak …\Content\Paks\TslGame-WindowsNoEditor_ui.pak …\Content\Paks\TslGame-WindowsNoEditor_sound.pak
…\BLGame\CookedContent\Script\BLGame.u
Any jump instructions (E9) are followed and the final address get’s logged
BattlEye has implemented a specific and rather lazy check to detect the presence of the public bypass known as NoEye, by checking the file size of any file found by GetFileAttributesExA with the name of BE_DLL.dll, suggesting the library file can be found on disk.
The devices Beep and Null are checked, and reported if present. These two are not normally available on any system, which would indicate someone manually enabled a device, also known as driver device hijacking. This is done to enable IOCTL communication with a malicious driver without requiring an independent driver object for said driver.
BattlEye will also queue the current thread for a one second sleep and measure the difference in tickcount from before and after the sleep:
BattlEye has added a very lazy integrity check to prevent people loading the 7zip library into game processes and overwriting the sections. This was done to mitigate the previous pattern scans and anomaly detections, and battleye decided to only add integrity checks for this specific 7zip library.
Battleye checks the presence of the windows hardware abstraction layer dynamic link library (hal.dll), and reports to server if it is loaded inside of the game process.
BattlEye also checks for various images loaded into the game process. These modules are presumably signed images that are somehow manipulated into abusive behaviour, but we can’t comment on the full extent of these modules, only the detections:
For reference, here are the enumerative ids for the modules:
The BattlEye shellcode will also search the system wide list of tcp connections (known as the tcp table), and report you for being connected to at least one of the specific cloudflare-gateway ip addresses belonging to the german pay-to-cheat website https://xera.ph/. This detection mechanism was added to the shellcode to detect any user using their launcher while the game is running, making them easily identifiable. The only problem with this mechanism is that the cloudflare-gateway ip addresses might switch hands later on and if the new owner of the respective ip addresses distribute software connecting to their servers on that specific port, false positives will without a doubt occur.
Users of the pay-to-cheat provider xera.ph have been reporting detections for a long time, without the developers being able to mitigate. When we contacted the responsible developers from xera.ph to make them aware of their stupidity, they misread the situtation and handed a free copy to us without thinking twice that we would crack it and release it. We won’t, but you probably shouldn’t send proprietary, licensed binaries for free to reverse engineers without the slightest expectation of piracy. ;)
For reference, here are the known report types from the shellcode: