在虎符上面看到一道逆向题,主要是python的字节码反汇编的问题,以前没遇到过,这里记录一下
python 字节码的反汇编
python提供了工具库 dis 可以将字节码显示成可读的形式
从变量赋值到常见情况
1 | def func(arg): |
对应的字节码:
1 | import dis |
对应的格式如下:
源码行号 | 指令偏移量 | 指令符号 | 指令参数 | 实际参数值
不同版本的CPython 指令长度可能不同,但是 3.7的每条指令是2个字节,所以我们去看dis 生成的字节码指令集的时候,指令偏移量总是从0开始,每增加一条在原来的偏移量上增加2 故,指令偏移量的值,一般都是: 0 , 2 , 4, 6 , 8 , … , 2n ( n>=0 )
实际上,python的字节码经函数处理之后阅读并不是很复杂,只需要点耐心
看题目前先来看一下几种常见的情况:
- for 循环
1 | def func(): |
- while 循环
1 | def func(): |
这里只列举for、while、if三种情况,可以发现其实我们看到的字节码并不复杂,很容易理解
game(2020 虎符)
题目给了一段python的字节码:
1 | # Python 2.7 |
分析过程
内容很多,仔细分析完可以得到大致的 “源代码”
1 | arr0 = [249,91,149,113,16,91,53,41] |
可以发现程序处理flag分组很明显,先看check0
这个函数将输入限制在(32,128)即课件字符的范围,所以在后面几个check中我们可以采取爆破的形式(刚开始尝试根据所给表反推,但是失败了,又一次被自己菜到)
对于check1,用于判断flag长度,我们可以根据这部分得到flag的长度为 39:
1 | length = 39 |
接着到check2,判断flag前6个字符,因为格式为flag{},所以可以得到前5个字符,然后通过条件得到第六个字符为5
1 | s = [ord(i) for i in 'flag{'] |
接着看check3,check3分为三部分即将flag分为三组处理
第一部分,通过运算和arr0依次判断,因为输入限制在(32,128)所以这里采取爆破的方式:
1 | temp = [] |
第二部分,主要过程为将flag后4个字符(不包括’}’)扩展成20个字符,然后分别于flag的第7到第27异或之后与arr1比对
通过这部分我们可以先还原出 flag的后四个字符(这里需要注意得到的后四个字符的顺序):
1 | b = [i for i in map(lambda x:x[0]^x[1],[(arr[7:27][i],arr1[i]) for i in range(2,20,3)])] |
接着再计算得到flag第7到27个字符
1 | arr[7:27] = [ chr(i) for i in map(lambda x:x[0]^x[1],zip(b,arr1))] |
最后一部分就是除和取余的操作,这里同样采取爆破的形式:
1 | temp = [] |
最后得到flag
1 | for i in range(len(arr)): |
exp
1 | from math import pow |
You are welcome to share this blog, so that more people can participate in it. If the images used in the blog infringe your copyright, please contact the author to delete them.