首页
课程
问答
CTF
社区
招聘
峰会
发现
排行榜
知识库
工具下载
看雪20年
看雪商城
证书查询
登录
注册
首页
社区
课程
招聘
发现
问答
CTF
排行榜
知识库
工具下载
峰会
看雪商城
证书查询
社区
逆向工程
发新帖
4
1
[分享]vm解释器入门
发表于: 2026-4-18 18:01
1591
[分享]vm解释器入门
mb_binusgki
2026-4-18 18:01
1591
# 前言 我们日常接触到的Cpu、VMware、Java JVM、VMProtect等等 都有核心的解释指令的功能,解释器也在多个领域有着举足轻重的地位,今天笔者将实现一个简单的自定义解释器程序作为入门练习,并使用自定义语法实现一个简单的阶乘练习 # 一、定义解释器结构 这是一个极简的解释器,里面仅包含5个通用寄存器,一个程序计数器,以及一个运行状态标识: ``` struct VM { int reg[5]; // 5个通用寄存器 R0~R4 int pc; // 程序计数器 bool running; // 是否运行 }; ``` # 二、定义指令 指令方面定义了:寄存器赋值、加法、减法、自定义输出寄存器、直接跳转、条件跳转、停止功能 其中R代表 Register 寄存器,IMM代表 Immediate 立即数,笔者暂时没有做MOV R,R\IMM的整合,因为是简单实现就分开为OP_MOV_IMM 和 OP_MOV_REG ``` /// < summary > /// | 指令 | 含义 | /// | ---------- | ------ | /// | OP_MOV_IMM R1, R2 | 寄存器赋值1 | /// | OP_MOV_REG R, IMM | 寄存器赋值2 | /// | ADD R1, R2 | 加法 | /// | SUB R1, R2 | 减法 | /// | PRINT R | 输出 | /// | OP_JMP IMM | 跳转 | /// | OP_JZ R, IMM | 条件跳转 | /// | OP_HALT | 停止 | /// < summary > enum { OP_MOV_IMM, OP_MOV_REG, OP_ADD, OP_SUB, OP_PRINT, OP_JMP, OP_JZ, OP_HALT }; ``` # 三、解释器实现 run方法的实现是整个解释器的核心,其中包含解释每个指令的逻辑,所有指令是混合在一起的,这会导致不同版本的解释器和不同版本的代码要做兼容,这就是微软一直以来在干的工作 例如其中OP_MOV_IMM后是接收2个参数,OP_PRINT后是接收一个参数,如果一个对不上后续指令都会出错 ``` //解释器 void run(VM& vm, int* code) { //初始化解释器 vm.pc = 0;//计数器置零 vm.running = true;//设置运行态 //循环解释 while (vm.running) { int op = code[vm.pc++]; // 取指令 switch (op) {//判断指令 case OP_MOV_IMM: { int reg = code[vm.pc++];//取寄存器编号 int val = code[vm.pc++];//取值 vm.reg[reg] = val;//寄存器赋值 break; } case OP_MOV_REG: { int dst = code[vm.pc++];//R1编号 int src = code[vm.pc++];//R2编号 vm.reg[dst] = vm.reg[src];//R1 = R2 break; } case OP_ADD: { int r1 = code[vm.pc++];//取操作数1 int r2 = code[vm.pc++];//取操作数2 vm.reg[r1] += vm.reg[r2];//加法操作 break; } case OP_SUB: { int r1 = code[vm.pc++];//取操作数1 int r2 = code[vm.pc++];//取操作数2 vm.reg[r1] -= vm.reg[r2];//减法操作 break; } case OP_PRINT: { int reg = code[vm.pc++];//取寄存器编号 printf("R%d = %d\n", reg, vm.reg[reg]);//输出寄存器的值 break; } case OP_JMP: { int addr = code[vm.pc++]; vm.pc = addr; break; } case OP_JZ: { int reg = code[vm.pc++]; int addr = code[vm.pc++]; if (vm.reg[reg] == 0) { vm.pc = addr; } break; } case OP_HALT: { vm.running = false;//改变运行态 break; } default://未知指令 printf("Unknown opcode: %d\n", op); vm.running = false; break; } } } ``` # 四、阶乘练习 最后是实现的阶乘练习,实现为5!=5*4*3*2*1=120 要特别说明一下,本轮练习笔者并未实现乘法功能,具体实现实在 inner_loop 中用加法实现的乘法功能 入参R0=5,内部用到R3作为临时变量,R1作为返回结果,R4为乘法计数 大体思路为:首先初始化参数,进入outer_loop先判断n是否等于0,因为n每次循环会--,等于0则退出,如果不为0则做result*n=1*5=5,此时第一轮result=5 n--,第二轮为result*n=5*4,以此类推 ``` //阶乘练习 int main() { VM vm = { 0 }; //入参 int n = 5; //| 寄存器 | 含义 | //| ---- | ------ | //| R0 | n | //| R1 | result | //| R2 | 常量1 | //| R3 | temp | //| R4 | counter | int program[] = { // 初始化 OP_MOV_IMM, R0, 5, // n = 5 OP_MOV_IMM, R1, 1, // result = 1 OP_MOV_IMM, R2, 1, // const 1 // ===== outer_loop (addr = 9) ===== OP_JZ, R0, 37, // if n == 0 -> end OP_MOV_IMM, R3, 0, // temp = 0 OP_MOV_REG, R4, R0, // counter = n // ===== inner_loop (addr = 18) ===== 乘法功能 OP_JZ, R4, 29, // if counter == 0 -> inner_end OP_ADD, R3, R1, // temp += result OP_SUB, R4, R2, // counter-- OP_JMP, 18, // 回到 inner_loop // ===== inner_end (addr = 29) ===== OP_MOV_REG, R1, R3, // result = temp OP_SUB, R0, R2, // n-- OP_JMP, 9, // 回到 outer_loop // ===== end (addr = 37) ===== OP_PRINT, R1, OP_HALT }; //运行 run(vm, program); return 0; } ``` # 五、总结 本次vm解释器实现为极简写法,用于入门原理了解,对于现实的情况会复杂很多,例如笔者并未做区分标志寄存器,是直接用4字节的通用寄存器作为标志寄存器判断,这会导致空间的浪费 本小结作为vm练习的开端,后续还会继续完善栈区和宏指令实现 欢迎讨论 如有错漏 请留言
传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!!
最后于
2026-4-18 23:35 被mb_binusgki编辑 ,原因:
#调试逆向
#系统底层
#VM保护
#问题讨论
#其他内容
#软件保护
#加密算法
收藏
・
4
点赞
・
1
打赏
分享
分享到微信
分享到QQ
分享到微博
赞赏记录
参与人
雪币
留言
时间
我的小拇指啊
谢谢你的细致分析,受益匪浅!
2026-4-20 21:09
查看更多
赞赏
×
1 雪花
5 雪花
10 雪花
20 雪花
50 雪花
80 雪花
100 雪花
150 雪花
200 雪花
支付方式:
微信支付
赞赏留言:
快捷留言
感谢分享~
精品文章~
原创内容~
精彩转帖~
助人为乐~
感谢分享~
最新回复
(
0
)
游客
登录
|
注册
方可回帖
回帖
表情
雪币赚取及消费
高级回复
返回
mb_binusgki
29
发帖
6
回帖
0
RANK
关注
私信
他的文章
[分享]注册表回调内核通信
391
[分享]简单编译器实现
1875
[分享]Lexer简单词法解析器入门
1235
[分享]vm解释器入门2
1297
[分享]vm解释器入门
1591
关于我们
联系我们
企业服务
看雪公众号
专注于PC、移动、智能设备安全研究及逆向工程的开发者社区
看原图
赞赏
×
雪币:
+
留言:
快捷留言
为你点赞!
返回
顶部