Necropolis v1 release
This commit is contained in:
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user