Skip to content

Commit

Permalink
Merge pull request #17 from kernelwernel/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
kernelwernel authored Dec 26, 2023
2 parents 8cd212c + e5eeb57 commit d421db0
Show file tree
Hide file tree
Showing 11 changed files with 945 additions and 254 deletions.
8 changes: 3 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,13 @@ vmaware
tmp
test
tmp.cpp
src/untested.cpp
src/vmtest.cpp
resources/
archive/
.vscode/
build/
milestones.md
bin/
src/*.asm
src/*.cu
notes.md
*.pdf
private/
resources/
archive.cpp
3 changes: 2 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ project(


# compiler flags
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

Expand Down Expand Up @@ -87,6 +87,7 @@ add_test(
)
endif()


# release stuff
if (NOT MSVC)
if(CMAKE_BUILD_TYPE MATCHES "Release")
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ To add your own function, follow the format below:
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!
#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

Expand Down
23 changes: 18 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@

**VMAware** (not to be confused with VMware) is an open-source, cross-platform, and incredibly simple C++ library for virtual machine detection.

It utilises a comprehensive list of low-level and high-level anti-VM techniques that gets accounted in a scoring system. The library is meant to be stupidly easy to use, designed for anybody wanting to integrate the library to their project.
It utilises a comprehensive list of low-level and high-level anti-VM techniques that gets accounted in a scoring system. The library is meant to be stupidly easy to use, designed for anybody wanting to integrate the library to their project without a hassle.

The library is:
- Very easy to use, with only 3 functions in its public interface
- Very flexible, with total fine-grained control
- Cross-platform **(NOTE: MACOS AND MSVC ARE NOT EFFECTIVE FOR NOW)**
- Very easy to use, with only 4 functions in its public interface
- Very flexible, with total fine-grained control over what gets executed
- Cross-platform **(NOTE: MACOS AND WINDOWS ARE NOT EFFECTIVE FOR NOW)**
- Header-only
- Available with C++11 and above
- Features up to 50+ techniques
- Able to detect VMware, VirtualBox, QEMU, KVM, Parallels, and much more
- Able to detect semi-VM technologies like hypervisors, docker, and wine
- Able to guess the VM brand
Expand All @@ -26,8 +27,11 @@ The library is:

**IMPORTANT:** The library is currently a beta, so more improvements and cross-compatibility fixes are planned (especially for Windows which I'm currently working on improving). I don't recommend using this for any serious projects for now.

Also, this library doesn't guarantee it'll be accurate. If you found a false negative then please create an issue with information on what your VM is, what OS you're using, and what .

- - -

<br>

## Example 🧪
```cpp
Expand All @@ -38,18 +42,21 @@ int main() {
if (VM::detect()) {
std::cout << "Virtual machine detected!" << std::endl;
std::cout << "VM name: " << VM::brand() << std::endl;
std::cout << "VM certainty: " << VM::percentage() << "%" << std::endl;
} else {
std::cout << "Running in baremetal" << std::endl;
}
}
```

<br>

## CLI tool 🔧
This project also provides a tiny, but handy CLI tool utilising the full potential of what the library can do. Also, running the CLI as root would give better results.

<img src="assets/image.png" width="500" title="cli">

<br>

## Installation 📥
To install the library, download or copy paste the `vmaware.hpp` file in the [release section](https://github.com/kernelwernel/VMAware/releases/) to your project. No CMake or shared object linkages are necessary, it's literally that simple.
Expand All @@ -74,10 +81,12 @@ cmake -S . -B build/ -G "Visual Studio 16 2019"
```
> NOTE: I'm most likely going to change my username in the future. If the github link doesn't exist, search for the VMAware project and you should find it.
<br>

## Documentation 📒
You can view the full docs [here](docs/documentation.md). Trust me, it's not too intimidating.

<br>

## Q&A ❓
- Who is this library for?
Expand All @@ -90,11 +99,12 @@ You can view the full docs [here](docs/documentation.md). Trust me, it's not too
> Yes. There are some techniques that are trivially spoofable, and there's nothing the library can do about it whether it's a deliberate false negative or even a false positive. This is a problem that every VM detection project is facing, which is why the library is trying to test every technique possible to get the best result based on the environment it's running under.
- Can I use this for malware?
> This project is not soliciting the development of malware for any malicious intentions. Even if you intend to use it that way, it'll most likely be flagged by antiviruses anyway.
> This project is not soliciting the development of malware for obvious reasons. Even if you intend to use it for concealment purposes, it'll most likely be flagged by antiviruses anyway.
- When will a 1.0 be available?
> Pretty soon, maybe around january 2024 (I just started university, so I can't guarantee anything)
<br>

## Issues and pull requests 📬
If you have any suggestions, ideas, or any sort of contribution, feel free to ask! I'll be more than happy to discuss. If you want to personally ask something in private, my discord is `kr.nl`
Expand All @@ -103,6 +113,7 @@ Contribution guidelines can be found [here](CONTRIBUTING.md).

If you found this project useful, a star would be appreciated :)

<br>

## Credits ✒️
- [Check Point Research](https://research.checkpoint.com/)
Expand All @@ -112,7 +123,9 @@ If you found this project useful, a star would be appreciated :)
- [Matteo Malvica](https://www.matteomalvica.com)
- N. Rin, EP_X0FF
- [Peter Ferrie, Symantec](https://github.com/peterferrie)
- [Graham Sutherland, LRQA Nettitude](https://www.nettitude.com/uk/)

<br>

## Legal 📜
I am not responsible nor liable for any damage you cause through any malicious usage of this project.
Expand Down
11 changes: 8 additions & 3 deletions TODO.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
- [ ] revise sidt check
- [ ] analyse the UUID check technique's efficiency
- [ ] fix c++11 debug function param error
- [X] fix c++11 debug function param error
- [ ] completely remove std::system() grep commands
- [ ] add github metrics for license (looks cool af)
- [ ] add CTest before alpha release
- [X] add CTest before alpha release
- [ ] maybe add a threadpool
- [ ] test for processor type and stuff like that for cpuid EAX=1
- [ ] grep everywhere for QEMU shit
- [ ]
- [ ] implement VM::EXTREME flag
- [ ] update the cli tool image in the readme
- [X] focus on hyperv detection (https://labs.nettitude.com/blog/vm-detection-tricks-part-3-hyper-v-raw-network-protocol/)
- [X] add percentage overload functionality for VM::detect()
- [ ] maybe document the project in codeproject.com when a 1.0 is ready


# Distant plans
- add ARM support
Expand Down
16 changes: 8 additions & 8 deletions cmake/ctest_checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,14 +113,16 @@ def check_docs(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)

if all_elements_have_pair:
return
else:
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:
Expand All @@ -147,16 +149,14 @@ def check_cli(flag_array):
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 all_elements_have_pair:
return
else:
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)
Expand Down
83 changes: 68 additions & 15 deletions docs/documentation.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
# Documentation
# `VM::detect()`

This is basically the only thing you need, which returns a bool. If the parameter is set to default, all the recommended checks will be performed. But you can optionally set what techniques are used:
This is basically the main function you're looking for, which returns a bool. If the parameter is set to nothing, all the <ins>recommended</ins> checks will be performed. But you can optionally set what techniques are used.

```cpp
#include "vmaware.hpp"

int main() {
/**
* The basic way to detect a VM where most checks will be
Expand All @@ -16,7 +18,8 @@ int main() {
* Essentially means only the brand, MAC, and hypervisor bit techniques
* should be performed. Note that the less flags you provide, the more
* likely the result will not be accurate. If you just want to check for
* a single technique, use VM::check() instead.
* a single technique, use VM::check() instead. Also, read the flag table
* at the end of this doc file for a full list of technique flags.
*/
bool is_vm2 = VM::detect(VM::BRAND | VM::MAC | VM::HYPERV_BIT);

Expand All @@ -37,13 +40,22 @@ int main() {
* Keep in mind that this could take a performance hit.
*/
bool is_vm4 = VM::detect(VM::ALL | VM::NO_MEMO);


/**
* If you want to treat any technique that was detected as positive,
* you can enable the VM::EXTREME flag which will return true if any
* technique has detected a hit despite the certainty score. This is
* not recommended for obvious reasons.
*/
bool is_vm5 = VM::detect(VM::EXTREME);
}
```

<br>

# `VM::brand()`
This will essentially return the VM brand as a `std::string`. The brand string return values are:
This will essentially return the VM brand as a `std::string`. The exact possible brand string return values are:
- `VMware`
- `VirtualBox`
- `KVM`
Expand All @@ -70,9 +82,12 @@ This will essentially return the VM brand as a `std::string`. The brand string r
- `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 for critical operations as if it's your golden bullet. 75% of the time it'll simply return `Unknown`.
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 dependent on what mechanisms detected a VM. Don't rely on this function for critical operations as if it's your golden bullet. Roughly 75% of the time it'll simply return `Unknown`.

```cpp
#include "vmaware.hpp"
#include <string>

int main() {
const std::string result = VM::brand();

Expand All @@ -94,16 +109,48 @@ This takes a single flag argument and returns a `bool`. It's essentially the sam
`VM::detect()` is meant for a range of techniques to be evaluated in the bigger picture with weights and biases in its scoring system, while `VM::check()` is meant for a single technique to be evaluated without any points or anything extra. It just gives you what the technique has found on its own. For example:

```cpp
if (VM::check(VM::VMID)) {
std::cout << "VMID technique detected a VM!\n";
}
#include "vmaware.hpp"
#include <iostream>

int main() {
if (VM::check(VM::VMID)) {
std::cout << "VMID technique detected a VM!\n";
}

if (VM::check(VM::HYPERVISOR_BIT)) {
std::cout << "Hypervisor bit is set, most definitely a VM!\n";
}

if (VM::check(VM::HYPERV_BIT)) {
std::cout << "Hypervisor bit is set, most definitely a VM!\n";
// invalid, will throw an std::invalid_argument exception
bool result = VM::check(VM::VMID | VM::HYPERVISOR_BIT;
}
```

<br>

# `VM::percentage()`
This will return a std::uint8_t between 0 and 100. It'll return the certainty of whether it has detected a VM based on all the techniques available as a percentage. The lower the value, the less chance it's a VM. The higher the value, the more likely chance it is. The parameters are treated the exact same way with the VM::detect() function.

```cpp
#include "vmaware.hpp"
#include <iostream>
#include <cstdint>

// invalid
bool result = VM::check(VM::SIDT | VM::RDTSC);
int main() {
// uint8_t and unsigned char works too
const std::uint8_t percent = VM::percentage();

if (percent == 100) {
std::cout << "Definitely a VM!\n";
} else if (percent == 0) {
std::cout << "Definitely NOT a VM";
} else {
std::cout << "Unsure if it's a VM";
}

// converted to std::uint32_t for console character encoding reasons
std::cout << "percentage: " << static_cast<std::uint32_t>(percent) << "%\n";
}
```

<br>
Expand All @@ -116,9 +163,9 @@ VMAware provides a convenient way to not only check for VMs, but also have the f
| ---------- | ----------- | --------------- | --------- | -------------- |
| `VM::VMID` | Check if the CPU manufacturer ID matches that of a VM brand | Yes | 100% | |
| `VM::BRAND` | Check if the CPU brand string contains any indications of VM keywords | Yes | 50% | |
| `VM::HYPERV_BIT` | Check if the hypervisor bit is set (always false on physical CPUs) | Yes | 95% | |
| `VM::HYPERVISOR_BIT` | Check if the hypervisor bit is set (always false on physical CPUs) | Yes | 95% | |
|`VM::CPUID_0X4` | Check if there are any leaf values between 0x40000000 and 0x400000FF that changes the CPUID output | Yes | 70% | |
| `VM::HYPERV_STR` | Check if brand string length is long enough (would be around 2 characters in a host machine while it's longer in a hypervisor) | Yes | 45% | |
| `VM::HYPERVISOR_STR` | Check if brand string length is long enough (would be around 2 characters in a host machine while it's longer in a hypervisor) | Yes | 45% | |
| `VM::RDTSC` | Benchmark RDTSC and evaluate its speed, usually it's very slow in VMs | Linux and Windows | 20% | |
| `VM::SIDT` | Check if SIDT instructions does anything to the interrupt descriptor table | Linux | 65% | |
| `VM::SIDT5` | Check if the 5th byte after sidt is null | Linux | 45% | |
Expand Down Expand Up @@ -153,7 +200,7 @@ VMAware provides a convenient way to not only check for VMs, but also have the f
| `VM::VM_PROCESSES` | Check for any VM processes that are active | Windows | 30% | |
| `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::WMIC` | Check top-level default window level | Windows | 20% | |
| `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% | |
Expand All @@ -163,11 +210,17 @@ VMAware provides a convenient way to not only check for VMs, but also have the f
| `VM::QEMU_BRAND` | Check for QEMU CPU brand with cpuid | Yes | 100% | |
| `VM::BOCHS_CPU` | Check for Bochs cpuid emulation oversights | Yes | 95% | |
| `VM::VPC_BOARD` | Check for VPC specific string in motherboard manufacturer | Windows | 20% | |
| `VM::HYPERV_WMI` | Check for Hyper-V wmi output | Windows | 80% | |
| `VM::HYPERV_REG` | Check for Hyper-V strings in registry | Windows | 80% | |
| `VM::BIOS_SERIAL` | Check if BIOS serial number is null | Windows | 60% | |
| `VM::VBOX_FOLDERS` | Check for VirtualBox-specific string for shared folder ID | Windows | 45% | |
| `VM::VBOX_MSSMBIOS` | Check VirtualBox MSSMBIOS registry for VM-specific strings | Windows | 75% | |

<br>

# Non-technique flags
| Flag | Description |
|------|-------------|
| `VM::ALL` | This will enable all the technique flags, including the cursor check. |
| `VM::ALL` | This will enable all the technique flags, including the cursor check that's disabled by default. |
| `VM::NO_MEMO` | This will disable memoization, meaning the result will not be fetched through a previous computation of the VM::detect() function. Note that this can take a performance hit. |
| `VM::EXTREME` | This will disregard the weights/biases and its scoring system. It will essentially treat any technique that found a hit as a VM detection no matter how low that technique's certainty is, so if a single technique is positive then it will return true. |
Binary file added papers/attacks_on_VMs.pdf
Binary file not shown.
Binary file added papers/vmde.pdf
Binary file not shown.
Loading

0 comments on commit d421db0

Please sign in to comment.