Created
June 22, 2020 18:10
-
-
Save OlivierLaflamme/2e0670718a904f21b03cb753df02cf67 to your computer and use it in GitHub Desktop.
Basically, this dynamically gets the offset to the CallbackList in the OBJECT_TYPE structure (in a really shitty, long-winded way - pls improve) so this will work on any Windows version 7+. The other structures which I've labeled CALLBACK_ENTRY and CALLBACK_ENTRY_ITEM that are completely undocumented have not changed from Windows 7 to Windows 10…
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment