梦幻的彼岸 发表于 2021-5-18 16:35:00

[翻译]规避技术:常规操作系统查询

本帖最后由 梦幻的彼岸 于 2021-5-18 17:49 编辑

备注
原文地址:https://evasions.checkpoint.com/techniques/generic-os-queries.html
原文标题:Evasions: Generic OS queries
更新日期:2021年5月18日
此文后期:根据自身所学进行内容扩充
因自身技术有限,只能尽自身所能翻译国外技术文章,供大家学习,若有不当或可完善的地方,希望可以指出,用于共同完善这篇文章。

static/image/hrline/1.gif
目录

[*]常规操作系统查询
[*]1. 检查用户名是否是特定的
[*]2. 检查计算机名称是否是特定的
[*]3. 检查主机名称是否是特定的
[*]4. 检查总内存是否过低
[*]5.检查主机操作系统的屏幕分辨率是否不正常
[*]6.检查处理器数量是否过少
[*]7. 检查显示器的数量是否很少
[*]8.检查硬盘大小和可用空间是否较小
[*]9. 检查系统的正常运行时间是否很小
[*]10. 检查操作系统是否从虚拟硬盘启动(Win8+)
[*]反制措施
[*]归功于

识别标志是通用的
识别标志对每种技术都是通用的:钩住使用的函数,并跟踪它是否被调用。例如,很难说出应用程序为什么要获取用户名。这并不一定意味着应用规避技术。所以在这种情况下,最好的办法就是拦截目标函数并跟踪其调用。
通过常规的操作系统检查进行检测
通常的主机具有有意义的非标准用户名/计算机名。特定的虚拟环境将一些预定义的名称分配给默认用户以及计算机名称。主机操作系统和虚拟机之间的其他区别包括RAM大小、HDD大小、监视器数量等等。虽然这些可能不是检测虚拟环境最可靠的方法,但它们仍然常用于恶意软件样本。
1. 检查用户名是否是特定的
请注意,检查是不区分大小写的。
使用的函数:

[*]GetUserNameA/W

代码样本
bool is_user_name_match(const std::string &s) {
    auto out_length = MAX_PATH;
    std::vector<uint8_t> user_name(out_length, 0);
    ::GetUserNameA((LPSTR)user_name.data(), (LPDWORD)&out_length);

    return (!lstrcmpiA((LPCSTR)user_name.data(), s.c_str()));
}
代码样本取自InviZzzible tool
反制措施
将用户名改为不可疑的用户名。
检测表

检查用户名是否为以下其中之一:
检测字符串
admin
andy
honey
john
john doe
malnetvm
maltest
malware
roo
sandbox
snort
tequilaboomboom
test
virus
virusclone
wilbert
Nepenthesnepenthes
Normancurrentuser
ThreatExpertusername
Sandboxieuser
VMwarevmware

2. 检查计算机名称是否是特定的
请注意,检查是不区分大小写的。
使用的函数:

[*]GetComputerNameA/W

代码样本
bool is_computer_name_match(const std::string &s) {
    auto out_length = MAX_PATH;
    std::vector<uint8_t> comp_name(out_length, 0);
    ::GetComputerNameA((LPSTR)comp_name.data(), (LPDWORD)&out_length);

    return (!lstrcmpiA((LPCSTR)comp_name.data(), s.c_str()));
}
代码样本取自InviZzzible tool
反制措施
将计算机名称改为不可疑的名称。
检测表

检查计算机名是否为以下其中之一:
检测字符串
klone_x64-pc
tequilaboomboom
AnubisTU-4NH09SMCG1HC
InsideTm

3. 检查主机名称是否是特定的
请注意,检查是不区分大小写的。
使用的函数:

[*]GetComputerNameExA/W

代码样本
bool is_host_name_match(const std::string &s) {
    auto out_length = MAX_PATH;
    std::vector<uint8_t> dns_host_name(out_length, 0);
    ::GetComputerNameExA(ComputerNameDnsHostname, (LPSTR)dns_host_name.data(), (LPDWORD)&out_length);

    return (!lstrcmpiA((LPCSTR)dns_host_name.data(), s.c_str()));
}
代码样本取自InviZzzible tool
反制措施
将主机名称改为不可疑的名称。
检测表

检查主机名是否为以下其中之一:
DetectString
SystemIT

4. 检查总内存是否过低
用来获取可执行路径的函数:

[*]GetMemoryStatusEx

代码样本
BOOL memory_space()
{
    DWORDLONG ullMinRam = (1024LL * (1024LL * (1024LL * 1LL))); // 1GB
   
    MEMORYSTATUSEX statex = {0};
    statex.dwLength = sizeof(statex);
    GlobalMemoryStatusEx(&statex); // calls NtQuerySystemInformation
   
    return (statex.ullTotalPhys < ullMinRam) ? TRUE : FALSE;
}
该代码样本的作者:al-khaser project
反制措施
修补/拦截NtQuerySystemInformation以在SystemBasicInformation中返回新数量的PhysicalPages。
提示:在本例中,它的第一个参数等于2-SystemPerformanceInformation枚举值。
或者,修补KUSER_SHARED_DATA中的NumberOfPhysicalPages。
5.检查主机操作系统的屏幕分辨率是否不正常

[*]使用以下一组函数:
[*]GetDesktopWindow
[*]GetWindowRect

或者:

[*]GetSystemMetrics
[*]SystemParametersInfo
[*]GetMonitorInfo

代码样本
看看这个StackOverflow主题
反制措施
改变屏幕分辨率,使之与常规主机的分辨率相匹配(例如1600x900)。
6.检查处理器数量是否过少
使用的函数:

[*]GetSystemInfo

除此之外,处理器的数量可以通过asm内联函数或内在函数从PEB获得,请参见下面的代码示例。
代码样本(variant 1, al-khaser project)
BOOL NumberOfProcessors()
{
#if defined (ENV64BIT)
      PULONG ulNumberProcessors = (PULONG)(__readgsqword(0x30) + 0xB8);
#elif defined(ENV32BIT)
      PULONG ulNumberProcessors = (PULONG)(__readfsdword(0x30) + 0x64);
#endif

    if (*ulNumberProcessors < 2)
      return TRUE;
    else
      return FALSE;
}
该代码样本的作者:al-khaser project
代码样本(variant 2, al-khaser project, asm inline)
__declspec(naked)
DWORD get_number_of_processors() {
    __asm {
      ; get pointer to Process Environment Block (PEB)
      mov eax, fs:0x30

      ; read the field containing target number
      mov eax,

      ; return from function
      retn
    }
}
该代码样本的作者:al-khaser project
代码样本(variant 3, pafish project)
int gensandbox_one_cpu_GetSystemInfo() {
    SYSTEM_INFO si;
    GetSystemInfo(&si);
    return si.dwNumberOfProcessors < 2 ? TRUE : FALSE;
}
该代码样本的作者:pafish project
反制措施
为虚拟机分配两个或多个内核。
作为一个替代方案,打补丁/拦截NtCreateThread,为每个新线程分配特定的内核。
7. 检查显示器的数量是否很少
使用的函数:

[*]EnumDisplayMonitors
[*]GetSystemMetrics (SM_MONITOR)

代码样本
BOOL CALLBACK MonitorEnumProc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData)
{
    int *Count = (int*)dwData;
    (*Count)++;
    return TRUE;
}

int MonitorCount()
{
    int Count = 0;
    if (EnumDisplayMonitors(NULL, NULL, MonitorEnumProc, (LPARAM)&Count))
      return Count;
    return -1; // signals an error
}
该代码样本归功于:StackOverflow forum
反制措施
在虚拟环境中至少添加一个显示器。
8.检查硬盘大小和可用空间是否较小
使用的函数:

[*]DeviceIoControl(..., IOCTL_DISK_GET_LENGTH_INFO, ...)
[*]GetDiskFreeSpaceExA/W

代码样本(checking drive total size)
int gensandbox_drive_size() {
    GET_LENGTH_INFORMATION size;
    DWORD lpBytesReturned;

    HANDLE drive = CreateFile("\\\\.\\PhysicalDrive0", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
    if (drive == INVALID_HANDLE_VALUE) {
      // Someone is playing tricks. Or not enough privileges.
      CloseHandle(drive);
      return FALSE;
    }
    BOOL result = DeviceIoControl(drive, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &size, sizeof(GET_LENGTH_INFORMATION), &lpBytesReturned, NULL);
    CloseHandle(drive);

    if (result != 0) {
      if (size.Length.QuadPart / 1073741824 <= 60) /* <= 60 GB */
      return TRUE;
    }

    return FALSE;
}
该代码样本的作者:al-khaser project
代码样本(checking drive free space)
int gensandbox_drive_size2() {
    ULARGE_INTEGER total_bytes;

    if (GetDiskFreeSpaceExA("C:\\", NULL, &total_bytes, NULL))
    {
      if (total_bytes.QuadPart / 1073741824 <= 60) /* <= 60 GB */
      return TRUE;
    }

    return FALSE;
}
该代码样本的作者:al-khaser project
反制措施
针对检查磁盘大小:使用特定CTL代码筛选到\\device\\HarddiskN的IRP设备控制请求:

[*]DRIVE_GEOMETRY_EX
[*]DRIVE_LAYOUT_EX
[*]PARTITION_INFO_EX

禁止检查可用空间:修补/挂接NtQueryVolumeInformationFile以处理这些类:

[*]FileFsSizeInformation
[*]FileFsFullSizeInformation

如果句柄指向\\Device\\HarddiskVolumeN。
9. 检查系统的正常运行时间是否很小
使用的函数:

[*]GetTickCount
[*]GetTickCount64
[*]NtQuerySystemInformation

代码样本
bool Generic::CheckSystemUptime() const {
    const DWORD uptime = 1000 * 60 * 12; // 12 minutes
    return GetTickCount() < uptime;
}
代码样本取自InviZzzible tool
代码样本
#define MIN_UPTIME_MINUTES 12
BOOL uptime_check()
{
    ULONGLONG uptime_minutes = GetTickCount64() / (60 * 1000);
    return uptime_minutes < MIN_UPTIME_MINUTES;
}
代码样本
BOOL uptime_check2()
{
    SYSTEM_TIME_OF_DAY_INFORMATIONSysTimeInfo;
    ULONGLONG uptime_minutes;
    NtQuerySystemInformation(SystemTimeOfDayInformation, &SysTimeInfo, sizeof(SysTimeInfo), 0);
    uptime_minutes = (SysTimeInfo.CurrentTime.QuadPart - SysTimeInfo.BootTime.QuadPart) / (60 * 1000 * 10000);
    return uptime_minutes < MIN_UPTIME_MINUTES;
}
反制措施

[*]调整KeBootTime值
[*]调整SharedUserData->TickCount, SharedUserData->TickCoundLowDeprecated值

10. 检查操作系统是否从虚拟硬盘启动(Win8+)

[*]使用的函数:
[*]IsNativeVhdBoot // 在主机操作系统上为false,在虚拟机内为true

代码样本(excerpt from malware)
在这里看一下恶意软件的节选。
代码样本(pafish project)
int gensandbox_IsNativeVhdBoot() {
    BOOL isnative = FALSE;

    IsNativeVhdBoot fnnative = (IsNativeVhdBoot) GetProcAddress(
      GetModuleHandleA("kernel32"), "IsNativeVhdBoot");

    /* IsNativeVhdBoot always returns 1 on query success */
    if (fnnative)
      fnnative(&isnative);
               
    return (isnative) ? TRUE : FALSE;
}
该代码样本的作者:pafish project
反制措施
拦截IsNativeVhdBoot并将其结果改为所需的结果。
反制措施
反措施存在于适当的分节中,见上文。
归功于
归功于开放源码项目,代码样本来自这些项目。

[*]al-khaser project on github
[*]pafish project on github
尽管Check Point工具InviZzzible已经实现了所有这些功能,但由于代码的模块化结构,需要更多的空间来展示这个工具的代码样本,以达到相同的目的。这就是为什么我们决定在整个百科全书中使用其他伟大的开源项目作为例子。

飞天 发表于 2021-5-18 22:23:14

感谢大婶分享翻译文件

1otus 发表于 2021-5-18 22:28:35

感谢楼主分享
页: [1]
查看完整版本: [翻译]规避技术:常规操作系统查询