一道vm逆向
题目给了两个文件,一个是机器码,用vs打开
不知道在干嘛,看另一个程序。
64位文件,无壳,直接ida启动
逻辑不复杂。打开code文件后将内容读入ptr,然后读入三个数字做处理。
查看关键函数sub_400806:
十个case,不同的运算,明显的vm特征。
(这是我改过名后的样子,方便看)
开头将三个数字读入outnum数组,并设置了三个值来操作数据
来看主体逻辑。case0是v5=0.即退出
case1是将后四个数据从后往前合成一个大数储存起来
case2是简单的下标--
3,4,5,6分别是加减乘和向右的位运算
7是mov,8也是mov,9是xor,10是or,default检查操作码是不是位于0-10,否则退出。
根据code里的opcode编写脚本还原伪代码
opcode=[9,4,4,9,0,0,8,1,0,8,2,1,8,3,2,6,1,4,5,1,0x15,7,0,1,4,0,3,1,0x6b,0xcc,0x7e,0x1d,8,1,3,4,0,1,2,0xa,4,0,9,0,0,8,1,0,8,2,1,8,3,2,6,3,8,5,3,3,7,0,3,3,0,2,1,0x7c,0x79,0x79,0x60,8,1,3,4,0,1,2,10,4,0,9,0,0,8,1,0,8,2,1,8,3,2,6,1,8,7,0,1,3,0,2,1,0xbd,0xbd,0xbc,0x5f,8,1,3,4,0,1,2,10,4,0,0]
aa=[]
cnt1=2
i=0
for a in range(len(opcode)):
if opcode[i]==0:
print("over")
break
elif opcode[i]== 1:
temp=(opcode[i+1])|(opcode[i+2]<<8)|(opcode[i+3]<<16)|(opcode[i+4]<<24)
i+=5
cnt1 += 1
print("mov outnum[",cnt1,"] ",hex(temp))
elif opcode[i] ==2:
cnt1-=1
print("sub cnt1 1")
i+=1
elif opcode[i] ==3:
print("add elx[",opcode[i+1],"] elx[",opcode[i+2],"]")
i += 3
elif opcode[i] ==4:
print("sub elx[", opcode[i + 1], "] elx[", opcode[i + 2], "]")
i += 3
elif opcode[i] ==5:
print("mul elx[",opcode[i+1],"] " ,opcode[i + 2])
i += 3
elif opcode[i] ==6:
print("ROR elx[",opcode[i+1],"] ", opcode[i + 2])
i += 3
elif opcode[i] ==7:
print("mov elx[", opcode[i + 1], "] elx[", opcode[i + 2], "]")
i+=3
elif opcode[i] ==8:
print("mov elx[", opcode[i+1], "] outnum[", opcode[i + 2], "]")
i += 3
elif opcode[i] ==9:
print("xor elx[", opcode[i + 1], "] elx[", opcode[i + 2], "]")
i += 3
elif opcode[i] ==10:
print("or elx[", opcode[i + 1], "] ", opcode[i + 2])
i += 3
结果是
xor elx[ 4 ] elx[ 4 ]
xor elx[ 0 ] elx[ 0 ]
mov elx[ 1 ] outnum[ 0 ]
mov elx[ 2 ] outnum[ 1 ]
mov elx[ 3 ] outnum[ 2 ]
ROR elx[ 1 ] 4
mul elx[ 1 ] 21
mov elx[ 0 ] elx[ 1 ]
sub elx[ 0 ] elx[ 3 ]
mov outnum[ 3 ] 0x1d7ecc6b
mov elx[ 1 ] outnum[ 3 ]
sub elx[ 0 ] elx[ 1 ]
sub cnt1 1
or elx[ 4 ] 0
xor elx[ 0 ] elx[ 0 ]
mov elx[ 1 ] outnum[ 0 ]
mov elx[ 2 ] outnum[ 1 ]
mov elx[ 3 ] outnum[ 2 ]
ROR elx[ 3 ] 8
mul elx[ 3 ] 3
mov elx[ 0 ] elx[ 3 ]
add elx[ 0 ] elx[ 2 ]
mov outnum[ 3 ] 0x6079797c
mov elx[ 1 ] outnum[ 3 ]
sub elx[ 0 ] elx[ 1 ]
sub cnt1 1
or elx[ 4 ] 0
xor elx[ 0 ] elx[ 0 ]
mov elx[ 1 ] outnum[ 0 ]
mov elx[ 2 ] outnum[ 1 ]
mov elx[ 3 ] outnum[ 2 ]
ROR elx[ 1 ] 8
mov elx[ 0 ] elx[ 1 ]
add elx[ 0 ] elx[ 2 ]
mov outnum[ 3 ] 0x5fbcbdbd
mov elx[ 1 ] outnum[ 3 ]
sub elx[ 0 ] elx[ 1 ]
sub cnt1 1
or elx[ 4 ] 0
over
代码逻辑比较简单,化简为这样一个表达式
(((num1>>4)*21-num3-0x1d7ecc6b)|((num3>>8)*3+num2-0x6079797c)|((num1>>8)+num2-0x5fbcbdbd))
根据判断函数,式子的结果应该等于0
使用z3求解三个未知数
编写脚本得到flag:
from z3 import*
from libnum import s2n,n2s
s=Solver()
num1=BitVec('num1',32)
num2=BitVec('num2',32)
num3=BitVec('num3',32)
s.add((((num1>>4)*21-num3-0x1d7ecc6b)|((num3>>8)*3+num2-0x6079797c)|((num1>>8)+num2-0x5fbcbdbd))==0)
s.add(num1&0xff==0x5e)
s.add(num3&0xff==0x5e)
s.add((num2 & 0xFF0000) == 0x5E0000)
print(s.check())
answer=s.model()
print(answer)
#[num2 = 1600020063, num3 = 1583243102, num1 = 1583308382]
aa=[1583308382,1600020063,1583243102]
flag="flag{"
for i in range(len(aa)):
flag += hex(aa[i])[2:]
flag+="}"
print(flag)
flag{5e5f5e5e5f5e5e5f5e5e5f5e}