@@ -571,6 +571,7 @@ struct VM {
571571 EDID,
572572 CPU_HEURISTIC,
573573 CLOCK,
574+ POST,
574575
575576 // Linux and Windows
576577 SIDT,
@@ -9163,7 +9164,6 @@ struct VM {
91639164 using PVARIABLE_NAME = VARIABLE_NAME*;
91649165 using NtEnumerateSystemEnvironmentValuesEx_t = NTSTATUS (__stdcall*)(ULONG, PVOID, PULONG);
91659166
9166- bool pk_checked = false ;
91679167 // Secure Boot stuff
91689168 bool found_dbDefault = false , found_dbxDefault = false , found_KEKDefault = false , found_PKDefault = false ;
91699169 /*
@@ -9190,7 +9190,7 @@ struct VM {
91909190 AdjustTokenPrivileges (hToken, FALSE , &tpDisable, sizeof (TOKEN_PRIVILEGES), nullptr , nullptr );
91919191 }
91929192 if (hToken) { CloseHandle (hToken); hToken = nullptr ; }
9193- };
9193+ };
91949194
91959195 if (!LookupPrivilegeValue (nullptr , SE_SYSTEM_ENVIRONMENT_NAME, &luid)) { cleanup (); return false ; }
91969196 TOKEN_PRIVILEGES tpEnable{}; tpEnable.PrivilegeCount = 1 ; tpEnable.Privileges [0 ].Luid = luid; tpEnable.Privileges [0 ].Attributes = SE_PRIVILEGE_ENABLED;
@@ -9218,12 +9218,12 @@ struct VM {
92189218 // ask for size
92199219 if (pNtEnum) {
92209220 hasFunction = true ;
9221- pNtEnum (1 , nullptr , &bufferLength);
9221+ pNtEnum (static_cast <ULONG>( 1 ) , nullptr , &bufferLength);
92229222 if (bufferLength != 0 ) {
92239223 enumSize = static_cast <SIZE_T>(bufferLength);
9224- NTSTATUS st = pNtAllocateVirtualMemory (hCurrentProcess, &enumBase, 0 , &enumSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
9224+ NTSTATUS st = pNtAllocateVirtualMemory (hCurrentProcess, &enumBase, 0 , &enumSize, static_cast <ULONG>( MEM_COMMIT | MEM_RESERVE), static_cast <ULONG>( PAGE_READWRITE) );
92259225 if (st == 0 && enumBase) {
9226- st = pNtEnum (1 , enumBase, &bufferLength);
9226+ st = pNtEnum (static_cast <ULONG>( 1 ) , enumBase, &bufferLength);
92279227 if (st == 0 ) { success = true ; }
92289228 else { SIZE_T zero = 0 ; pNtFreeVirtualMemory (hCurrentProcess, &enumBase, &zero, 0x8000 ); enumBase = nullptr ; enumSize = 0 ; }
92299229 }
@@ -9237,47 +9237,49 @@ struct VM {
92379237 auto ci_ascii_contains = [](const BYTE* data, size_t len, const char * pat) noexcept -> bool {
92389238 if (!data || len == 0 || !pat) return false ;
92399239 const size_t plen = strlen (pat); if (len < plen) return false ;
9240- const BYTE p0 = ( BYTE) ((pat[0 ] >= ' A' && pat[0 ] <= ' Z' ) ? (pat[0 ] + 32 ) : pat[0 ]);
9240+ const BYTE p0 = static_cast < BYTE> ((pat[0 ] >= ' A' && pat[0 ] <= ' Z' ) ? (pat[0 ] + 32 ) : pat[0 ]);
92419241 const BYTE* end = data + (len - plen);
92429242 for (const BYTE* p = data; p <= end; ++p) {
9243- BYTE c0 = *p; c0 = ( c0 >= ' A' && c0 <= ' Z' ) ? (c0 + 32 ) : c0;
9243+ BYTE c0 = *p; c0 = static_cast <BYTE>(( c0 >= ' A' && c0 <= ' Z' ) ? (c0 + 32 ) : c0) ;
92449244 if (c0 != p0) continue ;
92459245 bool ok = true ;
92469246 for (size_t j = 1 ; j < plen; ++j) {
9247- BYTE dj = p[j]; dj = ( dj >= ' A' && dj <= ' Z' ) ? (dj + 32 ) : dj;
9248- BYTE pj = ( BYTE) ((pat[j] >= ' A' && pat[j] <= ' Z' ) ? (pat[j] + 32 ) : pat[j]);
9247+ BYTE dj = p[j]; dj = static_cast <BYTE>(( dj >= ' A' && dj <= ' Z' ) ? (dj + 32 ) : dj) ;
9248+ BYTE pj = static_cast < BYTE> ((pat[j] >= ' A' && pat[j] <= ' Z' ) ? (pat[j] + 32 ) : pat[j]);
92499249 if (dj != pj) { ok = false ; break ; }
92509250 }
92519251 if (ok) return true ;
92529252 }
92539253 return false ;
9254- };
9254+ };
92559255 auto ci_utf16le_contains = [](const WCHAR* data, size_t wlen, const wchar_t * pat) noexcept -> bool {
92569256 if (!data || wlen == 0 || !pat) return false ;
92579257 const size_t plen = wcslen (pat); if (wlen < plen) return false ;
9258- const WCHAR p0 = ( pat[0 ] >= L' A' && pat[0 ] <= L' Z' ) ? (pat[0 ] + 32 ) : pat[0 ];
9258+ const WCHAR p0 = static_cast <WCHAR>(( pat[0 ] >= L' A' && pat[0 ] <= L' Z' ) ? (pat[0 ] + 32 ) : pat[0 ]) ;
92599259 const WCHAR* end = data + (wlen - plen);
92609260 for (const WCHAR* p = data; p <= end; ++p) {
9261- WCHAR c0 = *p; c0 = ( c0 >= L' A' && c0 <= L' Z' ) ? (c0 + 32 ) : c0;
9261+ WCHAR c0 = *p; c0 = static_cast <WCHAR>(( c0 >= L' A' && c0 <= L' Z' ) ? (c0 + 32 ) : c0) ;
92629262 if (c0 != p0) continue ;
92639263 bool ok = true ;
92649264 for (size_t j = 1 ; j < plen; ++j) {
9265- WCHAR dj = p[j]; dj = ( dj >= L' A' && dj <= L' Z' ) ? (dj + 32 ) : dj;
9266- WCHAR pj = ( pat[j] >= L' A' && pat[j] <= L' Z' ) ? (pat[j] + 32 ) : pat[j];
9265+ WCHAR dj = p[j]; dj = static_cast <WCHAR>(( dj >= L' A' && dj <= L' Z' ) ? (dj + 32 ) : dj) ;
9266+ WCHAR pj = static_cast <WCHAR>(( pat[j] >= L' A' && pat[j] <= L' Z' ) ? (pat[j] + 32 ) : pat[j]) ;
92679267 if (dj != pj) { ok = false ; break ; }
92689268 }
92699269 if (ok) return true ;
92709270 }
92719271 return false ;
9272- };
9272+ };
92739273
92749274 constexpr const char * vendor_ascii[] = { " msi" ," asrock" ," asus" ," asustek" ," gigabyte" ," giga-byte" ," micro-star" ," microstar" };
92759275 constexpr const wchar_t * vendor_wide[] = { L" msi" ,L" asrock" ,L" asus" ,L" asustek" ,L" gigabyte" ,L" giga-byte" ,L" micro-star" ,L" microstar" };
92769276 constexpr const char redhat_ascii[] = " red hat" ;
92779277 constexpr const wchar_t redhat_wide[] = L" red hat" ;
92789278
92799279 constexpr size_t MAX_VAR_SZ = 131072 ; // 128KB
9280- alignas (8 ) BYTE stackBuf[MAX_VAR_SZ]{};
9280+ // Use unique_ptr to move buffer from stack to heap (fixes 132KB stack usage)
9281+ std::unique_ptr<BYTE[]> stackBufPtr (new BYTE[MAX_VAR_SZ]());
9282+ BYTE* stackBuf = stackBufPtr.get ();
92819283
92829284 BYTE* pkDefaultBuf = nullptr , * pkBuf = nullptr , * kekDefaultBuf = nullptr , * kekBuf = nullptr ;
92839285 SIZE_T pkDefaultLen = 0 , pkLen = 0 , kekDefaultLen = 0 , kekLen = 0 ;
@@ -9286,17 +9288,17 @@ struct VM {
92869288 wchar_t guidStr[40 ] = {};
92879289 constexpr wchar_t hex[] = L" 0123456789ABCDEF" ;
92889290 guidStr[0 ] = L' {' ;
9289- guidStr[1 ] = hex[( guid.Data1 >> 28 ) & 0xF ]; guidStr[2 ] = hex[( guid.Data1 >> 24 ) & 0xF ]; guidStr[3 ] = hex[( guid.Data1 >> 20 ) & 0xF ]; guidStr[4 ] = hex[( guid.Data1 >> 16 ) & 0xF ];
9290- guidStr[5 ] = hex[( guid.Data1 >> 12 ) & 0xF ]; guidStr[6 ] = hex[( guid.Data1 >> 8 ) & 0xF ]; guidStr[7 ] = hex[( guid.Data1 >> 4 ) & 0xF ]; guidStr[8 ] = hex[guid.Data1 & 0xF ];
9291+ guidStr[1 ] = hex[static_cast < size_t >(( guid.Data1 >> 28 ) & 0xF ) ]; guidStr[2 ] = hex[static_cast < size_t >(( guid.Data1 >> 24 ) & 0xF ) ]; guidStr[3 ] = hex[static_cast < size_t >(( guid.Data1 >> 20 ) & 0xF ) ]; guidStr[4 ] = hex[static_cast < size_t >(( guid.Data1 >> 16 ) & 0xF ) ];
9292+ guidStr[5 ] = hex[static_cast < size_t >(( guid.Data1 >> 12 ) & 0xF ) ]; guidStr[6 ] = hex[static_cast < size_t >(( guid.Data1 >> 8 ) & 0xF ) ]; guidStr[7 ] = hex[static_cast < size_t >(( guid.Data1 >> 4 ) & 0xF ) ]; guidStr[8 ] = hex[static_cast < size_t >( guid.Data1 & 0xF ) ];
92919293 guidStr[9 ] = L' -' ;
9292- guidStr[10 ] = hex[( guid.Data2 >> 12 ) & 0xF ]; guidStr[11 ] = hex[( guid.Data2 >> 8 ) & 0xF ]; guidStr[12 ] = hex[( guid.Data2 >> 4 ) & 0xF ]; guidStr[13 ] = hex[guid.Data2 & 0xF ];
9294+ guidStr[10 ] = hex[static_cast < size_t >(( guid.Data2 >> 12 ) & 0xF ) ]; guidStr[11 ] = hex[static_cast < size_t >(( guid.Data2 >> 8 ) & 0xF ) ]; guidStr[12 ] = hex[static_cast < size_t >(( guid.Data2 >> 4 ) & 0xF ) ]; guidStr[13 ] = hex[static_cast < size_t >( guid.Data2 & 0xF ) ];
92939295 guidStr[14 ] = L' -' ;
9294- guidStr[15 ] = hex[( guid.Data3 >> 12 ) & 0xF ]; guidStr[16 ] = hex[( guid.Data3 >> 8 ) & 0xF ]; guidStr[17 ] = hex[( guid.Data3 >> 4 ) & 0xF ]; guidStr[18 ] = hex[guid.Data3 & 0xF ];
9296+ guidStr[15 ] = hex[static_cast < size_t >(( guid.Data3 >> 12 ) & 0xF ) ]; guidStr[16 ] = hex[static_cast < size_t >(( guid.Data3 >> 8 ) & 0xF ) ]; guidStr[17 ] = hex[static_cast < size_t >(( guid.Data3 >> 4 ) & 0xF ) ]; guidStr[18 ] = hex[static_cast < size_t >( guid.Data3 & 0xF ) ];
92959297 guidStr[19 ] = L' -' ;
9296- guidStr[20 ] = hex[( guid.Data4 [0 ] >> 4 ) & 0xF ]; guidStr[21 ] = hex[guid.Data4 [0 ] & 0xF ]; guidStr[22 ] = hex[( guid.Data4 [1 ] >> 4 ) & 0xF ]; guidStr[23 ] = hex[guid.Data4 [1 ] & 0xF ];
9298+ guidStr[20 ] = hex[static_cast < size_t >(( guid.Data4 [0 ] >> 4 ) & 0xF ) ]; guidStr[21 ] = hex[static_cast < size_t >( guid.Data4 [0 ] & 0xF ) ]; guidStr[22 ] = hex[static_cast < size_t >(( guid.Data4 [1 ] >> 4 ) & 0xF ) ]; guidStr[23 ] = hex[static_cast < size_t >( guid.Data4 [1 ] & 0xF ) ];
92979299 guidStr[24 ] = L' -' ;
92989300 size_t idx = 25 ;
9299- for (int i = 2 ; i < 8 ; ++i) { guidStr[idx++] = hex[( guid.Data4 [i] >> 4 ) & 0xF ]; guidStr[idx++] = hex[guid.Data4 [i] & 0xF ]; }
9301+ for (int i = 2 ; i < 8 ; ++i) { guidStr[idx++] = hex[static_cast < size_t >(( guid.Data4 [i] >> 4 ) & 0xF ) ]; guidStr[idx++] = hex[static_cast < size_t >( guid.Data4 [i] & 0xF ) ]; }
93009302 guidStr[37 ] = L' }' ; guidStr[38 ] = L' \0 ' ;
93019303
93029304 DWORD ret = GetFirmwareEnvironmentVariableW (name.c_str (), guidStr, stackBuf, static_cast <DWORD>(MAX_VAR_SZ));
@@ -9313,7 +9315,7 @@ struct VM {
93139315 // free and fail
93149316 SIZE_T zero = 0 ; pNtFreeVirtualMemory (hCurrentProcess, &base, &zero, 0x8000 );
93159317 outBuf = nullptr ; outLen = 0 ; return false ;
9316- };
9318+ };
93179319
93189320 PVARIABLE_NAME varName = reinterpret_cast <PVARIABLE_NAME>(enumBase);
93199321 const size_t bufSize = static_cast <size_t >(bufferLength);
@@ -9513,9 +9515,9 @@ struct VM {
95139515 const u16 word = static_cast <u16 >((edid[8 ] << 8 ) | edid[9 ]);
95149516
95159517 // 5 bits per character. 0x01='A', 0x1A='Z'
9516- const u8 c1 = ( word >> 10 ) & 0x1F ;
9517- const u8 c2 = ( word >> 5 ) & 0x1F ;
9518- const u8 c3 = word & 0x1F ;
9518+ const u8 c1 = static_cast < u8 >(( word >> 10 ) & 0x1F ) ;
9519+ const u8 c2 = static_cast < u8 >(( word >> 5 ) & 0x1F ) ;
9520+ const u8 c3 = static_cast < u8 >( word & 0x1F ) ;
95199521
95209522 // '?' is fallback for valid EDID range 1-26
95219523 out[0 ] = (c1 >= 1 && c1 <= 26 ) ? static_cast <char >(' A' + c1 - 1 ) : ' ?' ;
@@ -10049,8 +10051,8 @@ struct VM {
1004910051 while (got < 4 && *q) {
1005010052 const wchar_t c = *q;
1005110053 u32 nib = 0 ;
10052- if (c >= L' 0' && c <= L' 9' ) nib = c - L' 0' ;
10053- else if ((c | 0x20 ) >= L' a' && (c | 0x20 ) <= L' f' ) nib = ( c | 0x20 ) - L' a' + 10 ;
10054+ if (c >= L' 0' && c <= L' 9' ) nib = static_cast < u32 >( c - L' 0' ) ;
10055+ else if ((c | 0x20 ) >= L' a' && (c | 0x20 ) <= L' f' ) nib = static_cast < u32 >(( c | 0x20 ) - L' a' + 10 ) ;
1005410056 else break ;
1005510057
1005610058 val = (val << 4 ) | nib;
@@ -10111,9 +10113,8 @@ struct VM {
1011110113
1011210114 if (wHwId) {
1011310115 const u32 vid = find_vendor_hex (wHwId);
10114- // VID_INTEL = 0x8086, VID_AMD_ATI = 0x1002, VID_AMD_MICRO = 0x1022
10115- if (vid == 0x8086 ) intel_hits++;
10116- else if (vid == 0x1002 || vid == 0x1022 ) amd_hits++;
10116+ if (vid == VID_INTEL) intel_hits++;
10117+ else if (vid == VID_AMD_ATI || vid == VID_AMD_MICRO) amd_hits++;
1011710118 }
1011810119 }
1011910120 }
@@ -10260,6 +10261,61 @@ struct VM {
1026010261 SetupDiDestroyDeviceInfoList (devs);
1026110262 return !found;
1026210263 }
10264+
10265+
10266+ /* *
10267+ * @brief Check for anomalies in BIOS POST time
10268+ * @category Windows
10269+ * @implements VM::POST
10270+ */
10271+ [[nodiscard]] static bool post () {
10272+ /*
10273+ * The motherboard must test and calibrate memory timings, which is time-consuming
10274+ * The system physically scans PCIe buses, initializes the GPU, powers up USB controllers, and waits for storage drives to report ready
10275+ * Fans must spin up, and capacitors must charge
10276+ *
10277+ * On VMs, RAM is simply a block of memory allocated by the host OS. There is no training or calibration required
10278+ * There are no drives to spin up, no fans to check, and no complex PCIe negotiation
10279+ * So at the end, we see cases like VirtualBox machines reporting 0.9s of last bios time, or QEMU machines with OVMF reporting 0s
10280+ */
10281+ static constexpr wchar_t kSubKey [] = L" SYSTEM\\ CurrentControlSet\\ Control\\ Session Manager\\ Power" ;
10282+ static constexpr wchar_t kValueName [] = L" FwPOSTTime" ;
10283+ HKEY hKey;
10284+
10285+ long result = RegOpenKeyExW (
10286+ HKEY_LOCAL_MACHINE,
10287+ kSubKey ,
10288+ 0 ,
10289+ KEY_QUERY_VALUE,
10290+ &hKey
10291+ );
10292+
10293+ if (result != ERROR_SUCCESS) {
10294+ return false ;
10295+ }
10296+
10297+ DWORD data = 0 ;
10298+ DWORD dataSize = sizeof (data);
10299+
10300+ result = RegQueryValueExW (
10301+ hKey,
10302+ kValueName ,
10303+ NULL ,
10304+ NULL ,
10305+ reinterpret_cast <LPBYTE>(&data),
10306+ &dataSize
10307+ );
10308+
10309+ RegCloseKey (hKey);
10310+
10311+ if (result == ERROR_SUCCESS) {
10312+ if (data < 1500 ) { // 1.5s
10313+ return true ;
10314+ }
10315+ }
10316+
10317+ return false ;
10318+ }
1026310319 // ADD NEW TECHNIQUE FUNCTION HERE
1026410320#endif
1026510321
@@ -11355,6 +11411,7 @@ struct VM {
1135511411 case EDID: return " EDID" ;
1135611412 case CPU_HEURISTIC: return " CPU_HEURISTIC" ;
1135711413 case CLOCK: return " CLOCK" ;
11414+ case POST: return " POST" ;
1135811415 // END OF TECHNIQUE LIST
1135911416 case DEFAULT: return " setting flag, error" ;
1136011417 case ALL: return " setting flag, error" ;
@@ -11910,6 +11967,7 @@ std::pair<VM::enum_flags, VM::core::technique> VM::core::technique_list[] = {
1191011967 std::make_pair (VM::CLOCK, VM::core::technique (100 , VM::clock)),
1191111968 std::make_pair (VM::POWER_CAPABILITIES, VM::core::technique (45 , VM::power_capabilities)),
1191211969 std::make_pair (VM::CPU_HEURISTIC, VM::core::technique (90 , VM::cpu_heuristic)),
11970+ std::make_pair (VM::POST, VM::core::technique (100 , VM::post )),
1191311971 std::make_pair (VM::EDID, VM::core::technique (100 , VM::edid)),
1191411972 std::make_pair (VM::BOOT_LOGO, VM::core::technique (100 , VM::boot_logo)),
1191511973 std::make_pair (VM::GPU_CAPABILITIES, VM::core::technique (45 , VM::gpu_capabilities)),
0 commit comments