概述

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
MD5506493995bedb33ecca08921543643ba
SHA256ca8c781d1487ebe92350343472e6b9984e1b99c4c55b7deea6817fd89504a98d
CRC320x92ae2ef
文件大小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. 程序流程概述

程序执行以下主要步骤:

  1. 获取目标函数地址: 获取 ntdll.dll!RtlExitUserProcess 函数地址
  2. 创建目标进程: 创建挂起的 wmiprvse.exe 进程
  3. 查找窗口句柄: 枚举窗口以找到目标进程的窗口句柄
  4. 查找内存空洞: 在目标地址附近查找可用的内存区域
  5. 读取原始字节: 读取 RtlExitUserProcess 的原始字节
  6. 生成 Hook: 保存原始字节以备后续恢复
  7. 修改内存保护: 将 RtlExitUserProcess 的内存保护更改为 RWX
  8. 注入 JMP 指令: 写入 JMP 指令以重定向执行到 shellcode
  9. 写入 Shellcode: 将 shellcode 写入内存空洞
  10. 触发执行: 发送消息触发 shellcode 执行

2. 关键函数分析

2.1 main 函数 (0x140001678)

功能: 主函数,协调整个进程注入流程

关键操作:

  • 加载 ntdll.dll 并获取 RtlExitUserProcess 地址
  • 创建挂起的 wmiprvse.exe 进程
  • 枚举窗口查找目标进程的窗口句柄
  • 查找内存空洞用于 shellcode 注入
  • 修改 RtlExitUserProcess 函数以重定向执行
  • 写入 shellcode 并触发执行

重命名的变量:

  • ModuleHandleAntdll_handle
  • lpAddressRtlExitUserProcess_addr
  • lpBaseAddressshellcode_memory_hole
  • v15original_bytes
  • hProcessprocess_info
  • v13startup_info
  • v10target_hwnd
  • Buffercombined_shellcode
  • Addressjmp_instruction
  • Address_1jmp_offset
  • lParam[[【Cpp】enum|enum]]_callback_param
  • v12target_process_id

2.2 EnumWindowCallBack 函数 (0x140001533)

功能: 枚举窗口的回调函数,用于查找属于目标进程 ID 的窗口句柄

参数:

  • a1: 窗口句柄 (HWND)
  • a2: 回调参数,包含目标进程 ID 和存储窗口句柄的指针

逻辑:

  1. 获取窗口所属的进程 ID
  2. 比较进程 ID 是否匹配目标进程 ID
  3. 如果匹配,保存窗口句柄并停止枚举
  4. 如果不匹配,继续枚举

2.3 FindMemoryHole 函数 (0x14000158a)

功能: 在目标地址附近查找可用的内存区域(内存空洞)

参数:

  • a1: 目标进程句柄
  • a2: 目标地址(RtlExitUserProcess 地址)
  • a3: 需要分配的内存大小(333 字节)

逻辑:

  1. 从目标地址 - 0x70000000 开始搜索
  2. 每次递增 0x10000(64KB)
  3. 尝试使用 VirtualAllocEx 分配内存
  4. 如果分配成功,返回内存地址
  5. 搜索范围:目标地址 ± 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 部分:

  1. shellcode_loader (56 字节): 位于 0x140009005

    • 包含字符串: “PQRAPAQARASH”
    • 可能是 shellcode 的加载器/解密器
  2. 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. 内存保护修改

程序执行以下内存保护修改:

  1. RtlExitUserProcess:

    • 原始保护: 未知
    • 修改为: PAGE_EXECUTE_READWRITE (0x40)
    • 目的: 允许写入 JMP 指令
  2. 内存空洞:

    • 初始: PAGE_READWRITE (0x04)
    • 写入前: PAGE_READWRITE (0x04)
    • 写入后: PAGE_EXECUTE_READ (0x20)
    • 目的: 允许 shellcode 执行

6. Hook 机制

程序使用以下步骤实现 Hook:

  1. 读取原始字节: 读取 RtlExitUserProcess 的前 8 字节
  2. 保存原始字节: 调用 GenerateHook 保存原始字节
  3. 修改内存保护: 将内存保护改为 RWX
  4. 创建 JMP 指令:
    • 操作码: 0xE9 (JMP rel32)
    • 偏移量: 目标地址 - 源地址 - 5
  5. 写入 JMP 指令: 将 5 字节的 JMP 指令写入 RtlExitUserProcess
  6. 重定向执行: 当 RtlExitUserProcess 被调用时,执行跳转到 shellcode

7. 触发机制

程序使用 PostMessageA 发送 WM_CLOSE (0x10) 消息到目标窗口:

PostMessageA(hWnd, 0x10, 0, 0);

这会导致目标进程调用 RtlExitUserProcess,从而触发 Hook 并执行 shellcode。

检测特征

行为特征

  1. 进程注入: 将代码注入到合法进程(wmiprvse.exe)
  2. API Hooking: 挂钩 RtlExitUserProcess 函数
  3. 内存操作: 修改内存保护属性
  4. Shellcode 执行: 执行注入的 shellcode

文件特征

  1. 编译器: MinGW GCC 13.2.0
  2. 架构: x64
  3. 导入的 DLL: KERNEL32.dll, msvcrt.dll, USER32.dll, ntdll.dll

字符串特征

  • “calc.exe” - 可能是 shellcode 的目标
  • “wmiprvse.exe” - 目标进程
  • “RtlExitUserProcess” - 被挂钩的函数
  • “ntdll.dll” - 包含目标函数的 DLL

防御建议

检测方法

  1. API Hooking 检测: 监控对关键系统 API 的修改
  2. 进程注入检测: 监控跨进程内存写入操作
  3. 内存保护修改: 检测异常的内存保护属性修改
  4. 行为分析: 监控可疑的进程创建和窗口枚举行为

防御措施

  1. 启用 DEP: 数据执行保护可以防止某些类型的代码注入
  2. 启用 ASLR: 地址空间布局随机化可以增加攻击难度
  3. 使用 EDR: 端点检测和响应系统可以检测和阻止此类攻击
  4. 限制权限: 运行程序时使用最小权限原则
  5. 监控 API 调用: 监控对敏感 API 的调用

分析步骤总结

  1. ✅ 检查 IDA Pro 连接状态
  2. ✅ 获取数据库元数据信息
  3. ✅ 列出所有函数并识别关键函数
  4. ✅ 分析入口点和主要函数
  5. ✅ 反编译关键函数并添加注释
  6. ✅ 重命名变量和函数
  7. ✅ 更改变量和参数类型
  8. ✅ 反汇编关键函数并添加注释
  9. ✅ 创建分析报告 MarkDown 文件

结论

ThreadlessSpawn.exe 是一个使用高级进程注入技术的恶意程序。它通过挂钩 RtlExitUserProcess 函数,将 shellcode 注入到合法的 Windows 进程中,从而实现隐蔽的代码执行。该程序展示了恶意软件作者如何使用合法的 Windows API 来实现恶意目的,同时也说明了为什么需要多层防御策略来检测和阻止此类攻击。


分析日期: 2026-02-03
分析工具: IDA Pro + MCP
分析人员: Copilot Code Pro