[原创][翻译]规避技术:文件系统
source link: https://bbs.pediy.com/thread-267663.htm
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
备注
原文地址: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. 检查用户名是否是特定的
请注意,检查是不区分大小写的。
使用的函数:
- 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
反制措施
将用户名改为不可疑的用户名。
检测表
检查用户名是否为以下其中之一:
[general]
admin
honey
john doe
malnetvm
maltest
malware
sandbox
snort
tequilaboomboom
virus
virusclone
wilbert
Nepenthes
nepenthes
Norman
currentuser
ThreatExpert
username
Sandboxie
VMware
vmware
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
反制措施
将计算机名称改为不可疑的名称。
检测表
检查计算机名是否为以下其中之一:
[generic]
klone_x64-pc
tequilaboomboom
Anubis
TU-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
反制措施
将主机名称改为不可疑的名称。
检测表
检查主机名是否为以下其中之一:
Detect
String
[generic]
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, [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. 检查显示器的数量是否很少
使用的函数:
- 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_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已经实现了所有这些功能,但由于代码的模块化结构,需要更多的空间来展示这个工具的代码样本,以达到相同的目的。这就是为什么我们决定在整个百科全书中使用其他伟大的开源项目作为例子。
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK