怎么开发一个虚拟机
参考 https://xz.aliyun.com/news/3483
在此之前需要先理解一下字节码和汇编的区别
字节码与汇编的区别
字节码由虚拟机解释执行,汇编由CPU执行
不同平台的汇编指令集不同,所以汇编无法跨平台;然而基于不同的汇编指令集,可以开发对应平台的虚拟机,再由虚拟机解释执行统一的字节码,因此字节码可以跨平台
在我要实现的这个虚拟机里,字节码是操作码(opcode)
组成部分
这个虚拟机有三个主要部分
vm_start:
虚拟机的入口函数,对虚拟机环境进行初始化
vm_dispatcher:
调度器,解释opcode,并选择对应的handle函数执行,当handle执行完后会跳回这里,形成一个循环。
opcode :
程序可执行代码转换成的操作码
从过程上看有三个主要的函数
init:
初始化cpu_context
start:
check:
这是关键函数,它用来比较en_input与en_flag
简化版本
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#define OPCODE_N 7
#define F_LEN 12
// FLAG:1ds8i0*qA0-0
char *vm_stack;
char enc_flag[] = {0x23, 0x76, 0x61, 0x2a, 0x7b, 0x22, 0x38, 0x63, 0x53, 0x22, 0x3f,0x22};
enum regist{
R1 = 0xe1,
R2 = 0xe2,
R3 = 0xe3,
};
enum opcodes
{
MOV = 0xf1,
XOR = 0xf2,
RET = 0xf4,
READ = 0xf5,
};
unsigned char vm_code[] = {
0xf5,
0xf1,0xe1,0x0,0x00,0x00,0x00,0xf2,0xf1,0xe4,0x20,0x00,0x00,0x00,
0xf1,0xe1,0x1,0x00,0x00,0x00,0xf2,0xf1,0xe4,0x21,0x00,0x00,0x00,
0xf1,0xe1,0x2,0x00,0x00,0x00,0xf2,0xf1,0xe4,0x22,0x00,0x00,0x00,
0xf1,0xe1,0x3,0x00,0x00,0x00,0xf2,0xf1,0xe4,0x23,0x00,0x00,0x00,
0xf1,0xe1,0x4,0x00,0x00,0x00,0xf2,0xf1,0xe4,0x24,0x00,0x00,0x00,
0xf1,0xe1,0x5,0x00,0x00,0x00,0xf2,0xf1,0xe4,0x25,0x00,0x00,0x00,
0xf1,0xe1,0x6,0x00,0x00,0x00,0xf2,0xf1,0xe4,0x26,0x00,0x00,0x00,
0xf1,0xe1,0x7,0x00,0x00,0x00,0xf2,0xf1,0xe4,0x27,0x00,0x00,0x00,
0xf1,0xe1,0x8,0x00,0x00,0x00,0xf2,0xf1,0xe4,0x28,0x00,0x00,0x00,
0xf1,0xe1,0x9,0x00,0x00,0x00,0xf2,0xf1,0xe4,0x29,0x00,0x00,0x00,
0xf1,0xe1,0xa,0x00,0x00,0x00,0xf2,0xf1,0xe4,0x2a,0x00,0x00,0x00,
0xf1,0xe1,0xb,0x00,0x00,0x00,0xf2,0xf1,0xe4,0x2b,0x00,0x00,0x00,
0xf1,0xe1,0xc,0x00,0x00,0x00,0xf2,0xf1,0xe4,0x2c,0x00,0x00,0x00,
0xf4
};
/*
call read_
MOV R1,flag[0]
XOR
MOV R1,0x20;
MOV R1,flag[1]
XOR
MOV R1,0x21;
MOV R1,flag[2]
XOR
MOV R1,0x22
MOV R1,flag[3]
XOR
MOV R1,0x23;
MOV R1,flag[4]
XOR
MOV R1,0x24;
MOV R1,flag[5]
XOR
MOV R1,0x25;
MOV R1,flag[6]
XOR
MOV R1,0x26;
MOV R1,flag[7]
XOR
MOV R1,0x26
MOV R1,flag[7]
XOR
MOV R1,0X27
MOV R1,flag[7]
XOR
MOV R1,0x28
MOV R1,flag[7]
XOR
MOV R1,0X29
MOV R1,flag[7]
XOR
MOV R1,0x2A
MOV R1,flag[7]
XOR
MOV R1,0x2b
*/
typedef struct
{
unsigned char opcode;
void (*handle)(void *);
}vm_opcode;
typedef struct vm_cpus
{
int r1;
int r2;
int r3;
unsigned char *eip;
vm_opcode op_list[OPCODE_N]; //opcode list, store opcode and handle
}vm_cpu;
void mov(vm_cpu *cpu); //change flag position
void xor(vm_cpu *cpu); //xor flag , 0x1-0x9
void read_(vm_cpu *cpu); //call read ,read the flag
void xor(vm_cpu *cpu)
{
int temp;
temp = cpu->r1 ^ cpu->r2;
temp ^= 0x12;
cpu->r1 = temp;
cpu->eip +=1; //xor指令占一个字节
}
void read_(vm_cpu *cpu)
{
char *dest = vm_stack;
read(0,dest,12); //用于往虚拟机的栈上读入数据
cpu->eip += 1; //read_指令占一个字节
}
void mov(vm_cpu *cpu)
{
//mov指令的参数都隐藏在字节码中,指令表示后的一个字节是寄存器标识,第二到第五是要mov的数据在vm_stack上的偏移
//我这里只是实现了从vm_stack上取数据和存数据到vm_stack上
unsigned char *res = cpu->eip + 1; //寄存器标识
int *offset = (int *) (cpu->eip + 2); //数据在vm_stack上的偏移
char *dest = 0;
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:
{
int x = cpu->r1;
*(dest + *offset) = x;
break;
}
}
cpu->eip += 6;
//mov指令占六个字节,所以eip要向后移6位
}
void vm_init(vm_cpu *cpu)
{
cpu->r1 = 0;
cpu->r2 = 0;
cpu->r3 = 0;
cpu->eip = (unsigned char *)vm_code;
cpu->op_list[0].opcode = 0xf1;
cpu->op_list[0].handle = (void (*)(void *))mov;
cpu->op_list[1].opcode = 0xf2;
cpu->op_list[1].handle = (void (*)(void *))xor;
cpu->op_list[2].opcode = 0xf5;
cpu->op_list[2].handle = (void (*)(void *))read_;
vm_stack = malloc(0x512);
memset(vm_stack,0,0x512);
}
void vm_dispatcher(vm_cpu *cpu)
{
int i;
for(i=0 ; i < OPCODE_N ; i++)
{
if(*cpu->eip == cpu->op_list[i].opcode)
{
cpu->op_list[i].handle(cpu);
break;
}
}
}
void vm_start(vm_cpu *cpu)
{
cpu->eip = (unsigned char*)vm_code;
while((*cpu->eip)!= RET)
{
vm_dispatcher(cpu);
}
}
void check()
{
int i;
char *target = vm_stack;
for(i = 0; i < F_LEN; i++)
{
int offset = i + 0x20;
if((char)target[offset] != enc_flag[i])
{
puts("error");
exit(0);
}
else
{
continue;
}
}
puts("right");
exit(0);
}
int main()
{
vm_cpu* cpu = malloc(sizeof(vm_cpu));
memset(cpu,0,sizeof(vm_cpu));
puts("power by zs0zrc");
puts("---------------------");
puts("please input the flag:");
vm_init(cpu);
vm_start(cpu);
check();
return 0;
}
出自 https://bbs.kanxue.com/thread-281119.htm
怎么逆向分析虚拟机
以hgame vm为例
init:

我们输入的字符串保存在dword_140005040缓冲区里,关键的check函数是sub_1400010B0
为了方便阅读把缓冲区改成input
check函数:
__int64 __fastcall sub_1400010B0(__int64 a1)
{
while ( byte_140005360[*(unsigned int *)(a1 + 24)] != 255 )
sub_140001940(a1);
return *(unsigned __int8 *)(a1 + 32);
}
进入sub_140001940这个函数
这是一个dispatcher,用来处理字节码和其对应的handle函数
__int64 __fastcall sub_140001940(__int64 a1)
{
__int64 result; // rax
result = byte_140005360[*(unsigned int *)(a1 + 24)];
switch ( byte_140005360[*(unsigned int *)(a1 + 24)] )
{
case 0u:
result = sub_1400010F0(a1);
break;
case 1u:
result = sub_140001230(a1);
break;
case 2u:
result = sub_140001380(a1);
break;
case 3u:
result = sub_1400014D0(a1);
break;
case 4u:
result = sub_1400017F0(a1);
break;
case 5u:
result = sub_140001870(a1);
break;
case 6u:
result = sub_1400018F0(a1);
break;
case 7u:
result = sub_1400018A0(a1);
break;
default:
return result;
}
return result;
}
根据switch中的条件可知,缓冲区是opcode,所以a1 + 24 是rip,a1是虚拟机上下文结构体首地址
先把这个结构体创建出来,一般rip前几个是寄存器,寄存器用数组存储

这里给dispatcher函数的a1改成vm *类型,缓冲区命名为opcode
下面一个一个分析handle函数:
0 mov

这里多为赋值操作,因此可以推断是mov
1 push

这里是一个寄存器先自增,然后把寄存器的值赋给对应缓冲区,这与push指令原理基本一致。都是先自增指针然后把数据压入。
但是这样看起来不够直观,原因是vm结构体少了一项,根据地址偏移,ida会把这个内存空间理解为a1数组第1个元素的第0个寄存器,把这项命名为vm_rsp,就不会出现这种问题。
2 pop

跟上边相反,这里是pop
3 calculate

这里是负责计算的handle
4 cmp

这里比较了两个寄存器,然后设置了对应标志位的值,可以得出,lobyte里面的参数是zf标志位,把zf放在rsp下边可以提高代码可读性
5 jmp

很明显,这里更改了rip,而且没条件限制,说明是无条件跳转jmp指令
6 je

这里也是跳转指令,只不过加了zf = 0的条件,根据上边cmp指令,相等的情况下跳转,所以这里是je
7 jne

与je相反,这里换了个位置,所以是jne
py脚本

opcode = [0x00, 0x03, 0x02, 0x00, 0x03, 0x00, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x03, 0x02, 0x32, 0x03, 0x00, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x03, 0x02, 0x64, 0x03, 0x00, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x01, 0x00, 0x00, 0x03, 0x00, 0x08, 0x00, 0x02, 0x02, 0x01, 0x03, 0x04, 0x01, 0x00, 0x03, 0x05, 0x02, 0x00, 0x03, 0x00, 0x01, 0x02, 0x00, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x03, 0x00, 0x01, 0x03, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x03, 0x00, 0x03, 0x01, 0x28, 0x04, 0x06, 0x5F, 0x05, 0x00, 0x00, 0x03, 0x03, 0x00, 0x02, 0x01, 0x00, 0x03, 0x02, 0x96, 0x03, 0x00, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x04, 0x07, 0x88, 0x00, 0x03, 0x00, 0x01, 0x03, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x03, 0x00, 0x03, 0x01, 0x28, 0x04, 0x07, 0x63, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
rip = 0
def mov():
global rip,opcode
v2 = opcode[rip + 1]
if v2:
match v2:
case 1:
print("mov input[reg[2]],reg[0]")
case 2:
print("mov reg[%d],reg[%d]"%(opcode[rip + 2],opcode[rip + 3]))
case 3:
print("mov reg[%d],%d"%(opcode[rip + 2],opcode[rip + 3]))
else:
print("mov reg[0],input[reg[2]]")
rip += 4
def push():
global rip,opcode
v2 = opcode[rip + 1]
if v2:
match v2:
case 1:
print("push reg[0]")
case 2:
print("push reg[2]")
case 3:
print("push reg[3]")
else:
print("push reg[0]")
rip += 2
def pop():
global rip,opcode
v2 = opcode[rip + 1]
if v2:
match v2:
case 1:
print("pop reg[1]")
case 2:
print("pop reg[2]")
case 3:
print("pop reg[3]")
else:
print("pop reg[0]")
rip += 2
def calculate():
global rip,opcode
match opcode[rip + 1]:
case 0:
print("add reg[%d],reg[%d]"%(opcode[rip + 2],opcode[rip + 3]))
case 1:
print("sub reg[%d],reg[%d]"%(opcode[rip + 2],opcode[rip + 3]))
case 2:
print("mul reg[%d],reg[%d]"%(opcode[rip + 2],opcode[rip + 3]))
case 3:
print("xor reg[%d],reg[%d]"%(opcode[rip + 2],opcode[rip + 3]))
case 4:
print("shl reg[%d],reg[%d]"%(opcode[rip + 2],opcode[rip + 3]))
case 5:
print("shr reg[%d],reg[%d]"%(opcode[rip + 2],opcode[rip + 3]))
rip += 4
def cmp():
global rip,opcode
print("cmp reg[0],reg[1]")
rip += 1
def jmp():
global rip,opcode
print("jmp %d"%opcode[rip + 1])
rip += 2
def je():
global rip,opcode
print("je %d"%opcode[rip + 1])
rip += 2
def jne():
global rip,opcode
print("jne %d"%opcode[rip + 1])
rip += 2
def main():
while(opcode[rip] != 255):
match opcode[rip]:
case 0:
print("%d"%(rip),end = ' ')
mov()
case 1:
print("%d"%(rip),end = ' ')
push()
case 2:
print("%d"%(rip),end = ' ')
pop()
case 3:
print("%d"%(rip),end = ' ')
calculate()
case 4:
print("%d"%(rip),end = ' ')
cmp()
case 5:
print("%d"%(rip),end = ' ')
jmp()
case 6:
print("%d"%(rip),end = ' ')
je()
case 7:
print("%d"%(rip),end = ' ')
jne()
if __name__ == "__main__":
main()
得到如下opcode:
0 mov reg[2],0
4 add reg[2],reg[3]
8 mov reg[0],input[reg[2]]
12 mov reg[1],reg[0]
;r1 = r0 = input[r3]
16 mov reg[2],50
20 add reg[2],reg[3]
24 mov reg[0],input[reg[2]]
28 add reg[1],reg[0]
;r0 = input[50 + r3]
;r1 = input[50 + r3] + input[r3]
32 mov reg[2],100
36 add reg[2],reg[3]
40 mov reg[0],input[reg[2]]
;r0 = input[100 + r3]
44 xor reg[1],reg[0]
;r1 = (input[50 + r3] + input[r3]) ^ input[100 + r3]
48 mov reg[0],8
52 mov reg[2],reg[1]
56 shl reg[1],reg[0]
;r1 <<= 8
60 shr reg[2],reg[0]
;r2 >>= 8
64 add reg[1],reg[2]
;r1 += r2
68 mov reg[0],reg[1]
72 push reg[0]
;push r1(push enc)
74 mov reg[0],1
78 add reg[3],reg[0]
82 mov reg[0],reg[3]
;r0 = r3 + 1
86 mov reg[1],40
90 cmp reg[0],reg[1]
;cmp r3 + 1,40 ==> r3 = 39
91 je 95
93 jmp 0
95 mov reg[3],0
99 pop reg[1]
;r1 = enc_last
101 mov reg[2],150
105 add reg[2],reg[3]
109 mov reg[0],input[reg[2]]
113 cmp reg[0],reg[1]
;cmp input[150 + r3],enc_last[0]
114 jne 136
;!= ->zf = 1 jmp 136 X
116 mov reg[0],1
120 add reg[3],reg[0]
;r3 += 1
124 mov reg[0],reg[3]
128 mov reg[1],40
132 cmp reg[0],reg[1]
;enc_last 1->40
133 jne 99
这上边有我的一些分析,可以参考一下
清楚opcode逻辑后,用如下脚本可以解密flag。注意上边input里所有数据都是dword,所以用lazyida赋值数据的时候需要选择带dword的那项
src = [0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0000009B, 0x000000A8, 0x00000002, 0x000000BC, 0x000000AC, 0x0000009C, 0x000000CE, 0x000000FA, 0x00000002, 0x000000B9, 0x000000FF, 0x0000003A, 0x00000074, 0x00000048, 0x00000019, 0x00000069, 0x000000E8, 0x00000003, 0x000000CB, 0x000000C9, 0x000000FF, 0x000000FC, 0x00000080, 0x000000D6, 0x0000008D, 0x000000D7, 0x00000072, 0x00000000, 0x000000A7, 0x0000001D, 0x0000003D, 0x00000099, 0x00000088, 0x00000099, 0x000000BF, 0x000000E8, 0x00000096, 0x0000002E, 0x0000005D, 0x00000057, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x000000C9, 0x000000A9, 0x000000BD, 0x0000008B, 0x00000017, 0x000000C2, 0x0000006E, 0x000000F8, 0x000000F5, 0x0000006E, 0x00000063, 0x00000063, 0x000000D5, 0x00000046, 0x0000005D, 0x00000016, 0x00000098, 0x00000038, 0x00000030, 0x00000073, 0x00000038, 0x000000C1, 0x0000005E, 0x000000ED, 0x000000B0, 0x00000029, 0x0000005A, 0x00000018, 0x00000040, 0x000000A7, 0x000000FD, 0x0000000A, 0x0000001E, 0x00000078, 0x0000008B, 0x00000062, 0x000000DB, 0x0000000F, 0x0000008F, 0x0000009C, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00004800, 0x0000F100, 0x00004000, 0x00002100, 0x00003501, 0x00006400, 0x00007801, 0x0000F900, 0x00001801, 0x00005200, 0x00002500, 0x00005D01, 0x00004700, 0x0000FD00, 0x00006901, 0x00005C00, 0x0000AF01, 0x0000B200, 0x0000EC01, 0x00005201, 0x00004F01, 0x00001A01, 0x00005000, 0x00008501, 0x0000CD00, 0x00002300, 0x0000F800, 0x00000C00, 0x0000CF00, 0x00003D01, 0x00004501, 0x00008200, 0x0000D201, 0x00002901, 0x0000D501, 0x00000601, 0x0000A201, 0x0000DE00, 0x0000A601, 0x0000CA01, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000]
for i in range(40):
num = src[150 + 39 - i]
tmp = (((num << 8) & 0xff00) + ((num >> 8) & (0xff))) & 0xffff
flag = (tmp ^ src[100 + i]) - src[50 + i]
print(chr(flag),end = '')
hgame{y0ur_rever5e_sk1ll_i5_very_g0od!!}
以上两个脚本来自这个blog,但我看了作者的writeup后认为写的比较简略,花了几天来理解反编译的代码,所以分析后写了这篇笔记
https://www.cnblogs.com/biyifei/p/17133203.html
分析了babyvm和hgamevm后,我对虚拟机的认识更进一步。一方面是学会了在ida里创建context结构体来简化虚拟机代码,另一方面也对虚拟机的代码结构有了一定的印象,然后也提升了阅读汇编代码块的能力。
对虚拟机的分析暂告一段落
浙公网安备 33010602011771号