From dea58ee35a700e02e481f21408593adc33cb1013 Mon Sep 17 00:00:00 2001 From: kernel <77142078+kernelwernel@users.noreply.github.com> Date: Mon, 22 Jan 2024 18:12:46 +0000 Subject: [PATCH] final fix --- CONTRIBUTING.md | 160 ----------------- cmake/ctest_checks.py | 165 ----------------- fuzzers/cpuid_fuzzer.c | 392 ----------------------------------------- src/vmaware.hpp | 14 +- 4 files changed, 7 insertions(+), 724 deletions(-) delete mode 100644 CONTRIBUTING.md delete mode 100644 cmake/ctest_checks.py delete mode 100644 fuzzers/cpuid_fuzzer.c diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 2e3f351..0000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,160 +0,0 @@ -# CONTRIBUTION GUIDELINES - -Doubt anybody will even read this, but hey, what's up! :) - -This guideline will cover how contributions can be made. The process of adding a new detection mechanism is a bit complicated, so I'll outline the basic steps and the standard format needed to contribute. -
- -# Step 1: Choose the right branch -Make sure you choose the **dev** branch to make pull requests. - -
- -# Step 2: Understand the format of a detection function -A VM detection mechanism has 3 things: -1. A unique bit flag value for the function -2. The function itself -3. The inclusion of the function in the technique table - -
- -# Step 3: Add the unique flag value for your function -Go to the vmaware.hpp source code, then CTRL+F the string "`__UNIQUE_LABEL`". This will bring you to the list of flag values for every function in the library. It should look something like this: -```cpp -// ... -GAMARUE = 1ULL << 39, -WINDOWS_NUMBER = 1ULL << 40, -VMID_0X4 = 1ULL << 41, -VPC_BACKDOOR = 1ULL << 42, -PARALLELS_VM = 1ULL << 43, -// ... -``` - -To add one, make sure to add a name for your own function (in all caps) with an incremental value based on the previous unique flag variable. You can name it whatever you want, just be clear and succint with your naming. So for example: -```cpp -VPC_BACKDOOR = 1ULL << 42, -PARALLELS_VM = 1ULL << 43, -YOUR_FUNCTION = 1ULL << 44, // here -``` - - -
- -# Step 4: Add the detection function itself -To add your own function, follow the format below: - -```cpp -/** - * @brief Brief description [REQUIRED] - * @category x86, Linux, Apple, Windows, All systems [CHOOSE ONE OR MULTIPLE, REQUIRED] - * @link https://example.com [OPTIONAL] - * @author add your name here [OPTIONAL] - * @note add anything that people should know about [OPTIONAL] - */ -[[nodiscard]] static bool example() try { - if (disabled(YOUR_FUNCTION)) { - return false; - } - - #if (!MSVC) // This is a filter in case your function only works for a specific platform. There are many macros such as "LINUX", "MSVC", "APPLE", and "x86". It's also case sensitive, so don't make any typos! - return false; - #else - - // add your VM detection code here, make sure to return a boolean (true = VM, false = baremetal) - - #endif -} catch (...) { - #ifdef __VMAWARE_DEBUG__ - debug("YOUR_FUNCTION: catched error, returned false"); - #endif - return false; -} -``` - - -some key stuff you should be aware of: -- If you want to make a mechanism that's available only for x86, Linux, MSVC, and Apple, make sure to add the correct preprocessor based on what platforms your function can be ran under. -- Make sure to add `[[nodiscard]]`. -- Add a function try-catch block. -- Copy-paste the same code within the example's catch block for debug reasons. Don't forget to replace the `YOUR_FUNCTION` part with your own unique function flag string within the debug code. -- The library also uses integer size suffixes such as `u8`, `int32_t`, `u16`, instead of `uint8_t`, `std::int32_t`, or `unsigned short`. The full alias list goes as follows: -```cpp - using u8 = std::uint8_t; - using u16 = std::uint16_t; - using u32 = std::uint32_t; - using u64 = std::uint64_t; - using i8 = std::int8_t; - using i16 = std::int16_t; - using int32_t = std::int32_t; - using i64 = std::int64_t; -``` - -So for example, this is the code for a technique that checks CPUID bits (CPUID_0X4) as a reference: -```cpp -/** - * @brief Check if 0x40000000~0x400000FF cpuid input is present (mostly present in VMs, according to VMware) - * @link https://kb.vmware.com/s/article/1009458 - * @category x86 - */ -[[nodiscard]] static bool cpuid_0x4() try { - if (disabled(CPUID_0x4)) { - return false; - } - - #if (!x86) - return false; - #else - u32 a, b, c, d = 0; - - for (u8 i = 0; i < 0xFF; i++) { - cpuid(a, b, c, d, (0x40000000, + i)); // don't worry what this does, it's just an example after all - if ((a + b + c + d) != 0) { - return true; - } - } - - return false; - #endif -} catch (...) { - #ifdef __VMAWARE_DEBUG__ - debug("CPUID_0x4: catched error, returned false"); - #endif - return false; -} -``` - -After you have your function ready, CTRL+F again and search for "`__TECHNIQUE_LABEL`", then plop that function below the previous function and that's done for step 4. The hard part is over :) - -
- -# Step 5: Including your function to the technique table -CTRL+F again and search for "`__TABLE_LABEL`". This should show you the place where all the techniques are organised in a `std::map`. To add your own function, follow the format of: -``` -{ VM::YOUR_FUNCTION, { POINTS, VM::FUNCTION_PTR }} -``` - -- `VM::YOUR_FUNCTION`: Your function flag variable -- `POINTS`: How certain you think a VM has been detected if the function returns true based on a 0-100 score (think of it as a percentage) -- `VM::YOUR_FUNCTION`: The pointer to your function - -So this: -```cpp -{ VM::VPC_BACKDOOR, { 70, VM::vpc_backdoor }}, -{ VM::PARALLELS_VM, { 50, VM::parallels }}, -{ VM::SPEC_RDTSC, { 80, VM::speculative_rdtsc }} -``` - -Becomes this: -```cpp -{ VM::VPC_BACKDOOR, { 70, VM::vpc_backdoor }}, -{ VM::PARALLELS_VM, { 50, VM::parallels }}, -{ VM::SPEC_RDTSC, { 80, VM::speculative_rdtsc }}, -{ VM::YOUR_FUNCTION, { 50, VM::example }} -``` - -Double check if your comma placements are valid, as this can cause those cancerous metaprogramming errors in C++ over a single character misplacement lol - -
-
- -Hopefully all of this makes sense. If you have any questions, don't hesitate to create an issue or ask me on discord at `kr.nl` \ No newline at end of file diff --git a/cmake/ctest_checks.py b/cmake/ctest_checks.py deleted file mode 100644 index ef54137..0000000 --- a/cmake/ctest_checks.py +++ /dev/null @@ -1,165 +0,0 @@ -# this is just an internal script for CI/CD. The main goal is to -# check whether all of the techniques are actually updated since -# keeping track of the docs, the cli, and the table isn't easy, -# so I'm automating the checks in case I forget to update any. - -import sys -import re - -def fetch(): - # fetch file content - with open("../src/vmaware.hpp", 'r') as vmaware: - header_content = vmaware.readlines() - - # reversed since the table is at the very end of the vmaware.hpp file - header_content.reverse() - - # breakpoint - keyword = "const std::map VM::table = {" - - # fetch index of breakpoint - index_of_keyword = next((i for i, line in enumerate(header_content) if keyword in line), None) - - # remove everything before the breakpoint for simplification - if index_of_keyword is not None: - header_content = header_content[:index_of_keyword + 1] - - return header_content - - - -def filter(raw_content): - trimmed_content = [] - - # filter - trimmed_content = [s for s in raw_content if not ( - s.isspace() or - "//" in s or - ";" in s or - "VM::technique" in s - )] - - # strip all whitespace - trimmed_content = [s.strip() for s in trimmed_content] - - return trimmed_content - - - -def tokenize(trimmed_content): - flag_array = [] - - # pattern for VM::FLAG_EXAMPLE1 - pattern = r'\bVM::([A-Z0-9_]+)\b' - - # match and push to flag_array[] - for line in trimmed_content: - match = re.search(pattern, line) - - if match: - flag_array.append(match.group(0)) - else: - print("Unable to find flag variable for " + line) - sys.exit(1) - - return flag_array - - - -def check_docs(flag_array): - # fetch docs content - with open("../docs/documentation.md", 'r') as docs: - docs_content = docs.readlines() - - # strip whitespace - docs_content = [s.strip() for s in docs_content] - - # find indices - start = "# Flag table" - end = "# Non-technique flags" - - # extract the indexes - try: - start_index = docs_content.index(start) - end_index = docs_content.index(end) - except ValueError: - print("Couldn't find range index point \"# Flag table\" or \"# Non-technique flags\"") - start_index = end_index = None - sys.exit(1) - - # extract the range between the aforementioned indexes - if start_index is not None and end_index is not None: - extracted_range = docs_content[start_index + 1:end_index] - docs_content = extracted_range - - # filter elements with whitespace - docs_content = [s for s in docs_content if not s.isspace() and s and "VM::" in s] - - # extract flag string for every line - docs_flags = [] - pattern = r'`([^`]+)`' - for line in docs_content: - match = re.search(pattern, line) - - if match: - docs_flags.append(match.group(1)) - else: - print("Pattern not found in the line \"" + line + "\"") - sys.exit(1) - - set1 = set(docs_flags) - set2 = set(flag_array) - - # Check if every element in set1 has a corresponding element in set2 - all_elements_have_pair = set1.issubset(set2) and set2.issubset(set1) - - not_paired = set1.symmetric_difference(set2) - - if not_paired: - print("Mismatched elements found in documentation.md and vmaware.hpp, make sure to include the technique in both files") - print("Elements without a pair:", not_paired) - sys.exit(1) - - - - -def check_cli(flag_array): - # fetch docs content - with open("../src/cli.cpp", 'r') as cli: - cli_content = cli.readlines() - - # strip whitespace - cli_content = [s.strip() for s in cli_content] - - # filter elements with whitespace - cli_content = [s for s in cli_content if ("checker(" in s)] - - # extract the flags - cli_flags = [] - pattern = r'checker\((.*?),' - for line in cli_content: - match = re.search(pattern, line) - - if match: - cli_flags.append(match.group(1).strip()) - else: - print("Pattern not found in the string.") - - set1 = set(cli_flags) - set2 = set(flag_array) - - # check if every element in set1 has a corresponding element in set2 - not_paired = set1.symmetric_difference(set2) - - if not_paired: - print("Mismatched elements found in cli.cpp and vmaware.hpp, make sure to include the technique in both files") - print("Elements without a pair:", not_paired) - sys.exit(1) - - -raw_content = fetch() -trimmed_content = filter(raw_content) -flags = tokenize(trimmed_content) - -check_docs(flags) -check_cli(flags) \ No newline at end of file diff --git a/fuzzers/cpuid_fuzzer.c b/fuzzers/cpuid_fuzzer.c deleted file mode 100644 index ff263c1..0000000 --- a/fuzzers/cpuid_fuzzer.c +++ /dev/null @@ -1,392 +0,0 @@ -/** - * ██╗ ██╗███╗ ███╗ █████╗ ██╗ ██╗ █████╗ ██████╗ ███████╗ - * ██║ ██║████╗ ████║██╔══██╗██║ ██║██╔══██╗██╔══██╗██╔════╝ - * ██║ ██║██╔████╔██║███████║██║ █╗ ██║███████║██████╔╝█████╗ - * ╚██╗ ██╔╝██║╚██╔╝██║██╔══██║██║███╗██║██╔══██║██╔══██╗██╔══╝ - * ╚████╔╝ ██║ ╚═╝ ██║██║ ██║╚███╔███╔╝██║ ██║██║ ██║███████╗ - * ╚═══╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚══╝╚══╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ - * - * A C++ VM detection library - * - * =============================================================== - - * This program serves as an internal tool for fuzzing cpuid values - * and comparing them between baremetal outputs and VM outputs. - * - * =============================================================== - * - * - Made by: @kernelwernel (https://github.com/kernelwernel) - * - Repository: https://github.com/kernelwernel/VMAware - */ - - -#include -#include -#include -#include -#include -#include -#include - -#if (defined(_MSC_VER) || defined(_WIN32) || defined(_WIN64) || defined(__MINGW32__)) - #define MSVC 1 - #define LINUX 0 -#elif (defined(__GNUC__) || defined(__linux__)) - #define MSVC 0 - #define LINUX 1 -#else - #error "Unknown OS, aborting" -#endif - -#if (LINUX) - #define _GNU_SOURCE - #include - #include - #include - #include -#endif - -// unlikely macro -#define unlikely(x) __builtin_expect(!!(x), 0) -#define likely(x) __builtin_expect(!!(x), 1) - -// cpu brand shit for ecx idfk -#define intel_ecx 0x6c65746e -#define amd_ecx 0x69746e65 - -// cpuid leaf values -#define manufacturer 0x00000000 -#define proc_info 0x00000001 -#define cache_tlb 0x00000002 -#define serial 0x00000003 -#define topology 0x00000004 -#define topology2 0x0000000B -#define management 0x00000006 -#define extended 0x00000007 // ecx = 0 -#define extended2 0x00000007 // ecx = 1 -#define extended3 0x00000007 // ecx = 2 -#define xsave 0x0000000D -#define xsave2 0x0000000D // ecx = >2 -#define xsave3 0x0000000D // ecx = 0 -#define xsave4 0x0000000D // ecx = 1 -#define sgx 0x00000012 // ecx = 0 -#define sgx2 0x00000012 // ecx = 1 -#define sgx3 0x00000012 // ecx = >2 -#define proc_trace 0x00000014 // ecx = 0 -#define aes 0x00000019 -#define avx10 0x00000024 // ecx = 0 -#define extended_proc_info 0x80000001 -#define hypervisor 0x40000000 -#define max_leaf 0x80000000 -#define brand1 0x80000002 -#define brand2 0x80000003 -#define brand3 0x80000004 -#define L1_cache 0x80000005 -#define L2_cache 0x80000006 -#define capabilities 0x80000007 -#define virtual 0x80000008 -#define svm 0x8000000A -#define enc_mem_cap 0x8000001F -#define ext_info2 0x80000021 -#define amd_easter_egg 0x8fffffff -#define centaur_ext 0xC0000000 -#define centaur_feature 0xC0000001 - -// index macros -#define eax 0 -#define ebx 1 -#define ecx 2 -#define edx 3 - -// cli flags -#define leaf_mode 1 -#define scan_mode 2 - -// miscellaneous -#define null_leaf 0xFF -#define breakpoint 10000000 - - - - - - - - - - -typedef struct { - void (*taskFunction)(void*); // Function pointer to the task - void* arg; // Argument to the task function -} Task; - -typedef struct { - pthread_t* threads; // Array of thread IDs - Task* taskQueue; // Array to hold tasks - int queueSize; // Size of the task queue - int nextTaskIndex; // Index to insert the next task - int shutdown; // Flag to indicate pool shutdown - pthread_mutex_t mutex; // Mutex for synchronization - pthread_cond_t condition; // Condition variable for task availability -} ThreadPool; - -// function executed by each thread in the pool -void* threadFunction(void* arg) { - ThreadPool* pool = (ThreadPool*)arg; - - while (1) { - pthread_mutex_lock(&pool->mutex); - - while (pool->nextTaskIndex == 0 && !pool->shutdown) { - pthread_cond_wait(&pool->condition, &pool->mutex); - } - - if (pool->shutdown) { - pthread_mutex_unlock(&pool->mutex); - pthread_exit(NULL); - } - - Task task = pool->taskQueue[--pool->nextTaskIndex]; - - pthread_mutex_unlock(&pool->mutex); - - task.taskFunction(task.arg); - } - - return NULL; -} - -// initialize the thread pool -ThreadPool* initializeThreadPool(int poolSize) { - ThreadPool* pool = (ThreadPool*)malloc(sizeof(ThreadPool)); - if (!pool) { - perror("Error creating thread pool"); - exit(EXIT_FAILURE); - } - - pool->threads = (pthread_t*)malloc(poolSize * sizeof(pthread_t)); - pool->taskQueue = (Task*)malloc(poolSize * sizeof(Task)); - pool->queueSize = poolSize; - pool->nextTaskIndex = 0; - pool->shutdown = 0; - - pthread_mutex_init(&pool->mutex, NULL); - pthread_cond_init(&pool->condition, NULL); - - for (int i = 0; i < poolSize; i++) { - if (pthread_create(&pool->threads[i], NULL, threadFunction, (void*)pool) != 0) { - perror("Error creating thread"); - exit(EXIT_FAILURE); - } - } - - return pool; -} - -// submit a task to the thread pool -void submitTask(ThreadPool* pool, void (*taskFunction)(void*), void* arg) { - pthread_mutex_lock(&pool->mutex); - - if (pool->nextTaskIndex == pool->queueSize) { - fprintf(stderr, "Task queue is full. Task not submitted.\n"); - pthread_mutex_unlock(&pool->mutex); - return; - } - - pool->taskQueue[pool->nextTaskIndex].taskFunction = taskFunction; - pool->taskQueue[pool->nextTaskIndex].arg = arg; - pool->nextTaskIndex++; - - pthread_cond_signal(&pool->condition); - - pthread_mutex_unlock(&pool->mutex); -} - -// shutdown the thread pool -void shutdownThreadPool(ThreadPool* pool) { - pthread_mutex_lock(&pool->mutex); - - pool->shutdown = 1; - - pthread_cond_broadcast(&pool->condition); - - pthread_mutex_unlock(&pool->mutex); - - for (int i = 0; i < pool->queueSize; i++) { - pthread_join(pool->threads[i], NULL); - } - - free(pool->threads); - free(pool->taskQueue); - free(pool); -} - - - -// basic cpuid wrapper -static void cpuid -( - uint32_t *x, - const uint64_t leaf, - const uint64_t subleaf -) { - #if (MSVC) - __cpuidex((int32_t*)x, (int32_t)(leaf), (int32_t)(subleaf)); - #elif (LINUX) - __cpuid_count(leaf, subleaf, x[0], x[1], x[2], x[3]); - #endif -}; - -// get highest eax leaf -static uint32_t get_highest_leaf() { - uint32_t reg[4]; - cpuid(reg, max_leaf, null_leaf); - return (reg[eax]); -} - -// scan for predetermined leafs -void leaf_mode_fuzzer(const uint64_t p_max_leaf) { - uint32_t reg[4]; - const uint32_t leafs[36] = { - manufacturer, proc_info, cache_tlb, - serial, topology, topology2, - management, extended, extended2, - extended3, xsave, xsave2 , - xsave3, xsave4, sgx, - sgx2, sgx3 , proc_trace, - aes, avx10, extended_proc_info, - hypervisor, max_leaf, brand1, - brand2, brand3, L1_cache, - L2_cache, capabilities, virtual, - svm, enc_mem_cap, ext_info2, - amd_easter_egg, centaur_ext, centaur_feature - }; - - const size_t leaf_arr_size = (sizeof(leafs) / sizeof(leafs[0])); - - for (int i = 0; i < leaf_arr_size; i++) { - if (leafs[i] >= p_max_leaf) { - continue; - } - - cpuid(reg, leafs[i], null_leaf); - - if (likely( - reg[eax] || \ - reg[ebx] || \ - reg[ecx] || \ - reg[edx] - )) { - printf("leaf = %d\n", i); - printf("eax = %d\n", reg[eax]); - printf("ebx = %d\n", reg[ebx]); - printf("ecx = %d\n", reg[ecx]); - printf("edx = %d\n\n", reg[edx]); - } - } -} - -/* -atomic_int counter; - -void scan_mode_worker() { - for (int i = 0; i < divisor; i++) { - int x = start; - - for (; x < limit; x++) { - uint32_t reg[4]; - cpuid(reg, x, null_leaf); - - if (unlikely( - reg[eax] || \ - reg[ebx] || \ - reg[ecx] || \ - reg[edx] - )) { - printf("leaf = %d\n", i); - printf("eax = %d\n", reg[eax]); - printf("ebx = %d\n", reg[ebx]); - printf("ecx = %d\n", reg[ecx]); - printf("edx = %d\n\n", reg[edx]); - fprintf(file, "%s\n", logMessage); - } - } - - const int percent = (((i + 1) * 100) / p_max_leaf); - - printf("[LOG] Reached eax leaf %d (%d%%)\n", atomic_load(&counter), percent); - limit += breakpoint; - start += breakpoint; - } - -} - -// scan mode fuzzer -void scan_mode_fuzzer(const uint64_t p_max_leaf, const int32_t thread_count) { - uint32_t limit = breakpoint; - uint32_t start = 0; - - const int32_t threads = get_nprocs(); - - const uint32_t divisor = (uint32_t)(p_max_leaf / breakpoint); - printf("divisor = %d\n", divisor); - - atomic_init(&counter, 0); - ThreadPool* pool = initializeThreadPool(8); - - // Submit example tasks to the thread pool - for (int i = 0; i < 10; i++) { - int* taskNumber = (int*)malloc(sizeof(int)); - *taskNumber = i; - submitTask(pool, exampleTask, (void*)taskNumber); - } - - // Sleep to allow tasks to complete - sleep(2); - - // Shutdown the thread pool - shutdownThreadPool(pool); -} - - -void exampleTask(void* low_bound, void* upper_bound) { - int taskNumber = *(int*)arg; - printf("Task %d executed by thread %lu\n", taskNumber, pthread_self()); -} -*/ - - -int main(int argc, char *argv[]) { - uint8_t flags = 0; - - if (argc == 1) { - flags |= leaf_mode; - } else if (argc == 2) { - if (strcmp(argv[2], "--leaf") == 0) { - flags |= leaf_mode; - } else if (strcmp(argv[2], "--scan") == 0) { - flags |= scan_mode; - } else { - printf("%s", "Unknown flag provided, aborting\n"); - return 1; - } - } else { - printf("%s", "Too many flags provided, only use either --leaf or --scan\n"); - return 1; - } - - const uint64_t high_leaf = get_highest_leaf(); - printf("highest leaf = 0x%0lX\n", high_leaf); - - - - if (flags & leaf_mode) { - leaf_mode_fuzzer(high_leaf); - } else if (flags & scan_mode) { - //scan_mode_fuzzer(high_leaf); - } else { - return 1; - } - - return 0; -} \ No newline at end of file diff --git a/src/vmaware.hpp b/src/vmaware.hpp index fe7ad2c..68a06fb 100644 --- a/src/vmaware.hpp +++ b/src/vmaware.hpp @@ -221,11 +221,11 @@ struct VM { // likely and unlikely macros #if (LINUX) - #define unlikely(x) __builtin_expect(!!(x), 0) - #define likely(x) __builtin_expect(!!(x), 1) + #define VMAWARE_UNLIKELY(x) __builtin_expect(!!(x), 0) + #define VMAWARE_LIKELY(x) __builtin_expect(!!(x), 1) #else - #define unlikely - #define likely + #define VMAWARE_UNLIKELY + #define VMAWARE_LIKELY #endif public: @@ -1100,8 +1100,8 @@ struct VM { if (found) { if (brand == qemu) { return add(QEMU); } - if (likely(brand == vmware)) { return add(VMWARE); } - if (likely(brand == vbox)) { return add(VBOX); } + if (VMAWARE_LIKELY(brand == vmware)) { return add(VMWARE); } + if (VMAWARE_LIKELY(brand == vbox)) { return add(VBOX); } if (brand == bhyve) { return add(BHYVE); } if (brand == kvm) { return add(KVM); } if (brand == hyperv) { return add(HYPERV); } @@ -4638,7 +4638,7 @@ struct VM { auto it = table.find(p_flag); - if (unlikely(it == table.end())) { + if (VMAWARE_UNLIKELY(it == table.end())) { throw_error("Flag is not known"); }