1- // +build linux darwin
1+ // +build linux
22
33package vmdetect
44
55import (
66 "bytes"
7- "fmt "
7+ "os "
88 "os/exec"
9- "os/user"
109)
1110
1211/*
13- Checks if the program is being run using root.
14- */
15- func isRunningWithAdminRights () (bool , error ) {
16- if currentUser , err := user .Current (); err != nil {
17- return false , err
18- } else {
19- return currentUser .Uid == "0" , nil
12+ Checks if the DMI table contains vendor strings of known VMs.
13+ */
14+ func checkDMITable () bool {
15+ // TODO : instead of running a command, read files in /sys/class/dmi/id/* and look for vendor strings below
16+ output , err := exec .Command ("dmidecode" ).Output ()
17+
18+ if err != nil {
19+ PrintError (err )
20+ return false
2021 }
22+
23+ return bytes .Contains (output , []byte ("innotek" )) ||
24+ bytes .Contains (output , []byte ("VirtualBox" )) ||
25+ bytes .Contains (output , []byte ("vbox" ))
2126}
2227
2328/*
24- Tries to vmdetect VM using privileged access .
25- */
26- func privilegedChecks () ( bool , string , error ) {
29+ Checks printk messages to see if Linux detected an hypervisor .
30+ */
31+ func checkKernelRingBuffer () bool {
2732
28- output , err := exec . Command ( "dmidecode" ). Output ( )
33+ file , err := os . Open ( "/dev/kmsg" )
2934
30- if err == nil &&
31- (bytes .Contains (output , []byte ("innotek" )) ||
32- bytes .Contains (output , []byte ("VirtualBox" )) ||
33- bytes .Contains (output , []byte ("vbox" ))){
34- return true , "dmidecode" , nil
35+ if err != nil {
36+ PrintError (err )
37+ return false
3538 }
3639
37- output , err = exec . Command ( "dmesg" ). Output ( )
40+ buffer := make ([] byte , 1024 * 8 )
3841
39- if err == nil && bytes .Contains (output , []byte ("Hypervisor detected" )) {
40- return true , "dmesg" , nil
41- }
42-
43- return false , "" , nil
44- }
45-
46- /*
47- Tries to vmdetect VM using unprivileged access.
48- */
49- func unprivilegedChecks () (bool , string , error ) {
50- output , err := exec .Command ("hostnamectl" ).Output ()
42+ // Only reads the first 100 lines (reading character device in Go is annoying)
43+ for i := 0 ; i < 100 ; i ++ {
44+ if _ , err = file .Read (buffer ); err != nil {
45+ PrintError (err )
46+ return false
47+ }
5148
52- if err == nil && bytes .Contains (output , []byte (" vm\n " )) {
53- return true , "hostnamectl" , nil
49+ if bytes .Contains (buffer , []byte ("Hypervisor detected" )) {
50+ return true
51+ }
5452 }
5553
56- return false , "" , nil
54+ return false
5755}
5856
5957/*
6058 Public function returning true if a VM is detected.
6159 If so, a non-empty string is also returned to tell how it was detected.
62- */
63- func IsRunningInVirtualMachine () (bool , string , error ) {
64- isAdmin , err := isRunningWithAdminRights ()
60+ */
61+ func IsRunningInVirtualMachine () (bool , string ) {
6562
66- if err != nil {
67- return false , "" , err
63+ if checkDMITable () {
64+ return true , "DMI Table"
6865 }
6966
70- if isAdmin {
71- vmDetected , reason , err := privilegedChecks ()
72-
73- if err != nil {
74- return false , "" , err
75- }
76-
77- if vmDetected {
78- return true , reason , nil
79- }
80- } else {
81- fmt .Println ("[WARNING] Running as unprivileged user" )
67+ if checkKernelRingBuffer () {
68+ return true , "Kernel Ring Buffer"
8269 }
8370
84- return unprivilegedChecks ()
85- }
71+ return false , "nothing"
72+ }
0 commit comments