EMACLAB Anticheat Driver, Part 3: Anti-virtualization

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

Hypervisor checks

One interesting part about the anticheat is the hypervisor checks, but as far as i could find there’s nothing out of ordinary.

Most of those checks are straight taken from other anticheats, or have been talked about in game cheating forums.

The anti-hypervisor subsystem is orchestrated by EmacAntiHypervisorChecks (0xBCF13B34) and employs multiple techniques to detect virtual machines and poorly-implemented hypervisors:

Function Address Description
EmacAntiHypervisorChecks 0xBCF13B34 Main orchestrator dispatching all HV checks
EmacAntiHypervisorCheckName 0xBCF13A00 CPUID leaf 0x40000000 hypervisor brand string check
EmacAntiHypervisorLBR 0xBCF13A90 Last Branch Recording MSR 0x1D9 validation
EmacAntiHypervisorTrashMsr 0xBCF13C28 Synthetic MSR probe (0x40000000 range)
EmacAntiHypervisorAdditionalChecks 0xBCF13C5C KiErrata checks on kernel globals
EmacAntiHypervisorXsetbv 0xBCF13D4C XSETBV instruction probing
EmacAntiHypervisorTiming 0xBCF1837C RDTSC timing around CPUID
EmacHypervisorCheck 0xBCF16CA4 VMX extension detection
EmacHypervisorCheck_0 0xBCF16E10 SEH-based VMM probes
EmacAntiHypervisorCallVmfunc 0xBCEEB1DD VMFUNC instruction test
EmacDetectVirtualizationPci 0xBCF2FBA0 PCI vendor ID enumeration

LBR (Last Branch Recording) MSR Validation

EmacAntiHypervisorLBR writes to MSR 0x1D9 (IA32_DEBUGCTL) with LBR enable bits. Hypervisors that poorly virtualize debug MSRs will return unexpected values, revealing their presence:

__writemsr(0x1D9, 1);   // Enable LBR (bit 0)
value = __readmsr(0x1D9);
// Hypervisor that intercepts but mishandles → value differs → detected

Those checks focus on exploiting bad hypervisors img

img

Synthetic MSR Probes

EmacAntiHypervisorTrashMsr probes the VMM-reserved MSR range (0x40000000–0x400000FF). Real hardware generates #GP; VMMs may respond with values:

__try {
    value = __readmsr(0x40000000);
    // If no exception → VMM present
} __except {
    // Expected on real hardware
}

Messing with LBR virtualization img

img

KiErrata Kernel Variable Checks

EmacAntiHypervisorAdditionalChecks checks three kernel errata flags that may indicate system-level patches inconsistent with virtualization:

  • KiErrata1337Present
  • KiErrataSkx55Present
  • KiErrata704Present

KiErrata1337Present, KiErrataSkx55Present, KiErrata704Present img

CPUID Hypervisor Brand Check

EmacAntiHypervisorCheckName reads CPUID leaf 0x40000000 to check if the reported hypervisor is Microsoft Hyper-V ("Microsoft Hv"). Legitimate Hyper-V presence is expected on modern Windows; other brand strings may indicate third-party hypervisors.

Checks if CPUID hypervisor is Microsoft Hyper-V img

Intel CPU Feature Checks

Before performing Intel-specific checks, the driver:

  1. Verifies CPUID processor name contains “Intel” via EmacDetectCpuFeatures
  2. Checks if VMX extensions are enabled (CR4.VMXE) via EmacHypervisorCheck
  3. Uses SEH-wrapped VMCALL/VMFUNC to probe for hypervisor presence via EmacHypervisorCheck_0 and EmacAntiHypervisorCallVmfunc

Checks if CPUID processor name is Intel as proceeding checks are only available for Intel CPUs img

Then verifies if VMX extensions are enabled, perform calls to try detect hypervisors using SEH img

img

CPUID Timing Analysis

EmacAntiHypervisorTiming measures VM exit overhead using RDTSC around CPUID:

start = __rdtsc();
__cpuid(regs, 0);
end = __rdtsc();
// Real hardware: ~200 cycles | Virtualized: ~2000+ cycles

CPU timing checks to try detect virtualization img

XSETBV Probing

EmacAntiHypervisorXsetbv tests the XSETBV instruction behavior. On real hardware, specific XCR0 combinations will generate #GP, while poorly implemented hypervisors may handle them differently.


PCI Device Enumeration

EmacDetectVirtualizationPci (0xBCF2FBA0) scans PCI configuration space for known virtual hardware vendor IDs:

Vendor ID Platform
0x15AD VMware
0x1AB8 Parallels
0x1414 Hyper-V
0x1AF4 VirtIO (QEMU/KVM)
0x80EE VirtualBox

Returns vendor ID and device count. The decompiled logic scans for VMware (0x15AD) first, then VirtualBox (0x80EE).

PCI Timing Analysis

EmacGetPciLatency measures I/O port access latency for PCI config space reads (ports 0xCF8/0xCFC). TSC is measured around each PCI read at raised IRQL. Potentially used to detect DMA/PCIe-based cheating devices by analyzing access timing anomalies.

unsigned __int64 __fastcall EmacGetPciLatency(unsigned int a1, unsigned __int8 a2, char a3, char a4, char a5)
{
  unsigned __int64 v5; // rsi
  unsigned __int64 v9; // rbx
  int v10; // eax
  __int64 v11; // r15
  unsigned int v12; // edi
  unsigned __int64 v13; // r14
  int v14; // edi
  unsigned __int8 CurrentIrql; // bl
  unsigned int v17; // [rsp+20h] [rbp-10h] BYREF
  unsigned int v18; // [rsp+24h] [rbp-Ch] BYREF
  unsigned int v19; // [rsp+28h] [rbp-8h] BYREF
  unsigned int v20; // [rsp+50h] [rbp+20h] BYREF

  v5 = 0i64;
  v18 = 0;
  v20 = 0;
  v19 = 0;
  v17 = 0;
  v9 = a1;
  KeGetCurrentIrql();
  EmacGetTscTop(&v18, &v19);
  EmacGetTscBottom(&v20, &v17);
  EmacGetTscTop(&v18, &v19);
  EmacGetTscBottom(&v20, &v17);
  if ( (_DWORD)v9 )
  {
    v10 = a3 & 0x1F;
    v11 = (unsigned int)v9;
    v12 = a4 & 7 | (8 * (v10 | (32 * (a2 | 0xFFFF8000))));
    v13 = (unsigned int)v9;
    v14 = a5 & 0xFC | (v12 << 8);
    do
    {
      CurrentIrql = KeRaiseIrql(2u);
      EmacGetTscTop(&v18, &v19);
      __outdword(0xCF8u, v14);
      __indword(0xCFCu);
      EmacGetTscBottom(&v20, &v17);
      KeRaiseIrql(CurrentIrql);
      v5 += (v17 | ((unsigned __int64)v20 << 32)) - (v19 | ((unsigned __int64)v18 << 32));
      --v11;
    }
    while ( v11 );
  }
  else
  {
    v13 = v9;
  }
  return v5 / v13;
}

PCI Infrastructure Functions

Function Address Purpose
EmacEnumeratePciBridgeSubBus Traverses PCI bridge secondary buses
EmacBuildPciDeviceRecord Creates device info record from PCI config
EmacScanPciBusByVendorId Searches specific bus for target vendor
EmacEnumerateAllPciDevices Full PCI bus enumeration
EmacEnumeratePciBusDevices Per-bus device scan
EmacReadPciConfigSpace Raw PCI config space read via I/O ports
EmacIsAddressWithinPciRange Address range validation

FNV-1a Storage Device Whitelist

Legitimate storage device identifiers are whitelisted via FNV-1a hashes to avoid false positives on real hardware:

0x5F29C8DC, 0x3B5D28B5, 0x63C3F8B6, 0x6E4F0C16,
0x21CD3EBC, 0x79B1C26D, 0x64FCF14B, ...

IDA decompiled snippets

Hypervisor Checks Snippets

Conclusion

I was able to run the anti-cheat on virtualized environments like VMware and KVM, as long as i had Hyper-V running i had no problems, but that doesn’t necessarely means there’s was no flags. If you wanna play safely my guess is: use KVM with QEMU patches and enable Hyper-V, this should be enought for ban evasion, or, if you’re the ultimate linux user that should work for you.

The anti-hypervisor subsystem, while comprehensive, focuses primarily on detecting poorly-configured hypervisors. The PCI device enumeration and timing analysis suggest awareness of DMA-based cheating devices. The FNV-1a storage device whitelist helps reduce false positives from legitimate hardware.

Like i said those checks are nothing new, most modern anti-cheats like Riot Games Vanguard take this more seriously and have better, advanced checks to detect virtualization.