找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

2642

积分

0

好友

364

主题
发表于 18 小时前 | 查看: 0| 回复: 0

昨天,我们通过编写VM字节码程序了解了它的“取码、译码、执行”规则。今天,我们继续对那个程序进行一次深入的逆向分析,再次加深理解和感悟。

程序下载链接(历史文章):

  • smokestack.exe (对应《“逆向VM字节码程序”的学习(三)》)
  • SimpleVM.exe (对应本文分析的示例程序)
    链接与提取码详见原文,此处不再赘述。

一、 程序概览

通过IDA Pro MCP分析,该程序是一个基于虚拟机(VM)的应用程序,使用自定义字节码来执行逻辑。

二、 VM架构分析

1、 VM解释器函数

  • 地址: 0x41EBA8 (sub_41EBA8)
  • 功能: 核心VM解释器,循环读取并执行字节码指令

2、 VM状态结构

VM状态存储在一个数据结构中,关键字段包括:

  • offset +5120 (0x1400): 程序计数器(PC)
  • offset +5124 (0x1404): 未知寄存器(-1初始值)
  • offset +5128 (0x1408): 停止标志(halt flag)
  • offset +0-4095: 字节码数组(1024个DWORD,即4KB字节码空间)

3、 VM指令集

通过反编译解释器函数,识别出以下13条指令:

指令 0: HALT

  • 操作: 停止VM执行
  • PC变化: PC++
  • 功能: 程序正常退出

指令 1: PUSH (立即数)

  • 操作: 将下一个DWORD作为立即数压入栈
  • PC变化: PC += 2
  • 功能: 压栈操作数
  • 格式: [1] [immediate_value]

指令 2: POP

  • 操作: 从栈中弹出一个值
  • PC变化: PC++
  • 功能: 出栈

指令 3: ADD

  • 操作: 弹出两个值,相加后压栈
  • PC变化: PC++
  • 功能: 加法运算 (v2 = pop(); v1 = pop(); push(v1 + v2))
  • 错误检查: 检查溢出

指令 4: SUB

  • 操作: 弹出两个值,相减后压栈
  • PC变化: PC++
  • 功能: 减法运算 (v2 = pop(); v1 = pop(); push(v1 - v2))
  • 错误检查: 检查溢出

指令 5: MUL

  • 操作: 弹出两个值,相乘后压栈
  • PC变化: PC++
  • 功能: 乘法运算 (v2 = pop(); v1 = pop(); push(v1 * v2))
  • 错误检查: 检查乘法溢出

指令 6: DIV

  • 操作: 弹出两个值,相除后压栈
  • PC变化: PC++
  • 功能: 除法运算 (v2 = pop(); v1 = pop(); push(v1 / v2))
  • 错误检查: 除数为0时报错 "Division by zero"

指令 7: JMP (无条件跳转)

  • 操作: 直接跳转到指定地址
  • PC变化: PC = [PC+1]
  • 功能: 无条件跳转
  • 格式: [7] [target_address]

指令 8: JNZ (条件跳转)

  • 操作: 如果栈顶非零则跳转
  • PC变化:
    • 如果pop() != 0: PC += 2
    • 否则: PC = [PC+1]
  • 功能: 条件跳转
  • 格式: [8] [target_address]

指令 9: INPUT

  • 操作: 读取用户输入并压栈
  • PC变化: PC++
  • 功能: 输入操作

指令 10: SWAP

  • 操作: 交换栈顶两个元素
  • PC变化: PC++
  • 功能: v2 = pop(); v1 = pop(); push(v2); push(v1)

指令 11: OUTPUT

  • 操作: 输出栈顶值
  • PC变化: PC++
  • 功能: 输出操作

指令 12: EXIT

  • 操作: 设置停止标志
  • PC变化: 不变
  • 功能: 强制退出VM

三、 字节码数据位置

1、 字节码段1

  • 地址: 0x423524
  • 大小: 至少11个DWORD
  • 内容:
    01 00 00 00  ; PUSH
    05 00 00 00  ; [value: 5]
    01 00 00 00  ; PUSH
    03 00 00 00  ; [value: 3]
    01 00 00 00  ; PUSH
    02 00 00 00  ; [value: 2]
    05 00 00 00  ; MUL
    03 00 00 00  ; ADD
    0b 00 00 00  ; OUTPUT
    0c 00 00 00  ; EXIT
    00 00 00 00  ; HALT

    分析: 这段代码计算 5 + (3 * 2) = 11 并输出

2、 字节码段2

  • 地址: 0x423554
  • 大小: 至少14个DWORD
  • 内容:
    01 00 00 00  ; PUSH
    05 00 00 00  ; [value: 5]
    01 00 00 00  ; PUSH
    05 00 00 00  ; [value: 5]
    04 00 00 00  ; SUB
    08 00 00 00  ; JNZ
    0b 00 00 00  ; [target: 11]
    01 00 00 00  ; PUSH
    78 03 00 00  ; [value: 0x378 = 888]
    0b 00 00 00  ; OUTPUT
    0c 00 00 00  ; EXIT
    01 00 00 00  ; PUSH
    e7 03 00 00  ; [value: 0x3e7 = 999]
    0b 00 00 00  ; OUTPUT
    0c 00 00 00  ; EXIT

    分析:

    1. 计算 5 - 5 = 0
    2. 如果结果非0跳转到地址11
    3. 否则输出888并退出
    4. 跳转目标: 输出999并退出

后续还有函数指针表数据。

四、 程序执行流程

1 初始化流程

(1)start (0x421984) - 程序入口
(2)sub_41F0C4 / sub_41F104 - 加载字节码到VM
(3)sub_41F060 - 拷贝字节码数组
(4)sub_41E9B8 - 初始化VM状态
(5)sub_41EBA8 - 执行字节码

2 VM执行过程

(1)检查halt标志
(2)检查PC是否越界 (PC < 0x400)
(3)读取当前指令: opcode = vm[PC]
(4)根据opcode执行相应操作
(5)更新PC
(6)循环执行直到halt

五、辅助函数

  • sub_41EA08: 栈PUSH操作
  • sub_41EA9C: 栈POP操作
  • sub_41EB30: INPUT操作
  • sub_405A70, sub_405D70, sub_4044E4: OUTPUT相关
  • sub_405F50: PC越界错误处理
  • sub_405F58: 溢出错误处理
  • sub_41B698: 错误信息显示
  • sub_407684: 错误退出

六、安全特性

程序包含多项安全检查:

  1. PC边界检查: 防止代码执行越界
  2. 算术溢出检查: ADD/SUB/MUL操作检查溢出
  3. 除零检查: DIV操作检查除数
  4. 栈边界检查: 防止栈溢出/下溢

七、逆向分析建议

1 提取完整字节码

可以通过以下方式提取完整字节码:

  • 0x4235240x423554 读取完整数据
  • 分析调用 sub_41F060 时传递的长度参数

2 编写反汇编器

基于识别的指令集,可以编写Python脚本将字节码反汇编为可读格式。

3 编写VM模拟器

可以用Python重新实现VM解释器,用于动态分析和调试。

八、 示例分析

字节码段1执行过程:

PUSH 5      ; stack = [5]
PUSH 3      ; stack = [5, 3]
PUSH 2      ; stack = [5, 3, 2]
MUL         ; stack = [5, 6]  (3*2)
ADD         ; stack = [11]    (5+6)
OUTPUT      ; 输出: 11
EXIT        ; 退出
HALT

字节码段2执行过程:

PUSH 5      ; stack = [5]
PUSH 5      ; stack = [5, 5]
SUB         ; stack = [0]     (5-5)
JNZ 11      ; 0==0, 不跳转
PUSH 888    ; stack = [888]
OUTPUT      ; 输出: 888
EXIT        ; 退出

九、 总结

这是一个设计良好的基于栈的虚拟机实现,具有:

  • 完整的算术运算指令集
  • 控制流指令(JMP/JNZ)
  • I/O操作(INPUT/OUTPUT)
  • 完善的错误检查机制
  • 清晰的架构设计

该VM可用于:

  • 代码混淆
  • 程序保护
  • 脚本执行引擎
  • CTF挑战

十、 进一步研究方向

  1. 提取并分析所有字节码段
  2. 分析INPUT函数的具体实现
  3. 查找可能的flag检查逻辑
  4. 编写完整的VM模拟器用于动态分析
  5. 寻找可能的VM漏洞或绕过机制

十一、感悟

通过这个系列的学习,我们完成了从逆向分析到理解VM工作原理的闭环。字节码技术作为编程语言与硬件之间的桥梁,其核心价值在于跨平台性、安全性和可优化性。掌握这类逆向工程技能,不仅能深入理解软件保护机制,也为解决复杂的安全挑战(如CTF中的VM逆向题)打下坚实基础。

如果你对逆向工程、虚拟机技术或CTF挑战有更多兴趣,欢迎到 云栈社区 与更多技术爱好者交流探讨。




上一篇:从零到一:我的白标Perp DEX开发手记之充提模块设计
下一篇:使用WebGPU与SDL3构建原生JavaScript游戏运行时
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-2-2 23:45 , Processed in 0.406007 second(s), 42 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表