Skip to content

Commit

Permalink
added UML and KMSG techniques
Browse files Browse the repository at this point in the history
  • Loading branch information
kernelwernel committed Aug 14, 2024
1 parent c4bd6d1 commit a7a392a
Show file tree
Hide file tree
Showing 6 changed files with 314 additions and 5 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ If you know a project, or if you're working on a project that uses VMAware, let
- mrjaxser
- [iMonket](https://github.com/PrimeMonket)
- Eric Parker's discord community
- [ShellCode33](https://github.com/ShellCode33)

<br>

Expand Down
1 change: 1 addition & 0 deletions docs/documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ This will essentially return the VM brand as a `std::string`. The exact possible
- `Xbox NanoVisor (Hyper-V)`
- `SimpleVisor`
- `Hyper-V artifact (not an actual VM)`
- `User-mode Linux`

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. This is especially true for VMware sub-versions (ESX, GSX, Fusion, etc...) Don't rely on this function for critical operations as if it's your golden bullet. It's arguably unreliable and it'll most likely return `Unknown` (assuming it is actually running under a VM).

Expand Down
6 changes: 5 additions & 1 deletion release_notes.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
- completely fixed all false positives due to Hyper-V artifacts with new Hyper-X mechanism
- completely fixed all false positives due to Hyper-V artifacts with new "Hyper-X" mechanism designed by @NotRequiem
<p align="center">
<img src="assets/Hyper-X.png" align="center" title="VMAware">
<br>
Expand All @@ -7,3 +7,7 @@
<br>

- added new VM brand `Hyper-V artifacts (not an actual VM)`
- added 3 new techniques:
- `VM::QEMU_VIRTUAL_DMI`
- `VM::QEMU_USB`
- `VM::HYPERVISOR_DIR`
7 changes: 6 additions & 1 deletion src/cli.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ Microsoft Azure Hyper-V
Xbox NanoVisor (Hyper-V)
SimpleVisor
Hyper-V artifact (not an actual VM)
User-mode Linux
)";

std::exit(0);
Expand Down Expand Up @@ -285,6 +286,7 @@ std::string type(const std::string &brand_str) {
{ "Virtual Apple", "Hypervisor (type 2)" },
{ "NetBSD NVMM", "Hypervisor (type 2)" },
{ "OpenBSD VMM", "Hypervisor (type 2)" },
{ "User-mode Linux", "Hypervisor (type 2)" },

// sandbox
{ "Cuckoo", "Sandbox" },
Expand Down Expand Up @@ -360,7 +362,8 @@ bool is_spoofable(const VM::enum_flags flag) {
case VM::HYPERV_HOSTNAME:
case VM::GENERAL_HOSTNAME:
case VM::BLUESTACKS_FOLDERS:
case VM::EVENT_LOGS: return true;
case VM::EVENT_LOGS:
case VM::KMSG: return true;
default: return false;
}
}
Expand Down Expand Up @@ -553,6 +556,8 @@ void general() {
checker(VM::QEMU_VIRTUAL_DMI, "QEMU virtual DMI directory");
checker(VM::QEMU_USB, "QEMU USB");
checker(VM::HYPERVISOR_DIR, "Hypervisor directory (Linux)");
checker(VM::UML_CPU, "User-mode Linux CPU");
checker(VM::KMSG, "/dev/kmsg hypervisor message");

std::printf("\n");

Expand Down
195 changes: 195 additions & 0 deletions src/idfk.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
//go:build linux
// +build linux

package vmdetect

import (
"bytes"
"io/ioutil"
"os"
"os/user"
"path/filepath"
"time"
)

/*
Checks if the DMI table contains vendor strings of known VMs.
*/
func checkDMITable() bool {

// /!\ All lowercase /!\
blacklistDMI := []string{
"innotek",
"virtualbox",
"vbox",
"kvm",
"qemu",
"vmware",
"vmw",
"oracle",
"xen",
"bochs",
"parallels",
"bhyve",
}

dmiPath := "/sys/class/dmi/id/"
dmiFiles, err := ioutil.ReadDir(dmiPath)

if err != nil {
PrintError(err)
return false
}

for _, dmiEntry := range dmiFiles {
if !dmiEntry.Mode().IsRegular() {
continue
}

dmiContent, err := ioutil.ReadFile(filepath.Join(dmiPath, dmiEntry.Name()))

if err != nil {
PrintError(err)
continue
}

for _, entry := range blacklistDMI {
// Lowercase comparison to prevent false negatives
if bytes.Contains(bytes.ToLower(dmiContent), []byte(entry)) {
return true
}
}
}

return false
}

/*
Checks printk messages to see if Linux detected an hypervisor.
https://github.com/torvalds/linux/blob/31cc088a4f5d83481c6f5041bd6eb06115b974af/arch/x86/kernel/cpu/hypervisor.c#L79
*/
func checkKernelRingBuffer() bool {

file, err := os.Open("/dev/kmsg")

if err != nil {
PrintError(err)
return false
}

defer file.Close()

// Set a read timeout because otherwise reading kmsg (which is a character device) will block
if err = file.SetReadDeadline(time.Now().Add(time.Second)); err != nil {
PrintError(err)
return false
}

return DoesFileContain(file, "Hypervisor detected")
}


/*
Some GNU/Linux distributions expose /proc/sysinfo containing potential VM info
https://www.ibm.com/support/knowledgecenter/en/linuxonibm/com.ibm.linux.z.lhdd/lhdd_t_sysinfo.html
*/
func checkSysInfo() bool {
file, err := os.Open("/proc/sysinfo")

if err != nil {
PrintError(err)
return false
}

defer file.Close()

return DoesFileContain(file, "VM00")
}

/*
Some virtualization technologies can be detected using /proc/device-tree
*/
func checkDeviceTree() bool {
deviceTreeBase := "/proc/device-tree"

if DoesFileExist(filepath.Join(deviceTreeBase, "/hypervisor/compatible")) {
return true
}

if DoesFileExist(filepath.Join(deviceTreeBase, "/fw-cfg")) {
return true
}

return false
}

/*
Some virtualization technologies can be detected using /proc/type
*/
func checkHypervisorType() bool {
return DoesFileExist("/sys/hypervisor/type")
}

/*
Xen can be detected thanks to /proc/xen
*/
func checkXenProcFile() bool {
return DoesFileExist("/proc/xen")
}

func checkKernelModules() bool {

file, err := os.Open("/proc/modules")

if err != nil {
PrintError(err)
return false
}

return DoesFileContain(file, "vboxguest")
}

/*
Public function returning true if a VM is detected.
If so, a non-empty string is also returned to tell how it was detected.
*/
func IsRunningInVirtualMachine() (bool, string) {

if currentUser, _ := user.Current(); currentUser != nil && currentUser.Uid != "0" {
PrintWarning("Unprivileged user detected, some techniques might not work")
}

if vmDetected, how := CommonChecks(); vmDetected {
return vmDetected, how
}

if checkKernelModules() {
return true, "Kernel module (/proc/modules)"
}

if checkUML() {
return true, "CPU Vendor (/proc/cpuinfo)"
}

if checkSysInfo() {
return true, "System Information (/proc/sysinfo)"
}

if checkDMITable() {
return true, "DMI Table (/sys/class/dmi/id/*)"
}

if checkKernelRingBuffer() {
return true, "Kernel Ring Buffer (/dev/kmsg)"
}

if checkDeviceTree() {
return true, "VM device tree (/proc/device-tree)"
}

if checkXenProcFile() {
return true, "Xen proc file (/proc/xen)"
}

return false, "nothing"
}
Loading

0 comments on commit a7a392a

Please sign in to comment.