参考链接:
https://www.bilibili.com/video/BV1JL4y1p7Tt/?spm_id_from=333.999.0.0
https://bbs.kanxue.com/thread-282542.htm
https://blog.csdn.net/weixin_35967330/article/details/114390031?spm=1001.2014.3001.5501
https://0xd13a.github.io/ctfs/0ctf2017/py/
前言:
python作为Reverse中的高级语言题目,占比逐渐上升,于是想着专门写一篇关于python逆向的文章。
这里分为四部分:直接反编译,pyc字节码,exe解包和加花的pyc
觉得看不懂的可以点击下方链接查看水番正文师傅的视频。d=====( ̄▽ ̄*)b
https://www.bilibili.com/video/BV1JL4y1p7Tt/?spm_id_from=333.999.0.0
不同版本的python魔数头
Python 版本 | 十六进制文件头 |
---|---|
Python 2.7 | 03f30d0a00000000 |
Python 3.0 | 3b0c0d0a00000000 |
Python 3.1 | 4f0c0d0a00000000 |
Python 3.2 | 6c0c0d0a00000000 |
Python 3.3 | 9e0c0d0a0000000000000000 |
Python 3.4 | ee0c0d0a0000000000000000 |
Python 3.5 | 170d0d0a0000000000000000 |
Python 3.6 | 330d0d0a0000000000000000 |
Python 3.7 | 420d0d0a000000000000000000000000 |
Python 3.8 | 550d0d0a000000000000000000000000 |
Python 3.9 | 610d0d0a000000000000000000000000 |
Python 3.10 | 6f0d0d0a000000000000000000000000 |
Python 3.11 | a70d0d0a000000000000000000000000 |
接下来我们需要先了解一下PyCodeObject(Python代码的编译结果,PyCodeObject对象可以由虚拟机加载后直接运行,而pyc文件就是PyCodeObject对象在硬盘上的保存形式)
名称 | 大小 | 作用 |
---|---|---|
Magic | long | 魔数,区分python不同版本的字节码 |
Mtime | long | 修改时间 |
Type_code | byte | 表示PyCodeObject对象 |
co_argcount/co_nlocals/co_stacksize/co_flags | long | PyCodeObject结构体各个域 |
Type_string | byte | 字符串 |
co_code size | long | PyCodeObject的co_code |
co_code value | bytes | |
Type_list | byte | 列表 |
co_consts size | long | 列表元素个数 |
Type_int | byte | co_consts[0]是一个整型 |
co_consts[0] | long | |
Type_string | byte | co_consts[1]是一个字符串 |
co_consts[1] size | long | |
co_consts[1] value | bytes | |
Type_code | byte | co_consts[2]是PyCodeObject |
co_consts[2] |
也可以去直接看这篇文章:https://www.bilibili.com/video/BV1JL4y1p7Tt/?spm_id_from=333.999.0.0
直接反编译
一般使用uncompyle6或者Pycdc将pyc文件反编译成py文件
uncompyle6
下载:https://github.com/rocky/python-uncompyle6
命令:pip install uncompyle6
检测:uncompyle6 --version
注意:下载的uncompyle6的版本最好别高于所使用的python版本
使用命令:uncompyle6 -o output_file.py your_file.pyc(-o目标生成python文件名原pyc文件名)
pycdc
下载:https://github.com/extremecoders-re/decompyle-builds
使用命令:pycdc -o output_file.py your_file.pyc
在线的反编译网站:
https://tool.lu/pyc/
https://toolkk.com/tools/pyc-decomplie
https://www.lddgo.net/string/pyc-compile-decompile
pyc字节码
前言
-
pyc字节码有点类似于汇编语言
-
dis库:用于反汇编Python字节码,将python函数或代码对象的字节码指令序列转换成可读形式,显示每个字节码指令的操作码和操作数,以及相应行号和位置信息
dis.dis函数作用:接受python函数对象或者代码作为参数; -
marshal:Python 标准库中的一个模块,提供了对 Python 对象进行序列化(转换为字节流)和反序列化(从字节流恢复为对象)功能
-
不同python版本的pyc文件头:
python2的pyc文件前4个字节是固定的魔数(03 F3 0D 0A),紧接着后4个字节表示编译这个.pyc的解释器版本号
python3的pyc文件前4个字节是固定的魔数(33 0D 0D 0A),紧接着两个字节时间戳表示.py文件最后修改时间,紧接着后4个字节源文件大小,最后是源文件名的字符串,以null字节结尾
注意:Python3的pyc文件头部并非固定的 16 个字节,而是一个不确定的长度,至少是 12 个字节,加上源文件名字符串的长度
pyc转字节码
如何获取pyc字节码:
import dis,marshal
f=open("文件.pyc","rb").read()
code=marshal.loads(f[8:])
dis.dis(code)
不理解的可以对照官方文档搜索去还原字节码的含义,这里列举几个常见的:
名称 | 含义 |
---|---|
LOAD_CONST | 加载const 变量,比如数值,字符串等等, 一般用于传递给函数作为参数 |
LOAD_FAST | 一般用于加载局部变量的值,也就是读取值,用于计算或者函数调用传传等 |
STORE_FAST | 一般用于保存值到局部变量 |
CALL_FUNCTION | CALL_FUNCTION n,其中 n 表示函数调用时传递的参数数量表示在此处调用了一个函数,并且传递了n个参数 |
例题
[羊城杯 2020]Bytecode
4 0 LOAD_CONST 0 (3)
3 LOAD_CONST 1 (37)
6 LOAD_CONST 2 (72)
9 LOAD_CONST 3 (9)
12 LOAD_CONST 4 (6)
15 LOAD_CONST 5 (132)
18 BUILD_LIST 6
21 STORE_NAME 0 (en)
5 24 LOAD_CONST 6 (101)
27 LOAD_CONST 7 (96)
30 LOAD_CONST 8 (23)
33 LOAD_CONST 9 (68)
36 LOAD_CONST 10 (112)
39 LOAD_CONST 11 (42)
42 LOAD_CONST 12 (107)
45 LOAD_CONST 13 (62)
48 LOAD_CONST 7 (96)
51 LOAD_CONST 14 (53)
54 LOAD_CONST 15 (176)
57 LOAD_CONST 16 (179)
60 LOAD_CONST 17 (98)
63 LOAD_CONST 14 (53)
66 LOAD_CONST 18 (67)
69 LOAD_CONST 19 (29)
72 LOAD_CONST 20 (41)
75 LOAD_CONST 21 (120)
78 LOAD_CONST 22 (60)
81 LOAD_CONST 23 (106)
84 LOAD_CONST 24 (51)
87 LOAD_CONST 6 (101)
90 LOAD_CONST 25 (178)
93 LOAD_CONST 26 (189)
96 LOAD_CONST 6 (101)
99 LOAD_CONST 27 (48)
102 BUILD_LIST 26
105 STORE_NAME 1 (output)
7 108 LOAD_CONST 28 ('welcome to GWHT2020')
111 PRINT_ITEM
112 PRINT_NEWLINE
9 113 LOAD_NAME 2 (raw_input)
116 LOAD_CONST 29 ('please input your flag:')
119 CALL_FUNCTION 1
122 STORE_NAME 3 (flag)
10 125 LOAD_NAME 3 (flag)
128 STORE_NAME 4 (str)
12 131 LOAD_NAME 5 (len)
134 LOAD_NAME 4 (str)
137 CALL_FUNCTION 1
140 STORE_NAME 6 (a)
13 143 LOAD_NAME 6 (a)
146 LOAD_CONST 30 (38)
149 COMPARE_OP 0 (<)
152 POP_JUMP_IF_FALSE 173
14 155 LOAD_CONST 31 ('lenth wrong!')
158 PRINT_ITEM
159 PRINT_NEWLINE
15 160 LOAD_NAME 7 (exit)
163 LOAD_CONST 32 (0)
166 CALL_FUNCTION 1
169 POP_TOP
170 JUMP_FORWARD 0 (to 173)
17 >> 173 LOAD_NAME 8 (ord)
176 LOAD_NAME 4 (str)
179 LOAD_CONST 32 (0)
182 BINARY_SUBSCR
183 CALL_FUNCTION 1
186 LOAD_CONST 33 (2020)
189 BINARY_MULTIPLY
190 LOAD_NAME 8 (ord)
193 LOAD_NAME 4 (str)
196 LOAD_CONST 34 (1)
199 BINARY_SUBSCR
200 CALL_FUNCTION 1
203 BINARY_ADD
204 LOAD_CONST 33 (2020)
207 BINARY_MULTIPLY
208 LOAD_NAME 8 (ord)
211 LOAD_NAME 4 (str)
214 LOAD_CONST 35 (2)
217 BINARY_SUBSCR
218 CALL_FUNCTION 1
221 BINARY_ADD
222 LOAD_CONST 33 (2020)
225 BINARY_MULTIPLY
226 LOAD_NAME 8 (ord)
229 LOAD_NAME 4 (str)
232 LOAD_CONST 0 (3)
235 BINARY_SUBSCR
236 CALL_FUNCTION 1
239 BINARY_ADD
240 LOAD_CONST 33 (2020)
243 BINARY_MULTIPLY
244 LOAD_NAME 8 (ord)
247 LOAD_NAME 4 (str)
250 LOAD_CONST 36 (4)
253 BINARY_SUBSCR
254 CALL_FUNCTION 1
257 BINARY_ADD
258 LOAD_CONST 37 (1182843538814603)
261 COMPARE_OP 2 (==)
264 POP_JUMP_IF_FALSE 275
18 267 LOAD_CONST 38 ('good!continue\xe2\x80\xa6\xe2\x80\xa6')
270 PRINT_ITEM
271 PRINT_NEWLINE
272 JUMP_FORWARD 15 (to 290)
20 >> 275 LOAD_CONST 39 ('bye~')
278 PRINT_ITEM
279 PRINT_NEWLINE
21 280 LOAD_NAME 7 (exit)
283 LOAD_CONST 32 (0)
286 CALL_FUNCTION 1
289 POP_TOP
23 >> 290 BUILD_LIST 0
293 STORE_NAME 9 (x)
24 296 LOAD_CONST 40 (5)
299 STORE_NAME 10 (k)
25 302 SETUP_LOOP 128 (to 433)
305 LOAD_NAME 11 (range)
308 LOAD_CONST 41 (13)
311 CALL_FUNCTION 1
314 GET_ITER
>> 315 FOR_ITER 114 (to 432)
318 STORE_NAME 12 (i)
26 321 LOAD_NAME 8 (ord)
324 LOAD_NAME 4 (str)
327 LOAD_NAME 10 (k)
330 BINARY_SUBSCR
331 CALL_FUNCTION 1
334 STORE_NAME 13 (b)
27 337 LOAD_NAME 8 (ord)
340 LOAD_NAME 4 (str)
343 LOAD_NAME 10 (k)
346 LOAD_CONST 34 (1)
349 BINARY_ADD
350 BINARY_SUBSCR
351 CALL_FUNCTION 1
354 STORE_NAME 14 (c)
28 357 LOAD_NAME 14 (c)
360 LOAD_NAME 0 (en)
363 LOAD_NAME 12 (i)
366 LOAD_CONST 4 (6)
369 BINARY_MODULO
370 BINARY_SUBSCR
371 BINARY_XOR
372 STORE_NAME 15 (a11)
29 375 LOAD_NAME 13 (b)
378 LOAD_NAME 0 (en)
381 LOAD_NAME 12 (i)
384 LOAD_CONST 4 (6)
387 BINARY_MODULO
388 BINARY_SUBSCR
389 BINARY_XOR
390 STORE_NAME 16 (a22)
30 393 LOAD_NAME 9 (x)
396 LOAD_ATTR 17 (append)
399 LOAD_NAME 15 (a11)
402 CALL_FUNCTION 1
405 POP_TOP
31 406 LOAD_NAME 9 (x)
409 LOAD_ATTR 17 (append)
412 LOAD_NAME 16 (a22)
415 CALL_FUNCTION 1
418 POP_TOP
32 419 LOAD_NAME 10 (k)
422 LOAD_CONST 35 (2)
425 INPLACE_ADD
426 STORE_NAME 10 (k)
429 JUMP_ABSOLUTE 315
>> 432 POP_BLOCK
33 >> 433 LOAD_NAME 9 (x)
436 LOAD_NAME 1 (output)
439 COMPARE_OP 2 (==)
442 POP_JUMP_IF_FALSE 453
34 445 LOAD_CONST 38 ('good!continue\xe2\x80\xa6\xe2\x80\xa6')
448 PRINT_ITEM
449 PRINT_NEWLINE
450 JUMP_FORWARD 15 (to 468)
36 >> 453 LOAD_CONST 42 ('oh,you are wrong!')
456 PRINT_ITEM
457 PRINT_NEWLINE
37 458 LOAD_NAME 7 (exit)
461 LOAD_CONST 32 (0)
464 CALL_FUNCTION 1
467 POP_TOP
39 >> 468 LOAD_NAME 5 (len)
471 LOAD_NAME 4 (str)
474 CALL_FUNCTION 1
477 STORE_NAME 18 (l)
40 480 LOAD_NAME 8 (ord)
483 LOAD_NAME 4 (str)
486 LOAD_NAME 18 (l)
489 LOAD_CONST 43 (7)
492 BINARY_SUBTRACT
493 BINARY_SUBSCR
494 CALL_FUNCTION 1
497 STORE_NAME 19 (a1)
41 500 LOAD_NAME 8 (ord)
503 LOAD_NAME 4 (str)
506 LOAD_NAME 18 (l)
509 LOAD_CONST 4 (6)
512 BINARY_SUBTRACT
513 BINARY_SUBSCR
514 CALL_FUNCTION 1
517 STORE_NAME 20 (a2)
42 520 LOAD_NAME 8 (ord)
523 LOAD_NAME 4 (str)
526 LOAD_NAME 18 (l)
529 LOAD_CONST 40 (5)
532 BINARY_SUBTRACT
533 BINARY_SUBSCR
534 CALL_FUNCTION 1
537 STORE_NAME 21 (a3)
43 540 LOAD_NAME 8 (ord)
543 LOAD_NAME 4 (str)
546 LOAD_NAME 18 (l)
549 LOAD_CONST 36 (4)
552 BINARY_SUBTRACT
553 BINARY_SUBSCR
554 CALL_FUNCTION 1
557 STORE_NAME 22 (a4)
44 560 LOAD_NAME 8 (ord)
563 LOAD_NAME 4 (str)
566 LOAD_NAME 18 (l)
569 LOAD_CONST 0 (3)
572 BINARY_SUBTRACT
573 BINARY_SUBSCR
574 CALL_FUNCTION 1
577 STORE_NAME 23 (a5)
45 580 LOAD_NAME 8 (ord)
583 LOAD_NAME 4 (str)
586 LOAD_NAME 18 (l)
589 LOAD_CONST 35 (2)
592 BINARY_SUBTRACT
593 BINARY_SUBSCR
594 CALL_FUNCTION 1
597 STORE_NAME 24 (a6)
46 600 LOAD_NAME 19 (a1)
603 LOAD_CONST 0 (3)
606 BINARY_MULTIPLY
607 LOAD_NAME 20 (a2)
610 LOAD_CONST 35 (2)
613 BINARY_MULTIPLY
614 BINARY_ADD
615 LOAD_NAME 21 (a3)
618 LOAD_CONST 40 (5)
621 BINARY_MULTIPLY
622 BINARY_ADD
623 LOAD_CONST 44 (1003)
626 COMPARE_OP 2 (==)
629 POP_JUMP_IF_FALSE 807
47 632 LOAD_NAME 19 (a1)
635 LOAD_CONST 36 (4)
638 BINARY_MULTIPLY
639 LOAD_NAME 20 (a2)
642 LOAD_CONST 43 (7)
645 BINARY_MULTIPLY
646 BINARY_ADD
647 LOAD_NAME 21 (a3)
650 LOAD_CONST 3 (9)
653 BINARY_MULTIPLY
654 BINARY_ADD
655 LOAD_CONST 45 (2013)
658 COMPARE_OP 2 (==)
661 POP_JUMP_IF_FALSE 807
48 664 LOAD_NAME 19 (a1)
667 LOAD_NAME 20 (a2)
670 LOAD_CONST 46 (8)
673 BINARY_MULTIPLY
674 BINARY_ADD
675 LOAD_NAME 21 (a3)
678 LOAD_CONST 35 (2)
681 BINARY_MULTIPLY
682 BINARY_ADD
683 LOAD_CONST 47 (1109)
686 COMPARE_OP 2 (==)
689 POP_JUMP_IF_FALSE 804
49 692 LOAD_NAME 22 (a4)
695 LOAD_CONST 0 (3)
698 BINARY_MULTIPLY
699 LOAD_NAME 23 (a5)
702 LOAD_CONST 35 (2)
705 BINARY_MULTIPLY
706 BINARY_ADD
707 LOAD_NAME 24 (a6)
710 LOAD_CONST 40 (5)
713 BINARY_MULTIPLY
714 BINARY_ADD
715 LOAD_CONST 48 (671)
718 COMPARE_OP 2 (==)
721 POP_JUMP_IF_FALSE 801
50 724 LOAD_NAME 22 (a4)
727 LOAD_CONST 36 (4)
730 BINARY_MULTIPLY
731 LOAD_NAME 23 (a5)
734 LOAD_CONST 43 (7)
737 BINARY_MULTIPLY
738 BINARY_ADD
739 LOAD_NAME 24 (a6)
742 LOAD_CONST 3 (9)
745 BINARY_MULTIPLY
746 BINARY_ADD
747 LOAD_CONST 49 (1252)
750 COMPARE_OP 2 (==)
753 POP_JUMP_IF_FALSE 798
51 756 LOAD_NAME 22 (a4)
759 LOAD_NAME 23 (a5)
762 LOAD_CONST 46 (8)
765 BINARY_MULTIPLY
766 BINARY_ADD
767 LOAD_NAME 24 (a6)
770 LOAD_CONST 35 (2)
773 BINARY_MULTIPLY
774 BINARY_ADD
775 LOAD_CONST 50 (644)
778 COMPARE_OP 2 (==)
781 POP_JUMP_IF_FALSE 795
52 784 LOAD_CONST 51 ('congraduation!you get the right flag!')
787 PRINT_ITEM
788 PRINT_NEWLINE
789 JUMP_ABSOLUTE 795
792 JUMP_ABSOLUTE 798
>> 795 JUMP_ABSOLUTE 801
>> 798 JUMP_ABSOLUTE 804
>> 801 JUMP_ABSOLUTE 807
>> 804 JUMP_FORWARD 0 (to 807)
>> 807 LOAD_CONST 52 (None)
810 RETURN_VALUE
手搓源码
```python en = [3, 37, 72, 9, 6, 132] output = [101, 96, 23, 68, 112, 42, 107, 62, 96, 53, 176, 179, 98, 53, 67, 29, 41, 120, 60, 106, 51, 101, 178, 189, 101, 48] print('welcome to GWHT2020') flag = input('please input your flag:') str = flag a = len(str) if a < 38: print('lenth wrong!') exit(0) if ord(str[0]) + 2020 * ord(str[1]) + 2020 * ord(str[3]) + 2020 * ord(str[4]) == 1182843538814603: print('good!continue\xe2\x80\xa6\xe2\x80\xa6') else: print('bye~') exit(0) x = [] k = 5 for i in range(13): b = ord(str[k]) c = ord(str[k + 1]) a11 = c ^ en[i % 6] a22 = b ^ en[i % 6] x.append(a11) x.append(a22) k += 2 if x == output: print('good!continue\xe2\x80\xa6\xe2\x80\xa6') else: print('oh,you are wrong!') exit(0) l = len(str) a1 = ord(str[l - 7]) a2 = ord(str[l - 6]) a3 = ord(str[l - 5]) a4 = ord(str[l - 4]) a5 = ord(str[l - 3]) a6 = ord(str[l - 2]) if a1 * 3 + a2 * 2 + a3 * 5 == 1003: if a1 * 4 + a2 * 7 + a3 * 9 == 2013: if a1 + a2 * 8 + a3 * 2 == 1109: if a1 * 3 + a5 * 2 + a6 * 5 == 671: if a4 * 4 + a5 * 7 + a6 * 9 == 1252: if a4 + a5 * 8 + a6 * 2 == 644: print('congraduation!you get the right flag!') ```exp
``` from z3 import * en = [3, 37, 72, 9, 6, 132] output = [101, 96, 23, 68, 112, 42, 107, 62, 96, 53, 176, 179, 98, 53, 67, 29, 41, 120, 60, 106, 51, 101, 178, 189, 101, 48] flag = '' k = 0 x = [] for i in range(13): c = chr(output[k] ^ en[i % 6]) b = chr(output[k + 1] ^ en[i % 6]) x.append(b) x.append(c) k += 2 flag = ''.join(x) a1 = Int('a1') a2 = Int('a2') a3 = Int('a3') a4 = Int('a4') a5 = Int('a5') a6 = Int('a6') s = Solver() s.add(a1 * 3 + a2 * 2 + a3 * 5 == 1003) s.add(a1 * 4 + a2 * 7 + a3 * 9 == 2013) s.add(a1 + a2 * 8 + a3 * 2 == 1109) s.add(a4 * 3 + a5 * 2 + a6 * 5 == 671) s.add(a4 * 4 + a5 * 7 + a6 * 9 == 1252) s.add(a4 + a5 * 8 + a6 * 2 == 644) if s.check() == sat: result = s.model() print(result) s = [97, 101, 102, 102, 55, 51] for i in range(6): flag += chr(s[i]) print(flag) ```exe解包
判断
一般来说py文件打包生成的exe图标
查壳信息:
解包
pyinstxtractor.py
- 下载:https://sourceforge.net/projects/pyinstallerextractor/
- 下载的pyinstxtractor.py放在打包的exe路径下打开终端
- 使用命令:python pyinstxtractor.py 待解包文件名.exe
- 生成同名的extracted文件夹,一般会获得一个与待解包文件名.exe相同的pyc文件,后边与"直接反编译"步骤相同
特殊情况:使用pyinstxtractor.py解包后源文件一般不包含原始魔术数字和时间戳,引起反编译出错;而解包后的struct.pyc文件会保留原始的魔术数字和时间戳,对照补齐字节码后,在进行反编译
加花的pyc
这里直接采用一个python2编写的例题进行讲解:
根据uncompyle6和字节码判断花
可以发现在第七行停住了,则此处存在阻碍反编译的花指令
读取co_code长度
使用len(code.co_code)
去花并修改co_code 长度
使用010editor打开pyc文件,找到python2 文件目录下的opcode.h文件,其中存储着所有opcode以及其对应的值
十进制转换为十六进制为71,64
python2 中一条指令占3位,就能确定该六位是我们需要的花指令,选中后直接按退格键删掉
删除之后co_code = 27-6 = 21,找到1B(27)改为15(21):
保存 uncompyle6反编译
完结,散花!!!★,°:.☆( ̄▽ ̄)/$:.°★
标签:LOAD,逆向,CONST,NAME,BINARY,Python,str,ord From: https://www.cnblogs.com/N1ng/p/18341822