Üye
İlk olarak Device name'i ve
Öncelikle driveri Anti-Cheatlerden gizleyebilmek ve içerisindeki tespit edilen bazı özellikleri kullanabilmek için bazı Anti-Cheat fonksiyonlarını geçmemiz gerekir.
Minifilter
Anti-Cheat sistemleri loadlanan image ve modülleri bloklamak için Image Load Notify Routine ve Minifilter adında fonksiyonlar kullanır.
Minifilter Bypass
Image Load Notify Routine
Nasıl loadlandığı önemli değil, bu fonksiyon blacklistteki herhangi bir image loadlandığında bildirim verir. (BlackBoneDrv bu blacklist içerisinde)
Image Load Notify Routine Bypass
Düzeltmek için
Image Routine
Create Thread Notify Routine
Oyunun içinde herhangi bir thread oluşturmaya çalıştığınızda Anti-Cheat'in (Battleye) size corrupted memory hatası vermesinin sebebi PsSetCreateThreadNotifyRoutine'dir. IDA'da ntoskrnl.exe'yi açın ve PspCallThreadNotifyRoutines'e bir göz atın. Bu fonksiyon, kurulu bildirim rutinlerini tekrarlamaktan ve bunları çağırmakla görevlidir.
PspCallThreadNotifyRoutines
PspSetCreateThreadNotifyRoutine
Create Thread Notify Routine Bypass
Düzeltmek için
Manual Mapping
BlackBone driveri içerisinde bir kernel manual map bulunuyor muhtemelen çoğunuzun buraya gelmesinin sebebi de bu. BlackBone içerisindeki bu kernel manual map, map işlemi sırasında bir thread oluşturuyor bu threadın yanı sıra manual mapin tespit edilmesindeki başka bir etken manual maplenen image'in gizlenememesi, BlackBone manual maplenen DLL'in gizlenmesi için VAD hide kullanıyor ama VAD hide artık usermodedan bile tespit edilebilir yani VAD hide burada işimize yaramaz. Başka bir konumda bahsettiğim NXBit değiştirme yöntemini kullanırsanız Image Load Notify Routine'i geçmenize gerek kalmaz, sadece Thread Notify Routine'i geçerek manual mapi gizleyebilirsiniz.
BlackBone içerisindeki kernel manual map'i gizlemek için buradan açtığım diğer bir konudan devam edin.
Konuyu okuduktan sonra @berktuna991 'e ait olan bu konudan basit bir kernel manual map yapabilirsiniz.
Önemli Not
Burada bulunan KWipeHeader ve KNoTLS enumlarını silmeyin.
KWipeHeader enumu image PE headerlarını siler.
KNoTLS enumu tespit edilen TLS Callbacklerin çalıştırılmasını önler.
Driverınızı loadlarken public driver loaderları kullanmayın (DSEFix, TDL vb.) mümkünse kendi driver loaderınızı yazın.
Bağlantıları görmek için lütfen
Giriş Yap
içerisinde bulunan standart ioctl iletişimini değiştirinÖncelikle driveri Anti-Cheatlerden gizleyebilmek ve içerisindeki tespit edilen bazı özellikleri kullanabilmek için bazı Anti-Cheat fonksiyonlarını geçmemiz gerekir.
Minifilter
Anti-Cheat sistemleri loadlanan image ve modülleri bloklamak için Image Load Notify Routine ve Minifilter adında fonksiyonlar kullanır.
Minifilter Bypass
C:
BOOLEAN RemoveMiniFilter()
{
#if (REMOVE_FILTER == 1)
VirtualizerStart();
VirtualizerStrEncryptStart();
NTSTATUS status = STATUS_SUCCESS;
ULONG ulFilterListSize = 0;
PFLT_FILTER* ppFilterList = NULL;
ULONG i = 0;
PFLT_OPERATION_REGISTRATION pFltOperationRegistration = NULL;
FltEnumerateFilters(NULL, 0, &ulFilterListSize);
ppFilterList = (PFLT_FILTER*)ExAllocatePool(NonPagedPool, ulFilterListSize * sizeof(PFLT_FILTER));
if (NULL == ppFilterList)
{
return FALSE;
}
status = FltEnumerateFilters(ppFilterList, ulFilterListSize, &ulFilterListSize);
if (!NT_SUCCESS(status))
{
return FALSE;
}
if (lOperationsOffset == 0)
{
return FALSE;
}
try
{
for (i = 0; i < ulFilterListSize; i++)
{
pFltOperationRegistration = (PFLT_OPERATION_REGISTRATION)(*(PVOID*)((PUCHAR)ppFilterList[i] + lOperationsOffset));
try
{
while (IRP_MJ_OPERATION_END != pFltOperationRegistration->MajorFunction)
{
if (MmIsAddressValid(pFltOperationRegistration->PreOperation) &&
IsFromEACRange(pFltOperationRegistration->PreOperation))
{
FilterAddr = pFltOperationRegistration->PreOperation;
pFltOperationRegistration->PreOperation = DummyObjectPreCallback;
break;
}
pFltOperationRegistration = (PFLT_OPERATION_REGISTRATION)((PUCHAR)pFltOperationRegistration + sizeof(FLT_OPERATION_REGISTRATION));
}
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
}
FltObjectDereference(ppFilterList[i]);
}
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
}
VirtualizerStrEncryptEnd();
VirtualizerEnd();
ExFreePool(ppFilterList);
ppFilterList = NULL;
#endif
return TRUE;
}
BOOLEAN RestoreMiniFilter()
{
#if (REMOVE_FILTER == 1)
NTSTATUS status = STATUS_SUCCESS;
ULONG ulFilterListSize = 0;
PFLT_FILTER* ppFilterList = NULL;
ULONG i = 0;
PFLT_OPERATION_REGISTRATION pFltOperationRegistration = NULL;
try
{
VirtualizerStart();
VirtualizerStrEncryptStart();
FltEnumerateFilters(NULL, 0, &ulFilterListSize);
ppFilterList = (PFLT_FILTER*)ExAllocatePool(NonPagedPool, ulFilterListSize * sizeof(PFLT_FILTER));
if (NULL == ppFilterList)
{
return FALSE;
}
status = FltEnumerateFilters(ppFilterList, ulFilterListSize, &ulFilterListSize);
if (!NT_SUCCESS(status))
{
return FALSE;
}
if (lOperationsOffset == 0)
{
return FALSE;
}
for (i = 0; i < ulFilterListSize; i++)
{
pFltOperationRegistration = (PFLT_OPERATION_REGISTRATION)(*(PVOID*)((PUCHAR)ppFilterList[i] + lOperationsOffset));
try
{
while (IRP_MJ_OPERATION_END != pFltOperationRegistration->MajorFunction)
{
if (pFltOperationRegistration->PreOperation == DummyObjectPreCallback)
{
pFltOperationRegistration->PreOperation = pFltOperationRegistration->PreOperation;
}
pFltOperationRegistration = (PFLT_OPERATION_REGISTRATION)((PUCHAR)pFltOperationRegistration + sizeof(FLT_OPERATION_REGISTRATION));
}
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
}
FltObjectDereference(ppFilterList[i]);
}
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
}
VirtualizerStrEncryptEnd();
VirtualizerEnd();
ExFreePool(ppFilterList);
ppFilterList = NULL;
#endif
return TRUE;
}
Image Load Notify Routine
Nasıl loadlandığı önemli değil, bu fonksiyon blacklistteki herhangi bir image loadlandığında bildirim verir. (BlackBoneDrv bu blacklist içerisinde)
Image Load Notify Routine Bypass
C:
BOOLEAN FuckImageCallBack()
{
#if (REMOVE_IMAGEROUTINE == 1)
__try
{
VirtualizerStart();
VirtualizerStrEncryptStart();
ULONG64 NotifyAddr = 0, MagicPtr = 0;
ULONG64 PspLoadImageNotifyRoutine = (ULONG64)ImageCallBacks;
for (int i = 0; i < 64; i++)
{
MagicPtr = PspLoadImageNotifyRoutine + i * 8;
NotifyAddr = *(PULONG64)(MagicPtr);
if (MmIsAddressValid((PVOID)NotifyAddr) && NotifyAddr != 0)
{
NotifyAddr = *(PULONG64)(NotifyAddr & 0xfffffffffffffff8);
if (IsFromEACRange((PVOID)NotifyAddr))
{
EAC_ImageRoutine = (PVOID)NotifyAddr;
if (!NT_SUCCESS(PsRemoveLoadImageNotifyRoutine((PLOAD_IMAGE_NOTIFY_ROUTINE)NotifyAddr)))
DbgPrint("PsRemoveLoadImageNotifyRoutine başarısız oldu");
else
{
return TRUE;
}
}
}
}
VirtualizerStrEncryptEnd();
VirtualizerEnd();
}
except(EXCEPTION_EXECUTE_HANDLER)
{
VirtualizerStrEncryptStart();
VirtualizerStrEncryptEnd();
return FALSE;
}
#endif
return FALSE;
}
Düzeltmek için
C:
BOOLEAN RestoreImageCallBack()
{
VirtualizerStart();
VirtualizerStrEncryptStart();
#if (REMOVE_IMAGEROUTINE == 1)
if (!MmIsAddressValid((PVOID)EAC_ImageRoutine) ||
!NT_SUCCESS(PsSetLoadImageNotifyRoutine((PLOAD_IMAGE_NOTIFY_ROUTINE)EAC_ImageRoutine)))
else
{
return TRUE;
}
#endif
VirtualizerStrEncryptEnd();
VirtualizerEnd();
return FALSE;
}
Image Routine
C:
VOID ImageRoutine(PUNICODE_STRING FullImageName, HANDLE ProcessId, PIMAGE_INFO Info)
{
UNREFERENCED_PARAMETER(ProcessId);
//VirtualizerStart();
if (wcsstr(FullImageName->Buffer, L"Anti-Cheat Driver.sys")) // EasyAntiCheat.sys / BEDaisy.sys
{
EAC_Base = Info->ImageBase;
EAC_Base_Size = Info->ImageSize;
VirtualizerStrEncryptStart();
VirtualizerStrEncryptEnd();
}
}
Create Thread Notify Routine
Oyunun içinde herhangi bir thread oluşturmaya çalıştığınızda Anti-Cheat'in (Battleye) size corrupted memory hatası vermesinin sebebi PsSetCreateThreadNotifyRoutine'dir. IDA'da ntoskrnl.exe'yi açın ve PspCallThreadNotifyRoutines'e bir göz atın. Bu fonksiyon, kurulu bildirim rutinlerini tekrarlamaktan ve bunları çağırmakla görevlidir.
PspCallThreadNotifyRoutines
C:
int __fastcall PspCallThreadNotifyRoutines(__int64 a1, unsigned __int8 a2, char a3)
{
unsigned __int8 v3; // r14@1
__int64 v4; // r15@1
bool v5; // r12@1
__int64 v6; // rax@1
void *v7; // rbx@4
signed __int64 v8; // rsi@4
__int64 v9; // rbp@5
char v10; // al@8
__int64 v11; // rcx@8
__int64 v12; // rdi@10
void *v13; // rbx@16
signed __int64 v14; // rsi@16
__int64 v15; // rbp@17
__int64 v16; // rdi@21
void *v17; // rbx@13
signed __int64 v18; // rsi@13
__int64 v19; // rbp@25
__int64 v20; // rcx@26
__int64 v21; // rdi@27
v3 = a2;
v4 = a1;
v5 = *(_QWORD *)(a1 + 1944) != 0i64;
LODWORD(v6) = PspNotifyEnableMask;
if ( a2 )
{
if ( a3 )
{
if ( PspNotifyEnableMask & 0x10 )
{
v17 = &PspCreateThreadNotifyRoutine;
v18 = 64i64;
do
{
LODWORD(v6) = ExReferenceCallBackBlock(v17);
v19 = v6;
if ( v6 )
{
if ( ExGetCallBackBlockContext(v6) & 1 )
{
v21 = *(_QWORD *)(v4 + 544);
ExGetCallBackBlockRoutine(v20);
guard_dispatch_icall(*(_QWORD *)(v21 + 736), *(_QWORD *)(v4 + 1600), v3);
}
LODWORD(v6) = ExDereferenceCallBackBlock(v17, v19);
}
v17 = (char *)v17 + 8;
--v18;
}
while ( v18 );
}
}
else if ( PspNotifyEnableMask & 8 )
{
v7 = &PspCreateThreadNotifyRoutine;
v8 = 64i64;
do
{
LODWORD(v6) = ExReferenceCallBackBlock(v7);
v9 = v6;
if ( v6 )
{
v10 = ExGetCallBackBlockContext(v6);
if ( !(v10 & 1) && (!v5 || v10 & 2) )
{
v12 = *(_QWORD *)(v4 + 544);
ExGetCallBackBlockRoutine(v11);
guard_dispatch_icall(*(_QWORD *)(v12 + 736), *(_QWORD *)(v4 + 1600), v3);
}
LODWORD(v6) = ExDereferenceCallBackBlock(v7, v9);
}
v7 = (char *)v7 + 8;
--v8;
}
while ( v8 );
}
}
else if ( PspNotifyEnableMask & 0x10 || (LODWORD(v6) = PspNotifyEnableMask, PspNotifyEnableMask & 8) )
{
v13 = &PspCreateThreadNotifyRoutine;
v14 = 64i64;
do
{
LODWORD(v6) = ExReferenceCallBackBlock(v13);
v15 = v6;
if ( v6 )
{
if ( !v5 || ExGetCallBackBlockContext(v6) & 2 )
{
v16 = *(_QWORD *)(v4 + 544);
ExGetCallBackBlockRoutine(v15);
guard_dispatch_icall(*(_QWORD *)(v16 + 736), *(_QWORD *)(v4 + 1600), 0i64);
}
LODWORD(v6) = ExDereferenceCallBackBlock(v13, v15);
}
v13 = (char *)v13 + 8;
--v14;
}
while ( v14 );
}
return v6;
}
PspSetCreateThreadNotifyRoutine
C:
signed __int64 __fastcall PspSetCreateThreadNotifyRoutine(__int64 a1, unsigned int a2)
{
char v2; // si@1
void *v3; // rax@1
void *v4; // rdi@1
__int64 v5; // rbx@2
signed __int64 result; // rax@7
v2 = a2;
LODWORD(v3) = ExAllocateCallBack(a1, a2);
v4 = v3;
if ( v3 )
{
v5 = 0i64;
while ( !(unsigned __int8)ExCompareExchangeCallBack((char *)&PspCreateThreadNotifyRoutine + 8 * v5, v4, 0i64) )
{
v5 = (unsigned int)(v5 + 1);
if ( (unsigned int)v5 >= 0x40 )
{
ExFreePoolWithTag(v4, 0);
goto LABEL_11;
}
}
if ( v2 & 1 )
{
_InterlockedIncrement((volatile signed __int32 *)&PspCreateThreadNotifyRoutineNonSystemCount);
if ( !(PspNotifyEnableMask & 0x10) )
_interlockedbittestandset((volatile signed __int32 *)&PspNotifyEnableMask, 4u);
}
else
{
_InterlockedIncrement((volatile signed __int32 *)&PspCreateThreadNotifyRoutineCount);
if ( !(PspNotifyEnableMask & 8) )
_interlockedbittestandset((volatile signed __int32 *)&PspNotifyEnableMask, 3u);
}
result = 0i64;
}
else
{
LABEL_11:
result = 3221225626i64;
}
return result;
}
Create Thread Notify Routine Bypass
C:
BOOLEAN FuckThreadCallBack()
{
#if (REMOVE_THREADROUTINE == 1)
__try
{
VirtualizerStart();
VirtualizerStrEncryptStart();
ULONG64 NotifyAddr = 0, MagicPtr = 0;
ULONG64 PspCreateThreadNotifyRoutine = (ULONG64)ThreadCallBacks;
for (int i = 0; i < 64; i++)
{
MagicPtr = PspCreateThreadNotifyRoutine + i * 8;
NotifyAddr = *(PULONG64)(MagicPtr);
if (MmIsAddressValid((PVOID)NotifyAddr) && NotifyAddr != 0)
{
NotifyAddr = *(PULONG64)(NotifyAddr & 0xfffffffffffffff8);
if (IsFromRange((PVOID)NotifyAddr))
{
AC_ThreadRoutine = (PVOID)NotifyAddr;
if (!NT_SUCCESS(PsRemoveCreateThreadNotifyRoutine((PCREATE_THREAD_NOTIFY_ROUTINE)NotifyAddr)))
DbgPrint("PsRemoveCreateThreadNotifyRoutine başarısız oldu!");
else
{
return TRUE;
}
}
}
}
VirtualizerStrEncryptEnd();
VirtualizerEnd();
}
except(EXCEPTION_EXECUTE_HANDLER)
{
VirtualizerStrEncryptStart();
VirtualizerStrEncryptEnd();
return FALSE;
}
#endif
return FALSE;
}
Düzeltmek için
C:
BOOLEAN RestoreThreadCallBack()
{
VirtualizerStart();
VirtualizerStrEncryptStart();
#if (REMOVE_THREADROUTINE == 1)
if (!MmIsAddressValid((PVOID)AC_ThreadRoutine) ||
!NT_SUCCESS(PsSetCreateThreadNotifyRoutine((PCREATE_THREAD_NOTIFY_ROUTINE)AC_ThreadRoutine)))
else
{
return TRUE;
}
#endif
VirtualizerStrEncryptEnd();
VirtualizerEnd();
return FALSE;
}
Manual Mapping
BlackBone driveri içerisinde bir kernel manual map bulunuyor muhtemelen çoğunuzun buraya gelmesinin sebebi de bu. BlackBone içerisindeki bu kernel manual map, map işlemi sırasında bir thread oluşturuyor bu threadın yanı sıra manual mapin tespit edilmesindeki başka bir etken manual maplenen image'in gizlenememesi, BlackBone manual maplenen DLL'in gizlenmesi için VAD hide kullanıyor ama VAD hide artık usermodedan bile tespit edilebilir yani VAD hide burada işimize yaramaz. Başka bir konumda bahsettiğim NXBit değiştirme yöntemini kullanırsanız Image Load Notify Routine'i geçmenize gerek kalmaz, sadece Thread Notify Routine'i geçerek manual mapi gizleyebilirsiniz.
BlackBone içerisindeki kernel manual map'i gizlemek için buradan açtığım diğer bir konudan devam edin.
Konuyu okuduktan sonra @berktuna991 'e ait olan bu konudan basit bir kernel manual map yapabilirsiniz.
Önemli Not
C++:
if (!blackbone::Driver().MmapDll(programpid, L"deneme.dll", KWipeHeader | KNoTLS))
Burada bulunan KWipeHeader ve KNoTLS enumlarını silmeyin.
KWipeHeader enumu image PE headerlarını siler.
KNoTLS enumu tespit edilen TLS Callbacklerin çalıştırılmasını önler.
Driverınızı loadlarken public driver loaderları kullanmayın (DSEFix, TDL vb.) mümkünse kendi driver loaderınızı yazın.