Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Disabling detection flag types in VM::detect() is not functioning (detections run anyways) #276

Open
ConnorBP opened this issue Feb 20, 2025 · 5 comments

Comments

@ConnorBP
Copy link

ConnorBP commented Feb 20, 2025

While trying to disable some faulty detections (different issue I will also make), I noticed that the users were still being flagged and that the detections were still running even though I have disabled them in the arguments to VM::detect().

Here is a minimal example to reproduce the issue:

// includes code for detecting thread counts from: https://github.com/llvm/llvm-project/issues/72267

#include <Windows.h>
#include "vmaware.hpp"
#include <iostream>
#include <thread>
#include <string>

// modified my function
size_t MyHardwareConcurrency() noexcept
{
	DWORD length = 0;
	size_t concurrency = 0;
	const auto validConcurrency = [&concurrency]() noexcept -> size_t {
		if (concurrency == 0)
			return std::thread::hardware_concurrency();
		else
			return concurrency;
	};
	if (GetLogicalProcessorInformationEx(RelationAll, nullptr, &length) != FALSE) {
		return validConcurrency();
	}
	if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
		return validConcurrency();
	}
	std::unique_ptr<void, void (*)(void*)> buffer(std::malloc(length), std::free);
	if (!buffer) {
		return validConcurrency();
	}
	auto* mem = reinterpret_cast<unsigned char*>(buffer.get());
	if (GetLogicalProcessorInformationEx(
		RelationAll, reinterpret_cast<PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX>(mem), &length) == false) {
		return validConcurrency();
	}
	DWORD i = 0;
	while (i < length) {
		const auto* proc = reinterpret_cast<PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX>(mem + i);
		if (proc->Relationship == RelationProcessorCore) {
			for (WORD group = 0; group < proc->Processor.GroupCount; ++group) {
				for (KAFFINITY mask = proc->Processor.GroupMask[group].Mask; mask != 0; mask >>= 1) {
					concurrency += mask & 1;
				}
			}
		}
		i += proc->Size;
	}
	return validConcurrency();
}

uint32_t get_thread_count()
{
	static const uint32_t g_count = []()
	{
#ifdef _WIN32
		::SYSTEM_INFO sysInfo;
		::GetNativeSystemInfo(&sysInfo);
		return sysInfo.dwNumberOfProcessors;
#else
		return ::sysconf(_SC_NPROCESSORS_ONLN);
#endif
	}();

	return g_count;
}

// in llvm-project/libcxx/src/thread.cpp
DWORD getWin32Count()
{
	SYSTEM_INFO info;
	GetSystemInfo(&info);
	return info.dwNumberOfProcessors;
}

// this outputs some diagnostic information
void PrintCpuInfo() {
	bool vmDetected = VM::detect(VM::DEFAULT, VM::DISABLE(VM::TIMER, VM::POWER_CAPABILITIES, VM::SETUPAPI_DISK, VM::INTEL_THREAD_MISMATCH));
	//bool vmDetected = VM::detect();
	std::cout << "detected vm? " << (vmDetected ? "YES" : "NO") << std::endl;

	// loop through the technique table, where all the techniques are stored
	for (const uint8_t tmp : VM::technique_vector) {

		// check if the technique is cached already
		if (VM::memo::is_cached(tmp)) {
			const VM::memo::data_t data = VM::memo::cache_fetch(tmp);

			if (data.result) {
				std::cout << "detected with check: " << VM::flag_to_string((VM::enum_flags)tmp) << std::endl;
			}

			continue;
		}
	}

	auto cpu = VM::cpu::get_model();
	std::cout << "threads: " << std::thread::hardware_concurrency() << " cpu: " << cpu.string << std::endl;
	std::cout << MyHardwareConcurrency() << '\n';
	std::cout << get_thread_count() << '\n';
	std::cout << getWin32Count() << '\n';

	std::cout << "press enter to continue..." << std::endl;
	getchar();
}

Output on my Ryzen 7 7700X 8-Core machine:

detected vm? YES
detected with check: TIMER
detected with check: POWER_CAPABILITIES
threads: 16 cpu:
16
16
16
press enter to continue...

As you can see, both TIMER and POWER_CAPABILITIES run and flag me regardless of me disabling them explicitly.

Additionally core count seems to return threads and not physical cores it seems. But that's a separate issue.

Side note: it would be nice if the api exposed a flag emum static or getter which accumulates all flag types which have been detected so far. It would make diagnostics and detection reports significantly easier.

@kernelwernel
Copy link
Owner

I see the issue now, the problem is that you're adding VM::DEFAULT to the argument list while disabling techniques. It should be fixed if you remove the VM::DEFAULT argument, but that's definitely an oversight on my part. I'll fix that very soon.

Side note: it would be nice if the api exposed a flag emum static or getter which accumulates all flag types which have been detected so far. It would make diagnostics and detection reports significantly easier.

Would you prefer if I added a new function called VM::detected_enums()? It'll return a std::vector of enums to determine what was detected.

@kernelwernel
Copy link
Owner

Also I'm not entirely sure why you have VM::memo::is_cached() available, I'm pretty sure that should be private for internal purposes...

@ConnorBP
Copy link
Author

Here is the document that showcases including default alongside disable: https://github.com/kernelwernel/VMAware/blob/main/docs/documentation.md#vmdetect

It would seem intuitive that it works this way where it would AND the flags of the disable parameter, but it OR's them instead.

@ConnorBP
Copy link
Author

Also I'm not entirely sure why you have VM::memo::is_cached() available, I'm pretty sure that should be private for internal purposes...

That was the best way i could find on the current API to get a list of exactly which checks failed.

prefer if I added a new function called VM::detected_enums()? It'll return a std::vector of enums to determine what was detected.

That would be perfect

@kernelwernel
Copy link
Owner

Also I'm not entirely sure why you have VM::memo::is_cached() available, I'm pretty sure that should be private for internal purposes...

That was the best way i could find on the current API to get a list of exactly which checks failed.

    // this will loop through all the enums in the technique_vector variable,
    // and then checks each of them and outputs the enum that was detected
    for (const auto technique_enum : VM::technique_vector) {
        if (VM::check(technique_enum)) {
            const std::string name = VM::flag_to_string(technique_enum);
            std::cout << "VM::" << name << " was detected\n";
        }
    }

This could work fine, I'm 99% sure there shouldn't be any hickups in this code but you can use this if it's convenient :)

prefer if I added a new function called VM::detected_enums()? It'll return a std::vector of enums to determine what was detected.

That would be perfect

Sounds good, I'll let you know when it's complete. Shouldn't take more than a day.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants