| 
 
TA的每日心情|  | 开心 2022-1-9 18:41
 | 
|---|
 签到天数: 5 天 [LV.2]偶尔看看I | 
 
| 备注 原文地址:https://evasions.checkpoint.com/techniques/generic-os-queries.html
 原文标题:Evasions: Generic OS queries
 更新日期:2021年5月18日
 此文后期:根据自身所学进行内容扩充
 因自身技术有限,只能尽自身所能翻译国外技术文章,供大家学习,若有不当或可完善的地方,希望可以指出,用于共同完善这篇文章。
 
 
  目录
 
 常规操作系统查询1. 检查用户名是否是特定的2. 检查计算机名称是否是特定的3. 检查主机名称是否是特定的4. 检查总内存是否过低5.检查主机操作系统的屏幕分辨率是否不正常6.检查处理器数量是否过少7. 检查显示器的数量是否很少8.检查硬盘大小和可用空间是否较小9. 检查系统的正常运行时间是否很小10. 检查操作系统是否从虚拟硬盘启动(Win8+)反制措施归功于
 识别标志是通用的
 识别标志对每种技术都是通用的:钩住使用的函数,并跟踪它是否被调用。例如,很难说出应用程序为什么要获取用户名。这并不一定意味着应用规避技术。所以在这种情况下,最好的办法就是拦截目标函数并跟踪其调用。
 通过常规的操作系统检查进行检测
 通常的主机具有有意义的非标准用户名/计算机名。特定的虚拟环境将一些预定义的名称分配给默认用户以及计算机名称。主机操作系统和虚拟机之间的其他区别包括RAM大小、HDD大小、监视器数量等等。虽然这些可能不是检测虚拟环境最可靠的方法,但它们仍然常用于恶意软件样本。
 1. 检查用户名是否是特定的
 请注意,检查是不区分大小写的。
 使用的函数:
 
 代码样本
 
 复制代码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
 反制措施
 将用户名改为不可疑的用户名。
 检测表
 
 | 检查用户名是否为以下其中之一: |  | 检测 | 字符串 |  | [general] | admin |  | andy |  | honey |  | john |  | john doe |  | malnetvm |  | maltest |  | malware |  | roo |  | sandbox |  | snort |  | tequilaboomboom |  | test |  | virus |  | virusclone |  | wilbert |  | Nepenthes | nepenthes |  | Norman | currentuser |  | ThreatExpert | username |  | Sandboxie | user |  | VMware | vmware | 
 2. 检查计算机名称是否是特定的
 请注意,检查是不区分大小写的。
 使用的函数:
 
 代码样本
 
 复制代码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
 反制措施
 将计算机名称改为不可疑的名称。
 检测表
 
 | 检查计算机名是否为以下其中之一: |  | 检测 | 字符串 |  | [generic] | klone_x64-pc |  | tequilaboomboom |  | Anubis | TU-4NH09SMCG1HC |  | InsideTm | 
 3. 检查主机名称是否是特定的
 请注意,检查是不区分大小写的。
 使用的函数:
 
 代码样本
 
 复制代码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
 反制措施
 将主机名称改为不可疑的名称。
 检测表
 
 | 检查主机名是否为以下其中之一: |  | Detect | String |  | [generic] | SystemIT | 
 4. 检查总内存是否过低
 用来获取可执行路径的函数:
 
 代码样本
 
 复制代码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.检查主机操作系统的屏幕分辨率是否不正常
 
 使用以下一组函数:GetDesktopWindowGetWindowRect
 或者:
 
 GetSystemMetricsSystemParametersInfoGetMonitorInfo
 代码样本
 看看这个StackOverflow主题
 反制措施
 改变屏幕分辨率,使之与常规主机的分辨率相匹配(例如1600x900)。
 6.检查处理器数量是否过少
 使用的函数:
 
 除此之外,处理器的数量可以通过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, [eax + 0x64]
        ; 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. 检查显示器的数量是否很少
 使用的函数:
 
 EnumDisplayMonitorsGetSystemMetrics (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_EXDRIVE_LAYOUT_EXPARTITION_INFO_EX
 禁止检查可用空间:修补/挂接NtQueryVolumeInformationFile以处理这些类:
 
 FileFsSizeInformationFileFsFullSizeInformation
 如果句柄指向\\Device\\HarddiskVolumeN。
 9. 检查系统的正常运行时间是否很小
 使用的函数:
 
 GetTickCountGetTickCount64NtQuerySystemInformation
 代码样本
 
 复制代码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_INFORMATION  SysTimeInfo;
    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并将其结果改为所需的结果。
 反制措施
 反措施存在于适当的分节中,见上文。
 归功于
 归功于开放源码项目,代码样本来自这些项目。
 尽管Check Point工具InviZzzible已经实现了所有这些功能,但由于代码的模块化结构,需要更多的空间来展示这个工具的代码样本,以达到相同的目的。这就是为什么我们决定在整个百科全书中使用其他伟大的开源项目作为例子。
 | 
 |