[GWCTF 2019]babyvm

题目:[GWCTF 2019]babyvm

64位程序,好像是个vm题?
在这里插入图片描述
ida64查看主函数

void __fastcall __noreturn main(int a1, char **a2, char **a3)
{
  __int64 v3[2]; // [rsp+10h] [rbp-10h] BYREF

  v3[1] = __readfsqword(0x28u);
  v3[0] = 0LL;
  puts("Please input something:");
  sub_CD1(v3);
  sub_E0B(v3);
  sub_F83(v3);
  puts("And the flag is GWHT{true flag}");
  exit(0);
}

看一下sub_F83函数,这应该是check函数了,qword_2022A8存放的应该是加密后的flag。前两个应该就是加密函数了。

unsigned __int64 sub_F83()
{
  int i; // [rsp+Ch] [rbp-14h]
  unsigned __int64 v2; // [rsp+18h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  for ( i = 0; dword_2022A4 - 1 > i; ++i )
  {
    if ( *(qword_2022A8 + i + 32) != aFzAmAmFmtSum[i] )
    {
      puts("WRONG!");
      exit(0);
    }
  }
  puts("Congratulation?");
  puts("tips: input is the start");
  return __readfsqword(0x28u) ^ v2;
}

先用angr解一下试试水。

import angr
p=angr.Project("./Desktop/attachment",auto_load_libs=False)
sm=p.factory.simulation_manager(p.factory.entry_state())
sm.explore(find=0x401081)
print(sm.found[0].posix.dumps(0)) 

在这里插入图片描述
解出来是个假flag,不足为奇,是vm题不错了。

解vm题第一步需要搞清楚指令的格式,操作码和操作数。

sub_CD1函数是vm初始化的过程,unk_202060里面存储的是操作码,接下来每一个操作码对应一个函数实现(如0xF1对应sub_B5F函数)
在这里插入图片描述

unsigned __int64 __fastcall sub_CD1(__int64 a1)
{
  unsigned __int64 v2; // [rsp+18h] [rbp-8h]

  v2 = __readfsqword(0x28u);                    // 反调试
  *a1 = 0;
  *(a1 + 4) = 18;
  *(a1 + 8) = 0;
  *(a1 + 12) = 0;
  *(a1 + 16) = &unk_202060;                     // unk_202060是操作码的内容
  *(a1 + 24) = 0xF1;                            // 0xF1操作码代表sub_B5F函数
  *(a1 + 32) = sub_B5F;
  *(a1 + 40) = 0xF2;                            // 0xF2操作码代表sub_A64函数
  *(a1 + 48) = sub_A64;
  *(a1 + 56) = 0xF5;                            // 0xF5操作码代表sub_AC5函数
  *(a1 + 64) = sub_AC5;
  *(a1 + 72) = 0xF4;                            // 0xF4操作码代表sub_956函数
  *(a1 + 80) = sub_956;
  *(a1 + 88) = 0xF7;                            // 0xF7操作码代表sub_A08函数
  *(a1 + 96) = sub_A08;
  *(a1 + 104) = 0xF8;                           // 0xF8操作码代表sub_8F0函数
  *(a1 + 112) = sub_8F0;
  *(a1 + 120) = 0xF6;                           // 0xF6操作码代表sub_99C函数
  *(a1 + 128) = sub_99C;
  qword_2022A8 = malloc(0x512uLL);
  memset(qword_2022A8, 0, 0x512uLL);
  return __readfsqword(0x28u) ^ v2;
}

分析一下各个操作码进行的操作。

0xF1

0xE1,0xE2,0xE3,0xE4对应5个寄存器,实际的具体位置分别是a1,a1+4,a1+8,a1+12,而*(qword_2022A8 + *v2)对应内存单元。

0xF1进行的操作类似于mov操作符,

unsigned __int64 __fastcall sub_B5F(__int64 a1)
{
  int *v2; // [rsp+28h] [rbp-18h]
  unsigned __int64 v3; // [rsp+38h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  v2 = (*(a1 + 16) + 2LL);
  switch ( *(*(a1 + 16) + 1LL) )
  {
    case 0xE1:
      *a1 = *(qword_2022A8 + *v2);
      break;
    case 0xE2:
      *(a1 + 4) = *(qword_2022A8 + *v2);
      break;
    case 0xE3:
      *(a1 + 8) = *(qword_2022A8 + *v2);
      break;
    case 0xE4:
      *(qword_2022A8 + *v2) = *a1;
      break;
    case 0xE5:
      *(a1 + 12) = *(qword_2022A8 + *v2);
      break;
    case 0xE7:
      *(qword_2022A8 + *v2) = *(a1 + 4);
      break;
    default:
      break;
  }
  *(a1 + 16) += 6LL;
  return __readfsqword(0x28u) ^ v3;
}

0xF2

异或xor,两个操作数间隔4。

unsigned __int64 __fastcall sub_A64(__int64 a1)
{
  unsigned __int64 v2; // [rsp+18h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  *a1 ^= *(a1 + 4);
  ++*(a1 + 16);
  return __readfsqword(0x28u) ^ v2;
}

0xF5

是个读取输入字符并判断长度的操作,存入qword_2022A8。

unsigned __int64 __fastcall sub_AC5(__int64 a1)
{
  const char *buf; // [rsp+10h] [rbp-10h]
  unsigned __int64 v3; // [rsp+18h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  buf = qword_2022A8;
  read(0, qword_2022A8, 0x20uLL);
  dword_2022A4 = strlen(buf);
  if ( dword_2022A4 != 21 )
  {
    puts("WRONG!");
    exit(0);
  }
  ++*(a1 + 16);
  return __readfsqword(0x28u) ^ v3;
}

0xF4

空指令nop

unsigned __int64 __fastcall sub_956(__int64 a1)
{
  unsigned __int64 v2; // [rsp+18h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  ++*(a1 + 16);
  return __readfsqword(0x28u) ^ v2;
}

0xF7

相乘操作mul,两个操作数间隔12。

unsigned __int64 __fastcall sub_A08(__int64 a1)
{
  unsigned __int64 v2; // [rsp+18h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  *a1 *= *(a1 + 12);
  ++*(a1 + 16);
  return __readfsqword(0x28u) ^ v2;
}

0xF8

交换swap

unsigned __int64 __fastcall sub_8F0(int *a1)
{
  int v2; // [rsp+14h] [rbp-Ch]
  unsigned __int64 v3; // [rsp+18h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  v2 = *a1;
  *a1 = a1[1];
  a1[1] = v2;
  ++*(a1 + 2);
  return __readfsqword(0x28u) ^ v3;
}

0xF6

线性运算

unsigned __int64 __fastcall sub_99C(__int64 a1)
{
  unsigned __int64 v2; // [rsp+18h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  *a1 = *(a1 + 8) + 2 * *(a1 + 4) + 3 * *a1;
  ++*(a1 + 16);
  return __readfsqword(0x28u) ^ v2;
}

反汇编

机器码备注
0xF1mov
0xF2xor
0xF4nop
0xF5input
0xF7mul
0xF8swap
0xF6线性运算
0xE1寄存器r1
0xE2寄存器r2
0xE3寄存器r3
0xE4内存单元flag
0xE5寄存器r4

现在开始翻译unk_202060中的字节码

0xF5,                               #获取输入
0xF1, 0xE1, 0x00, 0x00, 0x00, 0x00, #mov r1,flag[0]
0xF2,                               #xor r1,r2
0xF1, 0xE4, 0x20, 0x00, 0x00, 0x00, #mov flag[32],r1
0xF1, 0xE1, 0x01, 0x00, 0x00, 0x00, #mov r1,flag[1]
0xF2,                               #xor r1,r2
0xF1, 0xE4, 0x21, 0x00, 0x00, 0x00, #mov flag[33],r1
0xF1, 0xE1, 0x02, 0x00, 0x00, 0x00, #mov r1,flag[2]
0xF2,                               #xor r1,r2
0xF1, 0xE4, 0x22, 0x00, 0x00, 0x00, #mov flag[34],r1
0xF1, 0xE1, 0x03, 0x00, 0x00, 0x00, 
0xF2, 
0xF1, 0xE4, 0x23, 0x00, 0x00, 0x00, 
0xF1, 0xE1, 0x04, 0x00, 0x00, 0x00, 
0xF2, 
0xF1, 0xE4, 0x24, 0x00, 0x00, 0x00, 
0xF1, 0xE1, 0x05, 0x00, 0x00, 0x00, 
0xF2, 
0xF1, 0xE4, 0x25, 0x00, 0x00, 0x00, 
0xF1, 0xE1, 0x06, 0x00, 0x00, 0x00, 
0xF2, 
0xF1, 0xE4, 0x26, 0x00, 0x00, 0x00, 
0xF1, 0xE1, 0x07, 0x00, 0x00, 0x00, 
0xF2, 
0xF1, 0xE4, 0x27, 0x00, 0x00, 0x00, 
0xF1, 0xE1, 0x08, 0x00, 0x00, 0x00, 
0xF2, 
0xF1, 0xE4, 0x28, 0x00, 0x00,0x00, 
0xF1, 0xE1, 0x09, 0x00, 0x00, 0x00, 
0xF2, 
0xF1, 0xE4, 0x29, 0x00, 0x00, 0x00, 
0xF1, 0xE1, 0x0A, 0x00, 0x00, 0x00, 
0xF2, 
0xF1, 0xE4, 0x2A, 0x00, 0x00, 0x00, 
0xF1, 0xE1, 0x0B, 0x00, 0x00, 0x00, 
0xF2, 
0xF1, 0xE4, 0x2B, 0x00, 0x00, 0x00, 
0xF1, 0xE1, 0x0C, 0x00, 0x00, 0x00, 
0xF2, 
0xF1, 0xE4, 0x2C, 0x00, 0x00, 0x00, 
0xF1, 0xE1, 0x0D, 0x00, 0x00, 0x00, 
0xF2, 
0xF1, 0xE4, 0x2D, 0x00, 0x00, 0x00, 
0xF1, 0xE1, 0x0E, 0x00, 0x00, 0x00, 
0xF2, 
0xF1, 0xE4, 0x2E, 0x00, 0x00, 0x00, 
0xF1, 0xE1, 0x0F, 0x00, 0x00, 0x00, 
0xF2, 
0xF1, 0xE4, 0x2F, 0x00, 0x00, 0x00, 
0xF1, 0xE1, 0x10, 0x00, 0x00, 0x00, 
0xF2, 
0xF1, 0xE4, 0x30, 0x00, 0x00, 0x00, 
0xF1, 0xE1, 0x11, 0x00, 0x00, 0x00, 
0xF2, 
0xF1, 0xE4, 0x31, 0x00, 0x00, 0x00, 
0xF1, 0xE1, 0x12, 0x00, 0x00, 0x00, 
0xF2, 
0xF1, 0xE4, 0x32, 0x00, 0x00, 0x00, 
0xF1, 0xE1, 0x13, 0x00, 0x00, 0x00, 
0xF2, 
0xF1, 0xE4, 0x33, 0x00, 0x00, 0x00, 
0xF4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 

#主要是下面这部分字节码,stack即flag
    0xf5,#read(0,buf,0x20)
	0xf1,0xe1,0x0,0x0,0x0,0x0,#r1 = flag[0]
	0xf1,0xe2,0x1,0x0,0x0,0x0,#r2 = flag[1]
	0xf2,#r1 = r1^r2
	0xf1,0xe4,0x0,0x0,0x0,0x0,#stack[0] = r1
	
	0xf1,0xe1,0x1,0x0,0x0,0x0,#r1 = flag[1]
	0xf1,0xe2,0x2,0x0,0x0,0x0,#r2 = flag[2]
	0xf2,#r1 = r1^r2
	0xf1,0xe4,0x1,0x0,0x0,0x0,#stack[1] = r1
	
	0xf1,0xe1,0x2,0x0,0x0,0x0,#r1 = flag[2]
	0xf1,0xe2,0x3,0x0,0x0,0x0,#r2 = flag[3]
	0xf2,#r1 = r1^r2
	0xf1,0xe4,0x2,0x0,0x0,0x0,#stack[2] = r1

	0xf1,0xe1,0x3,0x0,0x0,0x0,#r1 = flag[3]
	0xf1,0xe2,0x4,0x0,0x0,0x0,#r2 = flag[4]
	0xf2,#r1 = r1^r2
	0xf1,0xe4,0x3,0x0,0x0,0x0,#stack[3] = r1

	0xf1,0xe1,0x4,0x0,0x0,0x0,#r1 = flag[4]
	0xf1,0xe2,0x5,0x0,0x0,0x0,#r2 = flag[5]
	0xf2,#r1 = r1^r2
	0xf1,0xe4,0x4,0x0,0x0,0x0,#stack[4] = r1

	0xf1,0xe1,0x5,0x0,0x0,0x0,#r1 = flag[5]
	0xf1,0xe2,0x6,0x0,0x0,0x0,#r2 = flag[6]
	0xf2,#r1 = r1^r2
	0xf1,0xe4,0x5,0x0,0x0,0x0,#stack[5] = r1

	0xf1,0xe1,0x6,0x0,0x0,0x0,#r1 = flag[6]
	0xf1,0xe2,0x7,0x0,0x0,0x0,#r2 = flag[7]
	0xf1,0xe3,0x8,0x0,0x0,0x0,#r3 = flag[8]
	0xf1,0xe5,0xC,0x0,0x0,0x0,#r4 = flag[12]
	0xf6, #r1 = (3*r1+2*r2+r3)
	0xf7, #r1 = r1*r4
	0xf1,0xe4,0x6,0x0,0x0,0x0,#stack[6] = r1

	0xf1,0xe1,0x7,0x0,0x0,0x0,#r1 = flag[7]
	0xf1,0xe2,0x8,0x0,0x0,0x0,#r2 = flag[8]
	0xf1,0xe3,0x9,0x0,0x0,0x0,#r3 = flag[9]
	0xf1,0xe5,0xC,0x0,0x0,0x0,#r4 = flag[12]
	0xf6, #r1 = (3*r1+2*r2+r3)
	0xf7, #r1 = r1*r4
	0xf1,0xe4,0x7,0x0,0x0,0x0,#stack[7] = r1

	0xf1,0xe1,0x8,0x0,0x0,0x0,#r1 = flag[8]
	0xf1,0xe2,0x9,0x0,0x0,0x0,#r2 = flag[9]
	0xf1,0xe3,0xA,0x0,0x0,0x0,#r3 = flag[10]
	0xf1,0xe5,0xC,0x0,0x0,0x0,#r4 = flag[12]
	0xf6, #r1 = (3*r1+2*r2+r3)
	0xf7, #r1 = r1*r4
	0xf1,0xe4,0x8,0x0,0x0,0x0,#stack[8] = r1

	0xf1,0xe1,0xD,0x0,0x0,0x0,#r1 = flag[13]
	0xf1,0xe2,0x13,0x0,0x0,0x0,#r2 = flag[19]
	0xf8,#r1 = r2,r2 = r1
	0xf1,0xe4,0xD,0x0,0x0,0x0,#stack[13] = r1
	0xf1,0xe7,0x13,0x0,0x0,0x0,#stack[19] = r2

	0xf1,0xe1,0xE,0x0,0x0,0x0,#r1 = flag[14]
	0xf1,0xe2,0x12,0x0,0x0,0x0,#r2 = flag[18]
	0xf8,#r1 = r2,r2 = r1
	0xf1,0xe4,0xE,0x0,0x0,0x0,#stack[14] = r1
	0xf1,0xe7,0x12,0x0,0x0,0x0,#stack[18] = r2

	0xf1,0xe1,0xF,0x0,0x0,0x0,#r1 = flag[15]
	0xf1,0xe2,0x11,0x0,0x0,0x0,#r2 = flag[17]
	0xf8,#r1 = r2,r2 = r1
	0xf1,0xe4,0xF,0x0,0x0,0x0,#stack[15] = r1
	0xf1,0xe7,0x11,0x0,0x0,0x0,#stack[17] = r2
	0xf4#ret

再找一下check函数,直接看qword_2022A8的交叉引用,找到真正的check函数

unsigned __int64 sub_F00()
{
  int i; // [rsp+Ch] [rbp-14h]
  unsigned __int64 v2; // [rsp+18h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  for ( i = 0; dword_2022A4 - 1 > i; ++i )
  {
    if ( *(qword_2022A8 + i) != byte_202020[i] )
      exit(0);
  }
  return __readfsqword(0x28u) ^ v2;
}
byte_202020[]={0x69, 0x45, 0x2A, 0x37, 0x09, 0x17, 
               0xC5, 0x0B, 0x5C, 0x72, 0x33, 0x76, 
               0x33, 0x21, 0x74, 0x31, 0x5F, 0x33,
               0x73, 0x72, 0x00, 0x00, 0x00, 0x00,
               0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
               0x00, 0x00}

EXP

from z3 import *
a=Real('a')
b=Real('b')
c=Real('c')

byte=[0x69, 0x45, 0x2A, 0x37, 0x09, 0x17, 0xC5, 0x0B, 0x5C, 0x72, 0x33, 0x76, 0x33, 0x21, 0x74, 0x31, 0x5F, 0x33, 0x73, 0x72,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0]

temp=byte[15]
byte[15]=byte[17]
byte[17]=temp

temp=byte[14]
byte[14]=byte[18]
byte[18]=temp

temp=byte[13]
byte[13]=byte[19]
byte[19]=temp
solve((3*a+2*b+c)*0x33==0x6dc5,(3*b+2*c+0x72)*0x33==0x5b0b,(3*c+2*0x72+0x33)*0x33==0x705c)
#a = 118,b = 51,c = 95

byte[6] = 118
byte[7] = 51
byte[8] = 95

byte[5]^=(byte[6])
byte[4]^=(byte[5])
byte[3]^=(byte[4])
byte[2]^=(byte[3])
byte[1]^=(byte[2])
byte[0]^=(byte[1])

flag=''
for i in range(32):
	flag+=chr(byte[i])
print(flag)

在这里插入图片描述

flag{Y0u_hav3_r3v3rs3_1t!}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Em0s_Er1t

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值