首页
社区
课程
招聘
[原创]python字节码逆向题 虎符第一题
发表于: 2020-4-20 22:18 8601

[原创]python字节码逆向题 虎符第一题

2020-4-20 22:18
8601
昨天上午弄了那么久以为能拿一血没想到被砍在前六个字符,是我太菜了。权当学习python字节码指令了。

下载给的就是asm文件,不是pyc文件,不能反编译成源代码,只能看汇编码,还好汇编不难。
源文件见附件(加了注释)

硬编码三个字符串,然后定义四个check函数,check0~check3.

输入的值保存在flag里面:
  37     174  LOAD_NAME             7  'raw_input'   #获取控制台的输入函数
         177  CALL_FUNCTION_0       0  None
         180  STORE_NAME            8  'flag'   //输入flag保存在此

后面就是对flag四次check,

check0:调用genexpr函数,对输入进行判断,输入的所有字符串范围  32 ~ 128
  6       0  LOAD_GLOBAL           0  'all'
           3  LOAD_GENEXPR             '<code_object <genexpr>>'   //输入的字符32~128
           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
# <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

check1函数,输入长度的函数,首先小于100,同时 满足(s*s)%777^233==513 :
# check1 line 8 of game.py                                      输入的值 先 * 再%777 再xor 233

   9       0  LOAD_GLOBAL           0  'len'  len函数
           3  LOAD_FAST             0  's'     形参
           6  CALL_FUNCTION_1       1  None     
           9  LOAD_CONST               100            #flag长度小于100
          12  COMPARE_OP            0  <
          15  POP_JUMP_IF_FALSE    58  'to 58'
          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          *               s*s
          37  LOAD_CONST               777
          40  BINARY_MODULO           tos=tos1%tos      (s*s)%777
          41  LOAD_CONST               233           
          44  BINARY_XOR              tos=tos1^tos         (s*s)%777^233
          45  LOAD_CONST               513      最后等于513   (s*s)%777^233==513    s=39
          48  COMPARE_OP            2  ==
        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
简单的可以爆破出flag长度是39:
for s in range(100):
    if (s*s)%777^233==513:
        print("flag长度:{}".format(s))
涉及到的关键字节码指令:
BINARY_MULTIPLY
实现 TOS = TOS1 * TOS 。

BINARY_MODULO
实现 TOS = TOS1 % TOS 。

BINARY_XOR
实现 TOS = TOS1 ^ TOS 。

python都是运行在虚拟机上的,写过stack_base的虚拟机可以看出值都是堆栈操作指令。

check2 函数:是对前六个字符操作,这就是卡着我的地方,每一个字符*128再+后面的值再*128,最后等于3533889469877,我以为可以爆破出来,我too young了:
 0  LOAD_GLOBAL           0  'ord'
           3  LOAD_FAST             0  's'
           6  LOAD_CONST               0
           9  BINARY_SUBSCR               a[0] 
          10  CALL_FUNCTION_1       1  None
          13  LOAD_CONST               128
          16  BINARY_MULTIPLY            a[0] *128
          17  LOAD_GLOBAL           0  'ord'
          20  LOAD_FAST             0  's'
          23  LOAD_CONST               1
          26  BINARY_SUBSCR           a[i]
          27  CALL_FUNCTION_1       1  None
          30  BINARY_ADD               a[0] *128+a[1]
          31  LOAD_CONST               128
          34  BINARY_MULTIPLY         (a[0] *128+a[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               (a[0] *128+a[1])*128+a[2]
          49  LOAD_CONST               128
          52  BINARY_MULTIPLY           ((a[0] *128+a[1])*128+a[2])*128
          53  LOAD_GLOBAL           0  'ord'
          56  LOAD_FAST             0  's'
          59  LOAD_CONST               3
          62  BINARY_SUBSCR         ((a[0] *128+a[1])*128+a[2])*128+a[3]   
          63  CALL_FUNCTION_1       1  None
          66  BINARY_ADD
          67  LOAD_CONST               128
          70  BINARY_MULTIPLY
          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
          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
         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'
后面才知道原来乘以128就是往后移7位,这样直接从最后一位手动移位可以弄出来flag{5 ,虽然有点繁琐   (微笑.jpg),而我搁哪爆破:

'''for i0 in range(127):
    for i1 in range(127):
        for i2 in range(127):
            for i3 in range(127):
                for i4 in range(127):
                    for i5 in range(127):
                        if ((((i0*128+i1)*128+i2)*128+i3)*128+i4)*128+i5==3533889469877:
                           print(i0)
                           print(i1)
                           print(i2)
                           print(i3)
                           print(i4)
                           print(i5)'''

关键字节码指令:
BINARY_ADD
实现 TOS = TOS1 + TOS 。


BINARY_SUBSCR
实现 TOS = TOS1[TOS] 。


check3函数:
  1. 先调用slice函数,对从第6位开始,30位截至,跨度为3进行解密:  

    6 9 12 15 18 21 24 27

  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'

  22      15  LOAD_FAST             1  'arr'
          18  LOAD_CONST               6           6开始30结束跨度3
          21  LOAD_CONST               30
          24  LOAD_CONST               3
          27  BUILD_SLICE_3         3     slice(TOS2, TOS1, TOS)
          30  BINARY_SUBSCR
          31  STORE_FAST            2  'a'
2.  满足(a[i]*17684+372511)%257 和 arr0里面的相同
          77  BINARY_MODULO                              (a[i]*17684+372511)%257
          78  LOAD_GLOBAL           4  'arr0'
          81  LOAD_FAST             3  'i'
          84  BINARY_SUBSCR             arr0[i]
          85  COMPARE_OP            3  !=

3.  接着倒着从倒数第二位取出来到33位置: 37 36 35 34 
  26      99  LOAD_FAST             1  'arr'
         102  LOAD_CONST               -2
         105  LOAD_CONST               33
         108  LOAD_CONST               -1
         111  BUILD_SLICE_3         3         slice(TOS2, TOS1, TOS)
         114  BINARY_SUBSCR          
         115  LOAD_CONST               5
         118  BINARY_MULTIPLY                tos*5  
         119  STORE_FAST            4  'b'

4. 取出arr1的 2  5  8  11 和flag9  12  15  18  异或的四个字母就是最后的几位倒叙的
  27     122  LOAD_GLOBAL           0  'map'
         125  LOAD_LAMBDA              '<code_object <lambda>>'
         128  MAKE_FUNCTION_0       0  None
         131  LOAD_GLOBAL           6  'c'
         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'

  28     156  LOAD_FAST             5  'c'
         159  LOAD_GLOBAL           7  'arr1'
         162  COMPARE_OP            3  !=
         165  POP_JUMP_IF_FALSE   172  'to 172'


5. 28到34 位置:字符同时满足  (?+117)%16 +99=arr2[i]  和  (?+107)/16+77==arr2[i+1]
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'

  32     200  LOAD_FAST             1  'arr'
         203  LOAD_FAST             3  'i'
         206  BINARY_SUBSCR         arr[i]
         207  LOAD_CONST               107
         210  BINARY_ADD          arr[i]+107
         211  LOAD_CONST               16
         214  BINARY_DIVIDE       (arr[i]+107)/16
         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          arr2[p]
         226  COMPARE_OP            3  !=
         229  POP_JUMP_IF_TRUE    268  'to 268'
         232  LOAD_FAST             1  'arr'
         235  LOAD_FAST             3  'i'
         238  BINARY_SUBSCR           arr[i]
         239  LOAD_CONST               117
         242  BINARY_ADD               arr[i]+117
         243  LOAD_CONST               16
         246  BINARY_MODULO           (arr[i]+117)%16
         247  LOAD_CONST               99
         250  BINARY_ADD                (arr[i]+117)%16 +99
         251  LOAD_GLOBAL           8  'arr2'
         254  LOAD_FAST             6  'p'
         257  LOAD_CONST               1
         260  BINARY_ADD                p+1
         261  BINARY_SUBSCR            arr2[p]
         262  COMPARE_OP            3  !=
       265_0  COME_FROM           229  '229'
         265  POP_JUMP_IF_FALSE   272  'to 272'

6 最后7 到27位又重新复制循环arr1里面的字符和4解出的四个异或,

然后有一个疑问就是满足

if ((ch + 107) / 16) + 77 == arr2[i] and ((ch + 117) % 16) + 99 == arr2[i+1]:我算只有一个!!!!


官方给的修改版keygen:

flag = [' ']*39


flag[-1] = '}'
arr0 = [249,91,149,113,16,91,53,41]
for i in range(8):
	for ch in range(32, 128):
		if (ch * 17684 + 372511) % 257 == arr0[i]:
			flag[6+i*3]=chr(ch)
arr1 = [43, 1, 6, 69, 20, 62, 6, 44, 24, 113, 6, 35, 0, 3, 6, 44, 20, 22, 127, 60]
key = [0]*4
key[0] = arr1[8] ^ ord(flag[15])
key[1] = arr1[5] ^ ord(flag[12])
key[2] = arr1[2] ^ ord(flag[9])
key[3] = arr1[11] ^ ord(flag[18])

flag[-2] = chr(key[0])
flag[-3] = chr(key[1])
flag[-4] = chr(key[2])
flag[-5] = chr(key[3])

for i in range(len(arr1)):
	flag[7+i] = chr(arr1[i] ^ key[i%4])

flag2=""
arr2 = [90, 100, 87, 109, 86, 108, 86, 105, 90, 104, 88, 102]
for i in range(0, len(arr2), 2):
	for ch in range(32, 128):
		if ((ch + 107) / 16) + 77 == arr2[i] and ((ch + 117) % 16) + 99 == arr2[i+1]:
			flag[28+i/2] += chr(ch)
print(flag)








[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

上传的附件:
收藏
免费 0
支持
分享
最新回复 (4)
雪    币: 0
活跃值: (115)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
大佬 check2中 为什么 乘以128就是往后移7位呢?
2020-4-21 11:37
0
雪    币: 19
活跃值: (93)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
3
2的倍数相当于以为 2**7 = 128
2020-4-21 18:11
0
雪    币: 3735
活跃值: (1188)
能力值: ( LV5,RANK:65 )
在线值:
发帖
回帖
粉丝
4
当时也是卡在了那个arr2的位置。爆破不出来,后面发现如果写`//` 而不是`/`的话就可以爆破出来了,队友说似乎是某个python2的版本以后/改过啥的。
    
然后那个128的位置当时看到是,套娃, (加上括号匹配就看到是很多层, 每层都是k*128 + flag[i]的形式,就一层层%,对128取余可以算出来。

 

最后于 2020-4-22 00:04 被令则编辑 ,原因: 加个括号匹配的图
2020-4-21 23:51
3
雪    币: 204
活跃值: (389)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
check0()里面的genexpr是如何还原出来的
2021-9-1 11:17
0
游客
登录 | 注册 方可回帖
返回
//