Windows C++ / bochs CPU oversights evasion

Author kernelwernel
Platform Windows
Language C++
Technique bochs CPU oversights evasion

Code

#if (defined(_MSC_VER) || defined(_WIN32) || defined(_WIN64) || defined(__MINGW32__))
#define MSVC 1
#define LINUX 0
#elif (defined(__linux__))
#define MSVC 0
#define LINUX 1
#else
#define MSVC 0
#define LINUX 0
#endif

#if (LINUX)
#include <cpuid.h>
#elif (MSVC)
#include <intrin.h>
#endif

#include <cstdint>
#include <array>
#include <iostream>
#include <regex>
#include <string>
#include <cstring>


// cross-platform wrapper function for linux and MSVC cpuid
void cpuid(
    std::uint32_t& a, 
    std::uint32_t& b, 
    std::uint32_t& c, 
    std::uint32_t& d,
    const std::uint32_t a_leaf
) {
#if (MSVC)
    std::int32_t x[4]{};
    __cpuid((std::int32_t*)x, static_cast<std::int32_t>(a_leaf));
    a = static_cast<std::uint32_t>(x[0]);
    b = static_cast<std::uint32_t>(x[1]);
    c = static_cast<std::uint32_t>(x[2]);
    d = static_cast<std::uint32_t>(x[3]);
#elif (LINUX)
    __get_cpuid(a_leaf, &a, &b, &c, &d);
#else
    return;
#endif
}

// checks if the leaf is high enough for the CPU to support
bool is_leaf_supported(const std::uint32_t p_leaf) {
    std::uint32_t eax, unused = 0;
    cpuid(eax, unused, unused, unused, 0x80000000);
    return (p_leaf <= eax);
};

// get the CPU product
std::string get_brand() {
    if (!is_leaf_supported(0x80000004)) {
        return "";
    }

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

    constexpr std::array<std::uint32_t, 3> ids = {{
        0x80000002,
        0x80000003,
        0x80000004
    }};

    std::string brand = "";

    for (const std::uint32_t& 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;
    }

    return brand;
}


// main bochs detection code
bool bochs_cpu() {
    std::uint32_t unused, ecx = 0;
    cpuid(unused, unused, ecx, unused, 0);

    constexpr std::uint32_t amd_ecx    = 0x444d4163; // "cAMD" (AMD)
    constexpr std::uint32_t intel_ecx1 = 0x6c65746e; // "ntel" (Intel)
    constexpr std::uint32_t intel_ecx2 = 0x6c65746f; // "otel" (Intel), this is because some Intel CPUs have a rare manufacturer string of "GenuineIotel"

    const bool intel = ((ecx == intel_ecx1) || (ecx == intel_ecx2));
    const bool amd = (ecx == amd_ecx);

    // if neither amd or intel, return false
    if (!(intel || amd)) {
        return false;
    }

    const std::string brand = get_brand();

    if (intel) {
        // technique 1: not a valid brand 
        if (brand == "              Intel(R) Pentium(R) 4 CPU        ") {
            return true;
        }
    } else if (amd) {
        // technique 2: "processor" should have a capital P
        if (brand == "AMD Athlon(tm) processor") {
            return true;
        }

        // technique 3: Check for absence of AMD easter egg for K7 and K8 CPUs
        constexpr std::uint32_t AMD_EASTER_EGG = 0x8fffffff; // this is the CPUID leaf of the AMD easter egg

        if (!is_leaf_supported(AMD_EASTER_EGG)) {
            return false;
        }

        std::uint32_t unused, eax = 0;
        cpuid(eax, unused, unused, unused, 1);

        constexpr std::uint8_t AMD_K7 = 6;
        constexpr std::uint8_t AMD_K8 = 15;

        auto is_k7 = [](const std::uint32_t eax) -> bool {
            const std::uint32_t family = (eax >> 8) & 0xF;
            const std::uint32_t model = (eax >> 4) & 0xF;
            const std::uint32_t extended_family = (eax >> 20) & 0xFF;

            if (family == 6 && extended_family == 0) {
                if (model == 1 || model == 2 || model == 3 || model == 4) {
                    return true;
                }
            }

            return false;
        };

        auto is_k8 = [](const std::uint32_t eax) -> bool {
            const std::uint32_t family = (eax >> 8) & 0xF;
            const std::uint32_t extended_family = (eax >> 20) & 0xFF;

            if (family == 0xF) {
                if (extended_family == 0x00 || extended_family == 0x01) {
                    return true;
                }
            }

            return false;
        };

        if (!(is_k7(eax) || is_k8(eax))) {
            return false;
        }

        std::uint32_t ecx_bochs = 0;
        cpuid(unused, unused, ecx_bochs, unused, AMD_EASTER_EGG);

        if (ecx_bochs == 0) {
            return true;
        }
    }

    return false;
}


int main() {
    std::cout << "is bochs present? = " << (bochs_cpu() ? "Yes, very likely a bochs emulated CPU\n" : "No, most likely baremetal\n");
    return 0;
}

Created

August 30, 2024

Last Revised

September 2, 2024