跳转至

2018 骇极杯

标签:c++, VM, hash

骇极杯的3道RE的writeup

这是上海的一个什么大学生信息安全竞赛,我并没有参加,只是做了一下了里面的re题

题目,脚本及ida数据库下载:下载地址

_backup文件夹内为题目原题,其余的题目文件能被我打过补丁。

CPP

IDA打开后很明显是c++写的,可以看到string类。

sub_4010A2把输入的每一位循环左移2位再跟位数异或,再跟常量比较,如果一致,会得到假的flag

flag is: flag{7h15_15_4_f4k3_F14G_=3=_rua!}

之后在sub_401332中,又把每一位和前一位进行了一些逻辑运算操作,刚好这学期在学数电,懂得一些逻辑运算的化简,化简后这个操作就是异或。用A代表a[j],B代表a[j-1],化简过程: $$ (A+B)\overline{AB}=(A+B)(\overline{A}+\overline{B})=A\overline{A}+A\overline{B}+B\overline{A}+B\overline{B})=A\overline{B}+B\overline{A}=A⊕B $$ 总共循环了四次,最后跟常量比较。

反解一下这两个过程算出flag就行了。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
a=[153,176,135,158,112,232,65,68,5,4,139,154,116,188,85,88,181,97,142,54,172,9,89,229,97,221,62,63,185,21,237,213]
for i in range(4):
    for j in range(1,32)[::-1]:
        a[j]^=a[j-1]
s=''
for i in range(32):
    a[i]^=i
    a[i]=((a[i]>>2)|(a[i]<<6))&0xff
    s+=chr(a[i])
print(s)

由于涉及循环左移右移,用C写更方便一些,不过后面还是补了一个用python写的,感觉也不会有多差

cyvm

VM题,指令比较简单,要注意的是他是用栈作为寄存器的,对ebp-20h有一个0x14的偏移,每个寄存器大小是4byte,要看汇编 才能看的出,ida的反编译不能很好的解析,实际上栈上的变量应该是这样的:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
-000000000000003C var_3C          dd ?
-0000000000000038 bytecode        dd ?
-0000000000000034 var_34          dd ?
-0000000000000030 var_30          dd ?
-000000000000002C _ip             dd ?
-0000000000000028 var_28          dd ?
-0000000000000024 var_24          dd ?
-0000000000000020 r0              dd ?
-000000000000001C r1              dd ?
-0000000000000018 r2              dd ?
-0000000000000014 r3              dd ?
-0000000000000010 _zf             dd ?
-000000000000000C var_C           dd ?
-0000000000000008 var_8           dq ?
+0000000000000000  s              db 8 dup(?)

bytecode中的14代表r0,15代表r1,16代表r2,17代表r3

bytecode对应分析后的指令:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
        0F              |             scanf("%s",str)
        10 14 20        |             mov r0,20h
        10 16 00        |             mov r2,00h
        09 24           |             jmp loc_1
loc_2:                  |     loc_2:
        0A 14 16        |             cmp r0,r2
        02 15 16 E9     |             mov r1,[str+r2]
        12 16 E8        |             inc r2
        02 17 16        |             mov r3,[str+r2]
        13 16 90        |             dec r2
        06 15 17 45     |             xor r1,r3
        06 15 16 76     |             xor r1,r2
        01 15 16        |             mov r1,[str+r2]
        12 16 FF        |             inc r2
loc_1:                  |    loc_1:     
        0A 14 16        |             cmp r0,r2
        0C 09           |             jz loc_2
        0E              |             return strncmp(str, s2, 32uLL) == 0
;bytecode中的E9 E8 90 45 76 FF没有任何意义,属于直接跳过的,这里就省略了

可以看出它把每一位跟后一位异或,再跟位数异或,最后和常量(s2)比较。

倒过来求解得出flag:

1
2
3
4
5
6
7
8
9
a=[0x0A,0x0C,0x04,0x1F,0x48,0x5A,0x5F,0x03,0x62,0x67,0x0E,0x61,0x1E,0x19,0x08,0x36,0x47,0x52,0x13,0x57,0x7C,0x39,0x54,0x4B,0x05,0x05,0x45,0x77,0x15,0x26,0x0E,0x62]
for i in range(32):
    a[i]^=i
s=''
for i in reversed(range(1,32)):
    a[i-1]^=a[i]
for i in range(32):
    s+=chr(a[i])
print(s)

What is it

这题目有点误导啊,看上去还以为是个elf文件,用010 editor打开发现是pe头,先改成加个.exe后缀吧。

IDA打开后,发现先要输入一个六位小写字母字符串,然后对其算md5,得到的结果通过以下运算:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
v15 = 0;
v14 = 0;
for ( j = 0; j <= 31; ++j )
{
  if ( strmd5[j] == 48 )
  {
    ++v15;
    v14 += j;
  }
}

然后要满足10 * v15 + v14 == 403

不多说,写脚本爆破,分几部分多开几个程序跑,记得开上睿频,

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
from hashlib import *
s='aaaaaa'
for a in 'opqrstuvwxyz':
    for b in 'abcdefghijklmnopqrstuvwxyz':
        for c in 'abcdefghijklmnopqrstuvwxyz':
            for d in 'abcdefghijklmnopqrstuvwxyz':
                for e in 'abcdefghijklmnopqrstuvwxyz':
                    for f in 'abcdefghijklmnopqrstuvwxyz':
                        s=a+b+c+d+e+f
                        smd5=md5(s.encode('ascii')).hexdigest()
                        count=0
                        for i in range(32):
                            if(smd5[i]=='0'):
                                count+=10
                                count+=i
                        if count==403:
                            print(s)
                            exit(0)
        print(s)

得到字符串ozulmt

然后接下来00402757的函数是对00402626smc。先把一个跟上文中的md5有关的值作为seed,md5是固定的,那后续的rand也是固定的,很迷= =,总之动调到00402626F5就完事儿了。F5之后看到先有个输入,然后校验特定位置(开头,结尾,中间几个“-”字符),剩下的位置校验的数值也是常量(跟之前md5有关,位置又是通过rand()和固定的seed得到的),不多说,直接在校验之前设下断点,把字符串拿到([ebp-6Bh]),对其格式就有flag了。

flag这部分太简单了,就没写脚本。

评论