概述
ThreadlessSpawn 无线程
ThreadlessSpawn.exe 分析报告
关于 _IAT_start__ 的说明
_IAT_start__ 是导入地址表(Import Address Table,IAT)的起始标记。在 MinGW 编译的程序中,当调用导入的 Windows API 函数时,实际上是通过 IAT 来间接调用的。
在本程序中,_IAT_start__ 指向 IAT 的起始地址,而 CreateProcessA 是 IAT 中的第一个导入函数。因此,代码中的:
mov rax, cs:__IAT_start__
call rax实际上是在调用 CreateProcessA 函数。这种间接调用方式允许 Windows 在加载时动态解析导入函数的地址。
文件信息
| 属性 | 值 |
|---|---|
| 文件名 | ThreadlessSpawn.exe |
| 文件路径 | 内存写入\ThreadlessSpawn.exe |
| 基址 | 0x140000000 |
| 大小 | 0x19000 |
| MD5 | 506493995bedb33ecca08921543643ba |
| SHA256 | ca8c781d1487ebe92350343472e6b9984e1b99c4c55b7deea6817fd89504a98d |
| CRC32 | 0x92ae2ef |
| 文件大小 | 0x147db (83995 bytes) |
| 编译器 | GCC: (x86_64-win32-seh-rev1, Built by MinGW-Builds project) 13.2.0 |
执行摘要
ThreadlessSpawn.exe 是一个使用”无线程生成”(Threadless Spawn)技术的进程注入恶意程序。该程序通过挂钩 RtlExitUserProcess 函数,将 shellcode 注入到合法的 Windows 进程(wmiprvse.exe)中,从而实现代码执行。
威胁等级
- 严重性: 高
- 类型: 进程注入 / Shellcode 注入
- 技术: API Hooking / 内存操作
技术分析
1. 程序流程概述
程序执行以下主要步骤:
- 获取目标函数地址: 获取
ntdll.dll!RtlExitUserProcess函数地址 - 创建目标进程: 创建挂起的
wmiprvse.exe进程 - 查找窗口句柄: 枚举窗口以找到目标进程的窗口句柄
- 查找内存空洞: 在目标地址附近查找可用的内存区域
- 读取原始字节: 读取
RtlExitUserProcess的原始字节 - 生成 Hook: 保存原始字节以备后续恢复
- 修改内存保护: 将
RtlExitUserProcess的内存保护更改为 RWX - 注入 JMP 指令: 写入 JMP 指令以重定向执行到 shellcode
- 写入 Shellcode: 将 shellcode 写入内存空洞
- 触发执行: 发送消息触发 shellcode 执行
2. 关键函数分析
2.1 main 函数 (0x140001678)
功能: 主函数,协调整个进程注入流程
关键操作:
- 加载
ntdll.dll并获取RtlExitUserProcess地址 - 创建挂起的
wmiprvse.exe进程 - 枚举窗口查找目标进程的窗口句柄
- 查找内存空洞用于 shellcode 注入
- 修改
RtlExitUserProcess函数以重定向执行 - 写入 shellcode 并触发执行
重命名的变量:
ModuleHandleA→ntdll_handlelpAddress→RtlExitUserProcess_addrlpBaseAddress→shellcode_memory_holev15→original_byteshProcess→process_infov13→startup_infov10→target_hwndBuffer→combined_shellcodeAddress→jmp_instructionAddress_1→jmp_offsetlParam→[[【Cpp】enum|enum]]_callback_paramv12→target_process_id
2.2 EnumWindowCallBack 函数 (0x140001533)
功能: 枚举窗口的回调函数,用于查找属于目标进程 ID 的窗口句柄
参数:
a1: 窗口句柄 (HWND)a2: 回调参数,包含目标进程 ID 和存储窗口句柄的指针
逻辑:
- 获取窗口所属的进程 ID
- 比较进程 ID 是否匹配目标进程 ID
- 如果匹配,保存窗口句柄并停止枚举
- 如果不匹配,继续枚举
2.3 FindMemoryHole 函数 (0x14000158a)
功能: 在目标地址附近查找可用的内存区域(内存空洞)
参数:
a1: 目标进程句柄a2: 目标地址(RtlExitUserProcess 地址)a3: 需要分配的内存大小(333 字节)
逻辑:
- 从目标地址 - 0x70000000 开始搜索
- 每次递增 0x10000(64KB)
- 尝试使用
VirtualAllocEx分配内存 - 如果分配成功,返回内存地址
- 搜索范围:目标地址 ± 0x70000000
内存分配参数:
flAllocationType: 0x3000 (MEM_COMMIT | MEM_RESERVE)flProtect: 0x40 (PAGE_EXECUTE_READWRITE)
2.4 GenerateHook 函数 (0x140001648)
功能: 保存原始字节到全局变量
参数:
a1: 原始字节(从 RtlExitUserProcess 读取的前 8 字节)
操作:
- 将原始字节保存到全局变量
g_original_RtlExitUserProcess_bytes - 打印成功消息
2.5 ConcatArrays 函数 (0x1400014a4)
功能: 连接两个字节数组
参数:
a1: 目标缓冲区a2: 第一个源数组a3: 第一个源数组的大小(56 字节)a4: 第二个源数组a5: 第二个源数组的大小(277 字节)
用途: 将 shellcode_loader(56 字节)和 shellcode(277 字节)合并成一个 333 字节的完整 shellcode
3. Shellcode 分析
程序包含两个 shellcode 部分:
-
shellcode_loader (56 字节): 位于 0x140009005
- 包含字符串: “PQRAPAQARASH”
- 可能是 shellcode 的加载器/解密器
-
shellcode (277 字节): 位于 0x140009029
- 包含字符串: “@A[AZAYAXZYX”
- 包含字符串: “calc.exe”
- 可能是实际的恶意载荷
总大小: 333 字节 (0x14D)
4. API 调用分析
程序使用以下 Windows API:
| API | 用途 |
|---|---|
GetModuleHandleA | 加载 ntdll.dll |
GetProcAddress | 获取 RtlExitUserProcess 地址 |
CreateProcessA | 创建 wmiprvse.exe 进程 |
WaitForInputIdle | 等待进程初始化 |
EnumWindows | 枚举窗口 |
GetWindowThreadProcessId | 获取窗口所属进程 ID |
VirtualAllocEx | 在目标进程中分配内存 |
VirtualProtectEx | 修改内存保护属性 |
WriteProcessMemory | 写入内存 |
PostMessageA | 发送消息触发执行 |
Sleep | 延迟执行 |
5. 内存保护修改
程序执行以下内存保护修改:
-
RtlExitUserProcess:
- 原始保护: 未知
- 修改为: PAGE_EXECUTE_READWRITE (0x40)
- 目的: 允许写入 JMP 指令
-
内存空洞:
- 初始: PAGE_READWRITE (0x04)
- 写入前: PAGE_READWRITE (0x04)
- 写入后: PAGE_EXECUTE_READ (0x20)
- 目的: 允许 shellcode 执行
6. Hook 机制
程序使用以下步骤实现 Hook:
- 读取原始字节: 读取
RtlExitUserProcess的前 8 字节 - 保存原始字节: 调用
GenerateHook保存原始字节 - 修改内存保护: 将内存保护改为 RWX
- 创建 JMP 指令:
- 操作码: 0xE9 (JMP rel32)
- 偏移量: 目标地址 - 源地址 - 5
- 写入 JMP 指令: 将 5 字节的 JMP 指令写入
RtlExitUserProcess - 重定向执行: 当
RtlExitUserProcess被调用时,执行跳转到 shellcode
7. 触发机制
程序使用 PostMessageA 发送 WM_CLOSE (0x10) 消息到目标窗口:
PostMessageA(hWnd, 0x10, 0, 0);这会导致目标进程调用 RtlExitUserProcess,从而触发 Hook 并执行 shellcode。
检测特征
行为特征
- 进程注入: 将代码注入到合法进程(wmiprvse.exe)
- API Hooking: 挂钩
RtlExitUserProcess函数 - 内存操作: 修改内存保护属性
- Shellcode 执行: 执行注入的 shellcode
文件特征
- 编译器: MinGW GCC 13.2.0
- 架构: x64
- 导入的 DLL: KERNEL32.dll, msvcrt.dll, USER32.dll, ntdll.dll
字符串特征
- “calc.exe” - 可能是 shellcode 的目标
- “wmiprvse.exe” - 目标进程
- “RtlExitUserProcess” - 被挂钩的函数
- “ntdll.dll” - 包含目标函数的 DLL
防御建议
检测方法
- API Hooking 检测: 监控对关键系统 API 的修改
- 进程注入检测: 监控跨进程内存写入操作
- 内存保护修改: 检测异常的内存保护属性修改
- 行为分析: 监控可疑的进程创建和窗口枚举行为
防御措施
- 启用 DEP: 数据执行保护可以防止某些类型的代码注入
- 启用 ASLR: 地址空间布局随机化可以增加攻击难度
- 使用 EDR: 端点检测和响应系统可以检测和阻止此类攻击
- 限制权限: 运行程序时使用最小权限原则
- 监控 API 调用: 监控对敏感 API 的调用
分析步骤总结
- ✅ 检查 IDA Pro 连接状态
- ✅ 获取数据库元数据信息
- ✅ 列出所有函数并识别关键函数
- ✅ 分析入口点和主要函数
- ✅ 反编译关键函数并添加注释
- ✅ 重命名变量和函数
- ✅ 更改变量和参数类型
- ✅ 反汇编关键函数并添加注释
- ✅ 创建分析报告 MarkDown 文件
结论
ThreadlessSpawn.exe 是一个使用高级进程注入技术的恶意程序。它通过挂钩 RtlExitUserProcess 函数,将 shellcode 注入到合法的 Windows 进程中,从而实现隐蔽的代码执行。该程序展示了恶意软件作者如何使用合法的 Windows API 来实现恶意目的,同时也说明了为什么需要多层防御策略来检测和阻止此类攻击。
分析日期: 2026-02-03
分析工具: IDA Pro + MCP
分析人员: Copilot Code Pro