Skip to content

Commit 2e3ef76

Browse files
authored
Merge pull request #616 from NotRequiem/main
Definitive removal of HPET detections
2 parents fd90133 + 28855d7 commit 2e3ef76

1 file changed

Lines changed: 11 additions & 81 deletions

File tree

src/vmaware.hpp

Lines changed: 11 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -4585,7 +4585,7 @@ struct VM {
45854585
// will be used in cpuid measurements later
45864586
u16 cycle_threshold = 1000;
45874587
if (util::hyper_x() == HYPERV_ARTIFACT_VM) {
4588-
cycle_threshold = 7500; // if we're running under Hyper-V, make VMAware detect nested virtualization
4588+
cycle_threshold = 3500; // if we're running under Hyper-V, make VMAware detect nested virtualization
45894589
}
45904590

45914591
#if (WINDOWS)
@@ -4767,6 +4767,11 @@ struct VM {
47674767
}
47684768

47694769
// RDTSC trap detection
4770+
// This detection uses two clocks and two loops, a loop that the hypervisor can spoof and a loop that the hypervisor cannot
4771+
// When RDTSC is hooked, the hypervisor usually "downscales" the result to hide the time passed or doesnt let TSC advance for the time it was vm-exiting
4772+
// However, the hypervisor have absolutely no way to downscale time for the second loop because it runs natively on the CPU without exiting
4773+
// This creates a discrepancy in the ratio of both loops
4774+
// The hypervisor cannot easily rewind the system wall clock (second loop, QIT/KUSER_SHARED_DATA) without causing system instability (network timeouts, audio lag)
47704775
static thread_local volatile u64 g_sink = 0; // thread_local volatile so that it doesnt need to be captured by the lambda
47714776

47724777
// First we start by randomizing counts WITHOUT syscalls and WITHOUT using instructions that can be trapped by hypervisors, this was a hard task
@@ -4867,16 +4872,18 @@ struct VM {
48674872

48684873
// first measurement
48694874
ULONG64 beforeqit = 0;
4870-
QueryInterruptTime(&beforeqit); // the kernel routine that backs up this api runs at CLOCK_LEVEL(13), only preempted by IPI, POWER_LEVEL and NMIs
4875+
QueryInterruptTime(&beforeqit); // never touches RDTSC/RDTSCP or transitions to kernel-mode, just reads from KUSER_SHARED_DATA, reason why we use it
48714876
const ULONG64 beforetsc = __rdtsc();
48724877

48734878
volatile u64 dummy = 0;
48744879
for (ULONG64 x = 0; x < count_first; ++x) {
48754880
dummy = rd_ptr(); // this loop will be intercepted by a RDTSC trap, downscaling our TSC
48764881
}
48774882

4883+
// the kernel routine that backs up this api runs at CLOCK_LEVEL(13), only preempted by IPI, POWER_LEVEL and NMIs
4884+
// meaning it's highly accurate even with kernel noise, hence we don't need cluster or median computations to get precise ratios
48784885
ULONG64 afterqit = 0;
4879-
QueryInterruptTime(&afterqit);
4886+
QueryInterruptTime(&afterqit);
48804887
const ULONG64 aftertsc = __rdtsc();
48814888

48824889
const ULONG64 dtsc1 = aftertsc - beforetsc;
@@ -6619,77 +6626,6 @@ struct VM {
66196626
if (EnumSystemFirmwareTables(acpi_signature, tables.data(), acpi_enum_size) != acpi_enum_size)
66206627
return false;
66216628

6622-
// High Precision Event Timer detection
6623-
bool found_hpet = false;
6624-
for (const auto table_id : tables) {
6625-
constexpr DWORD hpet_signature = 'TEPH';
6626-
if (table_id == hpet_signature) {
6627-
found_hpet = true;
6628-
}
6629-
}
6630-
6631-
if (!found_hpet) {
6632-
const char* manufacturer = "";
6633-
const char* model = "";
6634-
util::get_manufacturer_model(&manufacturer, &model);
6635-
6636-
struct whitelist_entry {
6637-
const char* man_substr;
6638-
const char* model_substr;
6639-
};
6640-
6641-
// The OMEN by HP 16-n0xxx family appears to expose an ACPI HPET table, but Linux kernels often report it as "dysfunctional" and disable it
6642-
// https://linux-hardware.org/?log=dmesg&probe=5ecdd1b28c
6643-
constexpr whitelist_entry whitelist[] = {
6644-
{ "hp", "omen" },
6645-
{ "micro-star", "bravo" },
6646-
{ "asustek", "fa" }, // fa506, fa507, fa707, etc...
6647-
{ "asustek", "Vivobook_ASUSLaptop" },
6648-
{ "asustek", "ROG Strix"} // G513RM, etc...
6649-
};
6650-
6651-
bool is_whitelisted = false;
6652-
6653-
auto contains_case_insensitive = [](const char* haystack_c, const char* needle_c) -> bool {
6654-
const unsigned char* h_ptr = reinterpret_cast<const unsigned char*>(haystack_c);
6655-
for (; *h_ptr; ++h_ptr) {
6656-
const unsigned char* h = h_ptr;
6657-
const unsigned char* n = reinterpret_cast<const unsigned char*>(needle_c);
6658-
while (*n && ((*h | 0x20) == (*n | 0x20))) {
6659-
++h; ++n;
6660-
}
6661-
if (!*n) return true;
6662-
}
6663-
return false;
6664-
};
6665-
6666-
for (const auto& entry : whitelist) {
6667-
bool man_match = false;
6668-
bool model_match = false;
6669-
6670-
if (manufacturer) {
6671-
if (contains_case_insensitive(manufacturer, entry.man_substr)) {
6672-
man_match = true;
6673-
}
6674-
}
6675-
6676-
if (man_match && model) {
6677-
if (contains_case_insensitive(model, entry.model_substr)) {
6678-
model_match = true;
6679-
}
6680-
}
6681-
6682-
if (man_match && model_match) {
6683-
is_whitelisted = true;
6684-
break;
6685-
}
6686-
}
6687-
6688-
if (util::is_running_under_translator() || is_whitelisted) {
6689-
found_hpet = true;
6690-
}
6691-
}
6692-
66936629
// DSDT special fetch
66946630
{
66956631
constexpr DWORD dsdt_sig = 'DSDT';
@@ -6755,12 +6691,6 @@ struct VM {
67556691
}
67566692
}
67576693

6758-
// Checks for non existent tables must run at the end because of is_hardened() logic
6759-
if (!found_hpet) {
6760-
debug("FIRMWARE: HPET table not found");
6761-
return true;
6762-
}
6763-
67646694
return false;
67656695
#elif (LINUX)
67666696
// Author: dmfrpro
@@ -11224,7 +11154,7 @@ struct VM {
1122411154
}
1122511155

1122611156
// The RTC (ACPI/CMOS RTC) timer can't be always detected via SetupAPI, it needs AML decode of the DSDT firmware table
11227-
// The HPET (PNP0103) timer presence is already checked on VM::FIRMWARE
11157+
// The HPET (PNP0103) timer presence check was removed, more info at: https://github.com/kernelwernel/VMAware/pull/616
1122811158
// Here, we check for the PIT/AT timer (PC-class System Timer)
1122911159
constexpr wchar_t pattern[] = L"pnp0100";
1123011160
constexpr size_t patLen = (sizeof(pattern) / sizeof(wchar_t)) - 1;

0 commit comments

Comments
 (0)