都2024年了,你不会还再被沙箱X出吧?
2024-1-4 22:0:50 Author: 安全的矛与盾(查看原文) 阅读量:33 收藏

前言

这次我们来聊聊反沙箱,众所周知。很多前人大佬都对反沙箱有自己的思路理解和实现方法,也开源了很多,但是安全厂商的AV沙箱也一直在进步。

啥是沙箱呢?沙箱能够模拟出软件执行所需的运行环境,通过进程hook技术等来对软件执行过程中的行为进行分析,判断是否有敏感的操作行为。或者更高级的检测手法,将获取到的程序API调用序列以及其他的一些行为特征输入到智能分析引擎中进行检测。

特别进入2024年,大数据,LLM大模型智能结合起来的沙箱行为识别,具备更强的检测能力。如果我们的程序没有做好反沙箱,很容易就被检测出来。所以做好反沙箱,来增加程序持久性是非常有必要的。

那沙箱的判断,个人认为,要有准确性、兼容性、通用性,如何通过这些去实现判断的准确性和兼容性呢,这就是比较繁琐的一个操作,网上开源的有很多方法以及代码,但是大多是从简单的直接方法去判断,比如内存大小、硬盘大小、硬件设备 、桌面壁纸的哈希值 ,效果不是十分理想。

一方面,很多杀软也增加了反反沙箱的规则,不加反沙箱还好,加了反沙箱反倒被反反沙箱检测出可疑行为,得不偿失。

另一方面,很多的判断方法是伤兵一千,自损八百,有时确实反了沙箱,但是也会有很多环境下的系统运行不起来,这就是我所说的兼容性 ,要尽可能适配所有操作系统,还要准确的判断出沙箱环境和虚拟机环境。

下面主要说利用时间流速来进行沙箱识别的方法。

时间流速

沙箱为了防止恶意代码长时间Sleep而不进行恶意行为,大部分沙箱都会选择进行时间加速。

但是问题就出现在这里,如果进行了时间加速,那Sleep函数中的时间流速是必然不同于正常值的,如果我们可以选择一个不会被修改的时间作为基准,就很容易识别出其中的差异。

      ┌────────────────┐        ┌──────────────────┐
      │                │compare │                  │
      │  time_base     ├───────►│  Sleep(xxx)      │
      └────────────────┘        └──────────────────┘

下面介绍的都是当前市面沙箱的通杀技能。

方法一:NTP时间基准

使用NTP服务器获取的时间作为基准。NTP服务的全称是 Network Time Protocol,基于UDP的时间同步协议,网络的时间可以同步到标准UTC时间。提供NTP对时服务的服务器有很多,比如微软的 time.windows.com.

关于NTP协议的报文以及怎么使用UDP协议获取标准时间相关的内容请参考网络,这里仅介绍实现思路:

在执行Sleep之前利用NTP服务器来获取一下时间戳,执行Sleep之后再获取一下NTP时间,对比两次的差异,伪代码如下:

#define DURATION 10*1000

__int64 start = GetNtpTime();
Sleep(DURATION);
BOOL is_sandbox=FALSE; 
if( GetNtpTime() - start < DURATION ){
  is_sandbox = TRUE;
}

方法二:线程同步事件

利用线程同步的机制来实现。主要思路如下,我们设置一个Event,然后将这个事件传入一个线程中,线程中会Sleep X秒后对Event进行激活,主线程使用 WaitForSingleObject 在规定时间内等待这个事件,如果在规定时间内出现超时,则不是沙箱,否则是沙箱。

伪代码如下:

void ThreadFunc(PHANDLE pevent) {
    Sleep(10000);
    SetEvent(*pevent);
}

int main() {
    BOOL is_sandbox = FALSE;
    HANDLE eventHandle = CreateEvent(NULL, TRUE, FALSE, NULL); // 手动重置事件,初始状态为非信号状态
    if (eventHandle == NULL) {
        std::cerr << "CreateEvent failed with " << GetLastError() << std::endl;
    }
    // 设置超时为8000毫秒(8秒)
    DWORD timeout = 9000// 9秒的等待时间
    // 等待事件或超时
    HANDLE hThread = CreateThread(
        NULL,
        0,
        (LPTHREAD_START_ROUTINE)ThreadFunc,
        &eventHandle,
        0,
        NULL
    );
    DWORD waitResult = WaitForSingleObject(eventHandle, timeout);
    if (waitResult != WAIT_TIMEOUT) {
        is_sandbox = TRUE;
    }
    return 0;

}

方法三: GetTickCount64

GetTickCount64 函数获取系统自启动以来处于工作状态的时间, 函数的分辨率限制为系统计时器的分辨率,通常介于 10 毫秒到 16 毫秒之间。

虽然微软文档说获取的时间并不是非常准确,作为时间基准已经足够用了。但是我们不可以直接使用这个函数,因为这个函数大概率已经被沙箱hook了,那我们要怎么获取到相同的效果呢?

我们这里逆向分析一下 GetTickCount64 的函数实现:

经过我的验证,函数中两个全局变量的偏移在不同的windows NT版本上都是一样的,所以我们可以直接使用如下汇编来完成自己的 GetTickCount64 函数,代码如下:

调试一下,看到和标准函数的输出相同,说明实现的没有问题:

接下来就比较简单了,与思路一相同,伪代码如下:

#define DURATION 10*1000

 __int64 start = MyGetTickCount64();
 Sleep(DURATION);
 BOOL is_sandbox = FALSE;
 if (MyGetTickCount64() - start < DURATION) {
  is_sandbox = TRUE;
 }

方法四:使用计时器

这个思路源自于MessageBox的源码,逆向MessageBox会发现其底层调用的其实是一个叫 MessageBoxTimeout 函数,此函数可以实现一个超时自动关闭窗口的弹框,并且可以停止阻塞当前进程,之前也分析过怎么用它实现延时效果以阻塞沙箱,细节内容见星球。

深层调试分析后发现此函数底层会调用一个定时器,来实现超时消息的处理,这里也模拟这个思路。

我们在主线程中设置一个超时器作为基准时间,然后在线程中Sleep,对比两个的返回时间是否一致,进而判断时间流速。伪代码如下:

void threadFunction(UINT_PTR *iTimerID) {
    Sleep(10000);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{   
    MSG Msg;
    UINT_PTR iTimerID;

    // Set our timer without window handle
    iTimerID = SetTimer(NULL0x19000NULL);
  
    HANDLE hThread = CreateThread(
        NULL,
        0,
        (LPTHREAD_START_ROUTINE)threadFunction,
        &iTimerID,
        0,
        NULL
    );
    BOOL is_sandbox = FALSE;
    // Because we are running in a console app, we should get the messages from
    // the queue and check if msg is WM_TIMER
    while (GetMessage(&Msg, NULL00))
    {
        if (Msg.message == WM_TIMER && Msg.wParam == iTimerID) {
            // 看线程是否结束

            //收到超时消息
            DWORD exitCode = 0;
            if (GetExitCodeThread(hThread, &exitCode)) {
                if (exitCode == STILL_ACTIVE) {
                    is_sandbox = FALSE;
                }
                else {
                    is_sandbox = TRUE;
                }
            }
            KillTimer(NULL, iTimerID);
            break;
        }
        TranslateMessage(&Msg);
        DispatchMessage(&Msg);
    }
    return 0;
}

知识星球

代码和思路星球里都有,除此之外还有各种大佬贴身交流,让你的技术之路不再孤单,别再犹豫,现在就是启程的最佳时刻。

2024年,新的开始,新的征程,不变的是继续提升安全技术矛与盾的决心。新年之际小编为大家准备了优惠券福利,让你的学习之旅更加轻松愉快。抓紧时间,名额有限,快来领取,开启你的新技能探索之旅吧!


文章来源: http://mp.weixin.qq.com/s?__biz=Mzg5MDc4OTUyNg==&mid=2247484334&idx=1&sn=014d6b6dd191be2b885670dd76d381b8&chksm=cefcb815438fd4479fa1d04ca72ed0cd46c731e20dc46398668d80bd55387c49130fd12cb40b&scene=0&xtrack=1#rd
如有侵权请联系:admin#unsafe.sh