Skip to content

Commit 3f6bad0

Browse files
committed
update: renamed HYPERV_ARTIFACT brand to HYPERV_ROOT and fixed is_hardening()
1 parent 0b33602 commit 3f6bad0

File tree

3 files changed

+65
-46
lines changed

3 files changed

+65
-46
lines changed

docs/documentation.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -649,7 +649,7 @@ This is the table of all the brands the lib supports.
649649
| Intel KGT (Trusty) | `brands::INTEL_KGT` | Hypervisor (type 1) | |
650650
| Microsoft Azure Hyper-V | `brands::AZURE_HYPERV` | Hypervisor (type 1) | |
651651
| SimpleVisor | `brands::SIMPLEVISOR` | Hypervisor (type 1) | |
652-
| Hyper-V artifact (not an actual VM) | `brands::HYPERV_ARTIFACT` | Unknown | Windows Hyper-V has a tendency to modify host hardware values with VM values. In other words, this brand signifies that you're running on a host system, but the Hyper-V that's installed (either by default or manually by the user) is misleadingly making the whole system look like it's in a VM when in reality it's not. <br><br> For more information, refer to [this graph](https://github.com/kernelwernel/VMAware/blob/main/assets/hyper-x/v5/Hyper-X_version_5.drawio.png). |
652+
| Hyper-V root partition (host system, not an actual VM) | `brands::HYPERV_ARTIFACT` | Host machine | Windows Hyper-V has a tendency to modify host hardware values with VM values. In other words, this brand signifies that you're running on a host system, but the Hyper-V that's installed (either by default or manually by the user) is misleadingly making the whole system look like it's in a VM when in reality it's not. <br><br> For more information, refer to [this graph](https://github.com/kernelwernel/VMAware/blob/main/assets/hyper-x/v5/Hyper-X_version_5.drawio.png). |
653653
| User-mode Linux | `brands::UML` | Paravirtualised/Hypervisor (type 2) | |
654654
| IBM PowerVM | `brands::POWERVM` | Hypervisor (type 1) | |
655655
| OpenStack (KVM) | `brands::OPENSTACK` | Hypervisor (type 1) | |

src/cli.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -627,7 +627,7 @@ static const char* get_vm_description(const std::string& vm_brand) {
627627
{ VM::brands::INTEL_KGT, "Intel Kernel Guard Technology (KGT) is a policy specification and enforcement framework for ensuring runtime integrity of kernel and platform assets. Demonstrated secure enclaves for critical OS components using VT-x/EPT before being superseded by CET (Control-flow Enforcement Technology) and HyperGuard in Windows 10." },
628628
{ VM::brands::AZURE_HYPERV, "Azure Hyper-V is Microsoft's cloud-optimized hypervisor variant powering Azure VMs. Implements Azure-specific virtual devices like NVMe Accelerated Networking and vTPMs. Supports nested virtualization for running Hyper-V/containers within Azure VMs, enabling cloud-based CI/CD pipelines and dev/test environments." },
629629
{ VM::brands::SIMPLEVISOR, "SimpleVisor is a minimalist Intel VT-x hypervisor by Alex Ionescu for Windows/Linux research. Demonstrates EPT-based memory isolation and hypercall handling. Used to study VM escapes and hypervisor rootkits, with hooks for intercepting CR3 changes and MSR accesses." },
630-
{ VM::brands::HYPERV_ARTIFACT, "VMAware detected Hyper-V operating as a type 1 hypervisor, not as a guest virtual machine. Although your hardware/firmware signatures match Microsoft's Hyper-V architecture, we determined that you're running on baremetal. This prevents false positives, as Windows sometimes runs under Hyper-V (type 1) hypervisor." },
630+
{ VM::brands::HYPERV_ROOT, "VMAware detected Hyper-V operating as a type 1 hypervisor, not as a guest virtual machine. Although your hardware/firmware signatures match Microsoft's Hyper-V architecture, we determined that you're running on baremetal. This prevents false positives, as Windows sometimes runs under Hyper-V (type 1) hypervisor." },
631631
{ VM::brands::UML, "User-Mode Linux (UML) allows running Linux kernels as user-space processes using ptrace-based virtualization. Primarily used for kernel debugging and network namespace testing. Offers lightweight isolation without hardware acceleration, but requires host/guest kernel version matching for stable operation." },
632632
{ VM::brands::POWERVM, "IBM PowerVM is a type 1 hypervisor for POWER9/10 systems, supporting Live Partition Mobility and Shared Processor Pools. Implements VIOS (Virtual I/O Server) for storage/networking virtualization, enabling concurrent AIX, IBM i, and Linux workloads with RAS features like predictive failure analysis." },
633633
{ VM::brands::GCE, "Google Compute Engine (GCE) utilizes KVM-based virtualization with custom Titanium security chips for hardware root of trust. Features live migration during host maintenance and shielded VMs with UEFI secure boot. Underpins Google Cloud's Confidential Computing offering using AMD SEV-SNP memory encryption." },
@@ -904,7 +904,7 @@ static void general(
904904
}
905905
#elif (CLI_WINDOWS)
906906
if (!is_admin()) {
907-
std::cout << note << " Not running as admin - NVRAM detections will be disabled.\n";
907+
std::cout << note << " Not running as admin, some technique may not run\n";
908908
}
909909
#endif
910910

@@ -1021,7 +1021,7 @@ static void general(
10211021

10221022
const bool is_red = (
10231023
(brand == VM::brands::NULL_BRAND) ||
1024-
(brand == VM::brands::HYPERV_ARTIFACT)
1024+
(brand == VM::brands::HYPERV_ROOT)
10251025
);
10261026

10271027
std::cout << bold << "VM brand: " << ansi_exit << (is_red ? red : green) << brand << ansi_exit << "\n";

src/vmaware.hpp

Lines changed: 61 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -498,7 +498,8 @@ namespace brands { // TODO, remove this in the 2.8.0 or any release after the 2.
498498
LEGACY(INTEL_KGT, "Intel KGT (Trusty)");
499499
LEGACY(AZURE_HYPERV, "Microsoft Azure Hyper-V");
500500
LEGACY(SIMPLEVISOR, "SimpleVisor");
501-
LEGACY(HYPERV_ARTIFACT, "Hyper-V artifact (host running Hyper-V)");
501+
// not in macro due to mismatch with VM::brands and brands:: renaming this to HYPERV_ROOT
502+
[[deprecated("Use VM::brands::HYPERV_ROOT instead")]] static constexpr const char* HYPERV_ARTIFACT = "Hyper-V root partition (host system, not an actual VM)";
502503
LEGACY(UML, "User-mode Linux");
503504
LEGACY(POWERVM, "IBM PowerVM");
504505
LEGACY(GCE, "Google Compute Engine (KVM)");
@@ -707,7 +708,7 @@ struct VM {
707708
INTEL_KGT,
708709
AZURE_HYPERV,
709710
SIMPLEVISOR,
710-
HYPERV_ARTIFACT,
711+
HYPERV_ROOT,
711712
UML,
712713
POWERVM,
713714
GCE,
@@ -762,7 +763,7 @@ struct VM {
762763
static constexpr u8 LINUX_END = VM::THREAD_COUNT;
763764
static constexpr u8 MACOS_START = VM::THREAD_COUNT;
764765
static constexpr u8 MACOS_END = VM::MAC_SYS;
765-
766+
766767
// this is specifically meant for VM::detected_count() to
767768
// get the total number of techniques that detected a VM
768769
static u8 detected_count_num;
@@ -3834,7 +3835,7 @@ struct VM {
38343835
else {
38353836
// Windows machine running under Hyper-V type 1
38363837
debug("HYPER_X: Detected Hyper-V host machine");
3837-
core::add(brand_enum::HYPERV_ARTIFACT);
3838+
core::add(brand_enum::HYPERV_ROOT);
38383839
state = HYPERV_ARTIFACT_VM;
38393840
}
38403841
}
@@ -4436,7 +4437,7 @@ struct VM {
44364437
static constexpr const char* INTEL_KGT = "Intel KGT (Trusty)";
44374438
static constexpr const char* AZURE_HYPERV = "Microsoft Azure Hyper-V";
44384439
static constexpr const char* SIMPLEVISOR = "SimpleVisor";
4439-
static constexpr const char* HYPERV_ARTIFACT = "Hyper-V artifact (host running Hyper-V)";
4440+
static constexpr const char* HYPERV_ROOT = "Hyper-V root partition (host system, not an actual VM)";
44404441
static constexpr const char* UML = "User-mode Linux";
44414442
static constexpr const char* POWERVM = "IBM PowerVM";
44424443
static constexpr const char* GCE = "Google Compute Engine (KVM)";
@@ -4520,7 +4521,7 @@ struct VM {
45204521
continue;
45214522
}
45224523

4523-
if (brand.first == brand_enum::HYPERV_ARTIFACT && score > 0) {
4524+
if (brand.first == brand_enum::HYPERV_ROOT && score > 0) {
45244525
brand_return.push_back({brand_enum::NULL_BRAND, 1});
45254526
memo::brand_list::store(brand_return);
45264527
return brand_return;
@@ -4537,7 +4538,7 @@ struct VM {
45374538

45384539
// remove Hyper-V artifacts and Unknown if found with other brands
45394540
if (active_count > 1) {
4540-
remove(brand_enum::HYPERV_ARTIFACT);
4541+
remove(brand_enum::HYPERV_ROOT);
45414542
remove(brand_enum::NULL_BRAND);
45424543
remove(brand_enum::UNKNOWN);
45434544
}
@@ -4640,7 +4641,7 @@ struct VM {
46404641
return brand_return;
46414642
}
46424643

4643-
static std::string brand_enum_to_string(const brand_enum brand) {
4644+
static const char* brand_enum_to_string(const brand_enum brand) {
46444645
switch (brand) {
46454646
case brand_enum::UNKNOWN: return "Invalid";
46464647
case brand_enum::VBOX: return VM::brands::VBOX;
@@ -4686,7 +4687,7 @@ struct VM {
46864687
case brand_enum::INTEL_KGT: return VM::brands::INTEL_KGT;
46874688
case brand_enum::AZURE_HYPERV: return VM::brands::AZURE_HYPERV;
46884689
case brand_enum::SIMPLEVISOR: return VM::brands::SIMPLEVISOR;
4689-
case brand_enum::HYPERV_ARTIFACT: return VM::brands::HYPERV_ARTIFACT;
4690+
case brand_enum::HYPERV_ROOT: return VM::brands::HYPERV_ROOT;
46904691
case brand_enum::UML: return VM::brands::UML;
46914692
case brand_enum::POWERVM: return VM::brands::POWERVM;
46924693
case brand_enum::GCE: return VM::brands::GCE;
@@ -5078,7 +5079,7 @@ struct VM {
50785079

50795080
/**
50805081
* @brief Check for timing anomalies in the system
5081-
* @category x86x86
5082+
* @category x86
50825083
* @implements VM::TIMER
50835084
*/
50845085
[[nodiscard]] static bool timer() {
@@ -12555,7 +12556,7 @@ struct VM {
1255512556
case brand_enum::BAREVISOR: return "Hypervisor (type 1)";
1255612557
case brand_enum::HYPERPLATFORM: return "Hypervisor (type 1)";
1255712558
case brand_enum::MINIVISOR: return "Hypervisor (type 1)";
12558-
case brand_enum::HYPERV_ARTIFACT: return "Unknown"; // This refers to the type 1 hypervisor where Windows normally runs under, we put "Unknown" to clarify you're not running under a VM if this is detected
12559+
case brand_enum::HYPERV_ROOT: return "Host machine"; // This refers to the type 1 hypervisor where Windows normally runs under, we put "Unknown" to clarify you're not running under a VM if this is detected
1255912560
case brand_enum::NULL_BRAND: return "Unknown";
1256012561
case brand_enum::UNKNOWN: return "Invalid";
1256112562
}
@@ -12577,22 +12578,26 @@ struct VM {
1257712578

1257812579

1257912580
static std::string conclusion(const flagset &flags = core::generate_default()) {
12580-
std::string brand_tmp = brand(flags);
12581+
if (memo::conclusion::cached) {
12582+
return memo::conclusion::fetch();
12583+
}
12584+
1258112585
const u8 percent_tmp = percentage(flags);
1258212586
const bool has_hardener = is_hardened();
12583-
12587+
1258412588
constexpr const char* very_unlikely = "Very unlikely";
1258512589
constexpr const char* unlikely = "Unlikely";
1258612590
constexpr const char* potentially = "Potentially";
1258712591
constexpr const char* might = "Might be";
1258812592
constexpr const char* likely = "Likely";
1258912593
constexpr const char* very_likely = "Very likely";
1259012594
constexpr const char* inside_vm = "Running inside";
12591-
12595+
1259212596
auto make_conclusion = [&](const char* category) -> std::string {
12593-
if (memo::conclusion::cached) {
12594-
return memo::conclusion::fetch();
12595-
}
12597+
const brand_list_t& list = brands::brand_list(flags);
12598+
12599+
const brand_enum first_brand = brands::brand_enum(list);
12600+
1259612601

1259712602
const char* hardener = "";
1259812603

@@ -12609,40 +12614,48 @@ struct VM {
1260912614
// condition fixes that issue.
1261012615
if (
1261112616
!has_hardener && (
12612-
(brand_tmp == brands::ACRN) ||
12613-
(brand_tmp == brands::ANUBIS) ||
12614-
(brand_tmp == brands::BSD_VMM) ||
12615-
(brand_tmp == brands::INTEL_HAXM) ||
12616-
(brand_tmp == brands::APPLE_VZ) ||
12617-
(brand_tmp == brands::INTEL_KGT) ||
12618-
(brand_tmp == brands::POWERVM) ||
12619-
(brand_tmp == brands::OPENSTACK) ||
12620-
(brand_tmp == brands::AWS_NITRO) ||
12621-
(brand_tmp == brands::OPENVZ) ||
12622-
(brand_tmp == brands::INTEL_TDX) ||
12623-
(brand_tmp == brands::AMD_SEV) ||
12624-
(brand_tmp == brands::AMD_SEV_ES) ||
12625-
(brand_tmp == brands::AMD_SEV_SNP) ||
12626-
(brand_tmp == brands::NSJAIL) ||
12627-
(brand_tmp == brands::NULL_BRAND)
12617+
(first_brand == brand_enum::ACRN) ||
12618+
(first_brand == brand_enum::ANUBIS) ||
12619+
(first_brand == brand_enum::BSD_VMM) ||
12620+
(first_brand == brand_enum::INTEL_HAXM) ||
12621+
(first_brand == brand_enum::APPLE_VZ) ||
12622+
(first_brand == brand_enum::INTEL_KGT) ||
12623+
(first_brand == brand_enum::POWERVM) ||
12624+
(first_brand == brand_enum::OPENSTACK) ||
12625+
(first_brand == brand_enum::AWS_NITRO) ||
12626+
(first_brand == brand_enum::OPENVZ) ||
12627+
(first_brand == brand_enum::INTEL_TDX) ||
12628+
(first_brand == brand_enum::AMD_SEV) ||
12629+
(first_brand == brand_enum::AMD_SEV_ES) ||
12630+
(first_brand == brand_enum::AMD_SEV_SNP) ||
12631+
(first_brand == brand_enum::NSJAIL) ||
12632+
(first_brand == brand_enum::NULL_BRAND)
1262812633
)
1262912634
) {
1263012635
addition = " an ";
1263112636
}
1263212637

12638+
std::string brand_str = "";
12639+
1263312640
// this is basically just to remove the capital "U",
1263412641
// since it doesn't make sense to see "an Unknown"
12635-
if (brand_tmp == brands::NULL_BRAND) {
12636-
brand_tmp = "unknown";
12642+
if (first_brand == brand_enum::NULL_BRAND) {
12643+
brand_str = "unknown";
12644+
} else {
12645+
if (core::is_enabled(flags, MULTIPLE)) {
12646+
brand_str = brands::brand_multiple(flags);
12647+
} else {
12648+
brand_str = brands::brand_enum_to_string(first_brand);
12649+
}
1263712650
}
1263812651

12639-
// Hyper-V artifacts are an exception due to how unique the circumstance is
1264012652
const std::string result =
1264112653
std::string(category) +
1264212654
addition +
1264312655
hardener +
12644-
brand_tmp +
12645-
(brand_tmp == brands::HYPERV_ARTIFACT ? "" : " VM");
12656+
brand_str +
12657+
// Hyper-V artifacts are an exception due to how unique the circumstance is
12658+
(first_brand == brand_enum::HYPERV_ROOT ? "" : " VM");
1264612659

1264712660
memo::conclusion::store(result.c_str());
1264812661

@@ -12694,11 +12707,17 @@ struct VM {
1269412707

1269512708
const bool hv_present = (check(VM::HYPERVISOR_BIT) || check(VM::HYPERVISOR_STR));
1269612709
const bool has_hyper_x = []() {
12697-
if (check(VM::HYPERVISOR_BIT)) {
12710+
if (check(VM::HYPERVISOR_BIT) || check(VM::HYPERVISOR_STR)) {
1269812711
return false;
1269912712
}
1270012713

12701-
return memo::cache_table[VM::HYPERVISOR_BIT].brand_name == brand_enum::HYPERV_ARTIFACT;
12714+
const brand_enum bit_brand = memo::cache_table[VM::HYPERVISOR_BIT].brand_name;
12715+
const brand_enum str_brand = memo::cache_table[VM::HYPERVISOR_STR].brand_name;
12716+
12717+
return (
12718+
(bit_brand == brand_enum::HYPERV_ROOT) ||
12719+
(str_brand == brand_enum::HYPERV_ROOT)
12720+
);
1270212721
}();
1270312722

1270412723
// rule 1: if VM::FIRMWARE is detected, so should VM::HYPERVISOR_BIT or VM::HYPERVISOR_STR
@@ -12719,7 +12738,7 @@ struct VM {
1271912738
}
1272012739
#endif
1272112740

12722-
#if (WINDOWS)
12741+
#if (WINDOWS)
1272312742
// rule 3: if VM::ACPI_SIGNATURE (QEMU) is detected, so should VM::FIRMWARE (QEMU)
1272412743
const enum brand_enum acpi_brand = detected_brand(VM::ACPI_SIGNATURE);
1272512744
if (acpi_brand == brand_enum::QEMU && firmware_brand != brand_enum::QEMU) {
@@ -12728,7 +12747,7 @@ struct VM {
1272812747
}
1272912748

1273012749
// rule 4: if VM::TRAP or VM::NVRAM is detected, so should VM::HYPERVISOR_BIT or VM::HYPERVISOR_STR
12731-
if ((check(VM::TRAP) || check(VM::NVRAM)) && !hv_present && !has_hyper_x) {
12750+
if ((check(VM::TRAP) || check(VM::NVRAM)) && (hv_present || (!hv_present && has_hyper_x))) {
1273212751
debug("is_hardened(): trap/NVRAM and hypervisor bit/str are not detected together");
1273312752
return true;
1273412753
}

0 commit comments

Comments
 (0)