news 2026/8/26 16:43:46

BlobRunner x64实现深度剖析:内联汇编为何失效,挂起线程如何救场

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
BlobRunner x64实现深度剖析:内联汇编为何失效,挂起线程如何救场

BlobRunner x64实现深度剖析:内联汇编为何失效,挂起线程如何救场

【免费下载链接】BlobRunnerQuickly debug shellcode extracted during malware analysis项目地址: https://gitcode.com/gh_mirrors/bl/BlobRunner

BlobRunner 是一款专为恶意软件分析设计的shellcode 调试工具:把提取出的 shellcode 载入一块可执行内存,再"跳"进去单步调试。x86 版本只需一行内联汇编完成跳转,但x64 编译器不支持内联汇编——BlobRunner 改用挂起线程(suspended thread)方案巧妙救场。下面结合源码 blobrunner.c 拆解它的实现思路。

BlobRunner 是什么:shellcode 快速调试上手

在恶意软件分析中,你经常需要从内存 dump 或样本里提取一段 shellcode,再搞清楚它到底做了什么。手动把字节数组塞进调试器脚本很繁琐,而 BlobRunner 把这件事压缩成一条命令:

BlobRunner.exe shellcode.bin

它的工作流只有三步:读取文件 → 分配内存并复制数据 → 跳入内存起点。常用参数(见 README.md):

  • --offset 0x0100:跳入指定偏移,而不是起点
  • --nopause:跳转前不暂停(危险,请先下好断点)
  • --jit:触发系统配置的 JIT 调试器

第一步:为 shellcode 准备一块 RWX 内存

process_file()(blobrunner.c)负责"搬家":

  1. fopen+fread把整个 shellcode 文件读进缓冲区
  2. 调用VirtualAlloc(NULL, fileLen, 0x3000, 0x40)分配内存
    • 0x3000= MEM_RESERVE | MEM_COMMIT(预留并提交)
    • 0x40= PAGE_EXECUTE_READWRITE,即RWX——可读、可写、可执行
  3. CopyMemory把数据拷入新地址,返回基址

💡 关键点:内存一开始就带执行权限,shellcode 落进去就能跑,无需二次修改权限。

x86 老方案:一行内联汇编直接跳

32 位实现非常朴素,execute()末尾就一句(blobrunner.c):

__asm jmp shell_entry;

shell_entry就是"基址 + offset"。主线程直接跳进 shellcode,调试器只需在跳转前下断点(或借助getchar()暂停)即可接管。简单、直接、零依赖——但这条路在 x64 上走不通。

为什么 x64 下内联汇编会失效?

微软 C/C++ 编译器(MSVC)的x64 工具链不支持__asm内联汇编,该特性只保留在 32 位(x86)目标上。原因是 x64 ABI 的寄存器分配与栈模型更复杂,内联汇编被从 64 位模式中移除,汇编只能写在独立的.asm文件里配合 MASM 编译。

于是同样的"跳进 shellcode"需求,在 x64 版本必须换一种写法。

救场方案:CreateThread 挂起线程

x64 分支(blobrunner.c)的思路:不自己跳,而是让系统新开一个线程去执行 shellcode,并且创建时先把它挂起

thread_handle = CreateThread(NULL, 0, shell_entry, NULL, 0x4, &thread_id);

逐参数拆解:

  • shell_entry:新线程入口点,即 shellcode 起点(基址 + offset)
  • 0x4:CREATE_SUSPENDED 标志——线程创建后处于挂起状态,尚未执行
  • 返回线程标识与句柄,供后续恢复

这样做一举三得:

  1. 天然的断点位置:线程入口已停在 shellcode 第一条指令上,调试器只需在线程恢复前下断点,即可在精确位置接管
  2. 独立线程栈:系统为新线程分配默认栈空间,shellcode 的push/call不会污染主线程栈
  3. 恢复控制权在调试器手里ResumeThread(blobrunner.c)执行之前,一切都静止

这一改动在 changelog.txt 的 0.0.2 版本记录中也有明确体现:"Added support for x64 debugging by using CreateThread and ResumeThread"。

实操:跑通一次 BlobRunner x64 shellcode 调试

  1. 用调试器(如 x64dbg)打开blobrunner64.exe,命令行参数传入 shellcode 文件
  2. 程序执行到getchar()暂停,提示"Navigate to the Thread Entry and set a breakpoint"
  3. 切到该线程视图,在线程入口设置断点
  4. 按任意键 → 线程被ResumeThread恢复 → 命中断点,开始单步调试 shellcode

--jit 参数:用一次 AccessViolation 触发 JIT 调试器

不想常驻调试器?加上--jit(blobrunner.c):

VirtualProtect(shell_entry, 1, PAGE_READWRITE, &oldp);

只把第一个字节的执行权限去掉。线程恢复后一执行就抛出0xC0000005(AccessViolation),Windows 会立刻拉起你预先配置的 JIT 调试器;接管后把执行权限补回来(如setpagerights <region>, ExecuteReadWrite)即可继续。

⚠️ 注意:--jit会强制启用--nopause,请确保 JIT 调试器已配置好,否则程序会直接崩溃退出。

BlobRunner x64 构建:最快编译步骤

  1. 获取源码:
git clone https://gitcode.com/gh_mirrors/bl/BlobRunner
  1. 打开x64 Native Tools Command Prompt,进入项目目录:
cl /Feblobrunner64.exe /Foblobrunner64.out blobrunner.c

产物blobrunner64.exe即可用于 x64 shellcode 调试(完整构建说明见 README.md)。

小结

对比项x86 版本x64 版本
跳转方式__asm jmp内联汇编CreateThread 挂起线程
暂停机制跳转前断点 / getchar线程创建即挂起,ResumeThread 前接管
栈环境主线程栈独立线程栈

一句话总结:内联汇编在 x64 失效是编译器层面的限制,而"挂起线程"把"跳转前暂停"变成了"线程尚未起跑",调试窗口因此自然出现

【免费下载链接】BlobRunnerQuickly debug shellcode extracted during malware analysis项目地址: https://gitcode.com/gh_mirrors/bl/BlobRunner

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/26 16:42:37

记录C++ 11

类模板语法声明和实现必须写在同一文件&#xff0c;编译器由类模板生成类的过程叫做类模块的实例化&#xff0c;编译器自动用具体的数据类型替换类模板中的类型参数生成模板类的代码&#xff0c;由模板类实例化得到的类叫做模板类非类型参数必须是整型、枚举或者指向静态区的指…

作者头像 李华
网站建设 2026/8/26 16:21:50

OpenCV ncnn 人脸识别(一)

1. ncnn 下载ncnn源码 Releases Tencent/ncnn GitHub 编译ncnn 打开mingw自带的终端 rd /s /q build mkdir build cd buildcmake .. -G "MinGW Makefiles" ^ -DCMAKE_BUILD_TYPERelease ^ -DNCNN_VULKANOFF ^ -DNCNN_BUILD_EXAMPLESOFF ^ -DNCNN_BUILD_TESTSOF…

作者头像 李华