- Game Name
- N/A
- Anticheat
- Battleye
- How long you been coding/hacking?
- 7 years
- Coding Language
- C++
BE is the second most popular mature, kernel mode anticheat. Battleye does many of the same things as EAC but it is less popular and easier to bypass. Despite being easier, you still need to know what you're doing if you want to start hacking a BE protected game. This article will tell you everything you need to get started with a Battleye bypass.
We have two guides which should be viewed before reading this BE specific guide:
Guide - How to Bypass Anticheat - Start Here Beginner's Guide
Guide - How to Bypass Kernel Anticheat & Develop Drivers
This article contains information compiled from many sources, full credits to these gentlemen: @iPower, @_xeroxz, vmcall & everyone at secret.club
(img courtesy of BattlEye – The Anti-Cheese Gold Standard)
Games utilizing Battleye
Battleye Anticheat Versions
It's important to understand that the version of BE is not the same on every game, on an older game it will be easier to bypass. Newer more popular games will have the latest version. Battleye has been around since 2004 and has been actively developed throughout it's history. Tutorials and information from 5 years ago will not work on the newest versions, but still worth reading. Battleye was first developed as a third party anticheat for Battlefield Vietnam and Battlefield 1942 but became more popular and robust with it's integration with ARMA 3 and DayZ.
Battleye Anticheat is a Kernel Mode Anticheat
A processor in a Windows computer has two different modes: kernel mode and user mode. The Usermode & Kernelmode construct is built into the CPU. The low level core functionality of the operating system is done in kernel mode, which is a privileged part of memory that is not accessible from user mode and executes with privileged status on the CPU. Drivers are not just limited to Hardware Drivers, you can make a .sys driver to do anything you want in kernel mode, including bypass anticheat and perform cheat functionality. Usermode and kernel are separated, nothing you do in usermode will bypass the kernel driver.
Because BE is a kernel mode anticheat you will also need to be in kernel to make a Battleye bypass.. You can use a VM or hypervisor to dump the Battleye module and reverse engineer it, keep in mind BE does have some emulation detection.
Read the main Kernel Guide to learn everything you need to do know before you start working on Battleye.
But Rake, I don't want to learn, I just want to paste a Battleye bypass!
Ok before we go to far I will give you a simple 6 step process that is the easiest way to paste your way into kernel:
Manually Mapped Driver Detection
To avoid your manually mapped driver getting detected you need to clear PiDDBCacheTable & MmUnloadedDrivers, and stop the enumeration of your own system pools & threads.
Battleye Anti Cheat Components
Battleye Anticheat Features
Battleye is actively scanning and uploading a lot of information to their servers while you play:
How does Battleye protect itself?
secret.club Battleye articles
secret.club has some of the best content regarding Battleye so you will definitely want to look at these
_xeroxz's Articles and Repos
@_xeroxz has done bunch of work on Battleye, on par with some of the secret.club articles, be sure to check them out too
Some important excerpts from his articles
BEDaisy Inline Hooks
BEDaisy places inline hooks on both NtWriteVirtualMemory and NtReadVirtualMemory inside of lsass.exe and csrss.exe. The reason for these hooks are because csrss.exe and lsass.exe need handles with PROCESS_VM_OPERATION in order to function properly. The handles that csrss.exe and lsass.exe would have to BEDaisy’s protected processes are stripped of PROCESS_VM_OPERATION via BEDaisy’s enumeration of the protected processes handle table by calling ExEnumHandleTable. In order to allow for csrss.exe and lsass.exe to read/write to the games memory BEDaisy proxies their read/write calls.
BEDaisy Imports
If you take a look at BEDaisy.sys’s import address table you can see this nice little import by the name of MmGetSystemRoutineAddress, This function is used to dynamically resolve imports at runtime. List of BEDaisy imports: battleyes imports ($24) · Snippets
LOADED KERNEL MODULE ENUMERATION
BEDaisy enumerates all loaded modules by calling NtQuerySystemInformation with SystemModuleInformation. If a black listed driver is found, the game will not run, drivers like the notorious intel lan driver, capcom, and gdrv are all blocked by BEDaisy.
RUNNING PROCESSES ENUMERATION
BEDaisy also constantly enumorates running processes using NtQuerySystemInformation except with SystemProcessInformation, this can also be easily hooked to filter out specific executables from BEDaisy’s queries.
ASYNCHRONOUS PROCEDURE CALL (APC)
BEDaisy registers APCs on all user mode threads in every process, the APC code that is executed simply calls RtlWalkFrameChain which inturn provides BEDaisy with all of the stack frames on the thread
== end of _xeroxz's content ==
ObRegisterCallbacks
Battleye blocks usermode access to a process by conventional means via ObRegisterCallbacks, essentially when you call OpenProcess() it will not let you get a handle to the game process so you can't read or write memory or attach a debugger. This was one of the first things implemented in Battleye. In order to circumvent that you need to hook their driver, collide with their callbacks, or simply remove their callbacks, read Douggem's article.
You can see it being called in @iPower 's log
In the past this was all that was needed to attach Cheat Engine to the game, but Battleye has been updated many times since this was implemented & it's protection has been improved over many years, just fixing ObRegisterCallbacks is no longer enough to bypass.
Bypass Process & Thread Callbacks
Here is a driver source code to disable the process and thread callbacks from anher0:
LuluVisor BEDaisy Logs
Here is an example of some of iPower's logs from his hypervisor, which show you what BE is doing in Fortnite:
Temporary Battleye Bypass for improperly implemented anticheat
It's happened a few times on a couple games where you can just unload the Battleye driver and the game doesn't stop running, it's easy to do, but unlikely it will work on most new games. @gulerardaeren posted this a while back, it has worked previously on Zula, Crossfire, Apex Legends and others as well
Dumped Modules
The first thing you need to do to reverse engineer Battleeye is to dump the system driver and usermode modules from memory, you will find a couple pre-made dumps below. Once you have the dumps you can load them into IDA Pro and start looking around.
GH Battleye Bypass Resources
External Resources
The most important thing you can do to learn about BattleEye is to watch this video made by the DayZ developers about how Battleeye helps them stop cheaters. This is also an excellent video for anyone wanting to learn about anticheat.
Checkout Douggem's site for good info and his video:
Credits: @iPower, @_xeroxz, douggem, vmcall & everyone at secret.club
Attachments
I have attached a bunch of files from github and other places in case they get deleted, many of these are copies of the files in the links above
We have two guides which should be viewed before reading this BE specific guide:
Guide - How to Bypass Anticheat - Start Here Beginner's Guide
Guide - How to Bypass Kernel Anticheat & Develop Drivers
This article contains information compiled from many sources, full credits to these gentlemen: @iPower, @_xeroxz, vmcall & everyone at secret.club
(img courtesy of BattlEye – The Anti-Cheese Gold Standard)
Games utilizing Battleye
- Fortnite
- PUBG
- Escape from Tarkov
- Rainbow Six Siege
- Ark Survival Evolved
- ARMA II
- ARMA III
- DAYZ
- H1Z1
- Surivial Of the Fittest
- PlanetSide 2
- Survarium
- Project Argo
- Unturned
- Insurgency
- Day of Infamy
- The Isle
- Line of Sight
- Conan Exiles
- Tibia
- Black Squad
- S4League
- Zula
- Islands of Nyne
- BlackLight Retribution
- SOS
- Pixark
- Heroes & Generals
- Bless Online
- and more
Battleye Anticheat Versions
It's important to understand that the version of BE is not the same on every game, on an older game it will be easier to bypass. Newer more popular games will have the latest version. Battleye has been around since 2004 and has been actively developed throughout it's history. Tutorials and information from 5 years ago will not work on the newest versions, but still worth reading. Battleye was first developed as a third party anticheat for Battlefield Vietnam and Battlefield 1942 but became more popular and robust with it's integration with ARMA 3 and DayZ.
Battleye Anticheat is a Kernel Mode Anticheat
A processor in a Windows computer has two different modes: kernel mode and user mode. The Usermode & Kernelmode construct is built into the CPU. The low level core functionality of the operating system is done in kernel mode, which is a privileged part of memory that is not accessible from user mode and executes with privileged status on the CPU. Drivers are not just limited to Hardware Drivers, you can make a .sys driver to do anything you want in kernel mode, including bypass anticheat and perform cheat functionality. Usermode and kernel are separated, nothing you do in usermode will bypass the kernel driver.
Because BE is a kernel mode anticheat you will also need to be in kernel to make a Battleye bypass.. You can use a VM or hypervisor to dump the Battleye module and reverse engineer it, keep in mind BE does have some emulation detection.
Read the main Kernel Guide to learn everything you need to do know before you start working on Battleye.
But Rake, I don't want to learn, I just want to paste a Battleye bypass!
Ok before we go to far I will give you a simple 6 step process that is the easiest way to paste your way into kernel:
- Video Tutorial - How to Make a Windows Kernel Mode Driver Tutorial
- Video Tutorial - Kernel 2 - Usermode Communication - IOCTL Tutorial
- Video Tutorial - How to Write Memory from Kernel - MmCopyVirtualMemory Tutorial
- Experiment with this source code Source Code - CSGO Kernel Driver Multihack
- Use kdmapper which uses a vulnerable Intel driver to manually map your kernel driver (make sure anticheat is not loaded yet)
- Start the game and use your usermode application to write to the game memory
Manually Mapped Driver Detection
To avoid your manually mapped driver getting detected you need to clear PiDDBCacheTable & MmUnloadedDrivers, and stop the enumeration of your own system pools & threads.
- PiDDBCacheTable & MmUnloadedDrivers
- system pool detection
- system thread detection
Battleye Anti Cheat Components
- BEService - Windows service that communicates with BEServer, which provides BEDaisy and BEClient communication capabilities
- BEDaisy - kernel driver that registers callbacks and minifilters to prevent cheaters from modifying the game
- BEClient - usermode DLL that is responsible for most of the detection vectors, it is mapped into the game process after initialization
- BEServer - backend-server that is responsible for collecting information and taking concrete actions against cheaters
Battleye Anticheat Features
- Debugger detection
- Signature based detection of known cheats
- Open game process handles
- Detection of manually mapped modules, i.e. executable pages not backed by a image on disk
- Process handle creation is blocked
- Overlays detection
- Steam Overlay hooks and hacks embedded in steam process's
- lsass.exe modifications
- game files integrity checks
- TCP connections to cheat sites
- module name and timestamp blacklist
- certificate blacklist
- driver blacklist
- stack walking / ret check
- single stepping to detect code outside of usermode memory range
- hypervisor detection
Battleye is actively scanning and uploading a lot of information to their servers while you play:
- all running processes
- all device drivers
- all window names
- options to upload more if anomalies are detected
How does Battleye protect itself?
- virtualization
- streams shellcode into memory
- integrity checks on it's modules & shellcode
- encrypted traffic with BE server
- encrypted named pipe communication
- it does extra logging on computers with lots of reversing tools
secret.club Battleye articles
secret.club has some of the best content regarding Battleye so you will definitely want to look at these
- BattlEye anti-cheat: analysis and mitigation
- BattlEye shellcode updates
- BattlEye stack walking
- BattlEye single stepping
- BattlEye hypervisor detection
- BattlEye communication hook
- Bypassing BattlEye from user-mode
- BattlEye reverse engineer tracking
- How anti-cheats detect system emulation
- How Escape from Tarkov ensures game integrity
- Cracking BattlEye packet encryption
- BattlEye client emulation
_xeroxz's Articles and Repos
@_xeroxz has done bunch of work on Battleye, on par with some of the secret.club articles, be sure to check them out too
Some important excerpts from his articles
BEDaisy Inline Hooks
BEDaisy places inline hooks on both NtWriteVirtualMemory and NtReadVirtualMemory inside of lsass.exe and csrss.exe. The reason for these hooks are because csrss.exe and lsass.exe need handles with PROCESS_VM_OPERATION in order to function properly. The handles that csrss.exe and lsass.exe would have to BEDaisy’s protected processes are stripped of PROCESS_VM_OPERATION via BEDaisy’s enumeration of the protected processes handle table by calling ExEnumHandleTable. In order to allow for csrss.exe and lsass.exe to read/write to the games memory BEDaisy proxies their read/write calls.
BEDaisy Imports
If you take a look at BEDaisy.sys’s import address table you can see this nice little import by the name of MmGetSystemRoutineAddress, This function is used to dynamically resolve imports at runtime. List of BEDaisy imports: battleyes imports ($24) · Snippets
LOADED KERNEL MODULE ENUMERATION
BEDaisy enumerates all loaded modules by calling NtQuerySystemInformation with SystemModuleInformation. If a black listed driver is found, the game will not run, drivers like the notorious intel lan driver, capcom, and gdrv are all blocked by BEDaisy.
RUNNING PROCESSES ENUMERATION
BEDaisy also constantly enumorates running processes using NtQuerySystemInformation except with SystemProcessInformation, this can also be easily hooked to filter out specific executables from BEDaisy’s queries.
ASYNCHRONOUS PROCEDURE CALL (APC)
BEDaisy registers APCs on all user mode threads in every process, the APC code that is executed simply calls RtlWalkFrameChain which inturn provides BEDaisy with all of the stack frames on the thread
== end of _xeroxz's content ==
ObRegisterCallbacks
Battleye blocks usermode access to a process by conventional means via ObRegisterCallbacks, essentially when you call OpenProcess() it will not let you get a handle to the game process so you can't read or write memory or attach a debugger. This was one of the first things implemented in Battleye. In order to circumvent that you need to hook their driver, collide with their callbacks, or simply remove their callbacks, read Douggem's article.
You can see it being called in @iPower 's log
Code:
[ LuluVisor ] TM -> KM Transition! Function called: ObRegisterCallbacks
[ LuluVisor ] Function called at: BEDaisy.sys+0028919c
Bypass Process & Thread Callbacks
Here is a driver source code to disable the process and thread callbacks from anher0:
C++:
#include <ntifs.h>
#include <windef.h>
// Pre-Processor definitions for our I/O control codes.
#define REMOVE_BEOBJECT_CALLBACKS_IOCTL CTL_CODE(FILE_DEVICE_KS, 0x806, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
#define RESTORE_BEOBJECT_CALLBACKS_IOCTL CTL_CODE(FILE_DEVICE_KS, 0x807, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
// Global variable to our device.
PDEVICE_OBJECT deviceObj = NULL;
// QWORD
typedef unsigned __int64 QWORD;
// OLD_CALLBACKS
typedef struct _OLD_CALLBACKS {
QWORD PreOperationProc;
QWORD PostOperationProc;
QWORD PreOperationThread;
QWORD PostOperationThread;
} OLD_CALLBACKS, *POLD_CALLBACKS;
// CALLBACK_ENTRY
typedef struct _CALLBACK_ENTRY {
WORD Version; // 0x0
WORD OperationRegistrationCount; // 0x2
DWORD unk1; // 0x4
PVOID RegistrationContext; // 0x8
UNICODE_STRING Altitude; // 0x10
} CALLBACK_ENTRY, *PCALLBACK_ENTRY; // header size: 0x20 (0x6C if you count the array afterwards - this is only the header. The array of CALLBACK_ENTRY_ITEMs is useless.)
// CALLBACK_ENTRY_ITEM
typedef struct _CALLBACK_ENTRY_ITEM {
LIST_ENTRY CallbackList; // 0x0
OB_OPERATION Operations; // 0x10
DWORD Active; // 0x14
CALLBACK_ENTRY *CallbackEntry; // 0x18
PVOID ObjectType; // 0x20
POB_PRE_OPERATION_CALLBACK PreOperation; // 0x28
POB_POST_OPERATION_CALLBACK PostOperation; // 0x30
QWORD unk1; // 0x38
} CALLBACK_ENTRY_ITEM, *PCALLBACK_ENTRY_ITEM; // size: 0x40
// Dummy object callback functions.
OB_PREOP_CALLBACK_STATUS DummyObjectPreCallback(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION OperationInformation) {
return(OB_PREOP_SUCCESS);
}
VOID DummyObjectPostCallback(PVOID RegistrationContext, POB_POST_OPERATION_INFORMATION OperationInformation) {
return;
}
QWORD GetCallbackListOffset(void) {
POBJECT_TYPE procType = *PsProcessType;
__try {
if (procType && MmIsAddressValid((void*)procType)) {
for (int i = 0xF8; i > 0; i -= 8) {
QWORD first = *(QWORD*)((QWORD)procType + i), second = *(QWORD*)((QWORD)procType + (i + 8));
if (first && MmIsAddressValid((void*)first) && second && MmIsAddressValid((void*)second)) {
QWORD test1First = *(QWORD*)(first + 0x0), test1Second = *(QWORD*)(first + 0x8);
if (test1First && MmIsAddressValid((void*)test1First) && test1Second && MmIsAddressValid((void*)test1Second)) {
QWORD testObjectType = *(QWORD*)(first + 0x20);
if (testObjectType == (QWORD)procType)
return((QWORD)i);
}
}
}
}
}
__except (EXCEPTION_EXECUTE_HANDLER) {
return(0);
}
}
void DisableBEObjectCallbacks(POLD_CALLBACKS oldCallbacks) {
POBJECT_TYPE procType = *PsProcessType;
if (procType && MmIsAddressValid((void*)procType)) {
__try {
QWORD callbackListOffset = GetCallbackListOffset();
if (callbackListOffset && MmIsAddressValid((void*)((QWORD)procType + callbackListOffset))) {
LIST_ENTRY *callbackList = (LIST_ENTRY*)((QWORD)procType + callbackListOffset);
if (callbackList->Flink && MmIsAddressValid((void*)callbackList->Flink)) {
CALLBACK_ENTRY_ITEM *firstCallback = (CALLBACK_ENTRY_ITEM*)callbackList->Flink;
CALLBACK_ENTRY_ITEM *curCallback = firstCallback;
do {
// Make sure the callback is valid.
if (curCallback && MmIsAddressValid((void*)curCallback) && MmIsAddressValid((void*)curCallback->CallbackEntry)) {
ANSI_STRING altitudeAnsi = { 0 };
UNICODE_STRING altitudeUni = curCallback->CallbackEntry->Altitude;
RtlUnicodeStringToAnsiString(&altitudeAnsi, &altitudeUni, 1);
if (!strcmp(altitudeAnsi.Buffer, "363220")) { // Check if this is BattlEye. If it is, disable the callback.
if (curCallback->PreOperation) {
oldCallbacks->PreOperationProc = (QWORD)curCallback->PreOperation;
curCallback->PreOperation = DummyObjectPreCallback;
}
if (curCallback->PostOperation) {
oldCallbacks->PostOperationProc = (QWORD)curCallback->PostOperation;
curCallback->PostOperation = DummyObjectPostCallback;
}
RtlFreeAnsiString(&altitudeAnsi);
break;
}
RtlFreeAnsiString(&altitudeAnsi);
}
// Get the next callback.
curCallback = curCallback->CallbackList.Flink;
} while (curCallback != firstCallback);
}
}
}
__except (EXCEPTION_EXECUTE_HANDLER) {
return;
}
}
POBJECT_TYPE threadType = *PsThreadType;
if (threadType && MmIsAddressValid((void*)threadType)) {
__try {
QWORD callbackListOffset = GetCallbackListOffset();
if (callbackListOffset && MmIsAddressValid((void*)((QWORD)threadType + callbackListOffset))) {
LIST_ENTRY *callbackList = (LIST_ENTRY*)((QWORD)threadType + callbackListOffset);
if (callbackList->Flink && MmIsAddressValid((void*)callbackList->Flink)) {
CALLBACK_ENTRY_ITEM *firstCallback = (CALLBACK_ENTRY_ITEM*)callbackList->Flink;
CALLBACK_ENTRY_ITEM *curCallback = firstCallback;
do {
// Make sure the callback is valid.
if (curCallback && MmIsAddressValid((void*)curCallback) && MmIsAddressValid((void*)curCallback->CallbackEntry)) {
ANSI_STRING altitudeAnsi = { 0 };
UNICODE_STRING altitudeUni = curCallback->CallbackEntry->Altitude;
RtlUnicodeStringToAnsiString(&altitudeAnsi, &altitudeUni, 1);
if (!strcmp(altitudeAnsi.Buffer, "363220")) { // Check if this is BattlEye. If it is, disable the callback.
if (curCallback->PreOperation) {
oldCallbacks->PreOperationThread = (QWORD)curCallback->PreOperation;
curCallback->PreOperation = DummyObjectPreCallback;
}
if (curCallback->PostOperation) {
oldCallbacks->PostOperationThread = (QWORD)curCallback->PostOperation;
curCallback->PostOperation = DummyObjectPostCallback;
}
RtlFreeAnsiString(&altitudeAnsi);
break;
}
RtlFreeAnsiString(&altitudeAnsi);
}
// Get the next callback.
curCallback = curCallback->CallbackList.Flink;
} while (curCallback != firstCallback);
}
}
}
__except (EXCEPTION_EXECUTE_HANDLER) {
return;
}
}
}
void RestoreBEObjectCallbacks(POLD_CALLBACKS oldCallbacks) {
POBJECT_TYPE procType = *PsProcessType;
if (procType && MmIsAddressValid((void*)procType)) {
__try {
QWORD callbackListOffset = GetCallbackListOffset();
if (callbackListOffset && MmIsAddressValid((void*)((QWORD)procType + callbackListOffset))) {
LIST_ENTRY *callbackList = (LIST_ENTRY*)((QWORD)procType + callbackListOffset);
if (callbackList->Flink && MmIsAddressValid((void*)callbackList->Flink)) {
CALLBACK_ENTRY_ITEM *firstCallback = (CALLBACK_ENTRY_ITEM*)callbackList->Flink;
CALLBACK_ENTRY_ITEM *curCallback = firstCallback;
do {
// Make sure the callback is valid.
if (curCallback && MmIsAddressValid((void*)curCallback) && MmIsAddressValid((void*)curCallback->CallbackEntry)) {
ANSI_STRING altitudeAnsi = { 0 };
UNICODE_STRING altitudeUni = curCallback->CallbackEntry->Altitude;
RtlUnicodeStringToAnsiString(&altitudeAnsi, &altitudeUni, 1);
if (!strcmp(altitudeAnsi.Buffer, "363220")) { // Check if this is BattlEye. If it is, restore the callback.
if (curCallback->PreOperation && oldCallbacks->PreOperationProc)
curCallback->PreOperation = (POB_PRE_OPERATION_CALLBACK)oldCallbacks->PreOperationProc;
if (curCallback->PostOperation && oldCallbacks->PostOperationProc)
curCallback->PostOperation = (POB_POST_OPERATION_CALLBACK)oldCallbacks->PostOperationProc;
RtlFreeAnsiString(&altitudeAnsi);
break;
}
RtlFreeAnsiString(&altitudeAnsi);
}
// Get the next callback.
curCallback = curCallback->CallbackList.Flink;
} while (curCallback != firstCallback);
}
}
}
__except (EXCEPTION_EXECUTE_HANDLER) {
return;
}
}
POBJECT_TYPE threadType = *PsThreadType;
if (threadType && MmIsAddressValid((void*)threadType)) {
__try {
QWORD callbackListOffset = GetCallbackListOffset();
if (callbackListOffset && MmIsAddressValid((void*)((QWORD)threadType + callbackListOffset))) {
LIST_ENTRY *callbackList = (LIST_ENTRY*)((QWORD)threadType + callbackListOffset);
if (callbackList->Flink && MmIsAddressValid((void*)callbackList->Flink)) {
CALLBACK_ENTRY_ITEM *firstCallback = (CALLBACK_ENTRY_ITEM*)callbackList->Flink;
CALLBACK_ENTRY_ITEM *curCallback = firstCallback;
do {
// Make sure the callback is valid.
if (curCallback && MmIsAddressValid((void*)curCallback) && MmIsAddressValid((void*)curCallback->CallbackEntry)) {
ANSI_STRING altitudeAnsi = { 0 };
UNICODE_STRING altitudeUni = curCallback->CallbackEntry->Altitude;
RtlUnicodeStringToAnsiString(&altitudeAnsi, &altitudeUni, 1);
if (!strcmp(altitudeAnsi.Buffer, "363220")) { // Check if this is BattlEye. If it is, disable the callback.
if (curCallback->PreOperation && oldCallbacks->PreOperationThread)
curCallback->PreOperation = (POB_PRE_OPERATION_CALLBACK)oldCallbacks->PreOperationThread;
if (curCallback->PostOperation && oldCallbacks->PostOperationThread)
curCallback->PostOperation = (POB_POST_OPERATION_CALLBACK)oldCallbacks->PostOperationThread;
RtlFreeAnsiString(&altitudeAnsi);
break;
}
RtlFreeAnsiString(&altitudeAnsi);
}
// Get the next callback.
curCallback = curCallback->CallbackList.Flink;
} while (curCallback != firstCallback);
}
}
}
__except (EXCEPTION_EXECUTE_HANDLER) {
return;
}
}
}
NTSTATUS ioRecieved(PDEVICE_OBJECT pDeviceObject, PIRP IRP) {
PIO_STACK_LOCATION pIoStackLocation = IoGetCurrentIrpStackLocation(IRP);
size_t size = 0;
// Handle the I/O request if we need to.
if (pIoStackLocation->Parameters.DeviceIoControl.IoControlCode == REMOVE_BEOBJECT_CALLBACKS_IOCTL){
OLD_CALLBACKS oldCallbacks = { 0 };
DisableBEObjectCallbacks(&oldCallbacks);
memcpy(IRP->AssociatedIrp.SystemBuffer, &oldCallbacks, sizeof(OLD_CALLBACKS));
size = sizeof(OLD_CALLBACKS);
}
if (pIoStackLocation->Parameters.DeviceIoControl.IoControlCode == RESTORE_BEOBJECT_CALLBACKS_IOCTL) {
RestoreBEObjectCallbacks((POLD_CALLBACKS)IRP->AssociatedIrp.SystemBuffer);
size = 0;
}
// Finish off.
IRP->IoStatus.Status = STATUS_SUCCESS;
IRP->IoStatus.Information = size;
IofCompleteRequest(IRP, IO_NO_INCREMENT);
return(STATUS_SUCCESS);
}
NTSTATUS CatchCreate(PDRIVER_OBJECT pDriverObject) {
return(STATUS_SUCCESS);
}
NTSTATUS CatchClose(PDRIVER_OBJECT pDriverObject) {
return(STATUS_SUCCESS);
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPath) {
// Create the device and get everything set up.
UNICODE_STRING deviceNameUnicodeString = { 0 }, deviceSymLinkUnicodeString = { 0 };
RtlInitUnicodeString(&deviceNameUnicodeString, L"\\Device\\mmarkdrv");
RtlInitUnicodeString(&deviceSymLinkUnicodeString, L"\\DosDevices\\mmarkdrv");
IoCreateDevice(pDriverObject, 0, &deviceNameUnicodeString, FILE_DEVICE_KS, FILE_DEVICE_SECURE_OPEN, 0, &deviceObj);
IoCreateSymbolicLink(&deviceSymLinkUnicodeString, &deviceNameUnicodeString);
// Get all the major functions set up.
pDriverObject->MajorFunction[IRP_MJ_CREATE] = CatchCreate;
pDriverObject->MajorFunction[IRP_MJ_CLOSE] = CatchClose;
pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ioRecieved;
return(STATUS_SUCCESS);
}
LuluVisor BEDaisy Logs
Here is an example of some of iPower's logs from his hypervisor, which show you what BE is doing in Fortnite:
Code:
[ LuluVisor ] PsGetThreadProcessId - BEDaisy.sys+002ad3ff
[ LuluVisor ] IoThreadToProcess - BEDaisy.sys+002ad435
[ LuluVisor ] PsGetProcessInheritedFromUniqueProcessId - BEDaisy.sys+002ad44c
[ LuluVisor ] _wcsnicmp - BEDaisy.sys+002af6fa
[ LuluVisor ] _wcsnicmp - BEDaisy.sys+002af6fa
[ LuluVisor ] _wcsnicmp - BEDaisy.sys+002af6fa
[ LuluVisor ] _wcsnicmp - BEDaisy.sys+002af6fa
[ LuluVisor ] _wcsnicmp - BEDaisy.sys+002af6fa
[ LuluVisor ] PsGetThreadProcessId - BEDaisy.sys+002ad3ff
[ LuluVisor ] IoThreadToProcess - BEDaisy.sys+002ad435
[ LuluVisor ] PsGetProcessInheritedFromUniqueProcessId - BEDaisy.sys+002ad44c
[ LuluVisor ] _wcsnicmp - BEDaisy.sys+002af6fa
[ LuluVisor ] _wcsnicmp - BEDaisy.sys+002af6fa
[ LuluVisor ] PsGetThreadProcessId - BEDaisy.sys+002ad3ff
[ LuluVisor ] IoThreadToProcess - BEDaisy.sys+002ad435
[ LuluVisor ] PsGetProcessInheritedFromUniqueProcessId - BEDaisy.sys+002ad44c
[ LuluVisor ] PsGetThreadProcessId - BEDaisy.sys+002ad3ff
[ LuluVisor ] IoThreadToProcess - BEDaisy.sys+002ad435
[ LuluVisor ] PsGetProcessInheritedFromUniqueProcessId - BEDaisy.sys+002ad44c
[ LuluVisor ] _wcsnicmp - BEDaisy.sys+002af6fa
[ LuluVisor ] _wcsnicmp - BEDaisy.sys+002af6fa
[ LuluVisor ] PsGetThreadProcessId - BEDaisy.sys+002ad3ff
[ LuluVisor ] IoThreadToProcess - BEDaisy.sys+002ad435
[ LuluVisor ] PsGetProcessInheritedFromUniqueProcessId - BEDaisy.sys+002ad44c
[ LuluVisor ] PsGetThreadProcessId - BEDaisy.sys+002ad3ff
[ LuluVisor ] IoThreadToProcess - BEDaisy.sys+002ad435
[ LuluVisor ] PsGetProcessInheritedFromUniqueProcessId - BEDaisy.sys+002ad44c
[ LuluVisor ] _wcsnicmp - BEDaisy.sys+002af6fa
[ LuluVisor ] PsGetProcessInheritedFromUniqueProcessId - BEDaisy.sys+002ad08d
[ LuluVisor ] _wcsnicmp - BEDaisy.sys+002af6fa
[ LuluVisor ] PsGetProcessInheritedFromUniqueProcessId - BEDaisy.sys+002ad08d
[ LuluVisor ] PsGetProcessInheritedFromUniqueProcessId - BEDaisy.sys+002ad08d
[ LuluVisor ] PsGetProcessInheritedFromUniqueProcessId - BEDaisy.sys+002ad08d
[ LuluVisor ] PsGetProcessInheritedFromUniqueProcessId - BEDaisy.sys+002ad08d
[ LuluVisor ] PsGetProcessInheritedFromUniqueProcessId - BEDaisy.sys+002ad08d
[ LuluVisor ] PsGetProcessInheritedFromUniqueProcessId - BEDaisy.sys+002ad08d
[ LuluVisor ] _wcsnicmp - BEDaisy.sys+002af6fa
[ LuluVisor ] PsGetProcessInheritedFromUniqueProcessId - BEDaisy.sys+002ad08d
[ LuluVisor ] PsGetProcessInheritedFromUniqueProcessId - BEDaisy.sys+002ad08d
[ LuluVisor ] PsGetProcessInheritedFromUniqueProcessId - BEDaisy.sys+002ad08d
[ LuluVisor ] PsGetProcessInheritedFromUniqueProcessId - BEDaisy.sys+002ad08d
[ LuluVisor ] _wcsnicmp - BEDaisy.sys+002af6fa
[ LuluVisor ] _wcsnicmp - BEDaisy.sys+002af6fa
[ LuluVisor ] _wcsnicmp - BEDaisy.sys+002af6fa
[ LuluVisor ] PsGetProcessInheritedFromUniqueProcessId - BEDaisy.sys+002ad08d
[ LuluVisor ] _wcsnicmp - BEDaisy.sys+002af6fa
[ LuluVisor ] PsGetProcessInheritedFromUniqueProcessId - BEDaisy.sys+002ad08d
[ LuluVisor ] _wcsnicmp - BEDaisy.sys+002af6fa
[ LuluVisor ] PsGetProcessInheritedFromUniqueProcessId - BEDaisy.sys+002ad08d
[ LuluVisor ] _wcsnicmp - BEDaisy.sys+002af6fa
[ LuluVisor ] _wcsnicmp - BEDaisy.sys+002af705
[ LuluVisor ] PsGetProcessInheritedFromUniqueProcessId - BEDaisy.sys+002ad08d
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002af747
[ LuluVisor ] PsGetProcessInheritedFromUniqueProcessId - BEDaisy.sys+002ad08d
[ LuluVisor ] KeReleaseMutex - BEDaisy.sys+002af75d
[ LuluVisor ] ExFreePool - BEDaisy.sys+002af768
[ LuluVisor ] KeWaitForMutexObject - BEDaisy.sys+002af773
[ LuluVisor ] PsGetProcessInheritedFromUniqueProcessId - BEDaisy.sys+002ad08d
[ LuluVisor ] KeReleaseMutex - BEDaisy.sys+002af829
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002b29b3
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002b29b3
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002b29b3
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002b29b3
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002b29b3
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002b29b3
[ LuluVisor ] PsGetProcessInheritedFromUniqueProcessId - BEDaisy.sys+002ad08d
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002b29b3
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002b29b3
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002b29b3
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002b29b3
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002b29b3
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002b29b3
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002b29b3
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002b29b3
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002b29b3
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002b29b3
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002b29b3
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002b29b3
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002b29b3
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002b29b3
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002b29b3
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002b29b3
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002b29b3
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002b29b3
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002b29b3
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002b29b3
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002b29b3
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002b29b3
[ LuluVisor ] ObfDereferenceObject - BEDaisy.sys+002af83f
[ LuluVisor ] ZwClose - BEDaisy.sys+002af84a
[ LuluVisor ] ZwQueryDirectoryObject - BEDaisy.sys+002af86b
[ LuluVisor ] RtlCompareUnicodeString - BEDaisy.sys+002af5af
[ LuluVisor ] PsGetProcessInheritedFromUniqueProcessId - BEDaisy.sys+002ad08d
[ LuluVisor ] RtlInitUnicodeString - BEDaisy.sys+002af5d0
[ LuluVisor ] ObOpenObjectByName - BEDaisy.sys+002af5f7
[ LuluVisor ] ObReferenceObjectByHandle - BEDaisy.sys+002af602
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002af60d
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002af618
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002af62e
[ LuluVisor ] ZwOpenFile - BEDaisy.sys+002af655
[ LuluVisor ] PsGetProcessInheritedFromUniqueProcessId - BEDaisy.sys+002ad08d
[ LuluVisor ] ObReferenceObjectByHandle - BEDaisy.sys+002af660
[ LuluVisor ] IoQueryFileDosDeviceName - BEDaisy.sys+002af66b
[ LuluVisor ] ObfDereferenceObject - BEDaisy.sys+002af676
[ LuluVisor ] ZwClose - BEDaisy.sys+002af681
[ LuluVisor ] KeWaitForMutexObject - BEDaisy.sys+002af6ef
[ LuluVisor ] PsGetProcessInheritedFromUniqueProcessId - BEDaisy.sys+002ad08d
[ LuluVisor ] _wcsnicmp - BEDaisy.sys+002af6fa
[ LuluVisor ] _wcsnicmp - BEDaisy.sys+002af6fa
[ LuluVisor ] _wcsnicmp - BEDaisy.sys+002af705
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002af747
[ LuluVisor ] KeReleaseMutex - BEDaisy.sys+002af75d
[ LuluVisor ] ExFreePool - BEDaisy.sys+002af768
[ LuluVisor ] KeWaitForMutexObject - BEDaisy.sys+002af773
[ LuluVisor ] KeReleaseMutex - BEDaisy.sys+002af829
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002b29b3
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002b29b3
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002b29b3
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002b29b3
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002b29b3
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002b29b3
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002b29b3
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002b29b3
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002b29b3
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002b29b3
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002b29b3
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002b29b3
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002b29b3
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002b29b3
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002b29b3
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002b29b3
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002b29b3
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002b29b3
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002b29b3
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002b29b3
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002b29b3
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002b29b3
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002b29b3
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002b29b3
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002b29b3
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002b29b3
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002b29b3
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002b29b3
[ LuluVisor ] ObfDereferenceObject - BEDaisy.sys+002af83f
[ LuluVisor ] ZwClose - BEDaisy.sys+002af84a
[ LuluVisor ] ZwQueryDirectoryObject - BEDaisy.sys+002af86b
[ LuluVisor ] RtlCompareUnicodeString - BEDaisy.sys+002af5af
[ LuluVisor ] ZwQueryDirectoryObject - BEDaisy.sys+002af86b
[ LuluVisor ] RtlCompareUnicodeString - BEDaisy.sys+002af5af
[ LuluVisor ] RtlInitUnicodeString - BEDaisy.sys+002af5d0
[ LuluVisor ] ObOpenObjectByName - BEDaisy.sys+002af5f7
[ LuluVisor ] ObReferenceObjectByHandle - BEDaisy.sys+002af602
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002af60d
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002af618
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002af62e
[ LuluVisor ] ZwOpenFile - BEDaisy.sys+002af655
[ LuluVisor ] ObReferenceObjectByHandle - BEDaisy.sys+002af660
[ LuluVisor ] IoQueryFileDosDeviceName - BEDaisy.sys+002af66b
[ LuluVisor ] ObfDereferenceObject - BEDaisy.sys+002af676
[ LuluVisor ] ZwClose - BEDaisy.sys+002af681
[ LuluVisor ] KeWaitForMutexObject - BEDaisy.sys+002af6ef
[ LuluVisor ] PsGetProcessInheritedFromUniqueProcessId - BEDaisy.sys+002ad08d
[ LuluVisor ] _wcsnicmp - BEDaisy.sys+002af6fa
[ LuluVisor ] _wcsnicmp - BEDaisy.sys+002af6fa
[ LuluVisor ] _wcsnicmp - BEDaisy.sys+002af6fa
[ LuluVisor ] _wcsnicmp - BEDaisy.sys+002af6fa
[ LuluVisor ] _wcsnicmp - BEDaisy.sys+002af705
[ LuluVisor ] MmIsAddressValid - BEDaisy.sys+002af747
[ LuluVisor ] KeReleaseMutex - BEDaisy.sys+002af75d
[ LuluVisor ] ExFreePool - BEDaisy.sys+002af768
[ LuluVisor ] KeWaitForMutexObject - BEDaisy.sys+002af773
[ LuluVisor ] KeReleaseMutex - BEDaisy.sys+002af829
Temporary Battleye Bypass for improperly implemented anticheat
It's happened a few times on a couple games where you can just unload the Battleye driver and the game doesn't stop running, it's easy to do, but unlikely it will work on most new games. @gulerardaeren posted this a while back, it has worked previously on Zula, Crossfire, Apex Legends and others as well
- Open The Game
- Open Process Hacker And Find BEService.exe
- Right Click To BEService.exe and click suspend(this will require administator permission)
- Open The Process Hunter and go to the Kernel Module
- Find BEDaisy.sys , right click and click unload driver
- If they didn't patched this method, You can even use cheat engine
Dumped Modules
The first thing you need to do to reverse engineer Battleeye is to dump the system driver and usermode modules from memory, you will find a couple pre-made dumps below. Once you have the dumps you can load them into IDA Pro and start looking around.
GH Battleye Bypass Resources
- iPower BE Logs & BEDaisy.sys Module Dump
- iPower Fortnite BE Logs
- 02/07/2020 BEDaisy.sys dump
- Download - Using BattlEye to bypass EAC/Vanguard.
- Old Battleye Bypass Source Code
- Release - BattlEye Bypass [+Tested on Rainbow Six Siege] (Driver) (Source)
- c5's old IDA Scripts for Analyzing BE
External Resources
The most important thing you can do to learn about BattleEye is to watch this video made by the DayZ developers about how Battleeye helps them stop cheaters. This is also an excellent video for anyone wanting to learn about anticheat.
Checkout Douggem's site for good info and his video:
- secret club
- Back Engineering - BattlEye
- xerox / badeye
- xerox / BEDaisy
- vmcall/battleye_emulation - battle eye usermode emulator
- vmcall/eye_mapper - BattlEye x64 usermode injector (patched)
- ArcherPeng/FuckBattlEye - Anticheat Bypass exploiting leaked handles.
- Tai7sy/BE_Fuck - BattlEye Emulator
- gigabitwize/dank - windows 8.1 battleeye bypass
- daswareinfach/Battleye-VAC-EAC-Kernel-Bypass
- https://github.com/huoji120/battleye/blob/master/code.c random battle eye code
- ContionMig/LSASS-Usermode-Bypass
- ACmagic/ByPass_BattleEye
- Schnocker/HLeaker
- yunseok/HWIDbypass
- Neijwiert/Feeder
- Schnocker/NoEye
- Download - Using BattlEye to bypass EAC/Vanguard.
Credits: @iPower, @_xeroxz, douggem, vmcall & everyone at secret.club
Attachments
I have attached a bunch of files from github and other places in case they get deleted, many of these are copies of the files in the links above
Attachments
You can download 0 Attachments
-
110.3 KB Views: 40
-
2.6 MB Views: 48
-
3.4 MB Views: 41
-
437.2 KB Views: 138
-
16.9 KB Views: 29
-
266.9 KB Views: 83
-
11.5 KB Views: 84
-
68 KB Views: 187
-
10 KB Views: 91
-
105.8 KB Views: 39
Last edited: