那CTF,那VMre,那些事(一)
字数 950 2025-08-07 08:22:18

VM虚拟机逆向分析技术详解

一、虚拟机保护技术基础概念

1.1 虚拟机保护定义

虚拟机保护(VM)是一种基于虚拟机的代码保护技术,它将基于x86汇编系统的可执行代码转换为字节码指令系统的代码,以达到不被轻易篡改和逆向的目的。

核心原理

  • 实现一个小型虚拟机
  • 将程序代码转换为自定义操作码(opcode)
  • 执行时通过解释操作码来执行对应函数

1.2 虚拟机基本结构

VMRUN (入口函数)
  ↓
Dispatcher (调度器,解释opcode并选择Handler)
  ↓
Handler (功能模块)
  ↑
Opcode (操作码)

1.3 关键组件

  1. 虚拟寄存器:用于传参或存放返回值
  2. EIP指针:指向正在解释的opcode地址
  3. Opcode列表:存放所有opcode及其处理函数

二、虚拟机实现技术

2.1 数据结构定义

typedef struct {
    unsigned long r1, r2, r3;  // 虚拟寄存器
    unsigned char *eip;        // 指向当前opcode
    vm_opcode op_list[OPCODE_N]; // opcode列表
} vm_cpu;

typedef struct {
    unsigned char opcode;
    void (*handle)(void*);
} vm_opcode;

2.2 虚拟机初始化

void *vm_init() {
    vm_cpu *cpu;
    cpu->r1 = cpu->r2 = cpu->r3 = 0;
    cpu->eip = (unsigned char *)vm_code;
    
    // 关联opcode与处理函数
    cpu->op_list[0].opcode = 0xf1;
    cpu->op_list[0].handle = (void (*)(void *))mov;
    
    // 其他opcode初始化...
    
    vm_stack = malloc(0x512);
    memset(vm_stack, 0, 0x512);
}

2.3 虚拟机入口函数

void vm_start(vm_cpu *cpu) {
    cpu->eip = (unsigned char*)opcodes;
    while((*cpu->eip) != 0xf4) {  // 0xf4为RET操作码
        vm_dispatcher(*cpu->eip);
    }
}

2.4 解释执行器

void vm_dispatcher(vm_cpu *cpu) {
    for(int j=0; j<OPCODE_N; j++) {
        if(*cpu->eip == cpu->op_list[j].opcode) {
            cpu->op_list[j].handle(cpu);
            break;
        }
    }
}

2.5 处理函数实现示例

MOV指令实现

void mov(vm_cpu *cpu) {
    unsigned char *res = cpu->eip + 1;  // 寄存器标识
    int *offset = (int *)(cpu->eip + 2); // 数据偏移
    char *dest = vm_stack;
    
    switch(*res) {
        case 0xe1: cpu->r1 = *(dest + *offset); break;
        case 0xe2: cpu->r2 = *(dest + *offset); break;
        case 0xe3: cpu->r3 = *(dest + *offset); break;
        case 0xe4: *(dest + *offset) = cpu->r1; break;
    }
    cpu->eip += 6;  // MOV指令占6字节
}

XOR指令实现

void xor(vm_cpu *cpu) {
    int num = cpu->r1 ^ cpu->r2;
    num ^= 0x12;
    cpu->r1 = num;
    cpu->eip += 1;  // XOR指令占1字节
}

三、CTF中的VM逆向分析

3.1 解题通用步骤

  1. 分析VM结构
  2. 分析opcode
  3. 编写parser
  4. 逆向算法

3.2 VM结构常见类型

  1. 基于栈
  2. 基于队列
  3. 基于信号量

3.3 Opcode常见类型

  1. 与VM数据结构对应的指令:push/pop
  2. 运算指令:add/sub/mul/xor等

四、实战案例分析

4.1 WxyVM1分析

题目特点

  • 无壳64位程序
  • 使用大数组进行运算

关键函数

__int64 sub_4005B6() {
    for (i = 0; i <= 14999; i += 3) {
        v2 = byte_6010C0[i + 2];
        v3 = byte_6010C0[i + 1];
        switch (byte_6010C0[i]) {
            case 1: *(&byte_604B80 + v3) += v2; break;
            case 2: *(&byte_604B80 + v3) -= v2; break;
            case 3: *(&byte_604B80 + v3) ^= v2; break;
            // ...
        }
    }
}

逆向脚本

from idc_bc695 import *
arr = [4294967236, 52, 34, 4294967217, ...]  # 目标数组
addr = 0x6010c0

for i in range(14997, -1, -3):  # 逆向处理
    v0 = Byte(addr + i)
    v3 = Byte(addr + i + 2)
    result = Byte(addr + i + 1)
    
    if v0 == 1: arr[result] -= v3
    if v0 == 2: arr[result] += v3
    if v0 == 3: arr[result] ^= v3

print(''.join(map(chr, [x & 0xff for x in arr])))

4.2 [网鼎杯2020青龙组]signal分析

VM结构分析

int vm_operad(int *a1, int a2) {
    while(op_index < a2) {
        switch(a1[op_index]) {
            case 1: op_code[v7] = v4; break;  // 存储
            case 2: v4 = a1[op_index+1] + op_code[v8]; break;  // 加
            case 3: v4 = op_code[v8] - a1[op_index+1]; break;  // 减
            case 4: v4 = a1[op_index+1] ^ op_code[v8]; break;  // 异或
            case 5: v4 = a1[op_index+1] * op_code[v8]; break;  // 乘
            case 7: if(op_code[v8] != a1[op_index+1]) exit(0); break;  // 验证
            // ...
        }
    }
}

逆向脚本

encrypts = [0x22, 0x3F, 0x34, 0x32, 0x72, 0x33, 0x18, 0xA7, 
            0x31, 0xF1, 0x28, 0x84, 0xC1, 0x1E, 0x7A]

data = [
    [4,0x10,8,3,5,1], [4,0x20,8,5,3,1], [3,2,8,0x0B,1],
    # ... 其他操作序列
]

def encrypted(c, li):
    for x in range(len(li)):
        if li[x] == 2: c += li[x+1]; x+=2
        elif li[x] == 3: c -= li[x+1]; x+=2
        elif li[x] == 4: c ^= li[x+1]; x+=2
        elif li[x] == 5: c *= li[x+1]; x+=2
        elif li[x] == 11: c -= 1; x+=1
        elif li[x] == 12: c += 1; x+=1
    return c

flag = ''
for x in range(len(encrypts)):
    for i in 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890':
        if encrypted(ord(i), data[x]) == encrypts[x]:
            flag += i
            break
print('flag{%s}' % flag)

五、总结与进阶

5.1 VM逆向特点

  1. 核心算法通常不复杂
  2. 主要考察switch分支分析能力
  3. 通常没有反调试和代码混淆

5.2 学习建议

  1. 掌握基本虚拟机结构
  2. 熟练分析switch-case结构
  3. 练习编写逆向脚本
  4. 学习使用angr等符号执行工具

5.3 未来发展方向

  1. 更复杂的虚拟机结构
  2. 结合反调试技术
  3. 多层虚拟机嵌套
  4. 动态生成opcode

六、参考资料

  1. https://www.cnblogs.com/nigacat/p/13039289.html
  2. https://www.freebuf.com/column/174623.html
  3. https://xz.aliyun.com/t/3851
  4. https://blog.csdn.net/weixin_43876357/article/details/108570305
VM虚拟机逆向分析技术详解 一、虚拟机保护技术基础概念 1.1 虚拟机保护定义 虚拟机保护(VM)是一种基于虚拟机的代码保护技术,它将基于x86汇编系统的可执行代码转换为字节码指令系统的代码,以达到不被轻易篡改和逆向的目的。 核心原理 : 实现一个小型虚拟机 将程序代码转换为自定义操作码(opcode) 执行时通过解释操作码来执行对应函数 1.2 虚拟机基本结构 1.3 关键组件 虚拟寄存器 :用于传参或存放返回值 EIP指针 :指向正在解释的opcode地址 Opcode列表 :存放所有opcode及其处理函数 二、虚拟机实现技术 2.1 数据结构定义 2.2 虚拟机初始化 2.3 虚拟机入口函数 2.4 解释执行器 2.5 处理函数实现示例 MOV指令实现 XOR指令实现 三、CTF中的VM逆向分析 3.1 解题通用步骤 分析VM结构 分析opcode 编写parser 逆向算法 3.2 VM结构常见类型 基于栈 基于队列 基于信号量 3.3 Opcode常见类型 与VM数据结构对应的指令:push/pop 运算指令:add/sub/mul/xor等 四、实战案例分析 4.1 WxyVM1分析 题目特点 : 无壳64位程序 使用大数组进行运算 关键函数 : 逆向脚本 : 4.2 [ 网鼎杯2020青龙组 ]signal分析 VM结构分析 : 逆向脚本 : 五、总结与进阶 5.1 VM逆向特点 核心算法通常不复杂 主要考察switch分支分析能力 通常没有反调试和代码混淆 5.2 学习建议 掌握基本虚拟机结构 熟练分析switch-case结构 练习编写逆向脚本 学习使用angr等符号执行工具 5.3 未来发展方向 更复杂的虚拟机结构 结合反调试技术 多层虚拟机嵌套 动态生成opcode 六、参考资料 https://www.cnblogs.com/nigacat/p/13039289.html https://www.freebuf.com/column/174623.html https://xz.aliyun.com/t/3851 https://blog.csdn.net/weixin_ 43876357/article/details/108570305