EMACLAB Anticheat Driver, Part 6: Integrity checks

  • File Name: EMAC-Driver-x64.sys
  • TimeDateStamp: 0x67CAFFCE (Friday, 7 March 2025 14:16:46 GMT)
  • Protector: VMProtect 3.8+

Integrity checks

To bypass kernel anticheats, cheaters can simply load their own driver, or even better, manual map it into system memory and try hide itself as much as possible and once the driver is mapped and hidden in memory anticheats have to find it. The anticheat kernel driver itself will not help if there are no integrity checks being actively made on the system, it’s important that anticheats actively scans for traces as well as use patterns and heuristics for detections.

How anticheats find unsigned code

Threads: Any code that actually runs in memory is attached to a thread, there are several threads running on the computer. You can iterate the system threads list and obtain thread stack back trace.

  • NMI - It’s possible to register non-maskable interrupts (NMIs) and obtain thread stack back trace.
  • APC - It’s possible to send APC to thread and obtain information, or, simply dump it directly.

The stack back trace contains all the informations necessary to find unsigned code being run, that’s great because scanning memory can be tricky and very error prone.

Kernel stack back trace

Sadly the NMI callback is virtualized, but it’s possible to sneak the code which iterate system threads and obtains its informations:

huge EasyAntiCheat thread stack trace code vibes here

void __fastcall EmacGetThreadStartAddressWorkItem(__int64 a1, _EMAC_WORK_ITEM_CONTEXT *context)
{
  void (__fastcall *KeSetEventFn)(PKEVENT, _QWORD, _QWORD); // rbp
  PIO_WORKITEM WorkItem; // rax
  _WORK_QUEUE_ITEM *Flink; // rdi
  _EMAC_WORK_ITEM_CONTEXT *Parameter; // rsi
  PIO_WORKITEM v7; // rcx
  void *ThreadWin32StartAddress; // rax

  if ( context )
  {
    KeSetEventFn = (void (__fastcall *)(PKEVENT, _QWORD, _QWORD))(((unsigned __int64)KeSetEvent ^ qword_FFFFF801BCFACC40) & -(__int64)(qword_FFFFF801BCFACC38 < ((unsigned __int64)KeSetEvent ^ qword_FFFFF801BCFACC40)));
    WorkItem = context->WorkItem;
    if ( WorkItem )
    {
      if ( context->Thread )
      {
        Flink = *(_WORK_QUEUE_ITEM **)WorkItem;
        if ( *(_QWORD *)WorkItem )
        {
          if ( Flink == (_WORK_QUEUE_ITEM *)WorkItem )
          {
            while ( Flink != (_WORK_QUEUE_ITEM *)context->WorkItem )
            {
              Parameter = (_EMAC_WORK_ITEM_CONTEXT *)Flink->Parameter;
              if ( IsWindows10() && *(PVOID *)&Parameter[1].Event.Header.Lock == context->Thread
                || (v7 = Parameter->WorkItem) != 0i64 && !EmacGetModuleInfoFromAddress((unsigned __int64)v7, 1) )
              {
                context->Unknown2 = Parameter->WorkItem;
                break;
              }
              Flink = (_WORK_QUEUE_ITEM *)Flink->List.Flink;
              if ( !Flink )
                break;
            }
          }
        }
      }
    }
    ThreadWin32StartAddress = (void *)EmacGetThreadWin32StartAddress(KeGetCurrentThread());
    context->ThreadStartAddress = ThreadWin32StartAddress;
    if ( !EmacIsAddressInCodeSectionRange(
            (unsigned __int64)ThreadWin32StartAddress,
            (_IMAGE_DOS_HEADER *)g_NtoskrnlBase,
            0i64) )
      context->ThreadStartAddress = 0i64;
    KeSetEventFn(&context->Event, 0i64, 0i64);
  }
}

char __fastcall EmacGetThreadStartAddress(PETHREAD Thread, void **a2, void **StartAddress)
{
  char v3; // bl
  void (__fastcall *KeInitializeEventFn)(PKEVENT, _QWORD, _QWORD); // r13
  void (__fastcall *IoQueueWorkItemFn)(struct _IO_WORKITEM *, void (__fastcall *)(__int64, _EMAC_WORK_ITEM_CONTEXT *), __int64, _EMAC_WORK_ITEM_CONTEXT *); // r15
  void (__fastcall *KeWaitForSingleObjectFn)(void *, _QWORD, _QWORD, _QWORD, _QWORD); // r12
  __int64 (__fastcall *ExAllocatePoolWithTagFn)(_QWORD, __int64, _QWORD); // r9
  void (__fastcall *ExFreePoolWithTagFn)(_EMAC_WORK_ITEM_CONTEXT *, _QWORD); // rbp
  _EMAC_WORK_ITEM_CONTEXT *Context; // rax MAPDST
  struct _IO_WORKITEM *workItem; // rax
  __int64 (__fastcall *IoAllocateWorkItemFn)(__int64); // [rsp+78h] [rbp+10h]

  v3 = 0;
  KeInitializeEventFn = (void (__fastcall *)(PKEVENT, _QWORD, _QWORD))(((unsigned __int64)KeInitializeEvent ^ qword_FFFFF801BCFACC40) & -(__int64)(((unsigned __int64)KeInitializeEvent ^ qword_FFFFF801BCFACC40) > qword_FFFFF801BCFACC38));
  IoAllocateWorkItemFn = (__int64 (__fastcall *)(__int64))(((unsigned __int64)IoAllocateWorkItem ^ qword_FFFFF801BCFACC40) & -(__int64)(((unsigned __int64)IoAllocateWorkItem ^ qword_FFFFF801BCFACC40) > qword_FFFFF801BCFACC38));
  IoQueueWorkItemFn = (void (__fastcall *)(struct _IO_WORKITEM *, void (__fastcall *)(__int64, _EMAC_WORK_ITEM_CONTEXT *), __int64, _EMAC_WORK_ITEM_CONTEXT *))(((unsigned __int64)IoQueueWorkItem ^ qword_FFFFF801BCFACC40) & -(__int64)(((unsigned __int64)IoQueueWorkItem ^ qword_FFFFF801BCFACC40) > qword_FFFFF801BCFACC38));
  KeWaitForSingleObjectFn = (void (__fastcall *)(void *, _QWORD, _QWORD, _QWORD, _QWORD))(((unsigned __int64)KeWaitForSingleObject ^ qword_FFFFF801BCFACC40) & -(__int64)(((unsigned __int64)KeWaitForSingleObject ^ qword_FFFFF801BCFACC40) > qword_FFFFF801BCFACC38));
  ExAllocatePoolWithTagFn = (__int64 (__fastcall *)(_QWORD, __int64, _QWORD))(((unsigned __int64)ExAllocatePoolWithTag ^ qword_FFFFF801BCFACC40) & -(__int64)(((unsigned __int64)ExAllocatePoolWithTag ^ qword_FFFFF801BCFACC40) > qword_FFFFF801BCFACC38));
  ExFreePoolWithTagFn = (void (__fastcall *)(_EMAC_WORK_ITEM_CONTEXT *, _QWORD))(((unsigned __int64)ExFreePoolWithTag ^ qword_FFFFF801BCFACC40) & -(__int64)(((unsigned __int64)ExFreePoolWithTag ^ qword_FFFFF801BCFACC40) > qword_FFFFF801BCFACC38));
  if ( a2 )
    *a2 = 0i64;
  if ( StartAddress )
    *StartAddress = 0i64;
  Context = (_EMAC_WORK_ITEM_CONTEXT *)ExAllocatePoolWithTagFn(0i64, 0x38i64, 'CAME');
  if ( Context )
  {
    *(_OWORD *)&Context->Event.Header.Lock = 0i64;
    *(_OWORD *)&Context->Event.Header.WaitListHead.Blink = 0i64;
    *(_OWORD *)&Context->WorkItem = 0i64;
    Context->ThreadStartAddress = 0i64;
    KeInitializeEventFn(&Context->Event, 0i64, 0i64);
    Context->Thread = Thread;
    Context->ThreadStartAddress = 0i64;
    Context->Unknown2 = 0i64;
    workItem = (struct _IO_WORKITEM *)IoAllocateWorkItemFn(g_EmacDeviceObject);
    Context->WorkItem = workItem;
    if ( workItem )
    {
      IoQueueWorkItemFn(workItem, EmacGetThreadStartAddressWorkItem, 1i64, Context);
      KeWaitForSingleObjectFn(Context, 0i64, 0i64, 0i64, 0i64);
      if ( a2 )
        *a2 = Context->Unknown2;
      if ( StartAddress )
        *StartAddress = Context->ThreadStartAddress;
      if ( Context->Unknown2 || Context->ThreadStartAddress )
        v3 = 1;
    }
    ExFreePoolWithTagFn(Context, 'CAME');
    LOBYTE(Context) = v3;
  }
  return (char)Context;
}

ULONG_PTR *__fastcall EmacVerifyKernelThreadsStackTrace(unsigned __int64 a1, void *a2, unsigned int a3, ULONG64 a4)
{
  ULONG64 v4; // r12
  unsigned int status; // ebx MAPDST
  unsigned int v7; // esi
  int (__fastcall *PsLookupThreadByThreadIdFn)(_QWORD, struct _KTHREAD **); // r13 MAPDST
  void (__fastcall *KeStackAttachProcessFn)(PEPROCESS, __int64 *); // rdi
  int v10; // eax
  void (*ObfDereferenceObjectFn)(void); // r15
  __int64 PsGetCurrentThreadIdFn; // r14 MAPDST
  size_t v14; // r13
  int v16; // ecx
  int OffsetKThreadStackLimit; // eax
  int OffsetKThreadStackBase; // eax
  int OffsetKThreadThreadLock; // eax
  unsigned int OffsetKThreadKernelStack; // eax
  __int64 OffsetKThreadKernelStack_1; // rdi
  unsigned int OffsetKThreadState; // eax
  __int64 OffsetKThreadState_1; // rsi
  int v24; // r14d
  const void **threadInitialStack; // rdi
  _BYTE *threadState; // rsi
  void *threadStackBase; // r15
  __int64 v28; // rdx
  void *threadCurrentStack; // rcx
  int v31; // r15d
  void *CurrentRip; // rdi
  void **Rsp; // rsi
  size_t i; // r14
  __int64 FunctionEntry; // rax
  size_t FrameIndex; // rax
  size_t v38; // r15
  unsigned int StackTraceThreadId; // r13d
  size_t y; // rdi
  unsigned __int64 currentStackFrame; // r14
  EMAC_MODULE_ENTRY *ModuleInfoFromAddress; // rsi
  __m128 si128; // xmm0
  unsigned __int64 lastStackFrame; // rax
  ULONG_PTR v45; // rdi
  unsigned int *v47; // r8
  ULONG_PTR *result; // rax
  bool largePage; // [rsp+40h] [rbp-C0h] BYREF
  int ThreadId; // [rsp+44h] [rbp-BCh]
  char CurrentIrql; // [rsp+48h] [rbp-B8h]
  struct _KTHREAD *Thread; // [rsp+58h] [rbp-A8h] MAPDST BYREF
  ULONG64 poolAddress; // [rsp+60h] [rbp-A0h] MAPDST
  void (*v55)(void); // [rsp+68h] [rbp-98h]
  int v56; // [rsp+70h] [rbp-90h]
  __int64 ImageBase; // [rsp+78h] [rbp-88h] BYREF
  size_t stackFramesCount; // [rsp+80h] [rbp-80h]
  PVOID FrameFileHeader; // [rsp+88h] [rbp-78h] BYREF
  void *HandlerData; // [rsp+A0h] [rbp-60h] BYREF
  char SubStr[16]; // [rsp+B0h] [rbp-50h] BYREF
  wchar_t Str2[8]; // [rsp+C0h] [rbp-40h] BYREF
  __m128 v65; // [rsp+D0h] [rbp-30h]
  unsigned __int8 (__fastcall *PsIsThreadTerminatingFn)(struct _KTHREAD *); // [rsp+E0h] [rbp-20h]
  void *threadStackLimit; // [rsp+E8h] [rbp-18h]
  __int64 (__stdcall *MmGetPhysicalAddressFn)(void *); // [rsp+F0h] [rbp-10h]
  unsigned __int8 (__fastcall *MmIsAddressValidFn_2)(const void *); // [rsp+F8h] [rbp-8h]
  __int64 threadLock; // [rsp+100h] [rbp+0h]
  void (__fastcall *KeReleaseSpinLockFn)(__int64, __int64); // [rsp+108h] [rbp+8h]
  _IMAGE_NT_HEADERS64 *ntoskrnlHeader; // [rsp+110h] [rbp+10h]
  BOOLEAN (__stdcall *MmIsAddressValidFn)(PVOID); // [rsp+118h] [rbp+18h] MAPDST
  __int64 (__fastcall *RtlLookupFunctionTableEntryFn)(void *, __int64 *, _QWORD); // [rsp+120h] [rbp+20h]
  void (__fastcall *RtlVirtualUnwindFn)(_QWORD, __int64, ULONG64, __int64, CONTEXT *, void **, DWORD64 *, _QWORD); // [rsp+128h] [rbp+28h]
  void (__fastcall *ExFreePoolWithTagFn)(ULONG64, _QWORD); // [rsp+130h] [rbp+30h]
  unsigned __int8 (__fastcall *MmIsAddressValidFn_1)(__int64); // [rsp+138h] [rbp+38h]
  void (__fastcall *RtlPcToFileHeaderFn)(unsigned __int64, PVOID *); // [rsp+140h] [rbp+40h]
  void (__fastcall *KeUnstackDetachProcessFn)(__int64 *); // [rsp+148h] [rbp+48h]
  struct _KTHREAD *CurrentThread; // [rsp+150h] [rbp+50h]
  __int128 EstablisherFrame; // [rsp+158h] [rbp+58h] BYREF
  __int64 (__fastcall *KeAcquireSpinLockRaiseToDpcFn)(__int64); // [rsp+168h] [rbp+68h]
  __m128i v83; // [rsp+170h] [rbp+70h] BYREF
  __m128 v84; // [rsp+180h] [rbp+80h]
  __m128i v85; // [rsp+190h] [rbp+90h] BYREF
  __int64 v86; // [rsp+1A0h] [rbp+A0h] BYREF
  __int128 v87; // [rsp+1A8h] [rbp+A8h]
  __int128 v88; // [rsp+1B8h] [rbp+B8h]
  unsigned __int64 baseAddress; // [rsp+1C8h] [rbp+C8h]
  __int64 stackFrames[32]; // [rsp+1D0h] [rbp+D0h] BYREF
  CONTEXT ContextRecord; // [rsp+2D0h] [rbp+1D0h] BYREF

  v4 = a4;
  status = 0;
  if ( KeGetCurrentIrql() > 1u )
  {
    v47 = (unsigned int *)(a4 + 0x30);
    status = 0xC0000148;
    result = (ULONG_PTR *)(a4 + 0x38);
    v45 = 0i64;
  }
  else
  {
    Thread = 0i64;
    v7 = 8;
    CurrentThread = KeGetCurrentThread();
    ThreadId = 8;
    baseAddress = 0i64;
    v86 = 0i64;
    v87 = 0i64;
    v88 = 0i64;
    memset(stackFrames, 0, sizeof(stackFrames));
    FrameFileHeader = 0i64;
    PsLookupThreadByThreadIdFn = (int (__fastcall *)(_QWORD, struct _KTHREAD **))(((unsigned __int64)PsLookupThreadByThreadId ^ qword_FFFFF801BCFACC40) & -(__int64)(qword_FFFFF801BCFACC38 < ((unsigned __int64)PsLookupThreadByThreadId ^ qword_FFFFF801BCFACC40)));
    KeStackAttachProcessFn = (void (__fastcall *)(PEPROCESS, __int64 *))(((unsigned __int64)KeStackAttachProcess ^ qword_FFFFF801BCFACC40) & -(__int64)(qword_FFFFF801BCFACC38 < ((unsigned __int64)KeStackAttachProcess ^ qword_FFFFF801BCFACC40)));
    KeUnstackDetachProcessFn = (void (__fastcall *)(__int64 *))(((unsigned __int64)KeUnstackDetachProcess ^ qword_FFFFF801BCFACC40) & -(__int64)(qword_FFFFF801BCFACC38 < ((unsigned __int64)KeUnstackDetachProcess ^ qword_FFFFF801BCFACC40)));
    RtlPcToFileHeaderFn = (void (__fastcall *)(unsigned __int64, PVOID *))((RtlPcToFileHeader ^ qword_FFFFF801BCFACC40) & -(__int64)(qword_FFFFF801BCFACC38 < (RtlPcToFileHeader ^ (unsigned __int64)qword_FFFFF801BCFACC40)));
    v10 = *(_DWORD *)(a1 + 0x3F8);
    MmIsAddressValidFn_1 = (unsigned __int8 (__fastcall *)(__int64))(((unsigned __int64)MmIsAddressValid ^ qword_FFFFF801BCFACC40) & -(__int64)(qword_FFFFF801BCFACC38 < ((unsigned __int64)MmIsAddressValid ^ qword_FFFFF801BCFACC40)));
    v56 = v10;
    ObfDereferenceObjectFn = (void (*)(void))(((unsigned __int64)ObfDereferenceObject ^ qword_FFFFF801BCFACC40) & -(__int64)(qword_FFFFF801BCFACC38 < ((unsigned __int64)ObfDereferenceObject ^ qword_FFFFF801BCFACC40)));
    v55 = ObfDereferenceObjectFn;
    if ( v10 >= 2 )
    {
      status = 0xC000000D;
      v45 = 0i64;
    }
    else
    {
      PsGetCurrentThreadIdFn = ((__int64 (*)(void))(((unsigned __int64)PsGetCurrentThreadId_0 ^ qword_FFFFF801BCFACC40) & -(__int64)(qword_FFFFF801BCFACC38 < ((unsigned __int64)PsGetCurrentThreadId_0 ^ qword_FFFFF801BCFACC40))))();
      KeStackAttachProcessFn(g_AttachedProcess, &v86);
      if ( !g_UnknownThreadStartAddress )
        EmacGetThreadStartAddress(KeGetCurrentThread(), 0i64, &g_UnknownThreadStartAddress);
      do
      {
        if ( *(_BYTE *)(a1 + 2000) || g_EmacNotReady )
          break;
        if ( v7 != (_DWORD)PsGetCurrentThreadIdFn && PsLookupThreadByThreadIdFn(v7, &Thread) >= 0 )
        {
          if ( Thread != CurrentThread && EmacIsSystemThread(Thread) )
          {
            ntoskrnlHeader = RtlImageNtHeader((_IMAGE_DOS_HEADER *)g_NtoskrnlBase);
            ImageBase = 0i64;
            EstablisherFrame = 0ui64;
            v14 = 0i64;
            HandlerData = 0i64;
            memset(&ContextRecord, 0, sizeof(ContextRecord));
            largePage = 0;
            stackFramesCount = 0i64;
            memset(stackFrames, 0, sizeof(stackFrames));
            if ( Thread && ntoskrnlHeader )
            {
              MmIsAddressValidFn = (BOOLEAN (__stdcall *)(PVOID))(((unsigned __int64)MmIsAddressValid ^ qword_FFFFF801BCFACC40) & -(__int64)(qword_FFFFF801BCFACC38 < ((unsigned __int64)MmIsAddressValid ^ qword_FFFFF801BCFACC40)));
              RtlLookupFunctionTableEntryFn = (__int64 (__fastcall *)(void *, __int64 *, _QWORD))((qword_FFFFF801BCFACC40 ^ RtlLookupFunctionTableEntry) & -(__int64)(qword_FFFFF801BCFACC38 < (qword_FFFFF801BCFACC40 ^ (unsigned __int64)RtlLookupFunctionTableEntry)));
              RtlVirtualUnwindFn = (void (__fastcall *)(_QWORD, __int64, ULONG64, __int64, CONTEXT *, void **, DWORD64 *, _QWORD))((qword_FFFFF801BCFACC40 ^ RtlVirtualUnwind) & -(__int64)(qword_FFFFF801BCFACC38 < (qword_FFFFF801BCFACC40 ^ (unsigned __int64)RtlVirtualUnwind)));
              ExFreePoolWithTagFn = (void (__fastcall *)(ULONG64, _QWORD))((qword_FFFFF801BCFACC40 ^ (unsigned __int64)ExFreePoolWithTag) & -(__int64)(qword_FFFFF801BCFACC38 < (qword_FFFFF801BCFACC40 ^ (unsigned __int64)ExFreePoolWithTag)));
              poolAddress = ((__int64 (__fastcall *)(_QWORD, __int64, _QWORD))((qword_FFFFF801BCFACC40 ^ (unsigned __int64)ExAllocatePoolWithTag) & -(__int64)(qword_FFFFF801BCFACC38 < (qword_FFFFF801BCFACC40 ^ (unsigned __int64)ExAllocatePoolWithTag))))(
                              (unsigned int)g_EmacPoolType,
                              0x2000i64,
                              'CAME');
              if ( poolAddress )
              {
                v16 = dword_FFFFF801BCFCC174;
                if ( (dword_FFFFF801BCFCC174 & 1) == 0 )
                {
                  dword_FFFFF801BCFCC174 |= 1u;
                  OffsetKThreadStackLimit = GetOffsetKThreadStackLimit();
                  v16 = dword_FFFFF801BCFCC174;
                  g_OffsetKThreadStackLimit = OffsetKThreadStackLimit;
                }
                if ( (v16 & 2) == 0 )
                {
                  dword_FFFFF801BCFCC174 = v16 | 2;
                  OffsetKThreadStackBase = GetOffsetKThreadStackBase();
                  v16 = dword_FFFFF801BCFCC174;
                  g_OffsetKThreadStackBase = OffsetKThreadStackBase;
                }
                if ( (v16 & 4) == 0 )
                {
                  dword_FFFFF801BCFCC174 = v16 | 4;
                  OffsetKThreadThreadLock = GetOffsetKThreadThreadLock();
                  v16 = dword_FFFFF801BCFCC174;
                  g_OffsetKThreadThreadLock = OffsetKThreadThreadLock;
                }
                if ( (v16 & 8) != 0 )
                {
                  OffsetKThreadKernelStack_1 = (unsigned int)g_OffsetKThreadKernelStack;
                }
                else
                {
                  dword_FFFFF801BCFCC174 = v16 | 8;
                  OffsetKThreadKernelStack = GetOffsetKThreadKernelStack();
                  v16 = dword_FFFFF801BCFCC174;
                  OffsetKThreadKernelStack_1 = OffsetKThreadKernelStack;
                  g_OffsetKThreadKernelStack = OffsetKThreadKernelStack;
                }
                if ( (v16 & 0x10) != 0 )
                {
                  OffsetKThreadState_1 = (unsigned int)g_OffsetKThreadState;
                }
                else
                {
                  dword_FFFFF801BCFCC174 = v16 | 0x10;
                  OffsetKThreadState = GetOffsetKThreadState();
                  OffsetKThreadKernelStack_1 = (unsigned int)g_OffsetKThreadKernelStack;
                  OffsetKThreadState_1 = OffsetKThreadState;
                  g_OffsetKThreadState = OffsetKThreadState;
                }
                MmIsAddressValidFn_2 = (unsigned __int8 (__fastcall *)(const void *))(((unsigned __int64)MmIsAddressValid ^ qword_FFFFF801BCFACC40) & -(__int64)(qword_FFFFF801BCFACC38 < ((unsigned __int64)MmIsAddressValid ^ qword_FFFFF801BCFACC40)));
                MmGetPhysicalAddressFn = (__int64 (__stdcall *)(void *))((qword_FFFFF801BCFACC40 ^ (unsigned __int64)MmGetPhysicalAddress) & -(__int64)(qword_FFFFF801BCFACC38 < (qword_FFFFF801BCFACC40 ^ (unsigned __int64)MmGetPhysicalAddress)));
                KeAcquireSpinLockRaiseToDpcFn = (__int64 (__fastcall *)(__int64))((qword_FFFFF801BCFACC40 ^ (unsigned __int64)KeAcquireSpinLockRaiseToDpc) & -(__int64)(qword_FFFFF801BCFACC38 < (qword_FFFFF801BCFACC40 ^ (unsigned __int64)KeAcquireSpinLockRaiseToDpc)));
                KeReleaseSpinLockFn = (void (__fastcall *)(__int64, __int64))((qword_FFFFF801BCFACC40 ^ (unsigned __int64)KeReleaseSpinLock) & -(__int64)(qword_FFFFF801BCFACC38 < (qword_FFFFF801BCFACC40 ^ (unsigned __int64)KeReleaseSpinLock)));
                PsIsThreadTerminatingFn = (unsigned __int8 (__fastcall *)(struct _KTHREAD *))((qword_FFFFF801BCFACC40 ^ (unsigned __int64)PsIsThreadTerminating) & -(__int64)(qword_FFFFF801BCFACC38 < (qword_FFFFF801BCFACC40 ^ (unsigned __int64)PsIsThreadTerminating)));
                KeGetCurrentIrql();
                v24 = 0;
                memset((void *)poolAddress, 0, 0x2000ui64);
                if ( g_OffsetKThreadStackLimit == -1
                  || g_OffsetKThreadStackBase == -1
                  || g_OffsetKThreadThreadLock == -1
                  || (_DWORD)OffsetKThreadKernelStack_1 == -1
                  || (_DWORD)OffsetKThreadState_1 == -1 )
                {
                  v31 = 0xC0000138;
                }
                else
                {
                  threadInitialStack = (const void **)((char *)Thread + OffsetKThreadKernelStack_1);
                  threadState = (char *)Thread + OffsetKThreadState_1;
                  threadStackBase = *(void **)((char *)Thread + (unsigned int)g_OffsetKThreadStackBase);
                  threadStackLimit = *(void **)((char *)Thread + (unsigned int)g_OffsetKThreadStackLimit);
                  threadLock = (__int64)Thread + (unsigned int)g_OffsetKThreadThreadLock;
                  CurrentIrql = KeAcquireSpinLockRaiseToDpcFn(threadLock);
                  if ( PsIsThreadTerminatingFn(Thread) || *threadState != 5 )
                  {
                    v24 = 0xC000004B;
                  }
                  else
                  {
                    threadCurrentStack = (void *)*threadInitialStack;
                    if ( *threadInitialStack > threadStackLimit
                      && threadCurrentStack < threadStackBase
                      && MmGetPhysicalAddressFn(threadCurrentStack)
                      && MmIsAddressValidFn_2(*threadInitialStack) )
                    {
                      v14 = (_BYTE *)threadStackBase - (_BYTE *)*threadInitialStack;
                      if ( v14 > 0x2000 )
                        v14 = 0x2000i64;
                      memmove_2((void *)poolAddress, *threadInitialStack, v14);
                    }
                    else
                    {
                      v24 = 0xC0000141;
                    }
                  }
                  LOBYTE(v28) = CurrentIrql;
                  KeReleaseSpinLockFn(threadLock, v28);
                  v31 = v24;
                  if ( v24 >= 0
                    && v14 - 73 <= 8118
                    && EmacVerifyInTextSection(*(_QWORD *)(poolAddress + 0x38), (__int64)g_NtoskrnlBase, ntoskrnlHeader) )
                  {
                    memset(&ContextRecord, 0, sizeof(ContextRecord));
                    CurrentRip = *(void **)(poolAddress + 0x38);
                    Rsp = (void **)(poolAddress + 0x40);
                    i = stackFramesCount;
                    ContextRecord.Rsp = (ULONG64)Rsp;
                    ContextRecord.Rip = (ULONG64)CurrentRip;
                    do
                    {
                      ImageBase = 0i64;
                      HandlerData = 0i64;
                      EstablisherFrame = 0i64;
                      if ( (unsigned __int64)CurrentRip <= qword_FFFFF801BCFACC38 )
                        break;
                      if ( (unsigned __int64)Rsp <= qword_FFFFF801BCFACC38 )
                        break;
                      if ( !MmIsAddressValidFn(CurrentRip) )
                        break;
                      if ( !MmIsAddressValidFn(Rsp) )
                        break;
                      largePage = 0;
                      if ( !EmacIsPageEntryValid((__int64)CurrentRip, &largePage, 0i64) )
                        break;
                      if ( largePage )
                        break;
                      stackFrames[i] = (__int64)CurrentRip;
                      if ( !EmacGetModuleInfoFromAddress((unsigned __int64)CurrentRip, 1) )
                        break;
                      FunctionEntry = RtlLookupFunctionTableEntryFn(CurrentRip, &ImageBase, 0i64);
                      if ( FunctionEntry )
                      {
                        RtlVirtualUnwindFn(
                          0i64,
                          ImageBase,
                          ContextRecord.Rip,
                          FunctionEntry,
                          &ContextRecord,
                          &HandlerData,
                          (DWORD64 *)&EstablisherFrame,
                          0i64);
                        CurrentRip = (void *)ContextRecord.Rip;
                        Rsp = (void **)ContextRecord.Rsp;
                      }
                      else
                      {
                        CurrentRip = *Rsp;
                        Rsp = (void **)(ContextRecord.Rsp + 8);
                        ContextRecord.Rip = (ULONG64)CurrentRip;
                        ContextRecord.Rsp += 8i64;
                      }
                      FrameIndex = i++;
                      stackFramesCount = i;
                      if ( FrameIndex >= 32 )
                        break;
                    }
                    while ( (unsigned __int64)CurrentRip >= qword_FFFFF801BCFACC38 && !g_EmacNotReady );
                  }
                }
                ExFreePoolWithTagFn(poolAddress, 'CAME');
                if ( v31 < 0 || (v38 = stackFramesCount, stackFramesCount - 1 > 31) )
                {
                  v7 = ThreadId;
                }
                else
                {
                  StackTraceThreadId = ThreadId;
                  y = 0i64;
                  do
                  {
                    if ( *(_BYTE *)(a1 + 0x7D0) )
                      break;
                    if ( g_EmacNotReady )
                      break;
                    currentStackFrame = stackFrames[y];
                    if ( currentStackFrame <= qword_FFFFF801BCFACC38 || !MmIsAddressValidFn_1(stackFrames[y]) )
                      break;
                    ModuleInfoFromAddress = EmacGetModuleInfoFromAddress(currentStackFrame, 1);
                    RtlPcToFileHeaderFn(currentStackFrame, &FrameFileHeader);
                    if ( ModuleInfoFromAddress )
                    {
                      if ( FrameFileHeader != ModuleInfoFromAddress->ImageBase )
                        break;
                      if ( ModuleInfoFromAddress->SubjectName[0] )
                      {
                        *(_QWORD *)Str2 = 0xB07F113F97FF5772ui64;
                        *(_QWORD *)&Str2[4] = 0x13112D07CA8DB1F8i64;
                        si128 = (__m128)_mm_load_si128((const __m128i *)Str2);
                        v65.m128_u64[0] = 0x77B95CDEC3F425C2i64;
                        v84.m128_u64[0] = 0xD6106250E59C3E3Fui64;
                        v65.m128_u64[1] = 0xA8723627E07A05FEui64;
                        v84.m128_u64[1] = 0x647E4969A3DA918Ci64;
                        v85.m128i_i64[1] = 0xA8723627E07A05FEui64;
                        v85.m128i_i64[0] = 0x77B95CDEC3F425B1i64;
                        v65 = _mm_xor_ps((__m128)_mm_load_si128(&v85), v65);// Decrypted UTF-8: s
                        *(__m128 *)Str2 = _mm_xor_ps(si128, v84);// Decrypted Raw (unprintable): 44 85 40 94 3f 11 7f b0 74 20 57 69 6e 64 6f 77
                        if ( strcmp(ModuleInfoFromAddress->SubjectName, (const char *)Str2) )// "Microsoft Corporation"
                          break;
                      }
                      if ( ModuleInfoFromAddress->AdditionalData[0] )
                      {
                        *(_QWORD *)SubStr = 0xB364033495E95D52ui64;
                        v83.m128i_i64[0] = 0xD6106250E59C3E3Fui64;
                        *(_QWORD *)&SubStr[8] = 0x647E4969A3DA918Ci64;
                        v83.m128i_i64[1] = 0x647E4969A3DA918Ci64;
                        *(__m128 *)SubStr = _mm_xor_ps((__m128)_mm_load_si128(&v83), *(__m128 *)SubStr);
                        if ( strstr((const char *)ModuleInfoFromAddress->AdditionalData, SubStr) )// "mcupdate"
                          break;
                      }
                    }
                    if ( v56 )
                    {
                      if ( v56 == 1
                        && ModuleInfoFromAddress
                        && !EmacIsAddressInCodeSectionRange(
                              currentStackFrame,
                              (_IMAGE_DOS_HEADER *)ModuleInfoFromAddress->ImageBase,
                              0i64) )
                      {
                        EmacReportThreadInvalidStackTrace_2(
                          Thread,
                          (__int64)g_UnknownThreadStartAddress,
                          StackTraceThreadId,
                          y,
                          v38,
                          currentStackFrame,
                          (__int64)ModuleInfoFromAddress,
                          a1);
                      }
                    }
                    else if ( !ModuleInfoFromAddress && !FrameFileHeader )
                    {
                      if ( y && y < v38 )
                        lastStackFrame = stackFrames[y - 1];
                      else
                        lastStackFrame = 0i64;
                      EmacReportThreadInvalidStackTrace(
                        Thread,
                        g_UnknownThreadStartAddress,
                        StackTraceThreadId,
                        y,
                        v38,
                        currentStackFrame,
                        lastStackFrame,
                        a1);
                    }
                    ++y;
                  }
                  while ( y <= v38 );
                  v7 = StackTraceThreadId;
                }
              }
              else
              {
                v7 = ThreadId;
              }
              ObfDereferenceObjectFn = v55;
            }
            else
            {
              v7 = ThreadId;
            }
          }
          ObfDereferenceObjectFn();
        }
        v7 += 4;
        ThreadId = v7;
        EmacDelayExecutionThread(1);
      }
      while ( v7 < 0x10000 );
      KeUnstackDetachProcessFn(&v86);
      v45 = 2008i64;
      if ( a3 < 2008 )
        status = 0xC0000004;
      else
        memmove_2(a2, (const void *)a1, 2008ui64);
      v4 = a4;
    }
    v47 = (unsigned int *)(v4 + 0x30);
    result = (ULONG_PTR *)(v4 + 0x38);
  }
  *result = v45;
  *v47 = status;
  return result;
}

The routine EmacGetThreadStartAddress queues an work item which will retrieve the thread start address, it is then verified if that address resides in any ntoskrnl.exe code section.

Now the NMI callback registration img

sadly goes to a vmenter, we cannot reverse any further img

Self integrity

I have spotted some procedures that will perform self-integrity checks in the driver.

this function finds driver file path using registry and reads the file contents into a buffer img

then relocations are stored in a list img

and finally, the buffer contents are xored with a simple key img

this function will decrypt the buffer into a copy img

this function will perform 1:1 integrity check between the memory and the file sections on disk img

Here’s the list of things that the anticheat is doing:

  • Checks MmUnloadedDrivers and PiDDBCache, those lists contains informations about kernel drivers loaded by the system
  • Checks if system images sections have the right PTE flags by iterating PsLoadedModuleList and comparing with the PE information from disk
  • Checks if processes have \Device\PhysicalMemory handle
  • Checks if some functions from HalPrivateDispatchTable are being tampered
  • Self-integrity checks at .text (code) and .idata (IAT), by comparing with the PE file on disk
  • Checks gDxgkInterface table from module win32kbase.sys, this table is oftenly used as what we call .data pointer hooks.
  • Enumerate all system handles and strip access mask from any unauthorized process
  • Actively walk throught system pages and perform physical memory dumps based on the PTE flags, this can be used to detect manually mapped drivers.
  • Iterate BigPool list, searches for known malicious tags.
  • Scans usermode process memory for patterns.
  • Monitors 8 critical syscalls for inline hooks by comparing in-memory bytes against a clean ntoskrnl copy
  • Detects aimbot float constants in memory (14 IEEE 754 values, >5 threshold triggers detection)
  • Scans module import tables for 11 suspicious APIs (>4 threshold triggers detection)
  • Checks KdDebuggerDataBlock for kernel debugger presence

That’s all i could find but there must be a lot more scans and checks sitting in virtualized code.

BigPool Scanning

Function: EmacScanBigPool — Queries SystemBigPoolInformation (class 0x42) via ZwQuerySystemInformation to enumerate large kernel pool allocations.

Algorithm

  1. First caches the ntoskrnl export table entries (function name → address mapping)
  2. Queries BigPool information iteratively (doubles buffer size on STATUS_INFO_LENGTH_MISMATCH)
  3. For each BigPool entry:
    • Checks allocation size is in range [0x3001, 0x400FFF]
    • Verifies it’s a paged allocation
    • Filters by pool tag: either 'None' (untagged) or all non-'CAME' tags (EMAC’s own tag is excluded)
    • Validates page entry and copies allocation via MmCopyMemory
  4. For each copied allocation:
    • Checks for PE headers (MZ/PE signatures) → sets PE flags
    • Scans for ntoskrnl import addresses — starting at offset 0x1000, checks every 8 bytes for pointers within ntoskrnl range
    • If import count is between 9 and 79 → reports as suspicious manually-mapped driver

Aimbot Float Constant Detection

Function: EmacDetectAimbotConstants (0xBCF41CAC) — A novel heuristic that scans memory for IEEE 754 floating-point constants commonly used in aimbot angle calculations:

Value Meaning
89.0f Near-max pitch angle
-89.0f Near-min pitch angle
180.0f Half revolution (yaw)
-180.0f Negative half revolution
360.0f Full revolution
1.5707963… (π/2) 90° in radians
6.2831853… (2π) Full circle in radians
+ 7 additional trig-related constants Various aimbot calculation values

Uses a bitmask to track which constants are found. If >5 unique aimbot constants appear in a memory region → flagged as aimbot code. This is a probabilistic heuristic — the specific combination of FPS-angle values makes false positives unlikely in game context.

Suspicious Import Detection

Function: EmacDetectSuspiciousImports — Scans a target module’s import table for 11 commonly-abused ntoskrnl APIs:

  1. MmCopyVirtualMemory
  2. PsLookupProcessByProcessId
  3. PsGetProcessSectionBaseAddress
  4. KeStackAttachProcess
  5. MmMapIoSpace
  6. PsGetProcessWow64Process
  7. PsGetProcessPeb
  8. PsGetProcessImageFileName
  9. Additional process/memory APIs

If >4 of these APIs are found in a single module’s import table → flagged as cheat driver.

MmUnloadedDrivers Blacklist

Function: EmacVerifyMmUnloadedDrivers (0xBCF2E98C) — Scans MmUnloadedDrivers for evidence of previously-loaded cheat tool drivers using XOR-encrypted strings with wildcard matching:

# Pattern Tool
1 blackbonedrv*.sys BlackBone memory injection framework
2 blackbonedrv10.sys BlackBone version 10
3 vbox.sys VirtualBox hypervisor driver
4 kernel-bridge.sys Kernel memory bridge tool
5 kprocesshacker.sys Process Hacker kernel driver
6 windowskernelexplorer.sys Windows Kernel Explorer
7 apimonitor-drv-x64.sys Rohitab API Monitor (64-bit)
8 apimonitor-drv-x86.sys Rohitab API Monitor (32-bit)
9 apimonitor-psn-x64.sys API Monitor PSN (64-bit)
10 apimonitor-psn-x86.sys API Monitor PSN (32-bit)
11 dbgview*.sys SysInternals DebugView

Syscall Integrity Monitoring

Function: EmacDetectSyscallHooks (0xBCF2E468) — Monitors critical syscalls for inline hooks by comparing in-memory bytes against a clean copy of ntoskrnl.exe loaded from disk.

Syscall FNV-1a Hash Purpose
NtOpenProcess 0x99A68185 Process handle acquisition
NtReadVirtualMemory 0x8C0AAAED Memory reading
NtWriteVirtualMemory 0x0B3DE7DF Memory writing
NtProtectVirtualMemory 0x9903CAA0 Memory protection changes
NtQueryVirtualMemory 0x7C202192 Memory information query
NtAllocateVirtualMemory 0x75225970 Memory allocation
NtSuspendThread 0x5BEF89C9 Thread suspension
NtCreateThreadEx 0x86DE5B7C Thread creation

System Table Integrity Checks

  • HalPrivateDispatchTable — Checks if dispatch functions have been tampered (common .data pointer hook target)
  • gDxgkInterface — Verifies the table in win32kbase.sys (frequently used for display kernel call interception)
  • KdDebuggerDataBlock — Captures KdDebuggerDataBlock and checks KdpDebugRoutineSelect, KdpTrap variants to detect kernel debuggers

Page Table Walking Infrastructure

Full 4-level page table walking (PML4 → PDPT → PD → PT) for physical memory validation:

Function Purpose
EmacGetPML4E Gets Page Map Level 4 Entry
EmacGetPDPE Gets Page Directory Pointer Entry
EmacGetPDE Gets Page Directory Entry
EmacGetPTE Gets Page Table Entry
EmacIsPageEntryValid Validates PTE (checks Present bit, large page flag)
EmacIsPhysicalPageValid Checks if physical page is valid
EmacIsPhysicalPageWritable Checks if page is writable
EmacIsPhysicalPageExecutable Checks if page is executable (NX bit clear)
EmacIsPhysicalPageNonPaged Checks if page is non-paged

Physical Memory Scanning

Walks physical pages looking for pages that are both writable AND executable (RWX) — common for injected code and manually mapped drivers.

Driver Self-Integrity Functions

Function Address Purpose
EmacGetDriverFromRegistry 0xBCF0D370 Gets driver file path from registry
EmacDecryptDriver 0xBCF0D6C4 XOR-decrypts the stored driver copy
EmacGetDriverDecrypted 0xBCF0D258 Returns decrypted driver buffer
EmacVerifyDriverIntegrityImportTable 0xBCF0D790 Compares non-.idata sections against disk
EmacVerifyDriverIntegrityReadableSection 0xBCF0D904 Compares readable sections against disk

IDA decompiled snippets

Integrity Snippets