Skip to content

Commit

Permalink
added bochs and major improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
kernelwernel committed Dec 20, 2023
1 parent 1090be9 commit 12fb67f
Show file tree
Hide file tree
Showing 2 changed files with 167 additions and 42 deletions.
15 changes: 11 additions & 4 deletions docs/documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ This will essentially return the VM brand as a `std::string`. The brand string r
- `JoeBox`
- `Thread Expert`
- `CW Sandbox`
- `SunBelt`
- `Comodo`
- `Bochs`


If none were detected, it will return `Unknown`. It's often not going to produce a satisfying result due to technical difficulties with accomplishing this, on top of being highly dependant on what mechanisms detected a VM. Don't rely on this function too much.

Expand Down Expand Up @@ -131,7 +135,7 @@ VMAware provides a convenient way to not only check for VMs, but also have the f
| `VM::USER` | Match the username for any defaulted ones | Windows | 35% | |
| `VM::DLL` | Match for VM-specific DLLs | Windows | 50% | |
| `VM::REGISTRY` | Look throughout the registry for all sorts of VMs | Windows | 75% | |
| `VM::SUNBELT` | Detect for Sunbelt technology | Windows | 10% | |
| `VM::SUNBELT_VM` | Detect for Sunbelt technology | Windows | 10% | |
| `VM::WINE_CHECK` | Find for a Wine-specific file | Windows | 85% | |
| `VM::BOOT` | Analyse the OS uptime | Yes | 5% | |
| `VM::VM_FILES` | Find if any VM-specific files exists | Windows | 20% | |
Expand All @@ -145,11 +149,14 @@ VMAware provides a convenient way to not only check for VMs, but also have the f
| `VM::LINUX_USER_HOST` | Check for default VM username and hostname for linux | Linux | 35% | |
| `VM::VBOX_WINDOW_CLASS` | Check for the window class for VirtualBox | Windows | 10% | |
| `VM::WINDOWS_NUMBER` | Check top-level default window level | Windows | 20% | |
| `VM::GAMARUE` | Check for Gamarue ransomeware technique which compares VM-specific Window product IDs | Windows | 40% | |
| `VM::GAMARUE` | Check for Gamarue ransomware technique which compares VM-specific Window product IDs | Windows | 40% | |
| `VM::VMID_0X4` | Check if the CPU manufacturer ID matches that of a VM brand with leaf 0x40000000 | Yes | 100% | |
| `VM::VPC_BACKDOOR` | Check for semi-documented detection mechanism for Virtual PC | Windows | 70% | |
|
|
| `VM::PARALLELS_VM` | Check for indications of Parallels VM | [TODO_ADD_THIS_SHIT] | 50% | |
| `VM::SPEC_RDTSC` | Check for RDTSC technique with speculative execution | [TODO_ADD_THIS_SHIT] | 80% | |
| `VM::LOADED_DLLS` | Check for DLLs of multiple VM brands | Windows | 75% | |
| `VM::QEMU_BRAND` | Check for QEMU CPU brand with cpuid | Yes | 100% | |


# Non-technique flags
| Flag | Description |
Expand Down
194 changes: 156 additions & 38 deletions src/vmaware.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,7 @@ struct VM {
static constexpr const char* CWSANDBOX = "CW Sandbox";
static constexpr const char* COMODO = "Comodo";
static constexpr const char* SUNBELT = "SunBelt";
static constexpr const char* BOCHS = "Bochs";

// VM scoreboard table specifically for VM::brand()
#if (MSVC)
Expand Down Expand Up @@ -797,6 +798,8 @@ struct VM {
PARALLELS_VM = 1ULL << 43,
SPEC_RDTSC = 1ULL << 44,
LOADED_DLLS = 1ULL << 45,
QEMU_BRAND = 1ULL << 46,
BOCHS_CPU = 1ULL << 47,

// __UNIQUE_LABEL, ADD YOUR UNIQUE FUNCTION FLAG VALUE ABOVE HERE

Expand Down Expand Up @@ -906,6 +909,51 @@ struct VM {
return false;
}


[[nodiscard]] static std::string get_cpu_brand() {
if (!cpuid_supported) {
return "";
}

#if (!x86)
return "";
#else
// maybe not necessary but whatever
#if (LINUX)
if (!__get_cpuid_max(0x80000004, nullptr)) {
return "";
}
#endif

std::array<u32, 4> buffer{};
constexpr std::size_t buffer_size = sizeof(i32) * buffer.size();
std::array<char, 64> charbuffer{};

constexpr std::array<u32, 3> ids = {
leaf::brand1,
leaf::brand2,
leaf::brand3
};

std::string brand = "";

for (const u32 &id : ids) {
cpuid(buffer.at(0), buffer.at(1), buffer.at(2), buffer.at(3), id);

std::memcpy(charbuffer.data(), buffer.data(), buffer_size);

const char* convert = charbuffer.data();
brand += convert;
}

#ifdef __VMAWARE_DEBUG__
debug("BRAND: ", "cpu brand = ", brand);
#endif

return brand;
#endif
}

private:
static constexpr u64 DEFAULT = (~(CURSOR) & ALL);

Expand Down Expand Up @@ -964,38 +1012,8 @@ struct VM {
#if (!x86)
return false;
#else
// maybe not necessary but whatever
#if (LINUX)
if (!__get_cpuid_max(0x80000004, nullptr)) {
return false;
}
#endif

std::array<u32, 4> buffer{};
constexpr std::size_t buffer_size = sizeof(i32) * buffer.size();
std::array<char, 64> charbuffer{};

constexpr std::array<u32, 3> ids = {
leaf::brand1,
leaf::brand2,
leaf::brand3
};

std::string brand = "";

for (const u32 &id : ids) {
cpuid(buffer.at(0), buffer.at(1), buffer.at(2), buffer.at(3), id);

std::memcpy(charbuffer.data(), buffer.data(), buffer_size);

const char* convert = charbuffer.data();
brand += convert;
}

#ifdef __VMAWARE_DEBUG__
debug("BRAND: ", "cpu brand = ", brand);
#endif

std::string brand = get_cpu_brand();

// TODO: might add more potential keywords, be aware that it could (theoretically) cause false positives
constexpr std::array<const char*, 16> vmkeywords {
"qemu", "kvm", "virtual", "vm",
Expand All @@ -1012,22 +1030,50 @@ struct VM {

#ifdef __VMAWARE_DEBUG__
if (match) {
debug("BRAND: ", "match = ", vmkeywords.at(i));
debug("BRAND_KEYWORDS: ", "match = ", vmkeywords.at(i));
}
#endif

match_count += match;
}

#ifdef __VMAWARE_DEBUG__
debug("BRAND: ", "matches: ", static_cast<u32>(match_count));
debug("BRAND_KEYWORDS: ", "matches: ", static_cast<u32>(match_count));
#endif

return (match_count >= 1);
#endif
} catch (...) {
#ifdef __VMAWARE_DEBUG__
debug("BRAND: catched error, returned false");
debug("BRAND_KEYWORDS: catched error, returned false");
#endif
return false;
}


/**
* @brief Match for QEMU CPU brand
* @category x86
*/
[[nodiscard]] static bool cpu_brand_qemu() try {
if (!cpuid_supported || disabled(QEMU_BRAND)) {
return false;
}

#if (!x86)
return false;
#else
std::string brand = get_cpu_brand();

if (brand == "QEMU Virtual CPU") {
return add(QEMU);
}

return false;
#endif
} catch (...) {
#ifdef __VMAWARE_DEBUG__
debug("QEMU_BRAND: catched error, returned false");
#endif
return false;
}
Expand Down Expand Up @@ -3234,7 +3280,7 @@ struct VM {
#else
HMODULE hDll;

constexpr std::array<const char*, 5> szDlls = {
constexpr std::array<const char*, 12> szDlls = {
"avghookx.dll", // AVG
"avghooka.dll", // AVG
"snxhk.dll", // Avast
Expand Down Expand Up @@ -3274,6 +3320,74 @@ struct VM {
}


/**
* @brief Do various Bochs-related CPU stuff
* @category x86
* @note The idea for the CPU strings are from pafish
* @link https://github.com/a0rtega/pafish/blob/master/pafish/bochs.c
*/
[[nodiscard]] static bool bochs_cpu() try {
if (!cpuid_supported || disabled(BOCHS_CPU)) {
return false;
}

#if (!x86)
return false;
#else
constexpr u32 intel_ecx = 0x6c65746e;
constexpr u32 amd_ecx = 0x69746e65;

bool intel = false;
bool amd = false;

u32 unused, ecx = 0;
cpuid(unused, unused, ecx, unused, 0);

if (ecx == intel_ecx) { intel = true; }
if (ecx == amd_ecx) { amd = true; }

if (!(intel ^ amd)) { return false; }

const std::string brand = get_cpu_brand();

if (intel) {
// technique 1: not a valid brand
if (brand == " Intel(R) Pentium(R) 4 CPU ") { return add(BOCHS); }
} else if (amd) {
// technique 2: "processor" should be "Processor" instead in AMD
if (brand == "AMD Athlon(tm) processor") { return add(BOCHS); }

// technique 3: Check for AMD easter egg for K7 and K8 CPUs
u32 eax = 0;
cpuid(eax, unused, unused, unused, 1);

const u32 family = ((eax >> 8) & 0xF);

if (family != 6 && family != 15) { // AMD K7 = 6, AMD K8 = 15
return false;
}

u32 ecx_bochs = 0;
constexpr u32 easter_egg_leaf = 0x8fffffff;

cpuid(unused, unused, ecx_bochs, unused, easter_egg_leaf);

if (ecx_bochs == 0) {
return add(BOCHS);
}
}

return false;
#endif
} catch (...) {
#ifdef __VMAWARE_DEBUG__
debug("BOCHS_CPU:", "catched error, returned false");
#endif
return false;
}



// __TECHNIQUE_LABEL, label for adding techniques above this point


Expand Down Expand Up @@ -3502,7 +3616,9 @@ struct VM {
{ VM::JOEBOX, 0 },
{ VM::THREADEXPERT, 0 },
{ VM::CWSANDBOX, 0 },
{ VM::COMODO, 0 }
{ VM::COMODO, 0 },
{ VM::SUNBELT, 0 },
{ VM::BOCHS, 0 }
};


Expand Down Expand Up @@ -3580,7 +3696,9 @@ const std::map<VM::u64, VM::technique> VM::table = {
{ VM::VPC_BACKDOOR, { 70, VM::vpc_backdoor }},
{ VM::PARALLELS_VM, { 50, VM::parallels }},
{ VM::SPEC_RDTSC, { 80, VM::speculative_rdtsc }},
{ VM::LOADED_DLLS, { 75, VM::loaded_dlls }}
{ VM::LOADED_DLLS, { 75, VM::loaded_dlls }},
{ VM::QEMU_BRAND, { 100, VM::cpu_brand_qemu }},
{ VM::BOCHS_CPU, { 95, VM::bochs_cpu }}

// __TABLE_LABEL, add your technique above
// { VM::YOUR_FUNCTION, { POINTS, FUNCTION POINTER }}
Expand Down

0 comments on commit 12fb67f

Please sign in to comment.