Skip to content

Commit 62339fe

Browse files
author
Requiem
committed
feat: added BIOS POST timing checks
1 parent 1a98702 commit 62339fe

File tree

2 files changed

+91
-31
lines changed

2 files changed

+91
-31
lines changed

src/cli.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -828,6 +828,8 @@ static void general() {
828828
checker(VM::EDID, "EDID");
829829
checker(VM::CPU_HEURISTIC, "CPU heuristics");
830830
checker(VM::CLOCK, "system timers");
831+
checker(VM::POST, "BIOS POST time");
832+
831833
// ADD NEW TECHNIQUE CHECKER HERE
832834

833835
const auto t2 = std::chrono::high_resolution_clock::now();

src/vmaware.hpp

Lines changed: 89 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)