Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save OlivierLaflamme/2e0670718a904f21b03cb753df02cf67 to your computer and use it in GitHub Desktop.
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…
#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