VMP 分析

VMP 分析

📚 前置知识

本配方涉及以下核心技术,建议先阅读相关章节:

VMP (VMProtect 的简称) 是一种非常强大的软件保护解决方案,它使用虚拟化(一个"虚拟机")来保护代码。受保护的代码不再执行原生 CPU 指令,而是被转换成一种自定义的字节码,只有特定的、嵌入的虚拟机才能解释执行。

分析受 VMP 保护的代码是逆向工程中最具挑战性的任务之一。


1. 核心概念

1.1 虚拟机架构

VMP 的核心是一个嵌入式虚拟机,其基本架构如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
┌─────────────────────────────────────────────────────────────┐
│                      受保护的程序                            │
├─────────────────────────────────────────────────────────────┤
│  原生代码区域        │        VM 保护区域                    │
│  ┌─────────────┐    │    ┌───────────────────────────────┐  │
│  │ 正常函数     │    │    │  VM 入口 (VM Entry)           │  │
│  │ call vm_func│────│───→│  - 保存原生上下文              │  │
│  └─────────────┘    │    │  - 初始化 VM 上下文            │  │
│                     │    │  - 跳转到解释器循环             │  │
│                     │    ├───────────────────────────────┤  │
│                     │    │  解释器循环 (Dispatcher)       │  │
│                     │    │  while(true) {                │  │
│                     │    │    opcode = fetch(VIP);       │  │
│                     │    │    handler = handlers[opcode];│  │
│                     │    │    handler();                 │  │
│                     │    │    VIP += size;               │  │
│                     │    │  }                            │  │
│                     │    ├───────────────────────────────┤  │
│                     │    │  Handler 表                   │  │
│                     │    │  [vAdd, vSub, vMov, vJmp...]  │  │
│                     │    ├───────────────────────────────┤  │
│                     │    │  字节码数据区 (Bytecode)       │  │
│                     │    │  [0x12, 0x34, 0xAB, 0xCD...]  │  │
│                     │    └───────────────────────────────┘  │
└─────────────────────────────────────────────────────────────┘

1.2 关键组件详解

解释器循环 (Dispatcher):读取字节码并分发到相应的 handler:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// 典型的解释器循环伪代码
void vm_dispatcher(VMContext* ctx) {
    while (1) {
        uint8_t opcode = ctx->bytecode[ctx->vip];

        // 解码操作数
        uint32_t operands = decode_operands(ctx);

        // 查找并执行 handler
        HandlerFunc handler = ctx->handler_table[opcode];
        handler(ctx, operands);

        // 更新虚拟指令指针
        ctx->vip += instruction_size(opcode);

        // 检查是否退出 VM
        if (ctx->should_exit) break;
    }
}

VM 上下文结构:存储虚拟机的完整状态:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// 典型的 VM 上下文结构
typedef struct VMContext {
    // 虚拟寄存器(数量和用途因实现而异)
    uint64_t vregs[16];      // 通用虚拟寄存器
    uint64_t vip;            // 虚拟指令指针
    uint64_t vsp;            // 虚拟栈指针
    uint64_t vflags;         // 虚拟标志寄存器

    // 原生上下文备份
    uint64_t saved_regs[32]; // 保存的原生寄存器
    uint64_t saved_sp;       // 保存的原生栈指针

    // VM 运行时数据
    uint8_t* bytecode;       // 字节码基地址
    void** handler_table;    // Handler 函数表
    uint8_t* vm_stack;       // VM 私有栈

    // 控制标志
    int should_exit;         // 退出标志
    uint64_t return_value;   // 返回值
} VMContext;

Handler 示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 虚拟加法 handler
void handler_vAdd(VMContext* ctx, uint32_t operands) {
    uint8_t dst = (operands >> 8) & 0xF;   // 目标寄存器
    uint8_t src1 = (operands >> 4) & 0xF;  // 源寄存器1
    uint8_t src2 = operands & 0xF;         // 源寄存器2

    ctx->vregs[dst] = ctx->vregs[src1] + ctx->vregs[src2];

    // 更新标志位
    update_flags(ctx, ctx->vregs[dst]);
}

// 虚拟跳转 handler
void handler_vJmp(VMContext* ctx, uint32_t operands) {
    int32_t offset = (int32_t)operands;
    ctx->vip += offset;
}

// 虚拟调用原生函数 handler
void handler_vCallNative(VMContext* ctx, uint32_t operands) {
    void* func_addr = (void*)ctx->vregs[operands & 0xF];
    uint64_t arg1 = ctx->vregs[(operands >> 4) & 0xF];
    uint64_t arg2 = ctx->vregs[(operands >> 8) & 0xF];

    // 调用原生函数
    typedef uint64_t (*NativeFunc)(uint64_t, uint64_t);
    ctx->vregs[0] = ((NativeFunc)func_addr)(arg1, arg2);
}

1.3 突变技术

VMP 使用多种突变技术来增加分析难度:

1
2
3
4
5
6
7
8
突变类型                    效果
───────────────────────────────────────────────────────────
指令编码突变      同一虚拟指令,不同保护实例使用不同 opcode
Handler 突变     同一语义的 handler,代码结构完全不同
操作数加密       操作数经过加密,运行时解密
控制流混淆       在 handler 间插入大量垃圾代码和假分支
Handler 内联     将部分 handler 代码内联到 dispatcher
多层 VM          一个 VM 内部再嵌套另一个 VM

2. VMP 识别方法

2.1 静态识别特征

在 IDA Pro 或 Ghidra 中可以观察到的典型特征:

1
2
3
4
5
6
7
特征类型                典型表现
───────────────────────────────────────────────────────────
大量 PUSH 指令序列     VM 入口保存所有原生寄存器
间接跳转密集           jmp [reg + offset] 形式的 handler 分发
巨型函数               单个函数包含数千条指令(VM dispatcher)
无法识别的字符串       字节码数据被误识别为字符串
混乱的控制流图         CFG 呈现"蜘蛛网"状

VM 入口典型模式 (x86-64)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
; VM 入口示例
push    rbp
push    rax
push    rbx
push    rcx
push    rdx
push    rsi
push    rdi
push    r8
push    r9
push    r10
push    r11
push    r12
push    r13
push    r14
push    r15
pushfq                      ; 保存标志寄存器
lea     rsp, [rsp-0x80]     ; 分配 VM 栈空间
mov     rbp, rsp            ; 设置 VM 上下文基址
lea     rsi, [rip+bytecode] ; 加载字节码地址
xor     eax, eax            ; 初始化 VIP
jmp     vm_dispatcher       ; 进入解释器循环

VM 入口典型模式 (ARM64)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
; ARM64 VM 入口示例
stp     x29, x30, [sp, #-0x10]!
stp     x27, x28, [sp, #-0x10]!
stp     x25, x26, [sp, #-0x10]!
stp     x23, x24, [sp, #-0x10]!
stp     x21, x22, [sp, #-0x10]!
stp     x19, x20, [sp, #-0x10]!
stp     x17, x18, [sp, #-0x10]!
; ... 保存所有寄存器
sub     sp, sp, #0x100       ; 分配 VM 上下文空间
mov     x19, sp              ; VM 上下文指针
adr     x20, bytecode        ; 字节码基址
mov     x21, #0              ; VIP 初始化为 0
b       vm_dispatcher        ; 跳转到解释器

2.2 Frida 自动识别脚本

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
// vmp_detector.js - VMP 特征自动检测脚本

const VMPDetector = {
    // 检测结果
    results: {
        vmEntries: [],
        dispatchers: [],
        handlerTables: [],
        bytecodeRegions: []
    },

    // 扫描指定模块
    scanModule: function(moduleName) {
        const module = Process.getModuleByName(moduleName);
        console.log(`[*] Scanning ${moduleName} for VMP signatures...`);
        console.log(`    Base: ${module.base}, Size: ${module.size}`);

        this.findVMEntries(module);
        this.findDispatchers(module);
        this.analyzeControlFlow(module);

        return this.results;
    },

    // 查找 VM 入口
    findVMEntries: function(module) {
        // 特征:连续多个 PUSH 指令
        const pushPattern = Process.arch === 'arm64'
            ? 'FD 7B BF A9'  // stp x29, x30, [sp, #-0x10]!
            : '55 50 53';     // push rbp; push rax; push rbx

        Memory.scan(module.base, module.size, pushPattern, {
            onMatch: (address, size) => {
                // 验证是否为 VM 入口
                if (this.validateVMEntry(address)) {
                    this.results.vmEntries.push({
                        address: address,
                        offset: address.sub(module.base)
                    });
                    console.log(`[+] Found VM Entry at ${address}`);
                }
            },
            onComplete: () => {
                console.log(`[*] VM Entry scan complete. Found: ${this.results.vmEntries.length}`);
            }
        });
    },

    // 验证 VM 入口
    validateVMEntry: function(address) {
        try {
            // 读取后续指令,检查是否有更多 PUSH
            let pushCount = 0;
            let ptr = address;

            for (let i = 0; i < 20; i++) {
                const inst = Instruction.parse(ptr);
                if (!inst) break;

                const mnemonic = inst.mnemonic.toLowerCase();
                if (mnemonic.includes('push') || mnemonic.includes('stp')) {
                    pushCount++;
                } else if (pushCount > 5) {
                    // 找到足够多的 PUSH,可能是 VM 入口
                    return true;
                }

                ptr = ptr.add(inst.size);
            }

            return pushCount >= 8;  // 至少 8 个 PUSH 指令
        } catch (e) {
            return false;
        }
    },

    // 查找 Dispatcher
    findDispatchers: function(module) {
        // 特征:间接跳转 + 循环结构
        const indirectJumpPattern = Process.arch === 'arm64'
            ? 'BR X'   // br xN
            : 'FF 24'; // jmp [reg*scale+base]

        // 分析函数,查找包含间接跳转循环的函数
        const exports = module.enumerateExports();

        exports.forEach(exp => {
            if (exp.type === 'function') {
                this.analyzeFunction(exp.address, exp.name);
            }
        });
    },

    // 分析函数,判断是否为 dispatcher
    analyzeFunction: function(address, name) {
        try {
            let indirectJumps = 0;
            let backwardJumps = 0;
            let ptr = address;

            for (let i = 0; i < 500; i++) {
                const inst = Instruction.parse(ptr);
                if (!inst) break;

                const mnemonic = inst.mnemonic.toLowerCase();

                // 检查间接跳转
                if ((mnemonic === 'br' || mnemonic === 'blr' ||
                     mnemonic === 'jmp' || mnemonic === 'call') &&
                    inst.operands.some(op => op.type === 'reg')) {
                    indirectJumps++;
                }

                // 检查后向跳转(循环特征)
                if (mnemonic.startsWith('b') || mnemonic.startsWith('j')) {
                    const target = inst.operands[0];
                    if (target && target.type === 'imm') {
                        const targetAddr = ptr.add(target.value);
                        if (targetAddr.compare(ptr) < 0) {
                            backwardJumps++;
                        }
                    }
                }

                ptr = ptr.add(inst.size);
            }

            // 高间接跳转 + 后向跳转 = 可能是 dispatcher
            if (indirectJumps > 3 && backwardJumps > 0) {
                this.results.dispatchers.push({
                    address: address,
                    name: name,
                    indirectJumps: indirectJumps,
                    backwardJumps: backwardJumps
                });
                console.log(`[+] Potential Dispatcher: ${name} at ${address}`);
            }
        } catch (e) {
            // 忽略解析错误
        }
    },

    // 控制流分析
    analyzeControlFlow: function(module) {
        console.log('[*] Analyzing control flow complexity...');
        // 可扩展:计算函数的 cyclomatic complexity
    }
};

// 使用示例
function main() {
    const targetLib = 'libnative.so';

    try {
        const results = VMPDetector.scanModule(targetLib);

        console.log('\n========== VMP Detection Results ==========');
        console.log(`VM Entries: ${results.vmEntries.length}`);
        console.log(`Dispatchers: ${results.dispatchers.length}`);

        results.vmEntries.forEach((entry, i) => {
            console.log(`  [${i}] VM Entry at offset: ${entry.offset}`);
        });

        results.dispatchers.forEach((disp, i) => {
            console.log(`  [${i}] Dispatcher: ${disp.name} (indirect: ${disp.indirectJumps})`);
        });
    } catch (e) {
        console.error(`[-] Error: ${e.message}`);
    }
}

// 延迟执行,等待目标库加载
setTimeout(main, 1000);

3. 动态追踪技术

3.1 VM 执行追踪框架

以下是一个完整的 VM 执行追踪框架:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
// vm_tracer.js - VMP 执行追踪框架

class VMTracer {
    constructor(config) {
        this.config = Object.assign({
            moduleName: 'libnative.so',
            dispatcherAddress: null,
            maxTraceCount: 10000,
            dumpContext: true,
            logFile: '/data/local/tmp/vm_trace.log'
        }, config);

        this.traceCount = 0;
        this.handlers = new Map();
        this.executionTrace = [];
        this.vmContext = null;
    }

    // 初始化追踪
    init() {
        console.log('[*] Initializing VM Tracer...');

        // 定位 VM 组件
        this.module = Process.getModuleByName(this.config.moduleName);

        if (this.config.dispatcherAddress) {
            this.dispatcher = this.module.base.add(this.config.dispatcherAddress);
        } else {
            this.dispatcher = this.findDispatcher();
        }

        console.log(`[+] Dispatcher: ${this.dispatcher}`);

        // 设置追踪钩子
        this.setupTracing();
    }

    // 自动查找 Dispatcher
    findDispatcher() {
        // 使用之前的检测逻辑或手动指定
        throw new Error('Please specify dispatcher address in config');
    }

    // 设置追踪钩子
    setupTracing() {
        const self = this;

        // Hook Dispatcher 入口
        Interceptor.attach(this.dispatcher, {
            onEnter: function(args) {
                self.onDispatcherEnter(this.context);
            }
        });

        // 使用 Stalker 进行细粒度追踪
        this.setupStalker();
    }

    // Stalker 追踪设置
    setupStalker() {
        const self = this;

        Stalker.follow(Process.getCurrentThreadId(), {
            events: {
                call: true,
                ret: false,
                exec: true,
                block: false,
                compile: false
            },

            transform: function(iterator) {
                let instruction = iterator.next();

                do {
                    // 检查是否在 VM 模块范围内
                    const addr = instruction.address;
                    if (addr.compare(self.module.base) >= 0 &&
                        addr.compare(self.module.base.add(self.module.size)) < 0) {

                        // 检测 Handler 调用
                        if (self.isHandlerCall(instruction)) {
                            iterator.putCallout(function(context) {
                                self.onHandlerCall(context, addr);
                            });
                        }
                    }

                    iterator.keep();
                } while ((instruction = iterator.next()) !== null);
            },

            onReceive: function(events) {
                // 处理事件
            }
        });
    }

    // 判断是否为 Handler 调用
    isHandlerCall(instruction) {
        const mnemonic = instruction.mnemonic.toLowerCase();

        // 间接调用/跳转通常是 handler dispatch
        if (mnemonic === 'br' || mnemonic === 'blr' ||
            mnemonic === 'jmp' || mnemonic === 'call') {
            return true;
        }

        return false;
    }

    // Dispatcher 入口回调
    onDispatcherEnter(context) {
        console.log('[+] Entering VM Dispatcher');

        // 尝试解析 VM 上下文
        this.parseVMContext(context);
    }

    // Handler 调用回调
    onHandlerCall(context, address) {
        if (this.traceCount >= this.config.maxTraceCount) {
            Stalker.unfollow();
            this.dumpResults();
            return;
        }

        this.traceCount++;

        // 记录执行
        const entry = {
            index: this.traceCount,
            address: address.toString(),
            handler: this.identifyHandler(address, context),
            timestamp: Date.now()
        };

        // 可选:转储 VM 上下文
        if (this.config.dumpContext) {
            entry.context = this.dumpVMContext(context);
        }

        this.executionTrace.push(entry);

        // 实时输出
        if (this.traceCount % 100 === 0) {
            console.log(`[*] Traced ${this.traceCount} instructions...`);
        }
    }

    // 解析 VM 上下文
    parseVMContext(context) {
        // 这需要根据具体 VMP 实现来定制
        // 以下是一个通用示例

        try {
            // ARM64: X19 通常用作 VM 上下文指针
            // x86-64: RBP 或自定义寄存器

            const ctxPtr = Process.arch === 'arm64'
                ? context.x19
                : context.rbp;

            this.vmContext = {
                base: ctxPtr,
                vregs: [],
                vip: null,
                vsp: null
            };

            // 读取虚拟寄存器 (假设 16 个 64 位寄存器)
            for (let i = 0; i < 16; i++) {
                this.vmContext.vregs.push(
                    ctxPtr.add(i * 8).readU64()
                );
            }

            // 读取 VIP 和 VSP (位置需要根据实际实现调整)
            this.vmContext.vip = ctxPtr.add(0x80).readU64();
            this.vmContext.vsp = ctxPtr.add(0x88).readU64();

        } catch (e) {
            console.log(`[-] Failed to parse VM context: ${e.message}`);
        }
    }

    // 转储 VM 上下文
    dumpVMContext(context) {
        if (!this.vmContext) return null;

        try {
            const ctxPtr = this.vmContext.base;
            const vregs = [];

            for (let i = 0; i < 16; i++) {
                vregs.push(ctxPtr.add(i * 8).readU64().toString(16));
            }

            return {
                vregs: vregs,
                vip: ctxPtr.add(0x80).readU64().toString(16),
                vsp: ctxPtr.add(0x88).readU64().toString(16)
            };
        } catch (e) {
            return null;
        }
    }

    // 识别 Handler 类型
    identifyHandler(address, context) {
        // 检查是否已识别
        const key = address.toString();
        if (this.handlers.has(key)) {
            return this.handlers.get(key);
        }

        // 尝试通过代码模式识别
        const handlerType = this.analyzeHandlerCode(address);
        this.handlers.set(key, handlerType);

        return handlerType;
    }

    // 分析 Handler 代码
    analyzeHandlerCode(address) {
        try {
            let hasAdd = false, hasSub = false, hasMul = false;
            let hasLoad = false, hasStore = false;
            let hasCompare = false, hasBranch = false;

            let ptr = address;
            for (let i = 0; i < 30; i++) {
                const inst = Instruction.parse(ptr);
                if (!inst) break;

                const mnemonic = inst.mnemonic.toLowerCase();

                if (mnemonic.includes('add')) hasAdd = true;
                if (mnemonic.includes('sub')) hasSub = true;
                if (mnemonic.includes('mul')) hasMul = true;
                if (mnemonic.includes('ldr') || mnemonic.includes('mov')) hasLoad = true;
                if (mnemonic.includes('str')) hasStore = true;
                if (mnemonic.includes('cmp')) hasCompare = true;
                if (mnemonic.startsWith('b.') || mnemonic.startsWith('j')) hasBranch = true;

                ptr = ptr.add(inst.size);
            }

            // 根据特征推断 handler 类型
            if (hasAdd && !hasSub && !hasMul) return 'vAdd';
            if (hasSub && !hasAdd && !hasMul) return 'vSub';
            if (hasMul) return 'vMul';
            if (hasLoad && hasStore) return 'vMov';
            if (hasCompare && hasBranch) return 'vCmp/vJcc';
            if (hasBranch && !hasCompare) return 'vJmp';
            if (hasLoad && !hasStore) return 'vLoad';
            if (hasStore && !hasLoad) return 'vStore';

            return 'Unknown';
        } catch (e) {
            return 'Error';
        }
    }

    // 导出追踪结果
    dumpResults() {
        console.log('\n========== VM Trace Results ==========');
        console.log(`Total instructions traced: ${this.traceCount}`);
        console.log(`Unique handlers found: ${this.handlers.size}`);

        // 统计 Handler 使用频率
        const handlerStats = {};
        this.executionTrace.forEach(entry => {
            const h = entry.handler;
            handlerStats[h] = (handlerStats[h] || 0) + 1;
        });

        console.log('\nHandler Statistics:');
        Object.entries(handlerStats)
            .sort((a, b) => b[1] - a[1])
            .forEach(([handler, count]) => {
                console.log(`  ${handler}: ${count} (${(count/this.traceCount*100).toFixed(1)}%)`);
            });

        // 保存到文件
        const output = {
            config: this.config,
            stats: {
                totalInstructions: this.traceCount,
                uniqueHandlers: this.handlers.size,
                handlerStats: handlerStats
            },
            trace: this.executionTrace.slice(0, 1000)  // 只保存前 1000 条
        };

        const file = new File(this.config.logFile, 'w');
        file.write(JSON.stringify(output, null, 2));
        file.close();

        console.log(`\n[+] Trace saved to ${this.config.logFile}`);
    }
}

// 使用示例
function main() {
    const tracer = new VMTracer({
        moduleName: 'libnative.so',
        dispatcherAddress: 0x12340,  // 需要手动指定
        maxTraceCount: 5000,
        dumpContext: true
    });

    tracer.init();
    console.log('[*] VM Tracer started. Waiting for VM execution...');
}

setTimeout(main, 1000);

3.2 简化追踪脚本

如果只需要快速追踪,可以使用简化版本:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
// simple_vm_trace.js - 简化版 VM 追踪

// 配置
const CONFIG = {
    module: 'libnative.so',
    vmEntry: 0x1000,      // VM 入口偏移
    dispatcherStart: 0x1500,  // Dispatcher 起始偏移
    dispatcherEnd: 0x2000     // Dispatcher 结束偏移
};

function traceVM() {
    const mod = Process.getModuleByName(CONFIG.module);
    const vmEntry = mod.base.add(CONFIG.vmEntry);
    const dispStart = mod.base.add(CONFIG.dispatcherStart);
    const dispEnd = mod.base.add(CONFIG.dispatcherEnd);

    let traceLog = [];
    let isInVM = false;

    // Hook VM 入口
    Interceptor.attach(vmEntry, {
        onEnter: function(args) {
            isInVM = true;
            console.log('[+] Entering VM');

            // 开始 Stalker 追踪
            Stalker.follow(this.threadId, {
                events: { exec: true },

                onReceive: function(events) {
                    const parsed = Stalker.parse(events);
                    parsed.forEach(event => {
                        if (event[0] === 'exec') {
                            const addr = ptr(event[1]);

                            // 只记录 dispatcher 范围内的执行
                            if (addr.compare(dispStart) >= 0 &&
                                addr.compare(dispEnd) < 0) {
                                traceLog.push({
                                    addr: addr.sub(mod.base).toString(16),
                                    time: Date.now()
                                });
                            }
                        }
                    });
                }
            });
        },

        onLeave: function(retval) {
            isInVM = false;
            Stalker.unfollow();

            console.log(`[+] Exiting VM. Traced ${traceLog.length} instructions`);

            // 输出追踪结果
            if (traceLog.length > 0) {
                console.log('\nExecution trace (first 50):');
                traceLog.slice(0, 50).forEach((entry, i) => {
                    console.log(`  [${i}] 0x${entry.addr}`);
                });
            }
        }
    });
}

traceVM();

4. Handler 分析实战

💡 思路一句话: 对 VMP 分发器中的每个 opcode handler 逐个分析语义(MOV/ADD/XOR/CMP/JMP 等),建立自定义指令集文档,然后编写反汇编器将 VM 字节码翻译回可读伪代码。

4.1 Handler 识别与分类

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
// handler_analyzer.js - Handler 深度分析

class HandlerAnalyzer {
    constructor(moduleBase) {
        this.moduleBase = moduleBase;
        this.knownPatterns = this.initPatterns();
    }

    // 初始化已知 Handler 模式
    initPatterns() {
        return {
            // ARM64 模式
            arm64: {
                vAdd: {
                    pattern: 'add x*, x*, x*',
                    description: '虚拟加法'
                },
                vSub: {
                    pattern: 'sub x*, x*, x*',
                    description: '虚拟减法'
                },
                vXor: {
                    pattern: 'eor x*, x*, x*',
                    description: '虚拟异或'
                },
                vAnd: {
                    pattern: 'and x*, x*, x*',
                    description: '虚拟与'
                },
                vOr: {
                    pattern: 'orr x*, x*, x*',
                    description: '虚拟或'
                },
                vShl: {
                    pattern: 'lsl x*, x*, x*',
                    description: '虚拟左移'
                },
                vShr: {
                    pattern: 'lsr x*, x*, x*',
                    description: '虚拟右移'
                },
                vLoad: {
                    pattern: 'ldr x*, [x*]',
                    description: '虚拟内存读取'
                },
                vStore: {
                    pattern: 'str x*, [x*]',
                    description: '虚拟内存写入'
                },
                vPush: {
                    pattern: 'str x*, [x*, #-8]!',
                    description: '虚拟压栈'
                },
                vPop: {
                    pattern: 'ldr x*, [x*], #8',
                    description: '虚拟出栈'
                }
            },

            // x86-64 模式
            x64: {
                vAdd: {
                    pattern: 'add r*, r*',
                    description: '虚拟加法'
                },
                vSub: {
                    pattern: 'sub r*, r*',
                    description: '虚拟减法'
                },
                vXor: {
                    pattern: 'xor r*, r*',
                    description: '虚拟异或'
                },
                vMov: {
                    pattern: 'mov r*, r*',
                    description: '虚拟移动'
                },
                vLoad: {
                    pattern: 'mov r*, [r*]',
                    description: '虚拟内存读取'
                },
                vStore: {
                    pattern: 'mov [r*], r*',
                    description: '虚拟内存写入'
                }
            }
        };
    }

    // 分析单个 Handler
    analyzeHandler(address) {
        const result = {
            address: address,
            offset: address.sub(this.moduleBase).toString(16),
            type: 'Unknown',
            confidence: 0,
            disassembly: [],
            operations: [],
            dataFlow: {
                reads: [],
                writes: []
            }
        };

        try {
            // 反汇编 Handler
            let ptr = address;
            for (let i = 0; i < 50; i++) {
                const inst = Instruction.parse(ptr);
                if (!inst) break;

                result.disassembly.push({
                    address: ptr.sub(this.moduleBase).toString(16),
                    mnemonic: inst.mnemonic,
                    opStr: inst.opStr,
                    bytes: inst.size
                });

                // 分析操作
                this.analyzeInstruction(inst, result);

                // 检测 Handler 结束
                if (this.isHandlerEnd(inst)) {
                    break;
                }

                ptr = ptr.add(inst.size);
            }

            // 推断 Handler 类型
            this.inferHandlerType(result);

        } catch (e) {
            result.error = e.message;
        }

        return result;
    }

    // 分析单条指令
    analyzeInstruction(inst, result) {
        const mnemonic = inst.mnemonic.toLowerCase();

        // 记录操作类型
        if (mnemonic.includes('add')) {
            result.operations.push('ADD');
        } else if (mnemonic.includes('sub')) {
            result.operations.push('SUB');
        } else if (mnemonic.includes('mul')) {
            result.operations.push('MUL');
        } else if (mnemonic.includes('div')) {
            result.operations.push('DIV');
        } else if (mnemonic.includes('xor') || mnemonic.includes('eor')) {
            result.operations.push('XOR');
        } else if (mnemonic.includes('and')) {
            result.operations.push('AND');
        } else if (mnemonic.includes('or')) {
            result.operations.push('OR');
        } else if (mnemonic.includes('ldr') || mnemonic === 'mov') {
            result.operations.push('LOAD');
        } else if (mnemonic.includes('str')) {
            result.operations.push('STORE');
        } else if (mnemonic.includes('cmp')) {
            result.operations.push('CMP');
        } else if (mnemonic.startsWith('b') || mnemonic.startsWith('j')) {
            result.operations.push('BRANCH');
        }

        // 分析数据流
        // 简化版:基于操作数分析读写
        if (inst.operands) {
            inst.operands.forEach((op, i) => {
                if (op.type === 'reg') {
                    if (i === 0 && !mnemonic.includes('str') && !mnemonic.includes('cmp')) {
                        result.dataFlow.writes.push(op.value);
                    } else {
                        result.dataFlow.reads.push(op.value);
                    }
                }
            });
        }
    }

    // 检测 Handler 结束
    isHandlerEnd(inst) {
        const mnemonic = inst.mnemonic.toLowerCase();

        // 间接跳转通常标志着返回 dispatcher
        if (mnemonic === 'br' || mnemonic === 'blr' || mnemonic === 'ret') {
            return true;
        }

        // x86: jmp reg
        if (mnemonic === 'jmp' && inst.opStr && !inst.opStr.includes('0x')) {
            return true;
        }

        return false;
    }

    // 推断 Handler 类型
    inferHandlerType(result) {
        const ops = result.operations;

        // 统计操作
        const opCounts = {};
        ops.forEach(op => {
            opCounts[op] = (opCounts[op] || 0) + 1;
        });

        // 基于操作组合推断类型
        if (opCounts['ADD'] && !opCounts['SUB'] && !opCounts['MUL']) {
            result.type = 'vAdd';
            result.confidence = 0.8;
        } else if (opCounts['SUB'] && !opCounts['ADD'] && !opCounts['MUL']) {
            result.type = 'vSub';
            result.confidence = 0.8;
        } else if (opCounts['MUL']) {
            result.type = 'vMul';
            result.confidence = 0.8;
        } else if (opCounts['XOR'] && !opCounts['ADD'] && !opCounts['SUB']) {
            result.type = 'vXor';
            result.confidence = 0.8;
        } else if (opCounts['AND'] && !opCounts['OR']) {
            result.type = 'vAnd';
            result.confidence = 0.7;
        } else if (opCounts['OR'] && !opCounts['AND']) {
            result.type = 'vOr';
            result.confidence = 0.7;
        } else if (opCounts['CMP'] && opCounts['BRANCH']) {
            result.type = 'vCmp/vJcc';
            result.confidence = 0.9;
        } else if (opCounts['BRANCH'] && !opCounts['CMP']) {
            result.type = 'vJmp';
            result.confidence = 0.7;
        } else if (opCounts['LOAD'] > opCounts['STORE']) {
            result.type = 'vLoad';
            result.confidence = 0.6;
        } else if (opCounts['STORE'] > opCounts['LOAD']) {
            result.type = 'vStore';
            result.confidence = 0.6;
        }

        return result;
    }

    // 批量分析 Handler 表
    analyzeHandlerTable(tableAddress, count) {
        const handlers = [];
        const ptrSize = Process.pointerSize;

        console.log(`[*] Analyzing ${count} handlers from table at ${tableAddress}`);

        for (let i = 0; i < count; i++) {
            const handlerPtr = tableAddress.add(i * ptrSize).readPointer();

            if (!handlerPtr.isNull()) {
                const analysis = this.analyzeHandler(handlerPtr);
                analysis.index = i;
                handlers.push(analysis);

                console.log(`  [${i.toString().padStart(2, '0')}] ${analysis.type.padEnd(12)} @ 0x${analysis.offset}`);
            }
        }

        return handlers;
    }
}

// 使用示例
function analyzeHandlers() {
    const module = Process.getModuleByName('libnative.so');
    const analyzer = new HandlerAnalyzer(module.base);

    // 假设已知 handler 表地址
    const handlerTableOffset = 0x5000;
    const handlerCount = 64;

    const results = analyzer.analyzeHandlerTable(
        module.base.add(handlerTableOffset),
        handlerCount
    );

    // 输出统计
    const typeStats = {};
    results.forEach(h => {
        typeStats[h.type] = (typeStats[h.type] || 0) + 1;
    });

    console.log('\n========== Handler Type Statistics ==========');
    Object.entries(typeStats)
        .sort((a, b) => b[1] - a[1])
        .forEach(([type, count]) => {
            console.log(`  ${type}: ${count}`);
        });
}

setTimeout(analyzeHandlers, 1000);

4.2 Handler 语义恢复示例

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
// handler_semantics.js - Handler 语义恢复

// 已识别的 Handler 语义映射
const HandlerSemantics = {
    // 基于地址的 Handler 映射(需要实际分析后填充)
    handlers: new Map(),

    // 注册 Handler 语义
    register(address, semantics) {
        this.handlers.set(address.toString(), semantics);
    },

    // 获取 Handler 语义
    get(address) {
        return this.handlers.get(address.toString()) || { type: 'Unknown', action: '?' };
    },

    // 将执行序列翻译为伪代码
    translateToCode(trace) {
        const lines = [];
        const varCounter = { count: 0 };

        trace.forEach((entry, i) => {
            const sem = this.get(ptr(entry.address));
            const ctx = entry.context;

            let line = this.generateCodeLine(sem, ctx, varCounter);
            if (line) {
                lines.push(`/* ${i.toString().padStart(4, '0')} */ ${line}`);
            }
        });

        return lines.join('\n');
    },

    // 生成代码行
    generateCodeLine(semantics, context, varCounter) {
        switch (semantics.type) {
            case 'vAdd':
                return `v${semantics.dst} = v${semantics.src1} + v${semantics.src2};`;

            case 'vSub':
                return `v${semantics.dst} = v${semantics.src1} - v${semantics.src2};`;

            case 'vMul':
                return `v${semantics.dst} = v${semantics.src1} * v${semantics.src2};`;

            case 'vXor':
                return `v${semantics.dst} = v${semantics.src1} ^ v${semantics.src2};`;

            case 'vMov':
                return `v${semantics.dst} = v${semantics.src};`;

            case 'vLoadImm':
                return `v${semantics.dst} = 0x${semantics.imm.toString(16)};`;

            case 'vLoad':
                return `v${semantics.dst} = *(uint64_t*)(v${semantics.base} + ${semantics.offset});`;

            case 'vStore':
                return `*(uint64_t*)(v${semantics.base} + ${semantics.offset}) = v${semantics.src};`;

            case 'vCmp':
                return `flags = compare(v${semantics.src1}, v${semantics.src2});`;

            case 'vJcc':
                return `if (${semantics.condition}) goto L_${semantics.target.toString(16)};`;

            case 'vJmp':
                return `goto L_${semantics.target.toString(16)};`;

            case 'vCall':
                return `v0 = ${semantics.funcName}(v0, v1, v2);`;

            case 'vRet':
                return `return v0;`;

            default:
                return `// Unknown: ${semantics.type}`;
        }
    }
};

// 示例:注册已分析的 Handler
function registerKnownHandlers(moduleBase) {
    // 这些需要根据实际分析结果填充

    HandlerSemantics.register(moduleBase.add(0x1000), {
        type: 'vAdd',
        dst: 0,
        src1: 1,
        src2: 2
    });

    HandlerSemantics.register(moduleBase.add(0x1050), {
        type: 'vSub',
        dst: 0,
        src1: 1,
        src2: 2
    });

    HandlerSemantics.register(moduleBase.add(0x10A0), {
        type: 'vMov',
        dst: 0,
        src: 1
    });

    HandlerSemantics.register(moduleBase.add(0x10F0), {
        type: 'vLoadImm',
        dst: 0,
        imm: 0  // 运行时从字节码读取
    });

    // ... 继续注册其他 handler
}

5. 实战案例分析

💡 思路一句话: Frida hook VM 入口拿到字节码 → 分析分发器识别每个 handler → 逐条翻译 VM 指令 → 重建原始算法逻辑。比 OLLVM 更难,但方法论相同:让程序告诉你它在做什么。

5.1 案例:分析加密签名函数

假设目标 App 有一个被 VMP 保护的签名函数 generateSign(data, timestamp)

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
// case_sign_analysis.js - 签名函数 VMP 分析实战

const SignAnalyzer = {
    // 配置
    config: {
        module: 'libsecurity.so',
        signFuncOffset: 0x8000,
        dispatcherOffset: 0x8500
    },

    // 已收集的执行轨迹
    traces: [],

    // 开始分析
    start() {
        const module = Process.getModuleByName(this.config.module);
        const signFunc = module.base.add(this.config.signFuncOffset);

        console.log('[*] Starting sign function analysis...');
        console.log(`[*] Target: ${signFunc}`);

        // Hook 签名函数
        Interceptor.attach(signFunc, {
            onEnter: (args) => {
                this.currentInput = {
                    data: args[0].readUtf8String(),
                    timestamp: args[1].toInt32()
                };

                console.log(`\n[+] generateSign called`);
                console.log(`    data: ${this.currentInput.data}`);
                console.log(`    timestamp: ${this.currentInput.timestamp}`);

                // 开始追踪
                this.startTracing();
            },

            onLeave: (retval) => {
                this.stopTracing();

                const result = retval.readUtf8String();
                console.log(`    result: ${result}`);

                // 分析此次追踪
                this.analyzeCurrentTrace();
            }
        });
    },

    // 开始追踪
    startTracing() {
        this.currentTrace = {
            input: this.currentInput,
            handlers: [],
            operations: [],
            memoryAccess: [],
            apiCalls: []
        };

        // 设置详细追踪(使用之前的 VMTracer)
        // 这里简化处理
        console.log('[*] Tracing VM execution...');
    },

    // 停止追踪
    stopTracing() {
        console.log(`[*] Trace complete. Captured ${this.currentTrace.handlers.length} handlers`);
        this.traces.push(this.currentTrace);
    },

    // 分析当前追踪
    analyzeCurrentTrace() {
        const trace = this.currentTrace;

        console.log('\n========== Trace Analysis ==========');

        // 1. 统计 Handler 使用
        const handlerStats = {};
        trace.handlers.forEach(h => {
            handlerStats[h.type] = (handlerStats[h.type] || 0) + 1;
        });

        console.log('\nHandler usage:');
        Object.entries(handlerStats)
            .sort((a, b) => b[1] - a[1])
            .forEach(([type, count]) => {
                console.log(`  ${type}: ${count}`);
            });

        // 2. 分析 API 调用
        if (trace.apiCalls.length > 0) {
            console.log('\nDetected API calls:');
            trace.apiCalls.forEach(call => {
                console.log(`  ${call.name}(${call.args.join(', ')})`);
            });
        }

        // 3. 分析内存访问模式
        this.analyzeMemoryPattern(trace);

        // 4. 尝试重建算法
        this.reconstructAlgorithm(trace);
    },

    // 分析内存访问模式
    analyzeMemoryPattern(trace) {
        console.log('\nMemory access pattern:');

        // 按地址分组
        const accessGroups = {};
        trace.memoryAccess.forEach(access => {
            const region = this.identifyRegion(access.address);
            if (!accessGroups[region]) {
                accessGroups[region] = { reads: 0, writes: 0 };
            }
            if (access.type === 'read') {
                accessGroups[region].reads++;
            } else {
                accessGroups[region].writes++;
            }
        });

        Object.entries(accessGroups).forEach(([region, stats]) => {
            console.log(`  ${region}: ${stats.reads} reads, ${stats.writes} writes`);
        });
    },

    // 识别内存区域
    identifyRegion(address) {
        // 简化实现
        return 'unknown';
    },

    // 尝试重建算法
    reconstructAlgorithm(trace) {
        console.log('\n========== Algorithm Reconstruction ==========');

        // 基于操作序列推断
        const ops = trace.operations;

        // 查找常见加密操作模式
        let hasXor = ops.filter(o => o === 'XOR').length > 10;
        let hasRound = this.detectRoundStructure(trace);
        let hasSbox = this.detectSboxUsage(trace);

        console.log('Algorithm characteristics:');
        console.log(`  - XOR operations: ${hasXor ? 'Heavy (possible stream/block cipher)' : 'Minimal'}`);
        console.log(`  - Round structure: ${hasRound ? 'Detected' : 'Not detected'}`);
        console.log(`  - S-box usage: ${hasSbox ? 'Detected (possible AES/DES)' : 'Not detected'}`);

        // 输出推测
        console.log('\nPossible algorithm:');
        if (hasSbox && hasRound) {
            console.log('  -> Block cipher (AES/DES family)');
        } else if (hasXor && !hasSbox) {
            console.log('  -> Stream cipher or custom XOR-based');
        } else {
            console.log('  -> Custom/Unknown');
        }
    },

    // 检测轮结构
    detectRoundStructure(trace) {
        // 查找重复的操作模式
        // 简化实现
        return false;
    },

    // 检测 S-box 使用
    detectSboxUsage(trace) {
        // 查找表查找操作
        // 简化实现
        return false;
    },

    // 差分分析
    differentialAnalysis() {
        if (this.traces.length < 2) {
            console.log('[-] Need at least 2 traces for differential analysis');
            return;
        }

        console.log('\n========== Differential Analysis ==========');

        // 比较不同输入的执行轨迹
        const trace1 = this.traces[0];
        const trace2 = this.traces[1];

        // 找出差异点
        let diffPoints = [];
        const minLen = Math.min(trace1.handlers.length, trace2.handlers.length);

        for (let i = 0; i < minLen; i++) {
            if (trace1.handlers[i].type !== trace2.handlers[i].type) {
                diffPoints.push({
                    index: i,
                    trace1: trace1.handlers[i],
                    trace2: trace2.handlers[i]
                });
            }
        }

        console.log(`Execution divergence points: ${diffPoints.length}`);
        diffPoints.slice(0, 10).forEach(dp => {
            console.log(`  [${dp.index}] ${dp.trace1.type} vs ${dp.trace2.type}`);
        });
    }
};

// 启动分析
SignAnalyzer.start();

5.2 Unidbg 辅助分析

当 Frida 动态追踪不够高效时,可以使用 Unidbg 进行离线分析:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
// VMPAnalyzer.java - 使用 Unidbg 分析 VMP

import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.arm.backend.Unicorn2Factory;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.*;
import com.github.unidbg.memory.Memory;
import unicorn.Arm64Const;
import java.io.*;
import java.util.*;

public class VMPAnalyzer extends AbstractJni {

    private final AndroidEmulator emulator;
    private final VM vm;
    private final Module module;

    // VMP 追踪数据
    private List<TraceEntry> traceLog = new ArrayList<>();
    private Set<Long> handlerAddresses = new HashSet<>();
    private Map<Long, String> handlerTypes = new HashMap<>();

    // VMP 配置
    private long dispatcherStart;
    private long dispatcherEnd;
    private long vmContextReg;  // 存储 VM 上下文的寄存器

    public VMPAnalyzer() {
        // 创建模拟器
        emulator = AndroidEmulatorBuilder
            .for64Bit()
            .setProcessName("com.target.app")
            .addBackendFactory(new Unicorn2Factory(true))
            .build();

        Memory memory = emulator.getMemory();
        memory.setLibraryResolver(new AndroidResolver(23));

        vm = emulator.createDalvikVM();
        vm.setJni(this);
        vm.setVerbose(false);

        // 加载目标库
        DalvikModule dm = vm.loadLibrary(new File("libsecurity.so"), false);
        module = dm.getModule();

        // 设置 VMP 范围
        dispatcherStart = module.base + 0x8500;
        dispatcherEnd = module.base + 0x9000;
        vmContextReg = Arm64Const.UC_ARM64_REG_X19;  // ARM64 常用 X19
    }

    // 设置追踪钩子
    public void setupTracing() {
        // 代码追踪钩子
        emulator.getBackend().hook_add_new(
            new CodeHook() {
                @Override
                public void hook(Backend backend, long address, int size, Object user) {
                    // 只追踪 dispatcher 范围内的代码
                    if (address >= dispatcherStart && address < dispatcherEnd) {
                        recordExecution(address);
                    }
                }
            },
            dispatcherStart,
            dispatcherEnd,
            null
        );

        // 内存访问钩子
        emulator.getBackend().hook_add_new(
            new ReadHook() {
                @Override
                public void hook(Backend backend, long address, int size, Object user) {
                    recordMemoryRead(address, size);
                }
            },
            1,
            0,
            null
        );
    }

    // 记录执行
    private void recordExecution(long address) {
        TraceEntry entry = new TraceEntry();
        entry.address = address;
        entry.offset = address - module.base;
        entry.timestamp = System.nanoTime();

        // 读取 VM 上下文
        entry.vmContext = dumpVMContext();

        // 识别 Handler
        if (!handlerAddresses.contains(address)) {
            String handlerType = identifyHandler(address);
            handlerAddresses.add(address);
            handlerTypes.put(address, handlerType);
        }
        entry.handlerType = handlerTypes.get(address);

        traceLog.add(entry);
    }

    // 转储 VM 上下文
    private VMContext dumpVMContext() {
        VMContext ctx = new VMContext();

        // 读取上下文指针
        long ctxPtr = emulator.getBackend().reg_read(vmContextReg).longValue();

        // 读取虚拟寄存器 (假设 16 个 64 位寄存器)
        ctx.vregs = new long[16];
        for (int i = 0; i < 16; i++) {
            ctx.vregs[i] = emulator.getBackend().mem_read(ctxPtr + i * 8, 8)
                .getLong(0);
        }

        // 读取 VIP
        ctx.vip = emulator.getBackend().mem_read(ctxPtr + 0x80, 8).getLong(0);

        return ctx;
    }

    // 识别 Handler 类型
    private String identifyHandler(long address) {
        // 读取指令并分析
        byte[] code = emulator.getBackend().mem_read(address, 64);

        // 简化的模式匹配
        // 实际实现需要更复杂的反汇编分析

        return "Unknown";
    }

    // 记录内存读取
    private void recordMemoryRead(long address, int size) {
        // 可选:记录内存访问以分析数据流
    }

    // 执行目标函数
    public String executeSignFunction(String data, int timestamp) {
        // 清空之前的追踪
        traceLog.clear();

        // 调用签名函数
        DvmObject<?> result = vm.callStaticJniMethod(
            "Lcom/target/Security;",
            "generateSign(Ljava/lang/String;I)Ljava/lang/String;",
            new StringObject(vm, data),
            timestamp
        );

        return result.getValue().toString();
    }

    // 输出分析结果
    public void outputAnalysis() {
        System.out.println("\n========== VMP Analysis Results ==========");
        System.out.println("Total instructions traced: " + traceLog.size());
        System.out.println("Unique handlers: " + handlerAddresses.size());

        // Handler 统计
        Map<String, Integer> handlerStats = new HashMap<>();
        for (TraceEntry entry : traceLog) {
            handlerStats.merge(entry.handlerType, 1, Integer::sum);
        }

        System.out.println("\nHandler usage:");
        handlerStats.entrySet().stream()
            .sorted(Map.Entry.<String, Integer>comparingByValue().reversed())
            .forEach(e -> System.out.printf("  %s: %d%n", e.getKey(), e.getValue()));

        // 输出追踪日志
        exportTraceLog("vm_trace.json");
    }

    // 导出追踪日志
    private void exportTraceLog(String filename) {
        // JSON 序列化追踪数据
        // 实现略
    }

    // 数据类
    static class TraceEntry {
        long address;
        long offset;
        long timestamp;
        String handlerType;
        VMContext vmContext;
    }

    static class VMContext {
        long[] vregs;
        long vip;
    }

    public static void main(String[] args) {
        VMPAnalyzer analyzer = new VMPAnalyzer();
        analyzer.setupTracing();

        // 执行测试
        String result = analyzer.executeSignFunction("test_data", 1703001234);
        System.out.println("Sign result: " + result);

        // 输出分析
        analyzer.outputAnalysis();
    }
}

6. 去虚拟化工具

6.1 常用工具

工具名称类型适用场景链接
VMHunt学术研究自动化 VMP 分析GitHub
VMAttackIDA 插件静态/动态分析结合GitHub
VTILIR 框架构建去虚拟化器GitHub
NoVmp开源工具VMProtect 2.x 分析GitHub
Triton符号执行约束求解辅助分析GitHub

6.2 分析流程建议

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
┌────────────────────────────────────────────────────────────────┐
│                    VMP 分析完整流程                             │
├────────────────────────────────────────────────────────────────┤
│                                                                │
│  ┌─────────────┐    ┌──────────────┐    ┌──────────────────┐   │
│  │ 1. 识别 VMP │───→│ 2. 定位入口  │───→│ 3. 追踪 Handler  │   │
│  │   检测特征   │    │   找 Dispatcher│    │   收集执行轨迹  │   │
│  └─────────────┘    └──────────────┘    └──────────────────┘   │
│         │                  │                     │              │
│         ↓                  ↓                     ↓              │
│  使用静态分析       动态调试定位          Frida/Unidbg 追踪     │
│  查找 PUSH 序列     间接跳转              Stalker API           │
│                                                                │
│  ┌─────────────┐    ┌──────────────┐    ┌──────────────────┐   │
│  │6. 算法还原  │←───│5. 语义重建   │←───│4. Handler 分析   │   │
│  │  写出伪代码  │    │  翻译为 C 码 │    │  识别每个操作    │   │
│  └─────────────┘    └──────────────┘    └──────────────────┘   │
│         │                  │                     │              │
│         ↓                  ↓                     ↓              │
│  验证算法正确性      差分分析辅助          模式匹配识别         │
│  复现签名逻辑       多输入对比             建立 Handler 表      │
│                                                                │
└────────────────────────────────────────────────────────────────┘

7. 关键要点

  1. 不要试图完全去虚拟化 - 除非是研究目的,否则追踪执行比反编译 VM 更实用。

  2. 动态分析优先 - VMP 的突变性使静态分析极其困难,动态追踪是主要手段。

  3. 关注 Handler 语义 - 理解每个 Handler 做什么比理解 VM 整体架构更有价值。

  4. 差分分析 - 使用不同输入进行多次追踪,对比差异找出关键逻辑。

  5. 耐心和经验 - VMP 分析需要大量时间和经验积累,没有万能工具。


参考资源


相关内容

如果这篇文章对你有帮助,请我喝杯咖啡吧~
+5 支付宝支付宝
+5 微信微信