虎符ctf三道逆向中最简单的一道,比赛期间我只做出来这一道。题目对我来说挺新颖的,所以在这里也记录一下。刚写完vm的题解,有点累,所以这篇就简单点写。

只给出一个disasm.txt,部分如下:

1588254571359

查了下,是python的字节码。

pyc可以还原成py源代码,但是这个类似汇编的东西没办法还原出来(如果大佬们知道怎么还原,劳请告诉我一下)

dis模块

同时查到这个文本是怎么来的,python的dis模块。

下面有个小例子:

def f():
    s = [90, 100, 87, 109, 86, 108, 86, 105, 90, 104, 88, 102]
    b = [90, 100, 87, 109, 86, 108, 86, 105, 90, 104, 88, 102]
    #a = s[6:30:3]
    #
    #s = 'xctf{1231231}'
    #a = (((((((ord(s[0])*128+ord(s[1]))*128)+ord(s[2]))*128)+ord(s[3]))*128) + ord(s[4])) * 128 + ord(s[5]) 
    #print(a)
    a = map(lambda x:x+1, zip(s, b[2:5]))

import dis
dis.dis(f)

输出:

  2           0 LOAD_CONST               1 (90)
              2 LOAD_CONST               2 (100)
              4 LOAD_CONST               3 (87) 
              6 LOAD_CONST               4 (109)
              8 LOAD_CONST               5 (86) 
             10 LOAD_CONST               6 (108)
             12 LOAD_CONST               5 (86)
             14 LOAD_CONST               7 (105)
             16 LOAD_CONST               1 (90)
             18 LOAD_CONST               8 (104)
             20 LOAD_CONST               9 (88)
             22 LOAD_CONST              10 (102)
             24 BUILD_LIST              12
             26 STORE_FAST               0 (s)

  3          28 LOAD_CONST               1 (90)
             30 LOAD_CONST               2 (100)
             32 LOAD_CONST               3 (87)
             34 LOAD_CONST               4 (109)
             36 LOAD_CONST               5 (86)
             38 LOAD_CONST               6 (108)
             40 LOAD_CONST               5 (86)
             42 LOAD_CONST               7 (105)
             44 LOAD_CONST               1 (90)
             46 LOAD_CONST               8 (104)
             48 LOAD_CONST               9 (88)
             50 LOAD_CONST              10 (102)
             52 BUILD_LIST              12
             54 STORE_FAST               1 (b)

  9          56 LOAD_GLOBAL              0 (map)
             58 LOAD_CONST              11 (<code object <lambda> at 0x0000019F93CB0270, file "d:\桌面\20200419 虎符ctf\text.py", line 9>)
             60 LOAD_CONST              12 ('f.<locals>.<lambda>')
             62 MAKE_FUNCTION            0
             64 LOAD_GLOBAL              1 (zip)
             66 LOAD_FAST                0 (s)
             68 LOAD_FAST                1 (b)
             70 LOAD_CONST              13 (2)
             72 LOAD_CONST              14 (5)
             74 BUILD_SLICE              2
             76 BINARY_SUBSCR
             78 CALL_FUNCTION            2
             80 CALL_FUNCTION            2
             82 STORE_FAST               2 (a)
             84 LOAD_CONST               0 (None)
             86 RETURN_VALUE

Disassembly of <code object <lambda> at 0x0000019F93CB0270, file "d:\桌面\20200419 虎符ctf\text.py", line 9>:
  9           0 LOAD_FAST                0 (x)
              2 LOAD_CONST               1 (1)
              4 BINARY_ADD
              6 RETURN_VALUE

上面的输出的代码形式风格和题目给出的只有细微的差异(我用的是python3,而题目是python2,可能是这个原因)

然后可以去试着分析下题目给出的代码了,遇到不懂的点,可以自己写个函数,然后disasm,看看和题目中代码的差异。

分析字节码

首先是arr0,arr1,arr2这三个列表的赋值,很容易看出来。

然后有check0, check1,check2,check3四个检测函数。

check0

# <genexpr> line 6 of game.py

   6       0  LOAD_FAST             0  '.0'
           3  FOR_ITER             32  'to 38'
           6  STORE_FAST            1  'x'
           9  LOAD_GLOBAL           0  'ord'
          12  LOAD_FAST             1  'x'
          15  CALL_FUNCTION_1       1  None
          18  LOAD_GLOBAL           1  'range'
          21  LOAD_CONST               32
          24  LOAD_CONST               128
          27  CALL_FUNCTION_2       2  None
          30  COMPARE_OP            6  in
          33  YIELD_VALUE
          34  POP_TOP
          35  JUMP_BACK             3  'to 3'
          38  LOAD_CONST               None
          41  RETURN_VALUE
//推测是判断字符ord(x) in (32, 128)

# check0 line 5 of game.py

   6       0  LOAD_GLOBAL           0  'all'
           3  LOAD_GENEXPR             '<code_object <genexpr>>'
           6  MAKE_FUNCTION_0       0  None
           9  LOAD_FAST             0  's'
          12  GET_ITER
          13  CALL_FUNCTION_1       1  None
          16  CALL_FUNCTION_1       1  None
          19  RETURN_VALUE

//check0()判断字符介于(32,128)

配合着我代码中的注释就应该能看懂了吧,我就不多解释了。

check1

# check1 line 8 of game.py

   9       0  LOAD_GLOBAL           0  'len'
           3  LOAD_FAST             0  's'
           6  CALL_FUNCTION_1       1  None
           9  LOAD_CONST               100
          12  COMPARE_OP            0  <
          15  POP_JUMP_IF_FALSE    58  'to 58'
//if len(s)<100
          18  LOAD_GLOBAL           0  'len'
          21  LOAD_FAST             0  's'
          24  CALL_FUNCTION_1       1  None
          27  LOAD_GLOBAL           0  'len'
          30  LOAD_FAST             0  's'
          33  CALL_FUNCTION_1       1  None
          36  BINARY_MULTIPLY                             //乘
//len(s) * len(s)
          37  LOAD_CONST               777
          40  BINARY_MODULO                               //取模
//(len(s) * len(s))%777
          41  LOAD_CONST               233
          44  BINARY_XOR                                  //异或
//((len(s) * len(s))%777) ^ 233
          45  LOAD_CONST               513
          48  COMPARE_OP            2  ==
//if ((len(s) * len(s))%777) ^ 233 == 513
        51_0  COME_FROM            15  '15'
          51  POP_JUMP_IF_FALSE    58  'to 58'

  10      54  LOAD_GLOBAL           1  'True'
          57  RETURN_END_IF
        58_0  COME_FROM            51  '51'

  12      58  LOAD_GLOBAL           2  'False'
          61  RETURN_VALUE
          62  LOAD_CONST               None
          65  RETURN_VALUE

//check1()判断flag长度,经爆破得知len(flag) = 39

check1检测flag长度是否符合条件( if ((len(s) * len(s))%777) ^ 233 == 513

爆破可以求得长度为39

check2

# check2 line 14 of game.py

  15       0  LOAD_GLOBAL           0  'ord'
           3  LOAD_FAST             0  's'
           6  LOAD_CONST               0
           9  BINARY_SUBSCR                             //TOS = TOS1[TOS]
          10  CALL_FUNCTION_1       1  None
          13  LOAD_CONST               128
          16  BINARY_MULTIPLY        

//ord(s[0]) * 128

          17  LOAD_GLOBAL           0  'ord'
          20  LOAD_FAST             0  's'
          23  LOAD_CONST               1
          26  BINARY_SUBSCR                             
          27  CALL_FUNCTION_1       1  None
          30  BINARY_ADD    
          31  LOAD_CONST               128
          34  BINARY_MULTIPLY           

//(ord(s[0])*128+ord(s[1])) * 128

          35  LOAD_GLOBAL           0  'ord'
          38  LOAD_FAST             0  's'
          41  LOAD_CONST               2
          44  BINARY_SUBSCR
          45  CALL_FUNCTION_1       1  None
          48  BINARY_ADD
          49  LOAD_CONST               128
          52  BINARY_MULTIPLY

//(((ord(s[0])*128+ord(s[1]))*128) + ord(s[2])) * 128

          53  LOAD_GLOBAL           0  'ord'
          56  LOAD_FAST             0  's'
          59  LOAD_CONST               3
          62  BINARY_SUBSCR
          63  CALL_FUNCTION_1       1  None
          66  BINARY_ADD
          67  LOAD_CONST               128
          70  BINARY_MULTIPLY

//(((((ord(s[0])*128+ord(s[1]))*128)+ord(s[2])) * 128) + ord(s[3])) * 128

          71  LOAD_GLOBAL           0  'ord'
          74  LOAD_FAST             0  's'
          77  LOAD_CONST               4
          80  BINARY_SUBSCR
          81  CALL_FUNCTION_1       1  None
          84  BINARY_ADD
          85  LOAD_CONST               128
          88  BINARY_MULTIPLY

//(((((((ord(s[0])*128+ord(s[1]))*128)+ord(s[2]))*128)+ord(s[3]))*128) + ord(s[4])) * 128

          89  LOAD_GLOBAL           0  'ord'
          92  LOAD_FAST             0  's'
          95  LOAD_CONST               5
          98  BINARY_SUBSCR
          99  CALL_FUNCTION_1       1  None
         102  BINARY_ADD

//(((((((ord(s[0])*128+ord(s[1]))*128)+ord(s[2]))*128)+ord(s[3]))*128) + ord(s[4])) * 128 + ord(s[5]) 

         103  LOAD_CONST               3533889469877L
         106  COMPARE_OP            2  ==
         109  POP_JUMP_IF_FALSE   138  'to 138'
         112  LOAD_GLOBAL           0  'ord'
         115  LOAD_FAST             0  's'
         118  LOAD_CONST               -1
         121  BINARY_SUBSCR
         122  CALL_FUNCTION_1       1  None
         125  LOAD_CONST               125
         128  COMPARE_OP            2  ==
       131_0  COME_FROM           109  '109'
         131  POP_JUMP_IF_FALSE   138  'to 138'

  16     134  LOAD_GLOBAL           1  'True'
         137  RETURN_END_IF
       138_0  COME_FROM           131  '131'

  18     138  LOAD_GLOBAL           2  'False'
         141  RETURN_VALUE
         142  LOAD_CONST               None
         145  RETURN_VALUE

/* 
if 表达式 == 3533889469877L:
    if ord(s[-1]) == 125:
        return True
    else return false
else return false
*/
//check2()判断flag{}这个框架,以及flag{}主体的第一个字符flag[5]

check2用于判断前6个字符是否满足(((((((ord(s[0])*128+ord(s[1]))*128)+ord(s[2]))*128)+ord(s[3]))*128) + ord(s[4])) * 128 + ord(s[5]) == 3533889469877,同时判断最后一个字符是否是}

只要学过数学的小学生都知道6元一次等式根本不可能求出六个未知数,所以题目可能是假设了前五个字符是flag{,由此我们可以求出flag的第6个字符,也就是flag主体部分的第一个字符。

注意,我和炜昊都被这里坑到了,看到了flag[5],以为只有5个字符,恰好是flag{,忽略了第六个字符。

这个低级的下标错误平常不会错,但是每次到这个综合性题目中总会出错,还是要注意啊。

check3

# check3 line 20 of game.py

  21       0  LOAD_GLOBAL           0  'map'
           3  LOAD_GLOBAL           1  'ord'
           6  LOAD_FAST             0  's'
           9  CALL_FUNCTION_2       2  None
          12  STORE_FAST            1  'arr'
//e.g: map(function, iterable, ...)
//arr = map(ord, s)

  22      15  LOAD_FAST             1  'arr'
          18  LOAD_CONST               6
          21  LOAD_CONST               30
          24  LOAD_CONST               3
          27  BUILD_SLICE_3         3                   //切片,3个一组
          30  BINARY_SUBSCR
          31  STORE_FAST            2  'a'

//a = arr[6:30:3]

  23      34  SETUP_LOOP           62  'to 99'
          37  LOAD_GLOBAL           2  'range'
          40  LOAD_GLOBAL           3  'len'
          43  LOAD_FAST             2  'a'
          46  CALL_FUNCTION_1       1  None
          49  CALL_FUNCTION_1       1  None
          52  GET_ITER
          53  FOR_ITER             42  'to 98'
          56  STORE_FAST            3  'i'

//类似for i in range(len(a)) ,不过这里用的是迭代器

  24      59  LOAD_FAST             2  'a'
          62  LOAD_FAST             3  'i'
          65  BINARY_SUBSCR
          66  LOAD_CONST               17684
          69  BINARY_MULTIPLY
          70  LOAD_CONST               372511
          73  BINARY_ADD
          74  LOAD_CONST               257
          77  BINARY_MODULO
          78  LOAD_GLOBAL           4  'arr0'
          81  LOAD_FAST             3  'i'
          84  BINARY_SUBSCR
          85  COMPARE_OP            3  !=
          88  POP_JUMP_IF_FALSE    53  'to 53'
       
/*
if (a[i] * 17684 + 372511) % 257 == arr0[i]:(是==,我没搞错(因为jump if false))
    goto 53(继续循环)
else return flase
*/

  25      91  LOAD_GLOBAL           5  'False'
          94  RETURN_END_IF
        95_0  COME_FROM            88  '88'
          95  JUMP_BACK            53  'to 53'
          98  POP_BLOCK
        99_0  COME_FROM            34  '34'

  26      99  LOAD_FAST             1  'arr'
         102  LOAD_CONST               -2
         105  LOAD_CONST               33
         108  LOAD_CONST               -1
         111  BUILD_SLICE_3         3
         114  BINARY_SUBSCR
         115  LOAD_CONST               5
         118  BINARY_MULTIPLY
         119  STORE_FAST            4  'b'

//b = arr[-2:33:-1] * 5

  27     122  LOAD_GLOBAL           0  'map'
         125  LOAD_LAMBDA              '<code_object <lambda>>'
         128  MAKE_FUNCTION_0       0  None
         131  LOAD_GLOBAL           6  'zip'
         134  LOAD_FAST             4  'b'
         137  LOAD_FAST             1  'arr'
         140  LOAD_CONST               7
         143  LOAD_CONST               27
         146  SLICE+3
         147  CALL_FUNCTION_2       2  None
         150  CALL_FUNCTION_2       2  None
         153  STORE_FAST            5  'c'
//c = map(lambda x:函数体, zip(b, arr[7,27]))
//即c[i] = b[i] ^ arr[7,27][i] 

  28     156  LOAD_FAST             5  'c'
         159  LOAD_GLOBAL           7  'arr1'
         162  COMPARE_OP            3  !=
         165  POP_JUMP_IF_FALSE   172  'to 172'
  29     168  LOAD_GLOBAL           5  'False'
         171  RETURN_END_IF
       172_0  COME_FROM           165  '165'

/*
if c == arr1:
    goto 172
else return false
*/

  30     172  LOAD_CONST               0
         175  STORE_FAST            6  'p'

  31     178  SETUP_LOOP          105  'to 286'
         181  LOAD_GLOBAL           2  'range'
         184  LOAD_CONST               28
         187  LOAD_CONST               34
         190  CALL_FUNCTION_2       2  None
         193  GET_ITER
         194  FOR_ITER             88  'to 285'
         197  STORE_FAST            3  'i'

/*
for i in range(28, 34)
*/

  32     200  LOAD_FAST             1  'arr'
         203  LOAD_FAST             3  'i'
         206  BINARY_SUBSCR
         207  LOAD_CONST               107
         210  BINARY_ADD
         211  LOAD_CONST               16
         214  BINARY_DIVIDE
         215  LOAD_CONST               77
         218  BINARY_ADD

// ((arr[i] + 107)//16)+77  (除法//还是/存疑)

         219  LOAD_GLOBAL           8  'arr2'
         222  LOAD_FAST             6  'p'
         225  BINARY_SUBSCR
         226  COMPARE_OP            3  !=
         229  POP_JUMP_IF_TRUE    268  'to 268'
/* 
if arr2[p] != ((arr[i] + 107)//16)+77 :
    return false
*/
         232  LOAD_FAST             1  'arr'
         235  LOAD_FAST             3  'i'
         238  BINARY_SUBSCR
         239  LOAD_CONST               117
         242  BINARY_ADD
         243  LOAD_CONST               16
         246  BINARY_MODULO
         247  LOAD_CONST               99
         250  BINARY_ADD
         251  LOAD_GLOBAL           8  'arr2'
         254  LOAD_FAST             6  'p'
         257  LOAD_CONST               1
         260  BINARY_ADD
         261  BINARY_SUBSCR
         262  COMPARE_OP            3  !=
       265_0  COME_FROM           229  '229'
         265  POP_JUMP_IF_FALSE   272  'to 272'
/*
if (arr[i]+117)%16+99 == arr2[p+1]:
    goto 272
else return false
*/

  33     268  LOAD_GLOBAL           9  'false'
         271  RETURN_END_IF
       272_0  COME_FROM           265  '265'

  34     272  LOAD_FAST             6  'p'
         275  LOAD_CONST               2
         278  INPLACE_ADD                        //TOS = TOS1 + TOS
         279  STORE_FAST            6  'p'
         282  JUMP_BACK           194  'to 194'
         285  POP_BLOCK
       286_0  COME_FROM           178  '178'

//p += 2,然后继续循环

  35     286  LOAD_GLOBAL          10  'True'
         289  RETURN_VALUE

整理一下,check3的逻辑是这样的

def check3():
    arr = map(ord, flag)
    a = arr[6:30:3]
    for i in range(len(a)):
        if (a[i] * 17684 + 372511) % 257 != arr0[i]:
            return False
    b = arr[-2:33:-1] * 5
    c = map(lambda x: x[0]^x[1], zip(b, arr[7,27]))
    if c != arr1:
        return False
    p = 0
    for i in range(28, 34):
        if arr2[p] != (((arr[i]+107)//16)+77):
            return False
        if arr2[p+1] != ((arr[i]+117)%16+99):
            return False
        p += 2
    return True

逆向

倒着逆,脚本如下:

arr0 = [249, 91, 149, 113, 16, 91, 53 ,41]
arr1 = [43, 1, 6, 69, 20, 62, 6, 44, 24, 113, 6, 35, 0 , 3, 6, 44, 20, 22, 127, 60]
arr2 = [90, 100, 87, 109, 86, 108, 86, 105, 90, 104, 88, 102]


def check3():
    arr = map(ord, flag)
    a = arr[6:30:3]
    for i in range(len(a)):
        if (a[i] * 17684 + 372511) % 257 != arr0[i]:
            return False
    b = arr[-2:33:-1] * 5
    c = map(lambda x: x[0]^x[1], zip(b, arr[7,27]))
    if c != arr1:
        return False
    p = 0
    for i in range(28, 34):
        if arr2[p] != (((arr[i]+107)//16)+77):
            return False
        if arr2[p+1] != ((arr[i]+117)%16+99):
            return False
        p += 2
    return True


def solve_check1():
    for i in range(100):
        if ((i *i )%777) ^ 233 == 513:
            print(i)

def solve_5():
    for i,n in enumerate('flag{'):
        flag[i] = ord(n)
    flag[38] = ord('}')
    sum = 0
    for i in range(5):
        sum = (sum + flag[i]) * 128
        #print(sum)
    for i in range(32,128):
        if sum+i==3533889469877:
            flag[5] = i

def solve_6_30_3():
    for i in range(6, 30, 3):
        for j in range(32, 128):
            if (j * 17684 + 372511) % 257 == arr0[(i-6)//3]:
                flag[i] = j
                break

def solve_28_34():
    p = 0 
    for i in range(28,34):
        for j in range(32,128):
            if (((j+107)//16)+77) == arr2[p] and ((j+117)%16+99) == arr2[p+1]:
                flag[i] = j
                break
        p += 2

def solve_34_38():
    flag[34] = flag[18] ^ arr1[11]
    flag[35] = flag[9] ^ arr1[2]
    flag[36] = flag[12] ^ arr1[5]
    flag[37] = flag[15] ^ arr1[8]

def solve_7_27(): 
    b = flag[37:33:-1]*5
    for i in range(20):
        flag[i+7] = b[i] ^ arr1[i]


def main():
    solve_check1()
    global flag
    flag = [0] * 39
    solve_5()
    solve_6_30_3()
    solve_28_34()
    solve_34_38()
    solve_7_27()
    for i in flag:
        print(chr(i),end='')

main()

注意flag[7, 27]的值需要依靠flag[34, 38]求出。

Last modification:April 30th, 2020 at 10:15 pm