在生成可执行代码之前, 必须设计虚拟机.
因为有了虚拟机才有指令系统, 你才知道生成什么代码.
1. 虚拟机架构的指令系统虚拟机的设计完全模仿X86.
AX, BX, CX, DX是通用寄存器.
其他有一堆特殊用途的寄存器, 用来存放内存的段地址, 系统变量(比如IP, PSW), 等等.
见下图.
接下来是设计指令系统. 也就是有哪些指令, 指令的格式, 以及寻址方式.
指令表(部分)
指令名称
操作码
格式
说明
__mov
0x1010
mov op1, op2
传送指令
__add
0x1021
add op1, op2
加法
__sub
0x1050
sub op1, op2
减法
__mul
0x1070
mul op1, op2
乘法
__div
0x1090
div op1, op2
除法
__jmp
0x10a0
jmp op1
跳转
__jz
0x10a1
jz op1
PSW的第0位为1时跳转
__jnz
0x10a2
jnz op1
PSW的第0位为0时跳转
__callpub
0x10a3
callpub op1
准备调用基本函数
__parampub
0x10a4
parampub op1
向基本函数传送参数
__endcallpub
0x10a5
endcallpub op1
调用基本函数
__not
0x10d0
not op1
非
__test
0x10f2
test op1, op2, op3
条件测试
__ret
0x10f3
ret
返回
__ea
0x10f4
ea op1, op2
取地址
__mod
0x10f0
mod op1, op2
取模
__cast
cast op1, op2, op3
类型转换
基本寻址方式有5种:
立即数 直接寻址 寄存器寻址 数组寻址 常量段寻址
14~15bit
12~13bit
8~b11bit
6~7bit
4~5bit
0~3bit
字操作还是, 字节操作,双字操作
间接寻址的深度
基本寻址方式
字操作还是, 字节操作,双字操作
间接寻址的深度
基本寻址方式
2. 实现每一个指令.
我为每个指令写了一个函数, 来运行这个指令, 参数是指令本身.
第一步, 指令预处理, 根据寻址方式把操作转成物理地址, check地址是否有效
第二步, 执行指令的任务
第三步, 做退出前的动作, 比如IP++等等
指令函数的流程图:
1. 指令预处理
预处理的作用是根据指令的寻址方式和操所数, 得到操作数的实际地址, 使指令的实现了解如何得到地址, 统一处理实际地址, 就像在当前的系统中一样处理。
函数声明: BOOL CVirtualMachine::Preprocess1(PCOMMAND cmd, int &op1mode, int &op1reflvl, unsigned char* &dest, BOOL bValidate)
函数功能: 指令预处理(单操作数指令)
参数说明:
[IN]PCOMMAND cmd - 指令内容
[OUT]int &op1mode - 操作数1的寻址方式字
[OUT]int &op1reflvl - 操作数1的间接级别
[OUT]unsigned char* &dest - 实际地址
[IN]BOOL bValidate - 是否进行地址验证
返 回 值: BOOL - 成功或失败
2. 地址验证
地址验证是用来验证操作数的实际地址是否合法, 防止不正确的内存操作导致系统崩溃。
函数声明: BOOL CVirtualMachine::ValidateAddress(unsigned char* address)
函数功能: 验证地址是否合法
参数说明: [IN]unsigned char* address - 地址
返 回 值: BOOL - 成功或失败
3. 加载脚本函数
当虚拟机执行脚本时:
第一步, 载入脚本, 也就是载入代码到代码段, 载入静态数据到数据段. 没有栈, 原因前面面已经解释
第二步, 从第一条指令开始执行, 直到结束. 也就是遇到ret指令.
第三步, 释放资源.
函数声明: BOOL CVirtualMachine::LoadFunction(CFunction *pFunc)
函数功能: 加载脚本函数
参数说明:
[IN]CFunction *pFunc - 函数指针
返 回 值: BOOL - 成功或失败
By Jackie
http://jackie_juju.nease.net
本文地址:http://com.8s8s.com/it/it36989.htm