Skip to content

Commit

Permalink
hyper-x version 3
Browse files Browse the repository at this point in the history
  • Loading branch information
kernelwernel committed Oct 7, 2024
1 parent f8d2b56 commit a37d7d3
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 74 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
<br>
<img align="center" src="https://img.shields.io/github/actions/workflow/status/kernelwernel/VMAware/cmake-multi-platform.yml">
<img align="center" src="https://img.shields.io/github/downloads/kernelwernel/VMAware/total">
<img align="center" src="https://img.shields.io/github/license/kernelwernel/VMAware">
<img align="center" src="https://img.shields.io/github/license/a0rtega/pafish
">
<img align="center" src="https://img.shields.io/github/license/kernelwernel/Tourneys-bot">
</p>

Expand Down Expand Up @@ -141,7 +142,7 @@ You can view the full docs [here](docs/documentation.md). All the details such a
> Hyper-V has an obscure feature where if it's enabled in the host system, the CPU hardware values makes it look like the whole system is running inside Hyper-V, which isn't true. This makes it a challenge to determine whether the hardware values the library is collecting is either a real Hyper-V VM, or just the artifacts of what Hyper-V has left as a consequence of having it enabled in the host system. The reason why this is a problem is because the library might falsely conclude that your the host system is running in Hyper-V, which is a false positive. This is where the **Hyper-X** mechanism comes into play to distinguish between these two. This was designed by <a href="https://github.com/NotRequiem">Requiem</a>
<p align="center">
<img src="assets/Hyper-X_version_2.png" align="center" title="Hyper-X">
<img src="assets/Hyper-X_version_3.png" align="center" title="Hyper-X">
<br>
</details>

Expand Down
145 changes: 73 additions & 72 deletions src/vmaware.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -574,17 +574,24 @@ struct VM {
static flagset global_flags; // for certain techniques where the flags MUST be accessible

// macro for bypassing unused parameter/variable warnings
#define UNUSED(x) ((void)(x))
#define UNUSED(x) ((void)(x))

// likely and unlikely macros
#if (LINUX)
#define VMAWARE_UNLIKELY(x) __builtin_expect(!!(x), 0)
#define VMAWARE_LIKELY(x) __builtin_expect(!!(x), 1)
# define VMAWARE_UNLIKELY(x) __builtin_expect(!!(x), 0)
# define VMAWARE_LIKELY(x) __builtin_expect(!!(x), 1)
#else
#define VMAWARE_UNLIKELY
#define VMAWARE_LIKELY
# define VMAWARE_UNLIKELY
# define VMAWARE_LIKELY
#endif

// specifically for util::hyper_x() and memo::hyperv
enum hyperx_state : u8 {
HYPERV_REAL_VM = 1,
HYPERV_ARTIFACT_VM,
UNKNOWN
};

// various cpu operation stuff
struct cpu {
// cpuid leaf values
Expand Down Expand Up @@ -1132,21 +1139,19 @@ struct VM {
}
};

struct hyperv {
static bool is_hyperv_host;
static bool is_stored;
struct hyperx {
static hyperx_state state;

static bool fetch() {
return is_hyperv_host;
static hyperx_state fetch() {
return state;
}

static void store(const bool p_is_hyperv_host) {
is_hyperv_host = p_is_hyperv_host;
is_stored = true;
static void store(const hyperx_state p_state) {
state = p_state;
}

static bool is_cached() {
return is_stored;
return (state == hyperx_state::UNKNOWN);
}
};
};
Expand Down Expand Up @@ -1701,24 +1706,51 @@ struct VM {
* your the host system is running in Hyper-V, which is a false positive. This is where
* the Hyper-X mechanism comes into play to distinguish between these two.
* @author idea by Requiem (https://github.com/NotRequiem)
* @link graph to explain how this works: https://github.com/kernelwernel/VMAware/blob/main/assets/Hyper-X.png
* @link graph to explain how this works: https://github.com/kernelwernel/VMAware/blob/main/assets/Hyper-X_version_3.png
*/
[[nodiscard]] static bool hyper_x() {
#if (!MSVC)
return false;
#else
if (memo::hyperv::is_cached()) {
core_debug("HYPER_X: returned from cache = ", memo::hyperv::fetch());
return memo::hyperv::fetch();
core_debug("HYPER_X: returned from cache");
return (memo::hyperv::fetch() == hyperx_state::HYPERV_ARTIFACT_VM);
}


auto root_partition = []() -> bool {
// SMBIOS check
auto is_smbios_hyperv = []() -> bool {
const std::string smbios = SMBIOS_string();
core_debug("HYPER_X: SMBIOS string = ", smbios);
return (smbios == "VIRTUAL MACHINE");
};


// motherboard check
auto is_motherboard_hyperv = []() -> bool {
const bool motherboard = motherboard_string(L"Microsoft Corporation");
core_debug("HYPER_X: motherboard string match = ", motherboard);
return motherboard;
};


// event log check (slow, so in last place)
auto is_event_log_hyperv = []() -> bool {
std::wstring logName = L"Microsoft-Windows-Kernel-PnP/Configuration";
std::vector<std::wstring> searchStrings = { L"Virtual_Machine", L"VMBUS" };

return (util::query_event_logs(logName, searchStrings));
};


// VMProtect method for Hyper-V artifact detection
auto is_root_partition = []() -> bool {
u32 ebx, unused = 0;
cpu::cpuid(unused, ebx, unused, unused, 0x40000003);
return (ebx & 1);
};


auto eax = []() -> bool {
char out[sizeof(int32_t) * 4 + 1] = { 0 }; // e*x size + number of e*x registers + null terminator
cpu::cpuid((int*)out, cpu::leaf::hypervisor);
Expand All @@ -1730,63 +1762,33 @@ struct VM {
return ((eax == 11) || (eax == 12));
};

auto cpu_vmid = []() -> bool {
const auto cpu = cpu::cpu_manufacturer(cpu::leaf::hypervisor);

return (
(cpu.at(0) == "Microsoft Hv") ||
(cpu.at(1) == "Microsoft Hv")
);
};

// must require at least 2 to continue
const u8 points = (root_partition() + eax() + cpu_vmid());

if (points >= 2) {
// SMBIOS check
auto is_smbios_hyperv = []() -> bool {
const std::string smbios = SMBIOS_string();
core_debug("HYPER_X: SMBIOS string = ", smbios);
return (smbios == "VIRTUAL MACHINE");
};


// motherboard check
auto is_motherboard_hyperv = []() -> bool {
const bool motherboard = motherboard_string(L"Microsoft Corporation");
core_debug("HYPER_X: motherboard string match = ", motherboard);
return motherboard;
};


// event log check (slow, so in last place)
auto is_event_log_hyperv = []() -> bool {
std::wstring logName = L"Microsoft-Windows-Kernel-PnP/Configuration";
std::vector<std::wstring> searchStrings = { L"Virtual_Machine", L"VMBUS" };

return (util::query_event_logs(logName, searchStrings));
};
const bool has_hyperv_indications = (
is_smbios_hyperv() ||
is_motherboard_hyperv() ||
is_event_log_hyperv() ||
is_root_partition()
);

const bool is_real_hyperv_vm = (eax() && has_hyperv_indications);

// "if it's hyper-v and NOT an artifact"
const bool is_hyperv = (
is_smbios_hyperv() ||
is_motherboard_hyperv() ||
is_event_log_hyperv()
);
enum hyperx_state state;

memo::hyperv::store(is_hyperv);

if (is_hyperv) {
return true;
} else {
core::add(HYPERV_ARTIFACT);
return false;
}
if (is_real_hyperv_vm) {
state = hyperx_state::HYPERV_REAL_VM;
} else {
state = hyperx_state::HYPERV_ARTIFACT_VM;
}

memo::hyperv::store(false);
return false;
memo::hyperv::store(state);

if (is_real_hyperv_vm) {
core::add(HYPERV);
return false;
} else {
core::add(HYPERV_ARTIFACT_VM);
return true;
}
#endif
}

Expand Down Expand Up @@ -10430,14 +10432,13 @@ std::map<const char*, VM::brand_score_t> VM::core::brand_scoreboard{
};


// initial definitions for cache items
// initial definitions for cache items because ISO C++ forbids in-class initializations
std::map<VM::u8, VM::memo::data_t> VM::memo::cache_table;
VM::flagset VM::memo::cache_keys = 0;
std::string VM::memo::brand::brand_cache = "";
std::string VM::memo::multi_brand::brand_cache = "";
std::string VM::memo::cpu_brand::brand_cache = "";
bool VM::memo::hyperv::is_hyperv_host = false;
bool VM::memo::hyperv::is_stored = false;
VM::hyperx_state VM::memo::hyperx::state = VM::hyperx_state::UNKNOWN;

#ifdef __VMAWARE_DEBUG__
VM::u16 VM::total_points = 0;
Expand Down

0 comments on commit a37d7d3

Please sign in to comment.