@@ -4387,11 +4387,11 @@ struct VM {
43874387 return t2 - t1;
43884388 };
43894389
4390- constexpr u8 N = 100 ;
4390+ constexpr u16 N = 1000 ;
43914391
43924392 auto sample_avg = [&]() -> u64 {
43934393 u64 sum = 0 ;
4394- for (u8 i = 0 ; i < N; ++i) {
4394+ for (u16 i = 0 ; i < N; ++i) {
43954395 sum += cpuid ();
43964396 }
43974397 return (sum + N / 2 ) / N;
@@ -4405,7 +4405,11 @@ struct VM {
44054405 else if (avg >= cycleThreshold) {
44064406 avg = sample_avg ();
44074407 debug (" TIMER: 2nd pass average -> " , avg, " cycles" );
4408- if (avg >= cycleThreshold) return true ; // some CPUs like Intel's Emerald Rapids have much more cycles when executing CPUID than average, we should accept a high threshold
4408+ if (avg >= cycleThreshold) {
4409+ avg = sample_avg ();
4410+ debug (" TIMER: 3rd pass average -> " , avg, " cycles" );
4411+ if (avg >= cycleThreshold) return true ; // some CPUs like Intel's Emerald Rapids have much more cycles when executing CPUID than average, we should accept a high threshold
4412+ }
44094413 }
44104414
44114415 #if (WINDOWS)
@@ -4532,7 +4536,7 @@ struct VM {
45324536 );
45334537 }
45344538
4535- if (difference >= 30 ) {
4539+ if (difference >= 100 ) {
45364540 debug (" TIMER: An hypervisor has been detected intercepting RDTSC" );
45374541 return true ; // both ratios will always differ if a RDTSC trap is present, since the hypervisor can't account for the XOR/NOP loop
45384542 }
@@ -9835,6 +9839,232 @@ struct VM {
98359839 pNtFreeVirtualMemory (hCurrentProcess, &freeBase, &freeSize, MEM_RELEASE);
98369840 amd_target_mem = nullptr ;
98379841 }
9842+
9843+ if (spoofed) return spoofed;
9844+
9845+ // ok so if the CPU is intel, the motherboard should be intel aswell (and same with AMD)
9846+ // this doesnt happen in most public hardened configs out there so lets abuse it
9847+ constexpr unsigned int VID_INTEL = 0x8086 ;
9848+ constexpr unsigned int VID_AMD_ATI = 0x1002 ;
9849+ constexpr unsigned int VID_AMD_MICRO = 0x1022 ;
9850+
9851+ enum class MBVendor { Unknown = 0 , Intel = 1 , AMD = 2 };
9852+
9853+ auto detect_motherboard = []() -> MBVendor {
9854+ static constexpr const char * TOKENS[] = {
9855+ " host bridge" , " northbridge" , " southbridge" , " pci bridge" , " chipset" , " pch" , " fch" ,
9856+ " platform controller" , " lpc" , " sata controller" , " ahci" , " ide controller" , " usb controller" ,
9857+ " xhci" , " usb3" , " usb 3.0" , " usb 3" , " pcie root" , " pci express" , " sata" , nullptr
9858+ };
9859+
9860+ static bool meta_ready = false ;
9861+ static const char * token_ptrs[32 ];
9862+ static int token_lens[32 ];
9863+ static int token_count = 0 ;
9864+ static unsigned char first_unique[128 ];
9865+ static int first_unique_count = 0 ;
9866+
9867+ auto build_meta = [&]() {
9868+ if (meta_ready) return ;
9869+ int i = 0 ;
9870+ for (; TOKENS[i]; ++i) {}
9871+ token_count = i;
9872+ assert (token_count < 32 );
9873+ bool seen[128 ] = {};
9874+ for (int t = 0 ; t < token_count; ++t) {
9875+ token_ptrs[t] = TOKENS[t];
9876+ token_lens[t] = static_cast <int >(std::strlen (TOKENS[t]));
9877+ unsigned char fc = static_cast <unsigned char >(token_ptrs[t][0 ]);
9878+ if (fc < 128 && !seen[fc]) {
9879+ first_unique[first_unique_count++] = fc;
9880+ seen[fc] = true ;
9881+ }
9882+ }
9883+ meta_ready = true ;
9884+ };
9885+
9886+ auto ascii_lower = [](unsigned char c) -> unsigned char {
9887+ if (c >= ' A' && c <= ' Z' ) return static_cast <unsigned char >(c + (' a' - ' A' ));
9888+ return c;
9889+ };
9890+
9891+ const size_t STACK_CAP = 4096 ;
9892+ auto wide_to_ascii = [&](const wchar_t * wptr, char * stackBuf, size_t stackCap, std::vector<char >* heapBuf, size_t & outLen) -> const char * {
9893+ outLen = 0 ;
9894+ if (!wptr || *wptr == L' \0 ' ) return nullptr ;
9895+ const wchar_t * p = wptr;
9896+ char * out = stackBuf;
9897+ size_t cap = stackCap;
9898+ while (*p) {
9899+ wchar_t wc = *p++;
9900+ unsigned char c;
9901+ if (wc <= 127 ) {
9902+ c = ascii_lower (static_cast <unsigned char >(wc));
9903+ }
9904+ else {
9905+ c = 0 ;
9906+ }
9907+ if (outLen >= cap) {
9908+ if (!heapBuf) return nullptr ;
9909+ heapBuf->assign (stackBuf, stackBuf + cap);
9910+ out = heapBuf->data ();
9911+ cap = heapBuf->capacity ();
9912+ }
9913+ out[outLen++] = static_cast <char >(c);
9914+ }
9915+ return out;
9916+ };
9917+
9918+ using u32 = unsigned int ;
9919+ auto __memchr = [&](const char * data, size_t len) -> u32 {
9920+ if (!data || len == 0 ) return 0 ;
9921+ build_meta ();
9922+ u32 mask = 0 ;
9923+ for (int fi = 0 ; fi < first_unique_count; ++fi) {
9924+ unsigned char fc = first_unique[fi];
9925+ const void * cur = data;
9926+ size_t remaining = len;
9927+ while (remaining > 0 ) {
9928+ const void * found = memchr (cur, fc, remaining);
9929+ if (!found) break ;
9930+ const char * pos = static_cast <const char *>(found);
9931+ long long idx = pos - data;
9932+ for (int t = 0 ; t < token_count; ++t) {
9933+ if (static_cast <unsigned char >(token_ptrs[t][0 ]) != fc) continue ;
9934+ int tlen = token_lens[t];
9935+ if (idx + static_cast <size_t >(tlen) <= len) {
9936+ if (memcmp (data + idx, token_ptrs[t], static_cast <size_t >(tlen)) == 0 ) {
9937+ mask |= (1u << t);
9938+ }
9939+ }
9940+ }
9941+ remaining = len - (idx + 1 );
9942+ cur = data + idx + 1 ;
9943+ }
9944+ }
9945+ return mask;
9946+ };
9947+
9948+ auto find_vendor_hex = [&](const wchar_t * wptr) -> u32 {
9949+ if (!wptr) return 0 ;
9950+ const wchar_t * p = wptr;
9951+ while (*p) {
9952+ const wchar_t c = *p;
9953+ if ((c | 0x20 ) == L' v' ) {
9954+ if ((p[1 ] | 0x20 ) == L' e' && (p[2 ] | 0x20 ) == L' n' && p[3 ] == L' _' ) {
9955+ const wchar_t * q = p + 4 ;
9956+ u32 val = 0 ;
9957+ int got = 0 ;
9958+ while (got < 4 && *q) {
9959+ wchar_t wc = *q;
9960+ u32 nib = 0 ;
9961+ if (wc >= L' 0' && wc <= L' 9' ) nib = static_cast <u32 >(wc - L' 0' );
9962+ else if ((wc | 0x20 ) >= L' a' && (wc | 0x20 ) <= L' f' ) nib = static_cast <u32 >((wc | 0x20 ) - L' a' + 10 );
9963+ else break ;
9964+ val = static_cast <u32 >((val << 4 ) | nib);
9965+ ++got; ++q;
9966+ }
9967+ if (got == 4 ) return val;
9968+ }
9969+ }
9970+ ++p;
9971+ }
9972+ return 0 ;
9973+ };
9974+
9975+ // setupapi stuff
9976+ int intel_hits = 0 ;
9977+ int amd_hits = 0 ;
9978+ char stack_buf[4096 ]{};
9979+ std::vector<char > heap_buf;
9980+ std::vector<BYTE> prop_buf;
9981+
9982+ auto scan_devices = [&](const GUID* classGuid, DWORD flags) {
9983+ HDEVINFO hDevInfo = SetupDiGetClassDevsW (classGuid, nullptr , nullptr , flags);
9984+ if (hDevInfo == INVALID_HANDLE_VALUE) return ;
9985+
9986+ SP_DEVINFO_DATA devInfoData{};
9987+ devInfoData.cbSize = sizeof (SP_DEVINFO_DATA);
9988+ DWORD reqSize = 0 ;
9989+
9990+ for (DWORD i = 0 ; SetupDiEnumDeviceInfo (hDevInfo, i, &devInfoData); ++i) {
9991+
9992+ if (!SetupDiGetDeviceRegistryPropertyW (hDevInfo, &devInfoData, SPDRP_DEVICEDESC, nullptr , nullptr , 0 , &reqSize)) {
9993+ if (GetLastError () != ERROR_INSUFFICIENT_BUFFER) continue ;
9994+ }
9995+
9996+ if (prop_buf.size () < reqSize) prop_buf.resize (reqSize);
9997+
9998+ if (SetupDiGetDeviceRegistryPropertyW (hDevInfo, &devInfoData, SPDRP_DEVICEDESC, nullptr , prop_buf.data (), reqSize, nullptr )) {
9999+
10000+ const wchar_t * wDesc = reinterpret_cast <const wchar_t *>(prop_buf.data ());
10001+ size_t asciiLen = 0 ;
10002+ const char * asciiDesc = wide_to_ascii (wDesc, stack_buf, STACK_CAP, &heap_buf, asciiLen);
10003+
10004+ // check if the description contains any interesting stuff
10005+ if (__memchr (asciiDesc, asciiLen)) {
10006+ // if interesting get hwid to get vendor
10007+ if (!SetupDiGetDeviceRegistryPropertyW (hDevInfo, &devInfoData, SPDRP_HARDWAREID, nullptr , nullptr , 0 , &reqSize)) {
10008+ if (GetLastError () != ERROR_INSUFFICIENT_BUFFER) continue ;
10009+ }
10010+
10011+ if (prop_buf.size () < reqSize) prop_buf.resize (reqSize);
10012+
10013+ if (SetupDiGetDeviceRegistryPropertyW (hDevInfo, &devInfoData, SPDRP_HARDWAREID, nullptr , prop_buf.data (), reqSize, nullptr )) {
10014+ const wchar_t * wHwId = reinterpret_cast <const wchar_t *>(prop_buf.data ());
10015+ const u32 vid = find_vendor_hex (wHwId);
10016+
10017+ if (vid == VID_INTEL) intel_hits++;
10018+ else if (vid == VID_AMD_ATI || vid == VID_AMD_MICRO) amd_hits++;
10019+ }
10020+ }
10021+ }
10022+ }
10023+ SetupDiDestroyDeviceInfoList (hDevInfo);
10024+ };
10025+
10026+ // GUID_DEVCLASS_SYSTEM covers Host Bridges, LPC, PCI bridges Chipset/CPU etc
10027+ // GUID_DEVCLASS_USB covers USB controller stuff
10028+ // GUID_DEVCLASS_HDC covers SATA/IDE
10029+ const GUID* interesting_classes[] = {
10030+ &GUID_DEVCLASS_SYSTEM,
10031+ &GUID_DEVCLASS_USB,
10032+ &GUID_DEVCLASS_HDC
10033+ };
10034+
10035+ for (const GUID* guid : interesting_classes) {
10036+ scan_devices (guid, DIGCF_PRESENT);
10037+ }
10038+
10039+ // if no stuff then mybe query all devices in the system?
10040+ if (intel_hits == 0 && amd_hits == 0 ) {
10041+ scan_devices (nullptr , DIGCF_ALLCLASSES | DIGCF_PRESENT);
10042+ }
10043+
10044+ if (intel_hits > amd_hits) return MBVendor::Intel;
10045+ if (amd_hits > intel_hits) return MBVendor::AMD;
10046+ return MBVendor::Unknown;
10047+ };
10048+
10049+ const MBVendor vendor = detect_motherboard ();
10050+
10051+ switch (vendor) {
10052+ case MBVendor::Intel:
10053+ if (claimed_amd && !claimed_intel) {
10054+ debug (" CPU_HEURISTIC: CPU reports AMD but chipset looks Intel" );
10055+ spoofed = true ;
10056+ }
10057+ break ;
10058+ case MBVendor::AMD:
10059+ if (claimed_intel && !claimed_amd) {
10060+ debug (" CPU_HEURISTIC: CPU reports Intel but chipset looks AMD" );
10061+ spoofed = true ;
10062+ }
10063+ break ;
10064+ default :
10065+ debug (" CPU_HEURISTIC: Could not determine chipset vendor" );
10066+ break ;
10067+ }
983810068 #endif
983910069 return spoofed;
984010070 }
@@ -9937,6 +10167,7 @@ struct VM {
993710167 * @brief Check if Last Branch Record MSRs are correctly virtualized
993810168 * @category Windows
993910169 * @implements VM::LBR
10170+ * @note Currently investigating possible false flags with this
994010171 */
994110172 [[nodiscard]] static bool lbr () {
994210173 #if (x86)
@@ -10123,6 +10354,7 @@ struct VM {
1012310354 pNtFreeVirtualMemory (hCurrentProcess, &controlBase, &tmpSize, MEM_RELEASE);
1012410355 g_control_slot = nullptr ;
1012510356
10357+ // a breakpoint set anywhere in this function before slot_val is read will cause the kernel to not deliver any LBR info, thereby returning true
1012610358 return (slot_val == nullptr );
1012710359 #else
1012810360 return false ;
@@ -11757,7 +11989,7 @@ std::pair<VM::enum_flags, VM::core::technique> VM::core::technique_list[] = {
1175711989 std::make_pair (VM::CLOCK, VM::core::technique (100 , VM::clock)),
1175811990 std::make_pair (VM::POWER_CAPABILITIES, VM::core::technique (45 , VM::power_capabilities)),
1175911991 std::make_pair (VM::CPU_HEURISTIC, VM::core::technique (90 , VM::cpu_heuristic)),
11760- std::make_pair (VM::LBR, VM::core::technique (100 , VM::lbr)),
11992+ std::make_pair (VM::LBR, VM::core::technique (95 , VM::lbr)),
1176111993 std::make_pair (VM::EDID, VM::core::technique (100 , VM::edid)),
1176211994 std::make_pair (VM::BOOT_LOGO, VM::core::technique (100 , VM::boot_logo)),
1176311995 std::make_pair (VM::GPU_CAPABILITIES, VM::core::technique (45 , VM::gpu_capabilities)),
0 commit comments