Skip to content

Commit ed184ea

Browse files
authored
Merge pull request #143 from kernelwernel/dev
Dev
2 parents 1a98569 + e26bc2c commit ed184ea

File tree

5 files changed

+543
-311
lines changed

5 files changed

+543
-311
lines changed

README.md

Lines changed: 70 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -98,32 +98,86 @@ You can view the full docs [here](docs/documentation.md). All the details such a
9898
<br>
9999

100100
## Q&A ❓
101-
- How does it work?
101+
102+
<details>
103+
<summary>How does it work?</summary>
104+
<br>
105+
102106
> It utilises a comprehensive list of low-level and high-level anti-VM techniques that gets accounted in a scoring system. The scores (0-100) for each technique are arbitrarily given, and every technique that has detected a VM will have their score added to a single accumulative point, where a threshold point number will decide whether it's actually running in a VM.
103107
104-
- Who is this library for and what are the use cases?
108+
</details>
109+
110+
<details>
111+
<summary>Who is this library for and what are the use cases?</summary>
112+
<br>
113+
105114
> It's designed for security researchers, VM engineers, gamer developers, and pretty much anybody who needs a practical and rock-solid VM detection mechanism in their project. For example, the library is suitable if you're making a VM and you're testing the effectiveness of concealing itself, if you're a game developer/proprietary software developer, the library is useful to thwart against reverse engineers. If you're a malware analyst and you want to check the concealment capability of your VM, this would be the perfect tool to benchmark how well-concealed your VM is against malware. Additionally, software could optimize performance or resource usage based on the detected environment, and some applications might want to restrict usage in VMs to prevent unauthorized distribution or testing.
106115
107-
- Why another VM detection project?
108-
> There's already loads of projects that have the same goal such as [InviZzzible](https://github.com/CheckPointSW/InviZzzible), [pafish](https://github.com/a0rtega/pafish) and [Al-Khaser](https://github.com/LordNoteworthy/al-khaser). But the difference between the aforementioned projects is that they don't provide a programmable interface to interact with the detection mechanisms, on top of having little to no support for non-Windows systems. I wanted the core detection techniques to be accessible programmatically in a cross-platform way for everybody to get something useful out of it rather than providing just a CLI tool. It also contains a larger quantity of techniques, so it's basically just a VM detection library and tool on steroids with maximum flexibility.
116+
</details>
117+
118+
<details>
119+
<summary>Why another VM detection project?</summary>
120+
<br>
121+
122+
> There's already loads of projects that have the same goal such as
123+
<a href="https://github.com/CheckPointSW/InviZzzible">InviZzzible</a>, <a href="https://github.com/a0rtega/pafish">pafish</a> and <a href="https://github.com/LordNoteworthy/al-khaser">Al-Khaser</a>. But the difference between the aforementioned projects is that they don't provide a programmable interface to interact with the detection mechanisms, on top of having little to no support for non-Windows systems. I wanted the core detection techniques to be accessible programmatically in a cross-platform way for everybody to get something useful out of it rather than providing just a CLI tool. It also contains a larger quantity of techniques, so it's basically just a VM detection library and tool on steroids with maximum flexibility.
109124
110-
- How can the library distinguish between Hyper-V artifacts and an actual Hyper-V VM in the system?
111-
> Hyper-V has an obscure feature where if it's enabled in the host system, the CPU hardware values makes it look like the whole system is running inside Hyper-V, which isn't true. This makes it a challenge to determine whether the hardware values the library is collecting is either a real Hyper-V VM, or just the artifacts of what Hyper-V has left as a consequence of having it enabled in the host system. The reason why this is a problem is because the library might falsely conclude that your the host system is running in Hyper-V, which is a false positive. This is where the **Hyper-X** mechanism comes into play to distinguish between these two. This was designed by [Requiem](https://github.com/NotRequiem)
112-
> <p align="center">
113-
> <img src="assets/Hyper-X.png" align="center" title="Hyper-X">
114-
> <br>
125+
</details>
115126

116-
- Is it possible to spoof the result?
117-
> 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 positive or even a false negative. 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.
118127

119-
- What about using this for malware?
128+
<details>
129+
<summary>How does it compare to paid VM detection libraries? Wouldn't it make it inferior for having it open source?</summary>
130+
<br>
131+
132+
> There are a few paid software to protect the licensing of other software against against reverse engineers or software cracking, such as <a href="https://docs.sentinel.thalesgroup.com/home.htm">Thales' Sentinel RMS</a> and <a href="https://vmpsoft.com/">VMProtect</a>. Although these are not meant to ONLY be VM detection libraries, they are limited in their capabilities in different ways. Sentinel RMS' VM detection does not have as many VM brands (not to mention the pricing is only meant for corporations, not individuals), and VMProtect has a <a href="https://cyber.wtf/2023/02/09/defeating-vmprotects-latest-tricks/">very limited number of detection techniques</a>, where some of them don't require a lot of effort to bypass with only a few configurations to the VM (the detection mechanism has also been <a href="https://github.com/jmpoep/vmprotect-3.5.1/blob/d8fcb7c0ffd4fb45a8cfbd770c8b117d7dbe52b5/runtime/loader.cc#L2464">leaked</a>, so there's no benefit of having it closed source now). Speaking of which, the only downside to VMAware is that it's fully open source, which makes the job of bypassers easier compared to having it closed source. However, I'd argue that's a worthy tradeoff by having as many VM detection techniques in an open and interactive way, including having valuable community feedback to make the library more effective and accurate.
133+
134+
</details>
135+
136+
137+
<details>
138+
<summary>How can the library distinguish between Hyper-V artifacts and an actual Hyper-V VM in the system?</summary>
139+
<br>
140+
141+
> Hyper-V has an obscure feature where if it's enabled in the host system, the CPU hardware values makes it look like the whole system is running inside Hyper-V, which isn't true. This makes it a challenge to determine whether the hardware values the library is collecting is either a real Hyper-V VM, or just the artifacts of what Hyper-V has left as a consequence of having it enabled in the host system. The reason why this is a problem is because the library might falsely conclude that your the host system is running in Hyper-V, which is a false positive. This is where the **Hyper-X** mechanism comes into play to distinguish between these two. This was designed by <a href="https://github.com/NotRequiem">Requiem</a>
142+
143+
<p align="center">
144+
<img src="assets/Hyper-X.png" align="center" title="Hyper-X">
145+
<br>
146+
</details>
147+
148+
149+
<details>
150+
<summary>Is it possible to spoof the result?</summary>
151+
<br>
152+
153+
> 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 positive or even a false negative. This is a problem that every VM detection project is facing whether closed or open source, 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. Remember, EVERYTHING is technically spoofable.
154+
155+
</details>
156+
157+
<details>
158+
<summary>What about using this for malware?</summary>
159+
<br>
160+
120161
> 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 and nothing is obfuscated to begin with. Good fucking luck obfuscating 10k+ lines of C++ code lmao.
121162
122-
- Why GPL 3.0 and MIT?
123-
> I would've made it strictly MIT so proprietary software can make use of the library, but some of the techniques employed are from GPL 3.0 projects, and I have no choice but to use the same license for legal reasons. This gave me an idea to make an MIT version without all of the GPL code so it can also be used without forcing your code to be open-source. It should be noted that the MIT version removes **12** techniques out of 114 (as of 1.8 version), and the lesser the number of mechanisms, the less accurate the overall result might be.
163+
</details>
164+
165+
<details>
166+
<summary>Why GPL 3.0 and MIT?</summary>
167+
<br>
168+
169+
> I would've made it strictly MIT so proprietary software can make use of the library, but some of the techniques employed are from GPL 3.0 projects, and I have no choice but to use the same license for legal reasons. This gave me an idea to make an MIT version without all of the GPL code so it can also be used without forcing your code to be open-source. It should be noted that the MIT version removes <b>12</b> techniques out of 114 (as of 1.8 version), and the lesser the number of mechanisms, the less accurate the overall result might be.
170+
171+
</details>
172+
173+
<details>
174+
<summary>I have linker errors when compiling</summary>
175+
<br>
176+
177+
> If you're compiling with gcc or clang, add the <code>-lm</code> and <code>-lstdc++</code> flags, or use g++/clang++ compilers instead. If you're receiving linker errors from a brand new VM environment on Linux, update your system with `sudo apt/dnf/yum update -y` to install the necessary C++ components.
178+
179+
</details>
124180

125-
- I have linker errors when compiling
126-
> If you're compiling with gcc or clang, add the `-lm` and `-lstdc++` flags, or use g++/clang++ compilers instead. If you're receiving linker errors from a brand new VM environment on Linux, update your system with `sudo apt/dnf/yum update -y` to install the necessary C++ components.
127181

128182
<br>
129183

TODO.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@
4545
- [ ] check if bios date in /sys/class/dmi/id/ could be useful under QEMU
4646
- [ ] make the cli demo in the readme for the 1.8 version
4747
- [ ] fix the percentage thing for the disabled techniques
48+
- [ ] adopt the firmware technique from the vmprotect source code leak
49+
- [ ] update the Hyper-X graph with the cpu manufacturer part
4850

4951
# Distant plans
5052
- add the library to conan.io when released

src/cli.cpp

Lines changed: 31 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@
4343

4444
#include "vmaware.hpp"
4545

46-
constexpr const char* ver = "1.8";
47-
constexpr const char* date = "August 2024";
46+
constexpr const char* ver = "1.9";
47+
constexpr const char* date = "September 2024";
4848

4949
constexpr const char* bold = "\033[1m";
5050
constexpr const char* ansi_exit = "\x1B[0m";
@@ -73,6 +73,7 @@ enum arg_enum : std::uint8_t {
7373
};
7474

7575
std::bitset<14> arg_bitset;
76+
const std::uint8_t max_bits = static_cast<std::uint8_t>(VM::MULTIPLE) + 1;
7677

7778
#if (MSVC)
7879
class win_ansi_enabler_t
@@ -131,8 +132,8 @@ R"(Usage:
131132
-t | --type returns the VM type (if a VM was found)
132133
133134
Extra:
134-
--disable-notes no notes will be provided
135-
--spoofable allow spoofable techniques to be ran (not included by default)
135+
--disable-notes no notes will be provided
136+
--spoofable allow spoofable techniques to be ran (not included by default)
136137
137138
)";
138139
std::exit(0);
@@ -250,6 +251,7 @@ AWS Nitro System (KVM-based)
250251
Podman
251252
WSL
252253
OpenVZ
254+
ANY.RUN
253255
)";
254256

255257
std::exit(0);
@@ -305,6 +307,7 @@ std::string type(const std::string &brand_str) {
305307
{ "Anubis", "Sandbox" },
306308
{ "Comodo", "Sandbox" },
307309
{ "ThreatExpert", "Sandbox" },
310+
{ "ANY.RUN", "Sandbox"},
308311

309312
// misc
310313
{ "Bochs", "Emulator" },
@@ -323,7 +326,7 @@ std::string type(const std::string &brand_str) {
323326
{ "Hyper-V artifact (not an actual VM)", "No VM" },
324327
{ "User-mode Linux", "Paravirtualised" },
325328
{ "WSL", "Hybrid Hyper-V (type 1 and 2)" }, // debatable tbh
326-
{ "Apple Rosetta 2", "Binary Translation Layer/Emulator" }
329+
{ "Apple Rosetta 2", "Binary Translation Layer/Emulator" },
327330
};
328331

329332
auto it = type_table.find(brand_str);
@@ -438,6 +441,22 @@ bool is_disabled(const VM::enum_flags flag) {
438441
}
439442

440443

444+
std::bitset<max_bits> settings() {
445+
std::bitset<max_bits> tmp;
446+
447+
if (arg_bitset.test(SPOOFABLE)) {
448+
tmp.set(VM::SPOOFABLE);
449+
}
450+
451+
if (arg_bitset.test(ALL)) {
452+
tmp |= VM::ALL;
453+
tmp.set(VM::SPOOFABLE);
454+
}
455+
456+
return tmp;
457+
}
458+
459+
441460
void general() {
442461
const std::string detected = ("[ " + std::string(green) + "DETECTED" + std::string(ansi_exit) + " ]");
443462
const std::string not_detected = ("[" + std::string(red) + "NOT DETECTED" + std::string(ansi_exit) + "]");
@@ -476,20 +495,13 @@ void general() {
476495
};
477496

478497
bool notes_enabled = false;
479-
VM::enum_flags spoofable_setting;
480498

481499
if (arg_bitset.test(NOTES)) {
482500
notes_enabled = false;
483501
} else {
484502
notes_enabled = true;
485503
}
486504

487-
if (arg_bitset.test(SPOOFABLE)) {
488-
spoofable_setting = VM::SPOOFABLE;
489-
} else {
490-
spoofable_setting = VM::NULL_ARG;
491-
}
492-
493505
#if (LINUX)
494506
if (notes_enabled && !is_admin()) {
495507
std::cout << note << " Running under root might give better results\n";
@@ -519,7 +531,7 @@ void general() {
519531
checker(VM::DLL, "DLLs");
520532
checker(VM::REGISTRY, "registry");
521533
checker(VM::CWSANDBOX_VM, "Sunbelt CWSandbox directory");
522-
//checker(VM::WINE_CHECK, "Wine");
534+
checker(VM::WINE_CHECK, "Wine");
523535
checker(VM::VM_FILES, "VM files");
524536
checker(VM::HWMODEL, "hw.model");
525537
checker(VM::DISK_SIZE, "disk size");
@@ -530,7 +542,7 @@ void general() {
530542
checker(VM::MEMORY, "low memory space");
531543
checker(VM::VM_PROCESSES, "VM processes");
532544
checker(VM::LINUX_USER_HOST, "default Linux user/host");
533-
//checker(VM::VBOX_WINDOW_CLASS, "VBox window class");
545+
checker(VM::VBOX_WINDOW_CLASS, "VBox window class");
534546
checker(VM::GAMARUE, "gamarue ransomware technique");
535547
checker(VM::VMID_0X4, "0x4 leaf of VMID");
536548
checker(VM::PARALLELS_VM, "Parallels techniques");
@@ -610,14 +622,16 @@ void general() {
610622
checker(VM::SMBIOS_VM_BIT, "SMBIOS VM bit");
611623
checker(VM::PODMAN_FILE, "Podman file");
612624
checker(VM::WSL_PROC, "WSL string in /proc");
625+
checker(VM::ANYRUN_DRIVER, "ANY.RUN driver");
626+
checker(VM::ANYRUN_DIRECTORY, "ANY.RUN directory");
613627

614628
std::printf("\n");
615629

616630
#ifdef __VMAWARE_DEBUG__
617631
std::cout << "[DEBUG] theoretical maximum points: " << VM::total_points << "\n";
618632
#endif
619633

620-
std::string brand = VM::brand(VM::MULTIPLE, spoofable_setting);
634+
std::string brand = VM::brand(VM::MULTIPLE, settings());
621635

622636
std::cout << "VM brand: " << ((brand == "Unknown") || (brand == "Hyper-V artifact (not an actual VM)") ? red : green) << brand << ansi_exit << "\n";
623637

@@ -639,7 +653,7 @@ void general() {
639653
}
640654

641655
const char* percent_color = "";
642-
const std::uint8_t percent = VM::percentage(spoofable_setting);
656+
const std::uint8_t percent = VM::percentage(settings());
643657

644658
if (percent == 0) { percent_color = red; }
645659
else if (percent < 25) { percent_color = red_orange; }
@@ -649,7 +663,7 @@ void general() {
649663

650664
std::cout << "VM likeliness: " << percent_color << static_cast<std::uint32_t>(percent) << "%" << ansi_exit << "\n";
651665

652-
const bool is_detected = VM::detect(spoofable_setting);
666+
const bool is_detected = VM::detect(settings());
653667

654668
std::cout << "VM confirmation: " << (is_detected ? green : red) << std::boolalpha << is_detected << std::noboolalpha << ansi_exit << "\n";
655669

@@ -803,25 +817,6 @@ int main(int argc, char* argv[]) {
803817
std::cerr << "--stdout, --percent, --detect, --brand, --type, and --conclusion must NOT be a combination, choose only a single one\n";
804818
return 1;
805819
}
806-
807-
const std::uint8_t max_bits = static_cast<std::uint8_t>(VM::MULTIPLE) + 1;
808-
809-
auto settings = [&]() -> std::bitset<max_bits> {
810-
std::bitset<max_bits> setting_bits;
811-
812-
if (arg_bitset.test(SPOOFABLE)) {
813-
setting_bits.set(VM::SPOOFABLE);
814-
}
815-
816-
if (arg_bitset.test(ALL)) {
817-
setting_bits |= VM::ALL;
818-
setting_bits.set(VM::SPOOFABLE);
819-
}
820-
821-
setting_bits.set(NULL_ARG);
822-
823-
return setting_bits;
824-
};
825820

826821
if (arg_bitset.test(STDOUT)) {
827822
return (!VM::detect(VM::NO_MEMO, settings()));

0 commit comments

Comments
 (0)