180202 逆向-Whale(CrackMe200)

1625-5 王子昂 总结《2018年2月2日》 【连续第490天总结】
A. Whale-CrackMe200
B.

前言

昨天搞定了Pintools的安装,但是由于win下加载了太多库导致Example中的inscount无法准确记录运行指令数,而用proccount虽然可以记录下数百万次指令和各自所属的模块,应该可以精确记录程序的用户模块所执行的指令,但是时间开销有点巨大,而且输出文件中包含了大量冗余信息,不利用读取

在我相对而言更熟悉Python的情况下选择去找了Pin_Python的模块,虽然Github上有Windows版本,但据Readme所言编译用的是VS2005版本……尝试了许多次都无法跨过这些错误

正在创建库 obj-intel64/Python_Pin.lib 和对象 obj-intel64/Python_Pin.exp
libcpmt.lib(xonce.obj) : error LNK2001: 无法解析的外部符号 _purecall
libcpmt.lib(wlocale.obj) : error LNK2001: 无法解析的外部符号 _purecall
libcpmt.lib(locale0.obj) : error LNK2001: 无法解析的外部符号 _purecall
libcpmt.lib(xlocale.obj) : error LNK2001: 无法解析的外部符号 _purecall

无奈之下只好放弃python转而查阅C-Pin的文档

然而在等待编译、安装的过程中,静态分析先出了结果……OTZ

静态分析

这里写图片描述
看到这种结构,显然是自定义的一个解释器
机器码保存在a1中,全部dump下来

然后按照运行顺序,依次照抄
第一个opcode为0x104:

case 0x104u:
   v6 = *(_DWORD *)(a1 + 4 * v3);
   i = v3 + 1;
   push(v6);
   break;

调用了一个函数

int *__cdecl push(int a1)
{
  int *result; // eax

  if ( (_UNKNOWN *)p == &q )
    Error();
  result = (int *)(p + 4);                      // p = p + 4
  p = (int)result;
  *result = a1;                                 // *p = a1
  return result;
}

p作为esp,即堆栈指针来使用
这个函数可以看出来就是push

显然,另外有一个函数对应的是pop,即sub_4013e0

每个opcode对应的指令都不复杂,按照运行逻辑一一复刻即可

复刻运行并分析

打出log如下

0   push 0x118      | [280]
2   s[280] = input()        | []
3   push 0x0        | [0]
5   push 0x0        | [0, 0]
7   s[0] = 0        | []
8   push 0x2f       | [47]
10  push 0x0        | [47, 0]
12  get s[0]        | [47, 0]
13  sub 0-47        | [-47]
14  jqz 53  fail    | []
16  push 0x18       | [24]
18  push 0x0        | [24, 0]
20  get s[0]        | [24, 0]
21  plus 0+24       | [24]
22  get s[24]       | [3]
23  push 0x10       | [3, 16]
25  push 0x0        | [3, 16, 0]
27  get s[0]        | [3, 16, 0]
28  push 0x7        | [3, 16, 0, 7]
30  and 7&0         | [3, 16, 0]
31  plus 0+16       | [3, 16]
32  get s[16]       | [3, 65]
33  xor 65^3        | [66]
34  push 0x118      | [66, 280]
36  push 0x0        | [66, 280, 0]
38  get s[0]        | [66, 280, 0]
39  plus 0+280      | [66, 280]
40  get s[280]      | [66, 1]
41  sub 1-66        | [-65]
42  jnz 53  succ    | []
53  push 0x2f       | [47]
55  push 0x0        | [47, 0]
57  get s[0]        | [47, 0]
58  sub 0-47        | [-47]
59  jnz 65  succ    | []
65  push 0x21c      | [540]
67  printf 540      | []
68  Finshed         | 

单一段运行大概看不出来什么,但是可以发现第40处的指令取得了输入,然后和66作差,因为结果非0而跳向53导致结束
那么很明显第一个输入就应该是66,使其继续往下运行

于是给input[0]赋值为66,再跑一遍

...
40  get s[280]      | [66, 66]
41  sub 66-66       | [0]
42  jnz 53  fail    | []
44  push 0x0        | [0]
46  get s[0]        | [0]
47  stack++ 0       | [1]
48  push 0x0        | [1, 0]
50  s[0] = 1        | []
51  jmp 8       | []
8   push 0x2f       | [47]
10  push 0x0        | [47, 0]
12  get s[0]        | [47, 1]
13  sub 1-47        | [-46]
14  jqz 53  fail    | []
16  push 0x18       | [24]
18  push 0x0        | [24, 0]
20  get s[0]        | [24, 1]
21  plus 1+24       | [25]
22  get s[25]       | [36]
23  push 0x10       | [36, 16]
25  push 0x0        | [36, 16, 0]
27  get s[0]        | [36, 16, 1]
28  push 0x7        | [36, 16, 1, 7]
30  and 7&1         | [36, 16, 1]
31  plus 1+16       | [36, 17]
32  get s[17]       | [36, 110]
33  xor 110^36      | [74]
34  push 0x118      | [74, 280]
36  push 0x0        | [74, 280, 0]
38  get s[0]        | [74, 280, 1]
39  plus 1+280      | [74, 281]
40  get s[281]      | [74, 0]
41  sub 0-74        | [-74]
42  jnz 53  succ    | []
53  push 0x2f       | [47]
55  push 0x0        | [47, 0]
57  get s[0]        | [47, 1]
58  sub 1-47        | [-46]
59  jnz 65  succ    | []
65  push 0x21c      | [540]
67  printf 540      | []
68  Finshed         | 

有了第二个段运行就能理出来循环的逻辑了
8-14: if(i<47){
15-33: get(x)
34-42: if(input[i]==x)
53-68: (else)return False
}
那么只需要爆破42处的指令使其无论判断正负都判断通过,再记录33处指令的结果,即可得到flag列表了

脚本

完整模拟器如下:

orders = [0x104, 0x118, 0x305, 0x104, 0x0, 0x104, 0x0, 0x404, 0x104, 0x2f, 0x104, 0x0, 0x304, 0x201, 0x303, 0xd4, 0x104, 0x18, 0x104, 0x0, 0x304, 0x101, 0x504, 0x104, 0x10, 0x104, 0x0, 0x304, 0x104, 0x7, 0x102, 0x101, 0x504, 0x402, 0x104, 0x118, 0x104, 0x0, 0x304, 0x101, 0x504, 0x201, 0x403, 0xd4, 0x104, 0x0, 0x304, 0x301, 0x104, 0x0, 0x404, 0x203, 0x20, 0x104, 0x2f, 0x104, 0x0, 0x304, 0x201, 0x403, 0x104, 0x104, 0x218, 0x405, 0x103, 0x104, 0x21c, 0x405, 0x103]
s = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 110, 116, 105, 121, 76, 97, 98, 3, 36, 35, 49, 59, 19, 34, 54, 7, 21, 55, 92, 74, 124, 86, 38, 117, 88, 89, 44, 78, 124, 36, 79, 117, 94, 71, 81, 84, 14, 87, 36, 120, 67, 76, 42, 74, 10, 87, 91, 121, 44, 67, 42, 76, 127, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
input = [0]
stack = []
flag = []

i = 0
while(i!=len(orders)):
    print(i, end='\t')
    print(hex(orders[i])[2:], end='\t')
    if(orders[i] == 0x104):
        i += 1
        print('push 0x%x'% orders[i], end='\t\t| ')
        stack.append(orders[i])

    elif(orders[i] == 0x305):
        p = stack.pop()
        print('s[%d] = input()'%p, end='\t\t| ')
        for t in range(len(input)):
            s[p+t] = input[t]

    elif(orders[i] == 0x404):
        p = stack.pop()
        q = stack.pop()
        print('s[%d] = %d'%(p, q), end='\t\t| ')
        s[p] = q

    elif(orders[i] == 0x304 or orders[i] == 0x504):
        p = stack.pop()
        print('get s[%d]'%p, end='\t\t| ')
        stack.append(s[p])

    elif(orders[i] == 0x201):
        p = stack.pop()
        q = stack.pop()
        print('sub %d-%d'%(p, q), end='\t\t| ')
        stack.append(p-q)

    elif(orders[i] == 0x303):
        i += 1
        print("jqz %d" % (orders[i] // 4), end='\t')
        if(stack.pop()==0):
            print("succ", end='\t| ')
            print(stack)
            i = orders[i]//4
            continue
        else:
            print("fail", end='\t| ')
    elif(orders[i] == 0x403):
        i += 1
        print("jnz %d" % (orders[i] // 4), end='\t')
        if(stack.pop()!=0):
            print("succ", end='\t| ')
            # print(stack)
            # 爆破
            # i = orders[i]//4
            # continue
        else:
            print("fail", end='\t| ')

    elif(orders[i] == 0x101):
        p = stack.pop()
        q = stack.pop()
        print("plus %d+%d"%(p, q), end='\t\t| ')
        stack.append(p+q)

    elif(orders[i] == 0x102):
        p = stack.pop()
        q = stack.pop()
        print("and %d&%d\t"%(p, q), end='\t\t| ')
        stack.append(p & q)

    elif (orders[i] == 0x402):
        p = stack.pop()
        q = stack.pop()
        print("xor %d^%d" % (p, q), end='\t\t| ')
        stack.append(p ^ q)
        flag.append(p ^ q)

    elif(orders[i] == 0x405):
        p = stack.pop()
        print("printf %d"%p, end='\t\t| ')

    elif(orders[i] == 0x103):
        print("Finshed\t", end='\t\t| ')
        break

    elif(orders[i] == 0x301):
        p = stack.pop()
        print("stack++ %d"% p, end='\t\t| ')
        stack.append(p+1)

    elif(orders[i] == 0x203):
        i += 1
        print("jmp %d"% (orders[i]//4), end='\t\t| ')
        print(stack)
        i = orders[i]//4
        continue

    else:
        print("0x%x"%orders[i], end='\t\t| ')
        break
    i += 1
    print(stack)
print('\n')
print("执行指令数:" + str(i))
print(flag)
for i in flag:
    print(chr(i), end='')

就能得到flag

确认逻辑也是逐字符读取的,明天尝试用Pintools、Frida之类通过Hook/指令计数来爆破

C. 明日计划
PinTools、Frida

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值