[buuctf.reverse] 144_[XMAN2018排位赛]easyvm 147_[XNUCA2018]Code_Interpreter

144_[XMAN2018排位赛]easyvm

虚拟机的题很少,感觉两个都差不多,不过这个确实感觉麻烦了点。

没有删除符号表这让处理起来方便了不少。代码除循环和结束命令外都是3字节,1字节命令2字节数据。

  printf("Please input your flag:");
  flag = malloc(0x20u);
  scanf("%32s", flag);
  v3[1] = (int)&flag;
  memcpy(cipher, &unk_3E75, sizeof(cipher));
  v3[0] = (int)cipher;
  v2[1] = (int)v3;
  v2[0] = 0xEFBEADDE;
  R[0] = (char **)&v17;
  R[1] = (char **)&v11;
  R[2] = (char **)&v10;
  R[3] = (char **)&v16;
  R[4] = (char **)&v15;                         // R4,V15存计算结果
  R[5] = &v7;
  R[6] = (char **)&v9;
  R[7] = (char **)&v8;
  R[8] = (char **)&v14;
  R[9] = (char **)&v13;
  R[10] = (char **)&v12;
  R[11] = (char **)&flag;
  R[12] = (char **)v3;                          // cipher
  R[13] = (char **)v2;
  while ( 1 )
  {
    v22 = read_code(code, &ptr);                // pop
    v21 = v22 & 0xFE;
    v20 = v22 & 1;
    switch ( v22 & 0xFE )
    {
      case 0:
        v6 = read_code(code, &ptr);             // op a b : mov R[a],(char)R[b] mov R[b],(char)b
        v5 = read_code(code, &ptr);
        if ( v20 )
        {
          if ( v20 == 1 )
            mov((int *)R[v6], (char)*R[v5]);
        }
        else
        {
          mov((int *)R[v6], v5);
        }
        break;
      case 2:
        v6 = read_code(code, &ptr);             // op a b : mov R[a],(int)R[b] mov R[b],(int)b
        v5 = read_code(code, &ptr);
        if ( v20 )
        {
          if ( v20 == 1 )
            mov32((int *)R[v6], (int)*R[v5]);
        }
        else
        {
          mov32((int *)R[v6], v5);
        }
        break;
      case 4:

循环没有开始结束标志,只是个二选一第一次进第二次出,所以没有嵌套

      case 0x28:
        if ( inloop )                           // 单字节指令 循环
        {
          if ( v9 )
            ptr = loopstart;
          else
            inloop = 0;
        }
        else
        {
          inloop = 1;
          loopstart = ptr;
        }
        break;

操作命令一共20个,包含赋值,加,移位,逻辑运算,栈运算等。先把操作命令按这些功能打印出来(不大准确,只为方便观看)


def printcode(code):
    i = 0
    while i<246:
        op = code[i]; i+=1
        v1 = op&0xfe 
        v2 = op&1 
        if v1 < 0x28:
            a = code[i]; i+=1
            b = code[i]; i+=1
        
        if v1 == 0 and v2 == 1:
            print(f'mov R[{a}] R[{b}]')
        if v1 == 0 and v2 == 0:
            print(f'mov R[{a}] {b}')
        if v1 == 2 and v2 == 1:
            print(f'mov32 R[{a}] R[{b}]')
        if v1 == 2 and v2 == 0:
            print(f'mov32 R[{a}] {b}')
        if v1 == 4 and v2 == 1:
            print(f'lea_ch R[{a}] R[{b}]')
        if v1 == 6 and v2 == 1:
            print(f'lea_int R[{a}] R[{b}]')
        if v1 == 8 and v2 == 1:
            print(f'ldr_int R[{a}] R[{b}]')
        if v1 == 10 and v2 == 1:
            print(f'ldr_ch R[{a}] R[{b}]')
        if v1 == 12 and v2 == 1:
            print(f'add R[{a}] R[{b}]')
        if v1 == 12 and v2 == 0:
            print(f'add R[{a}] {b}')
        if v1 == 14 and v2 == 1:
            print(f'add_pint R[{a}] R[{b}]')
        if v1 == 14 and v2 == 0:
            print(f'add_pint R[{a}] {b}')
        if v1 == 16 and v2 == 1:
            print(f'add_pch R[{a}] R[{b}]')
        if v1 == 16 and v2 == 0:
            print(f'add_pcj R[{a}] {b}')
        if v1 == 18 and v2 == 1:
            print(f'xor R[{a}] R[{b}]')
        if v1 == 18 and v2 == 0:
            print(f'xor R[{a}] {b}')
        if v1 == 20 and v2 == 0:
            print(f'mod R[{a}] {b}')
        if v1 == 22 and v2 == 1:
            print(f'or R[{a}] R[{b}]')
        if v1 == 22 and v2 == 0:
            print(f'or R[{a}] {b}')
        if v1 == 24 and v2 == 1:
            print(f'and R[{a}] R[{b}]')
        if v1 == 24 and v2 == 0:
            print(f'and R[{a}] {b}')
        if v1 == 26 and v2 == 1:
            print(f'push R[{a}] R[{b}]')
        if v1 == 26 and v2 == 0:
            print(f'push R[{a}] {b}')
        if v1 == 28 and v2 == 1:
            print(f'pop R[{a}] R[{b}]')
        if v1 == 30 and v2 == 1:
            print(f'shr R[{a}] R[{b}]')
        if v1 == 30 and v2 == 0:
            print(f'shr R[{a}] {b}')
        if v1 == 32 and v2 == 1:
            print(f'shl R[{a}] R[{b}]')
        if v1 == 32 and v2 == 0:
            print(f'shl R[{a}] {b}')
        if v1 == 34 and v2 == 1:
            print(f'ror R[{a}] R[{b}]')
        if v1 == 34 and v2 == 0:
            print(f'ror R[{a}] {b}')
        if v1 == 36 and v2 == 1:
            print(f'cmp_l R[{a}] R[{b}]')
        if v1 == 36 and v2 == 0:
            print(f'cmp_l R[{a}] {b}')
        if v1 == 38 and v2 == 1:
            print(f'cmp_eq R[{a}] R[{b}]')
        if v1 == 38 and v2 == 0:
            print(f'cmp_eq R[{a}] {b}')
        if v1 == 40:
            print('loop')
        if v1 == 42:
            print('end')
    return        

code = open('easyvm', 'rb').read()[0x2e95: 0x2e95+246]
print(list(code))
printcode(code)

然后对输出的结果一点点啃。大概分成4段,

第一段是对输入的flag进行换位 

'''
lea_ch R[1] R[11] #R1 = flag
xor R[3] R[3]
xor R[0] R[0]
xor R[4] R[4]
'''
#第1段换位置
j=0
for i in range(32):
    j = (j + 51)%32
    stack[i] = flag[j]
	
'''
loop
add R[0] 51
mod R[0] 32
lea_ch R[9] R[1]
add_pch R[9] R[0]
ldr_ch R[10] R[9]
mov R[4] R[10]
push R[5] R[4]
add R[3] 1
cmp_l R[3] 32
loop
'''

第二段是将每个字符分成前3后5然后用后一个的后5和前一个的前3拼成新的字节

#第1字符的前3位

'''
xor R[0] R[0]
lea_int R[8] R[5]
add_pint R[8] 224
lea_int R[2] R[8]
ldr_int R[10] R[2]
mov R[0] R[10]
and R[0] 224
shr R[0] 5
mov R[4] R[0]
xor R[3] R[3]
'''
#第2段,前5后3组合
[((flag[i]<<3)|(flag[(i-1) %32]>>5))&0xff for i in range(32)]


'''
loop
ldr_int R[10] R[2]
mov R[0] R[10]
and R[0] 31
shl R[0] 3         # a&0x1f;a<<=3
push R[5] R[0]
lea_int R[8] R[5]
add_pint R[8] 224
lea_int R[2] R[8]
ldr_int R[10] R[2]
mov R[0] R[10]
and R[0] 224       # a&0xe0;a>>5
shr R[0] 5
pop R[5] R[10]
add R[10] R[0]
push R[5] R[10]
add R[3] 1
cmp_l R[3] 31
loop
'''

#最后一个
'''
ldr_int R[10] R[2]
mov R[0] R[10]
and R[0] 31
shl R[0] 3
add R[0] R[4]
push R[5] R[0]
'''

第3段是先把key和第i%4位与i相加后再与原文作异或

#第3段 与key循环+序号 异或
def ror8(a):
    return (a>>i)|((a&0xff)<<24)
r3=0
r4=0xEFBEADDE
for i in range(32):
    stack[i] = flag[i]^(r4+i)
    r4 = ror8(r4)

'''
xor R[3] R[3]
mov32 R[4] R[13]   #0xEFBEADDE
loop
lea_int R[8] R[5]
add_pint R[8] 224
lea_int R[2] R[8]
ldr_int R[10] R[2]
mov R[0] R[10]
push R[5] R[0]
mov R[0] R[4]
add R[0] R[3]
pop R[5] R[10]
xor R[10] R[0]
push R[5] R[10]
ror R[4] 8
add R[3] 1
cmp_l R[3] 32
loop
'''

第4段看了半天没看懂,大概意思是没有,因为先是pop后边马上就push了,似乎没有作什么改动

#似乎没用,并没有改变数据

'''
xor R[3] R[3]
xor R[4] R[4]
lea_ch R[1] R[12]
loop
lea_ch R[9] R[1]
add_pch R[9] R[3]
ldr_ch R[10] R[9]
mov R[0] R[10]
push R[5] R[0]
lea_int R[8] R[5]
add_pint R[8] 223
ldr_int R[10] R[8]
pop R[5] R[0]      #pop后直接push
push R[5] R[0]
cmp_eq R[0] R[10]  #v8 = (r0 != r10)? 1: 0; r7=v8
or R[4] R[7]
add R[3] 1
cmp_l R[3] 32
loop
end
'''

根据这个反过来写出3句来解密

cipher = open('easyvm', 'rb').read()[0x2e75: 0x2e75+32]
#print(cipher)
key = [0xDE, 0xAD, 0xBE, 0xEF]

m = [cipher[i]^(key[i%4] + i) for i in range(32)]    #3
v = [( (m[(i-1)%32]<<5) | (m[i]>>3) )&0xff for i in range(32)]   #2
flag = [0]*32    #1
p = 0
for i in range(32):
    p = (p+51)%32
    flag[p] = v[i]
print(bytes(flag))
#xman{ae791f19bdf77357ff10bb6b0e}
#flag{ae791f19bdf77357ff10bb6b0e}

147_[XNUCA2018]Code_Interpreter

流程基本一样,主程序里输入3个数,还有3个要求就是数的第几位是5e然后就进虚拟机运行处理

__int64 __fastcall main(int a1, char **a2, char **a3)
{
  int v4; // [rsp+14h] [rbp-Ch]
  FILE *stream; // [rsp+18h] [rbp-8h]

  if ( a1 > 1 )
  {
    stream = fopen(a2[1], "rb");
    if ( !stream )
    {
      fprintf(stderr, "Could not open file %s\n", a2[1]);
      exit(1);
    }
    fseek(stream, 0LL, 2);
    v4 = ftell(stream);
    fseek(stream, 0LL, 0);
    ptr = malloc(v4);
    if ( fread(ptr, v4, 1uLL, stream) != 1 )
    {
      fprintf(stderr, "Error reading file %s\n", a2[1]);
      exit(1);
    }
    puts("Play a game, prepare three number: ");
    printf("First: ");
    __isoc99_scanf("%d", &num1);
    printf("Second: ");
    __isoc99_scanf("%d", &num2);
    printf("Third: ");
    __isoc99_scanf("%d", &num3);
    sub_400806((__int64)ptr);     //进虚拟机检查
    if ( !dword_6024B0 && (unsigned __int8)num1 == 0x5E && (num2 & 0xFF0000) == 0x5E0000 && (unsigned __int8)num3 == 94 )   //限制条件
      printf("Congratulations! \nflag is: X-NUCA{%x%x%x}\n", (unsigned int)num1, (unsigned int)num2, (unsigned int)num3);
    else
      puts("Try again.");
    return 0LL;
  }
  else
  {
    printf("usage: ./%s <code>\n", *a2);
    return 0xFFFFFFFFLL;
  }
}

一共11个命令也比较容易理解

  dword_6020A0[0] = num1;
  dword_6020A4 = num2;
  result = (unsigned int)num3;
  dword_6020A8 = num3;
  k = 0;
  j = 2;
  i = 0;
  v5 = 1;
  while ( v5 )
  {
    switch ( *(_BYTE *)((unsigned int)i + a1) )
    {
      case 0:
        v5 = 0;
        break;
      case 1:                                   // read int:4  data[++j] = int 
        v2 = ++i;
        ++i;
        v14 = *(unsigned __int8 *)(v2 + a1);    // +1
        v3 = i++;
        v15 = (*(unsigned __int8 *)(v3 + a1) << 8) + v14;// +2
        v4 = i++;
        v16 = (*(unsigned __int8 *)((unsigned int)i + a1) << 24) + (*(unsigned __int8 *)(v4 + a1) << 16) + v15;// +4,+3,+2,+1
        dword_6020A0[++j] = v16;
        break;
      case 2:
        --j;
        break;
      case 3:                                   // a,b data[a]+=data[b]
        v6 = *(_BYTE *)((unsigned int)++i + a1);
        dword_6024A0[v6] += dword_6024A0[*(unsigned __int8 *)((unsigned int)++i + a1)];
        break;
      case 4:                                   // a,b data[a]-=data[b]
        v7 = *(_BYTE *)((unsigned int)++i + a1);
        dword_6024A0[v7] -= dword_6024A0[*(unsigned __int8 *)((unsigned int)++i + a1)];
        break;
      case 5:                                   // a,b data[a]*=b
        v8 = *(_BYTE *)((unsigned int)++i + a1);
        dword_6024A0[v8] *= *(unsigned __int8 *)((unsigned int)++i + a1);
        break;
      case 6:                                   // a,b data[a]>>=b
        v9 = *(_BYTE *)((unsigned int)++i + a1);
        dword_6024A0[v9] = (unsigned int)dword_6024A0[v9] >> *(_BYTE *)((unsigned int)++i + a1);
        break;
      case 7:                                   // a,b data[a]=data[b]
        v10 = *(_BYTE *)((unsigned int)++i + a1);
        dword_6024A0[v10] = dword_6024A0[*(unsigned __int8 *)((unsigned int)++i + a1)];
        break;
      case 8:                                   // a,b data[a]=data[k+b]
        v11 = *(_BYTE *)((unsigned int)++i + a1);
        dword_6024A0[v11] = dword_6020A0[k + *(unsigned __int8 *)((unsigned int)++i + a1)];
        break;
      case 9:                                   // a,b data[a]^=data[b]
        v12 = *(_BYTE *)((unsigned int)++i + a1);
        dword_6024A0[v12] ^= dword_6024A0[*(unsigned __int8 *)((unsigned int)++i + a1)];
        break;
      case 0xA:                                 // a,b data[a]|=data[b]
        v13 = *(_BYTE *)((unsigned int)++i + a1);
        dword_6024A0[v13] |= dword_6024A0[*(unsigned __int8 *)((unsigned int)++i + a1)];
        break;
      default:
        fprintf(stderr, "Invalid opcode. %d\n", *(unsigned __int8 *)((unsigned int)i + a1));
        exit(1);
    }
    result = (unsigned int)++i;
  }
  return result;

跟上一题一样,第一步先把命令文件翻译成好自己理解的方式

from pwn import u32
code = open('code', 'rb').read()

data = [0]*4
i=0
j=2
k=0
v5=1
while (v5 == 1):
    if code[i] == 0:
        break
    elif code[i] == 1:
        v = u32(code[i+1: i+5])
        print(f'data[{j}] ={v}');i+=5;j+=1
    elif code[i] == 2:
        i +=1; j-=1 
    elif code[i] == 3:
        print(f'data[{code[i+1]}] += data[{code[i+2]}]');i+=3
    elif code[i] == 4:
        print(f'data[{code[i+1]}] -= data[{code[i+2]}]');i+=3
    elif code[i] == 5:
        print(f'data[{code[i+1]}] *= {code[i+2]}');i+=3
    elif code[i] == 6:
        print(f'data[{code[i+1]}] >>= {code[i+2]}');i+=3
    elif code[i] == 7:
        print(f'data[{code[i+1]}] = data[{code[i+2]}]');i+=3
    elif code[i] == 8:
        print(f'data[{code[i+1]}] = num[{k} + {code[i+2]}]');i+=3
    elif code[i] == 9:
        print(f'data[{code[i+1]}] ^= data[{code[i+2]}]');i+=3
    elif code[i] == 10:
        print(f'data[{code[i+1]}] |= data[{code[i+2]}]');i+=3
    else:
        print('err {code[i]}')
        

然后一段一段的啃,这个理解的不大对,因为确实后边两句整不清了,但是前边两句一个是运算,另一个就是一个数字,估计是相等,不然还会怎么着呢。

data = [n1,n2,n3]

d0 = (n1>>4)*21 - n3  
d2 = 494849131
d0 -= n3 
d4 = (n1>>4)*21 - n3 - n4

'''
data[4] ^= data[4]
data[0] ^= data[0]
data[1] = num[0 + 0]
data[2] = num[0 + 1]
data[3] = num[0 + 2]
data[1] >>= 4
data[1] *= 21
data[0] = data[1]
data[0] -= data[3]
data[2] =494849131
data[1] = num[0 + 3]
data[0] -= data[1]
data[4] |= data[0]
'''

d0 = (n3>>8)*3 + n2 
d2 = 1618573692
d1 = n3 
d0 = (n3>>8)*3 + n2 - n4 
d4 |= d0
'''
data[0] ^= data[0]
data[1] = num[0 + 0]
data[2] = num[0 + 1]
data[3] = num[0 + 2]
data[3] >>= 8
data[3] *= 3
data[0] = data[3]
data[0] += data[2]
data[2] =1618573692
data[1] = num[0 + 3]
data[0] -= data[1]
data[4] |= data[0]
'''

d0 = (n1>>8) + n2 
d2 = 1606204861
d0 = (n1>>8) + n2 - n4
d4 |= d0
'''
data[0] ^= data[0]
data[1] = num[0 + 0]
data[2] = num[0 + 1]
data[3] = num[0 + 2]
data[1] >>= 8
data[0] = data[1]
data[0] += data[2]
data[2] =1606204861
data[1] = num[0 + 3]
data[0] -= data[1]
data[4] |= data[0]
'''

先整理出3个等式

(n1>>4)*21 - n3 == 494849131
(n3>>8)*3 + n2 == 1618573692
(n1>>8) + n2  == 1606204861

由于n2和n3都可以用n1表示,所以只需要爆破n1的前3个字节,然后比对就可以了

#n1 = xxx5e n2 = x 5e x x n3 = x x x 5e 
for n1 in range(0x5e, 0x100000000, 0x100):
    n2 = (1606204861 - (n1>>8))& 0xffffffff
    n3 = ((n1>>4)*21 - 494849131) & 0xffffffff
    if (n2&0xff0000) == 0x5e0000 and (n3&0xff == 0x5e) and (n3>>8)*3 + n2 == 1618573692:
        print('flag{'+hex(n1)[2:] + hex(n2)[2:] + hex(n3)[2:] + '}')
        break

#flag{5e5f5e5e5f5e5e5f5e5e5f5e}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值