Skip to content

Commit 402ae6d

Browse files
authored
Merge pull request #139 from kernelwernel/dev
Dev
2 parents a476dae + 3dc1565 commit 402ae6d

3 files changed

Lines changed: 89 additions & 25 deletions

File tree

TODO.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
- [ ] test the VM::modify_score() function
4545
- [ ] check if bios date in /sys/class/dmi/id/ could be useful under QEMU
4646
- [ ] make the cli demo in the readme for the 1.8 version
47+
- [ ] fix the percentage thing for the disabled techniques
4748

4849
# Distant plans
4950
- add the library to conan.io when released

src/cli.cpp

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ constexpr const char* date = "August 2024";
4848

4949
constexpr const char* bold = "\033[1m";
5050
constexpr const char* ansi_exit = "\x1B[0m";
51-
constexpr const char* red = "\x1B[38;2;239;75;75m";
51+
constexpr const char* red = "\x1B[38;2;239;75;75m";
5252
constexpr const char* orange = "\x1B[38;2;255;180;5m";
5353
constexpr const char* green = "\x1B[38;2;94;214;114m";
5454
constexpr const char* red_orange = "\x1B[38;2;247;127;40m";
@@ -58,6 +58,7 @@ constexpr const char* grey = "\x1B[38;2;108;108;108m";
5858
enum arg_enum : std::uint8_t {
5959
HELP,
6060
VERSION,
61+
ALL,
6162
DETECT,
6263
STDOUT,
6364
BRAND,
@@ -66,7 +67,6 @@ enum arg_enum : std::uint8_t {
6667
CONCLUSION,
6768
NUMBER,
6869
TYPE,
69-
HYPERV, // will be removed in the next release
7070
NOTES,
7171
SPOOFABLE,
7272
NULL_ARG
@@ -120,6 +120,7 @@ R"(Usage:
120120
Options:
121121
-h | --help prints this help menu
122122
-v | --version print cli version and other details
123+
-a | --all run the result with ALL the techniques enabled (might contain false positives)
123124
-d | --detect returns the result as a boolean (1 = VM, 0 = baremetal)
124125
-s | --stdout returns either 0 or 1 to STDOUT without any text output (0 = VM, 1 = baremetal)
125126
-b | --brand returns the VM brand string (consult documentation for full output list)
@@ -335,6 +336,10 @@ std::string type(const std::string &brand_str) {
335336
}
336337

337338
bool is_spoofable(const VM::enum_flags flag) {
339+
if (arg_bitset.test(ALL)) {
340+
return false;
341+
}
342+
338343
switch (flag) {
339344
case VM::MAC:
340345
case VM::DOCKERENV:
@@ -418,12 +423,28 @@ bool are_perms_required(const VM::enum_flags flag) {
418423
#endif
419424
}
420425

426+
427+
bool is_disabled(const VM::enum_flags flag) {
428+
if (arg_bitset.test(ALL)) {
429+
return false;
430+
}
431+
432+
switch (flag) {
433+
case VM::RDTSC:
434+
case VM::RDTSC_VMEXIT:
435+
case VM::CURSOR: return true;
436+
default: return false;
437+
}
438+
}
439+
440+
421441
void general() {
422442
const std::string detected = ("[ " + std::string(green) + "DETECTED" + std::string(ansi_exit) + " ]");
423443
const std::string not_detected = ("[" + std::string(red) + "NOT DETECTED" + std::string(ansi_exit) + "]");
424444
const std::string spoofable = ("[" + std::string(red) + " SPOOFABLE " + std::string(ansi_exit) + "]");
425445
const std::string note = ("[ NOTE ]");
426446
const std::string no_perms = ("[" + std::string(grey) + " NO PERMS " + std::string(ansi_exit) + "]");
447+
const std::string disabled = ("[" + std::string(grey) + " DISABLED " + std::string(ansi_exit) + "]");
427448
const std::string tip = (std::string(green) + "TIP: " + std::string(ansi_exit));
428449

429450
auto checker = [&](const VM::enum_flags flag, const char* message) -> void {
@@ -441,6 +462,11 @@ void general() {
441462
}
442463
#endif
443464

465+
if (is_disabled(flag)) {
466+
std::cout << disabled << " Skipped " << message << "\n";
467+
return;
468+
}
469+
444470
if (VM::check(flag)) {
445471
std::cout << detected << " Checking " << message << "...\n";
446472
detected_count++;
@@ -493,7 +519,7 @@ void general() {
493519
checker(VM::DLL, "DLLs");
494520
checker(VM::REGISTRY, "registry");
495521
checker(VM::CWSANDBOX_VM, "Sunbelt CWSandbox directory");
496-
checker(VM::WINE_CHECK, "Wine");
522+
//checker(VM::WINE_CHECK, "Wine");
497523
checker(VM::VM_FILES, "VM files");
498524
checker(VM::HWMODEL, "hw.model");
499525
checker(VM::DISK_SIZE, "disk size");
@@ -504,7 +530,7 @@ void general() {
504530
checker(VM::MEMORY, "low memory space");
505531
checker(VM::VM_PROCESSES, "VM processes");
506532
checker(VM::LINUX_USER_HOST, "default Linux user/host");
507-
checker(VM::VBOX_WINDOW_CLASS, "VBox window class");
533+
//checker(VM::VBOX_WINDOW_CLASS, "VBox window class");
508534
checker(VM::GAMARUE, "gamarue ransomware technique");
509535
checker(VM::VMID_0X4, "0x4 leaf of VMID");
510536
checker(VM::PARALLELS_VM, "Parallels techniques");
@@ -695,9 +721,10 @@ int main(int argc, char* argv[]) {
695721
std::exit(0);
696722
}
697723

698-
static constexpr std::array<std::pair<const char*, arg_enum>, 23> table {{
724+
static constexpr std::array<std::pair<const char*, arg_enum>, 24> table {{
699725
{ "-h", HELP },
700726
{ "-v", VERSION },
727+
{ "-a", ALL },
701728
{ "-d", DETECT },
702729
{ "-s", STDOUT },
703730
{ "-b", BRAND },
@@ -708,6 +735,7 @@ int main(int argc, char* argv[]) {
708735
{ "-t", TYPE },
709736
{ "--help", HELP },
710737
{ "--version", VERSION },
738+
{ "--all", ALL },
711739
{ "--detect", DETECT },
712740
{ "--stdout", STDOUT },
713741
{ "--brand", BRAND },
@@ -716,7 +744,6 @@ int main(int argc, char* argv[]) {
716744
{ "--brand-list", BRAND_LIST },
717745
{ "--number", NUMBER },
718746
{ "--type", TYPE },
719-
{ "--disable-hyperv-host", HYPERV },
720747
{ "--disable-notes", NOTES },
721748
{ "--spoofable", SPOOFABLE }
722749
}};
@@ -782,15 +809,17 @@ int main(int argc, char* argv[]) {
782809
auto settings = [&]() -> std::bitset<max_bits> {
783810
std::bitset<max_bits> setting_bits;
784811

785-
if (arg_bitset.test(HYPERV)) {
786-
std::cerr << "--disable-hyperv-host has been deprecated, the determination of whether it's a host Hyper-V or VM Hyper-V is now done automatically";
787-
return 1;
812+
if (arg_bitset.test(SPOOFABLE)) {
813+
setting_bits.set(VM::SPOOFABLE);
788814
}
789815

790-
if (arg_bitset.test(SPOOFABLE)) {
816+
if (arg_bitset.test(ALL)) {
817+
setting_bits |= VM::ALL;
791818
setting_bits.set(VM::SPOOFABLE);
792819
}
793820

821+
setting_bits.set(NULL_ARG);
822+
794823
return setting_bits;
795824
};
796825

src/vmaware.hpp

Lines changed: 49 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@
4949
* }
5050
*/
5151

52-
5352
#if (defined(_MSC_VER) || defined(_WIN32) || defined(_WIN64) || defined(__MINGW32__))
5453
#define MSVC 1
5554
#define LINUX 0
@@ -1036,12 +1035,16 @@ struct VM {
10361035
return vec;
10371036
}
10381037

1039-
// basically checks whether all the techniques were cached (with exception of VM::CURSOR)
1038+
// basically checks whether all the techniques were cached (with exception of techniques disabled by default)
10401039
static bool all_present() {
10411040
if (cache_table.size() == technique_count) {
10421041
return true;
1043-
} else if (cache_table.size() == static_cast<std::size_t>(technique_count) - 1) {
1044-
return (!cache_keys.test(CURSOR));
1042+
} else if (cache_table.size() == static_cast<std::size_t>(technique_count) - 3) {
1043+
return (
1044+
!cache_keys.test(CURSOR) &&
1045+
!cache_keys.test(RDTSC_VMEXIT) &&
1046+
!cache_keys.test(RDTSC)
1047+
);
10451048
}
10461049

10471050
return false;
@@ -1682,13 +1685,22 @@ struct VM {
16821685

16831686
core_debug("HYPER_X: eax = ", eax);
16841687

1685-
if (eax == 12) {
1688+
const bool is_eax_valid = ((eax == 11) || (eax == 12));
1689+
1690+
const std::array<std::string, 2> cpu = cpu::cpu_manufacturer(cpu::leaf::hypervisor);
1691+
1692+
const bool is_cpu_hyperv = (
1693+
(cpu.at(0) == "Microsoft Hv") ||
1694+
(cpu.at(1) == "Microsoft Hv")
1695+
);
1696+
1697+
if (is_eax_valid || is_cpu_hyperv) {
16861698
// SMBIOS check
1687-
const std::string p = SMBIOS_string();
1699+
const std::string smbios = SMBIOS_string();
16881700

1689-
core_debug("HYPER_X: SMBIOS string = ", p);
1701+
core_debug("HYPER_X: SMBIOS string = ", smbios);
16901702

1691-
if (p == "VIRTUAL MACHINE") {
1703+
if (smbios == "VIRTUAL MACHINE") {
16921704
return add(false);
16931705
}
16941706

@@ -1707,19 +1719,19 @@ struct VM {
17071719
std::wstring logName = L"Microsoft-Windows-Kernel-PnP/Configuration";
17081720
std::vector<std::wstring> searchStrings = { L"Virtual_Machine", L"VMBUS" };
17091721

1710-
const bool found = util::query_event_logs(logName, searchStrings);
1722+
const bool event_log = util::query_event_logs(logName, searchStrings);
17111723

1712-
if (found) {
1724+
if (event_log) {
17131725
return add(false);
17141726
}
17151727

17161728

17171729
// at this point, it's fair to assume it's Hyper-V artifacts on
17181730
// host since none of the "VM-only" techniques returned true
17191731
return add(true);
1720-
} else if (eax == 11) {
1732+
//} else if () {
17211733
// actual Hyper-V VM, might do something within this scope in the future idk
1722-
return add(false);
1734+
//return add(false);
17231735
} else {
17241736
return add(false);
17251737
}
@@ -3013,6 +3025,7 @@ struct VM {
30133025
* @category Windows
30143026
*/
30153027
[[nodiscard]] static bool cursor_check() try {
3028+
return true;
30163029
#if (!MSVC)
30173030
return false;
30183031
#else
@@ -4408,23 +4421,42 @@ struct VM {
44084421
if (intel) {
44094422
// technique 1: not a valid brand
44104423
if (brand == " Intel(R) Pentium(R) 4 CPU ") {
4424+
debug("BOCHS_CPU: technique 1 found");
44114425
return core::add(BOCHS);
44124426
}
44134427
} else if (amd) {
44144428
// technique 2: "processor" should have a capital P
44154429
if (brand == "AMD Athlon(tm) processor") {
4430+
debug("BOCHS_CPU: technique 2 found");
44164431
return core::add(BOCHS);
44174432
}
44184433

44194434
// technique 3: Check for absence of AMD easter egg for K7 and K8 CPUs
4420-
// note: QEMU may also have this but i'm not sure
4435+
/*
44214436
u32 unused, eax = 0;
44224437
cpu::cpuid(eax, unused, unused, unused, 1);
44234438
44244439
constexpr u8 AMD_K7 = 6;
44254440
constexpr u8 AMD_K8 = 15;
44264441
4427-
const u32 family = ((eax >> 8) & 0xF);
4442+
auto is_k7 = [](const u32 eax) -> bool {
4443+
const u32 family = (eax >> 8) & 0xF;
4444+
const u32 model = (eax >> 4) & 0xF;
4445+
const u32 extended_family = (eax >> 20) & 0xFF;
4446+
const u32 extended_model = (eax >> 16) & 0xF;
4447+
4448+
if (family == 6 && extended_family == 0) {
4449+
if (model == 1 || model == 2 || model == 3 || model == 4) {
4450+
return true;
4451+
}
4452+
}
4453+
4454+
return false;
4455+
};
4456+
4457+
auto is_k8 = [](const u32 eax) -> bool {
4458+
// TODO
4459+
};
44284460
44294461
if (family != AMD_K7 && family != AMD_K8) {
44304462
return false;
@@ -4434,8 +4466,10 @@ struct VM {
44344466
cpu::cpuid(unused, unused, ecx_bochs, unused, cpu::leaf::amd_easter_egg);
44354467
44364468
if (ecx_bochs == 0) {
4469+
debug("BOCHS_CPU: technique 3 found");
44374470
return core::add(BOCHS);
44384471
}
4472+
*/
44394473
}
44404474

44414475
return false;
@@ -5445,7 +5479,7 @@ struct VM {
54455479
* @note code documentation paper in /papers/www.offensivecomputing.net_vm.pdf
54465480
*/
54475481
[[nodiscard]] static bool offsec_sgdt() try {
5448-
#if (!MSVC || !x86)
5482+
#if (!MSVC || !x86 || GCC)
54495483
return false;
54505484
#elif (x86_32)
54515485
unsigned char m[6]{};

0 commit comments

Comments
 (0)