CTF中常见的四种python逆向

说在前面:

什么是pyc文件?

pyc是一种二进制文件,是由py文件经过编译后,生成的文件,是一种byte code,py文件变成pyc文件后,加载的速度有所提高,

pyc 文件是 Python 编译过的字节码文件。它是 Python 程序在运行过程中由源代码(通常是 .py 文件)自动或手动编译产生的二进制文件。

而且pyc是一种跨平台的字节码,是由[Python]的虚拟机来执行的,这个是类似于[Java]或者.NET的虚拟机的概念。pyc的内容,是跟python的版本相关的,不同版本编译后的pyc文件是不同的,2.5编译的pyc文件,2.4版本的python是无法执行的。

为什么需要pyc文件?

因为py文件是可以直接看到源码的,如果你是开发商业软件的话,不可能把源码也泄漏出去吧?所以就需要编译为pyc后,再发布出去。当然,pyc文件也是可以反编译的,不同版本编译后的pyc文件是不同的,根据python源码中提供的opcode,可以根据pyc文件反编译出py文件源码,网上可以找到一个反编译python2.3版本的pyc文件的工具,不过该工具从python2.4开始就要收费了,如果需要反编译出新版本的pyc文件的话,就需要自己动手了,不过你可以自己修改python的源代码中的opcode文件,重新编译python,从而防止不法分子的破解。

pyc文件

解法:uncompyle6直接反编译

eg.

def check():
	flag=1+1
	if(flag==2):
		return "right"
	return "error"
print(check())

这是我们所写的一个简单的python例子

现在我们来生成pyc文件 这里用的是python3

pyhton -m test.py

pyc文件也是可以运行的

我们在对应的文件夹的搜索框下输入powershell

然后输入

python .\test.pyc

image

可以看到即使是运行py文件也是可以运行的

但区别的是我们没法看到pyc文件里面是什么东西,即使拖进IDA里面也无济于事

所以这里我们需要下载一个工具

uncompyle6.exe

在终端打开并输入

pip install uncompyle

安装后包含uncompyle6 但是版本为3.8.0 会导致一些软件的反编译失败
建议使用下面命令回到3.7.4版本

pip install uncompyle6==3.7.4

安装以后,我们回到我们的tmp目录并打开powershell输入

uncompyle6.exe .\test.py

image test

这里我们就得到了源码

接下来的操作就跟windows逆向别无二致了

帮助网安学习,全套资料S信免费领取:
① 网安学习成长路径思维导图
② 60+网安经典常用工具包
③ 100+SRC分析报告
④ 150+网安攻防实战技术电子书
⑤ 最权威CISSP 认证考试指南+题库
⑥ 超1800页CTF实战技巧手册
⑦ 最新网安大厂面试题合集(含答案)
⑧ APP客户端安全检测指南(安卓+IOS)

txt里面是pyc字节码

解法:

  • 读py字节码
  • 根据opcode文件查询意思

我们先来得到我们test.py例子的字节码

在powershell下先输入python 然后输入

import dis,marshal
f=open("test.pyc", "rb").read()
f

dis和marshal库 一个是装载库 一个是反编译字节码的库

image test

我们可以将我们的test.pyc导入010Editor中配合着一起看

python2的前八个字节是python2的魔术字

python3的前十六个字节是python3的魔术字

所以我们可以不用读前十六位 我们只需要读后十六位的东西

code=marshal.loads(f[16:])
code

现在我们读进来的是二进制数据,我们可以用dis来进行反编译 就会得到python的字节码 可以理解成python的汇编读出来了

dis.dis(code)

image test

这就是python的汇编

我们这里是直接将pyc文件uncompyle6回去的,但是在CTF中一般都是把python的汇编直接复制粘贴出来丢给我们…

所以我们只能硬看…或者配合点东西 比如python的opcode一起看

补充一点:python2是3个字节为1个指令 python3是2个字节为1个指令

比如test.pyc中LOAD_CONST 指令就占用了0,1两个字节

所以下条指令就从3开始了

1           0 LOAD_CONST               0 (<code object keyinit at 0x0000028C1CC11D20, file "crackPYC.py", line 1>)
2 LOAD_CONST               1 ('keyinit')
4 MAKE_FUNCTION            0
6 STORE_NAME               0 (keyinit)

8           8 LOAD_NAME                1 (__name__)
10 LOAD_CONST               2 ('__main__')
12 COMPARE_OP               2 (==)
14 POP_JUMP_IF_FALSE      250

9          16 LOAD_NAME                2 (print)
18 LOAD_CONST               3 ('Can you crack pyc?')
20 CALL_FUNCTION            1
22 POP_TOP

10          24 LOAD_NAME                3 (input)
26 LOAD_CONST               4 ('Plz give me your flag:')
28 CALL_FUNCTION            1
30 STORE_NAME               4 (str)     #将输入的字符存入str内

11          32 LOAD_CONST               5 (108)
34 LOAD_CONST               6 (17)
36 LOAD_CONST               7 (42)
38 LOAD_CONST               8 (226)
40 LOAD_CONST               9 (158)
42 LOAD_CONST              10 (180)
44 LOAD_CONST              11 (96)
46 LOAD_CONST              12 (115)
48 LOAD_CONST              13 (64)
50 LOAD_CONST              14 (24)
52 LOAD_CONST              15 (38)
54 LOAD_CONST              16 (236)
56 LOAD_CONST              17 (179)
58 LOAD_CONST              18 (173)
60 LOAD_CONST              19 (34)
62 LOAD_CONST              20 (22)
64 LOAD_CONST              21 (81)
66 LOAD_CONST              22 (113)
68 LOAD_CONST              15 (38)
70 LOAD_CONST              23 (215)
72 LOAD_CONST              24 (165)
74 LOAD_CONST              25 (135)
76 LOAD_CONST              26 (68)
78 LOAD_CONST              27 (7)

12          80 LOAD_CONST              28 (119)
82 LOAD_CONST              29 (97)
84 LOAD_CONST              30 (45)
86 LOAD_CONST              31 (254)
88 LOAD_CONST              32 (250)
90 LOAD_CONST              33 (172)
92 LOAD_CONST              34 (43)
94 LOAD_CONST              35 (62)
96 BUILD_LIST              32           #建立容量为32的列表
98 STORE_NAME               5 (text)    #以上32个数据为text数组的数值

13         100 LOAD_NAME                6 (len)     
102 LOAD_NAME                4 (str)     
104 CALL_FUNCTION            1
106 LOAD_CONST              36 (32)
108 COMPARE_OP               3 (!=)
110 POP_JUMP_IF_TRUE       140           #判断str即输入字符串的长度是否为32,不是则跳转到140
112 LOAD_NAME                4 (str)     
114 LOAD_CONST              37 (0)
116 LOAD_CONST              27 (7)
118 BUILD_SLICE              2
120 BINARY_SUBSCR
122 LOAD_CONST              38 ('DASCTF{')
124 COMPARE_OP               3 (!=)
126 POP_JUMP_IF_TRUE       140           #判断str字符串的前七位是否为'DASCTF{',不是则跳转到140
128 LOAD_NAME                4 (str)
130 LOAD_CONST              39 (31)
132 BINARY_SUBSCR
134 LOAD_CONST              40 ('}')
136 COMPARE_OP               3 (!=)
138 POP_JUMP_IF_FALSE      154           #判断str字符串的最后一位也就是31位是否为'}',不是则跳转到154
#因为如果不跳转继续执行的话就会执行到输入字符串符合的一段代码使程序												    #退出

14     >>  140 LOAD_NAME                2 (print)    
142 LOAD_CONST              41 ('Bye bye~~')
144 CALL_FUNCTION            1
146 POP_TOP

15         148 LOAD_NAME                7 (exit)
150 CALL_FUNCTION            0
152 POP_TOP                              #退出程序

16     >>  154 LOAD_NAME                8 (list)    
156 LOAD_NAME                4 (str)
158 CALL_FUNCTION            1
160 STORE_NAME               9 (st)      #创建列表st

17         162 BUILD_LIST               0
164 STORE_NAME              10 (key)

18         166 LOAD_NAME                0 (keyinit)
168 LOAD_NAME               10 (key)
170 CALL_FUNCTION            1
172 POP_TOP

19         174 SETUP_LOOP              48 (to 224)
176 LOAD_NAME               11 (range)
178 LOAD_CONST              36 (32)
180 CALL_FUNCTION            1
182 GET_ITER
>>  184 FOR_ITER                36 (to 222)
186 STORE_NAME              12 (i)         #相当于for i in range(0,32)

20         188 LOAD_NAME               13 (ord)
190 LOAD_NAME                4 (str)
192 LOAD_NAME               12 (i)
194 BINARY_SUBSCR
196 CALL_FUNCTION            1
198 LOAD_NAME               10 (key)        
200 LOAD_NAME               12 (i)
202 LOAD_NAME                6 (len)
204 LOAD_NAME               10 (key)
206 CALL_FUNCTION            1            
208 BINARY_MODULO                          #key元素少于str元素,所以要把i和key的长度取余避免越界
210 BINARY_SUBSCR
212 BINARY_XOR
214 LOAD_NAME                9 (st)
216 LOAD_NAME               12 (i)
218 STORE_SUBSCR                           #此处代码将str和key中的元素进行异或处理后存入st
220 JUMP_ABSOLUTE          184             #相当于st[i] = ord(str[i]) ^ key[i % len(key)]
>>  222 POP_BLOCK                            

21     >>  224 LOAD_NAME                9 (st)        
226 LOAD_NAME                5 (text)
228 COMPARE_OP               2 (==)
230 POP_JUMP_IF_FALSE      242             #对比st数组和text数组,不相等则跳转到地址242处

22         232 LOAD_NAME                2 (print)
234 LOAD_CONST              42 ('Congratulations and you are good at PYC!')
236 CALL_FUNCTION            1
238 POP_TOP
240 JUMP_FORWARD             8 (to 250)

24     >>  242 LOAD_NAME                2 (print)
244 LOAD_CONST              43 ('Sorry,plz learn more about pyc.')
246 CALL_FUNCTION            1
248 POP_TOP
>>  250 LOAD_CONST              44 (None)
252 RETURN_VALUE

Disassembly of <code object keyinit at 0x0000028C1CC11D20, file "crackPYC.py", line 1>:
2           0 LOAD_CONST               1 (0)
2 STORE_FAST               1 (num)

3           4 SETUP_LOOP              42 (to 48)
6 LOAD_GLOBAL              0 (range)
8 LOAD_CONST               2 (8)   
10 CALL_FUNCTION            1
12 GET_ITER
>>   14 FOR_ITER                30 (to 46)
16 STORE_FAST               2 (i)         #相当于for i in range(0,8)
#从这里我们可以知道key的长度为8
4          18 LOAD_FAST                1 (num)
20 LOAD_CONST               3 (7508399208111569251)
22 BINARY_SUBTRACT
24 LOAD_CONST               4 (4294967295)
26 BINARY_MODULO
28 STORE_FAST               1 (num)

5          30 LOAD_FAST                0 (key)
32 LOAD_METHOD              1 (append)
34 LOAD_FAST                1 (num)
36 LOAD_CONST               5 (24)
38 BINARY_RSHIFT                         #不理解这一句的意思
40 CALL_METHOD              1            #但这一段代码就是给key赋值
42 POP_TOP
44 JUMP_ABSOLUTE           14
>>   46 POP_BLOCK
>>   48 LOAD_CONST               0 (None)

50 RETURN_VALUE

这段代码的总体意思就是将输入的str字符串与key数组进行异或加密后存入st数组并于text数组进行对比我们可以从代码中得之text数组的元素值也可以知道str的前七位必为’DASCTF{’,最后一位必为’}’,而key数组只有8位,所以对str的加密是8位8位的进行的又因为异或具有自反性,所以可以据’DASCTF{‘字符串与text前7个元素做异或处理得出前7位,再将’}'与text最后一位进行异或 处理得出第8位,就可以得到key的整个数组

这段汇编最关键的部分如下

Disassembly of <code object keyinit at 0x0000028C1CC11D20, file "crackPYC.py", line 1>:
2           0 LOAD_CONST               1 (0)
2 STORE_FAST               1 (num)

3           4 SETUP_LOOP              42 (to 48)
6 LOAD_GLOBAL              0 (range)
8 LOAD_CONST               2 (8)   
10 CALL_FUNCTION            1
12 GET_ITER
>>   14 FOR_ITER                30 (to 46)
16 STORE_FAST               2 (i)         #相当于for i in range(0,8)
#从这里我们可以知道key的长度为8
4          18 LOAD_FAST                1 (num)
20 LOAD_CONST               3 (7508399208111569251)
22 BINARY_SUBTRACT
24 LOAD_CONST               4 (4294967295)
26 BINARY_MODULO
28 STORE_FAST               1 (num)

5          30 LOAD_FAST                0 (key)
32 LOAD_METHOD              1 (append)
34 LOAD_FAST                1 (num)
36 LOAD_CONST               5 (24)
38 BINARY_RSHIFT                         #不理解这一句的意思
40 CALL_METHOD              1            #但这一段代码就是给key赋值
42 POP_TOP
44 JUMP_ABSOLUTE           14
>>   46 POP_BLOCK
>>   48 LOAD_CONST               0 (None)

50 RETURN_VALUE

首先就是一个num的初始化 因为LOAD_CONST推送到堆栈

然后STORE_FAST将TOS(python的栈)存储到本地中

这两条结合起来 其实意思就是

num=0

接下来 SETUP_LOOP(delta)

将一个循环的块推送到块堆栈。该块跨越当前指令,大小为delta字节。

LOAD_GLOBAL 定义一个全局变量 range LOAD_CONST定义一个常量 8

配合着utools里面程序员手册里面的Python库硬看

for i in range(8):

然后关键的这一步

首先LOAD_FAST 将num压入栈堆,然后又把一个常量(7508399208111569251)推送到堆栈中

然后又执行BINARY_SUBTRACT 也就是减操作 即栈的后一位减去栈顶,对应到代码中也就是num减去这个常量

然后又推了个值4294967295 然后进行BINARY_MODULO操作 这是栈顶后一位取余栈顶的值

最后STORE_FAST 存储到num这个变量

所以这关键的一步python代码应该是

num=(num-7508399208111569251)%4294967295

后面干的操作大体就是 LOAD_FAST num 然后LOAD_CONST 24 然后BINARY_RSHIFT 主要是就是栈顶后一位右移栈顶数据的值 然后存储到key里面 大概就是这么个意思

print(num>>24)

结合起来就是这样的

num=0
for i in range(8):
    num=(num-7508399208111569251)%4294967295
    print(num>>24)

这样我们就得到这道题的密钥

40
80
121
161
202
242
27
67

然后采用每八个字节都去异或一下这个密钥,flag就出来了

s=[108,17,42,226,158,180,96,115,64,24,38,236,179,173,34,22,81,113,38,215,165,135,68,7,119,97,
45,254,250,172,43,62]
key=[]
flag=''
num=0
for i in range(8):
    num=(num-7508399208111569251)%4294967295
    key.append(num>>24)
for i in range(32):
    flag += chr(key[i%len(key)] ^ s[i])					
print(flag)

打包成exe的pyc文件

解法:

  • 通过脚本变成结构体和一个文件
  • 重点:再把时间属性和版本的魔术字放回去保存
  • uncompyle6即可

image test

下载完题目发现这是个exe文件 但是图标又是很明显的pyc文件

所以这是个打包成exe的py文件

这里我们需要用到一个工具pyinstxtractor.py

把这个py文件复制到我们的题目文件夹里面

在搜索框中输入powershell 在打开的终端中输入

python .\pyinstxtractor.py .\attachment.exe

image test

运行后生成attachment.exe_extracted文件夹,进入之后看到一些源文件,由于我电脑上的python是3.8版本,解包要3.6版本,所以生成了不正常的入口文件login而不是login.pyc,想要变成正常的可反编译的pyc文件就要对生成文件进行修改。(如果不嫌麻烦可以换一下python3.6的环境)

现在开始修改login入口文件,这里用的是winhex。

修改之前需要了解一点,在将python文件打包成exe文件的过程中,会抹去pyc文件前面的部分信息,所以在反编译之前需要检查并添加上这部分信息,这部分信息可以通过struct文件获取。

windex中打开struct文件后,把struct文件前几个字节插入login开头。(具体要插入几个字节还是要看解包后的文件,我的文件是E3字节码前面的丢失,那么就只需要看struct中E3之前的字节码有哪些,ctrl + c复制,然后在login开头ctrl + v 粘贴即可。)

image test

image test

修改后如下

image test

保存后 将login后缀名修改为.pyc即可

将login.pyc复制粘贴到题目的文件夹后打开powershell终端

并且调用uncompyle6.exe

uncompyle6.exe .\login.pyc

就可以看到源码了

image test

import sys
input1 = input('input something:')
if len(input1) != 14:
    print('Wrong length!')
    sys.exit()
else:
    code = []
    for i in range(13):
        code.append(ord(input1[i]) ^ ord(input1[(i + 1)]))

    code.append(ord(input1[13]))
    a1 = code[2]
    a2 = code[1]
    a3 = code[0]
    a4 = code[3]
    a5 = code[4]
    a6 = code[5]
    a7 = code[6]
    a8 = code[7]
    a9 = code[9]
    a10 = code[8]
    a11 = code[10]
    a12 = code[11]
    a13 = code[12]
    a14 = code[13]
    if (a1 * 88 + a2 * 67 + a3 * 65 - a4 * 5 + a5 * 43 + a6 * 89 + a7 * 25 + a8 * 13 - a9 * 36 + a10 * 15 + a11 * 11 + a12 * 47 - a13 * 60 + a14 * 29 == 22748) & (a1 * 89 + a2 * 7 + a3 * 12 - a4 * 25 + a5 * 41 + a6 * 23 + a7 * 20 - a8 * 66 + a9 * 31 + a10 * 8 + a11 * 2 - a12 * 41 - a13 * 39 + a14 * 17 == 7258) & (a1 * 28 + a2 * 35 + a3 * 16 - a4 * 65 + a5 * 53 + a6 * 39 + a7 * 27 + a8 * 15 - a9 * 33 + a10 * 13 + a11 * 101 + a12 * 90 - a13 * 34 + a14 * 23 == 26190) & (a1 * 23 + a2 * 34 + a3 * 35 - a4 * 59 + a5 * 49 + a6 * 81 + a7 * 25 + (a8 << 7) - a9 * 32 + a10 * 75 + a11 * 81 + a12 * 47 - a13 * 60 + a14 * 29 == 37136) & (a1 * 38 + a2 * 97 + a3 * 35 - a4 * 52 + a5 * 42 + a6 * 79 + a7 * 90 + a8 * 23 - a9 * 36 + a10 * 57 + a11 * 81 + a12 * 42 - a13 * 62 - a14 * 11 == 27915) & (a1 * 22 + a2 * 27 + a3 * 35 - a4 * 45 + a5 * 47 + a6 * 49 + a7 * 29 + a8 * 18 - a9 * 26 + a10 * 35 + a11 * 41 + a12 * 40 - a13 * 61 + a14 * 28 == 17298) & (a1 * 12 + a2 * 45 + a3 * 35 - a4 * 9 - a5 * 42 + a6 * 86 + a7 * 23 + a8 * 85 - a9 * 47 + a10 * 34 + a11 * 76 + a12 * 43 - a13 * 44 + a14 * 65 == 19875) & (a1 * 79 + a2 * 62 + a3 * 35 - a4 * 85 + a5 * 33 + a6 * 79 + a7 * 86 + a8 * 14 - a9 * 30 + a10 * 25 + a11 * 11 + a12 * 57 - a13 * 50 - a14 * 9 == 22784) & (a1 * 8 + a2 * 6 + a3 * 64 - a4 * 85 + a5 * 73 + a6 * 29 + a7 * 2 + a8 * 23 - a9 * 36 + a10 * 5 + a11 * 2 + a12 * 47 - a13 * 64 + a14 * 27 == 9710) & (a1 * 67 - a2 * 68 + a3 * 68 - a4 * 51 - a5 * 43 + a6 * 81 + a7 * 22 - a8 * 12 - a9 * 38 + a10 * 75 + a11 * 41 + a12 * 27 - a13 * 52 + a14 * 31 == 13376) & (a1 * 85 + a2 * 63 + a3 * 5 - a4 * 51 + a5 * 44 + a6 * 36 + a7 * 28 + a8 * 15 - a9 * 6 + a10 * 45 + a11 * 31 + a12 * 7 - a13 * 67 + a14 * 78 == 24065) & (a1 * 47 + a2 * 64 + a3 * 66 - a4 * 5 + a5 * 43 + a6 * 112 + a7 * 25 + a8 * 13 - a9 * 35 + a10 * 95 + a11 * 21 + a12 * 43 - a13 * 61 + a14 * 20 == 27687) & (a1 * 89 + a2 * 67 + a3 * 85 - a4 * 25 + a5 * 49 + a6 * 89 + a7 * 23 + a8 * 56 - a9 * 92 + a10 * 14 + a11 * 89 + a12 * 47 - a13 * 61 - a14 * 29 == 29250) & (a1 * 95 + a2 * 34 + a3 * 62 - a4 * 9 - a5 * 43 + a6 * 83 + a7 * 25 + a8 * 12 - a9 * 36 + a10 * 16 + a11 * 51 + a12 * 47 - a13 * 60 - a14 * 24 == 15317):
        print('flag is GWHT{md5(your_input)}')
        print('Congratulations and have fun!')
    else:
        print('Sorry,plz try again...')

看到一堆数字 就明白这是要解方程 用z3库来写方便点

from z3 import *
#初始化变量
a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14 = Ints("a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12 a13 a14")
x = Solver()
#根据题目来添加限制
x.add(a1 * 88 + a2 * 67 + a3 * 65 - a4 * 5 + a5 * 43 + a6 * 89 + a7 * 25 + a8 * 13 - a9 * 36 + a10 * 15 + a11 * 11 + a12 * 47 - a13 * 60 + a14 * 29 == 22748)
x.add(a1 * 89 + a2 * 7 + a3 * 12 - a4 * 25 + a5 * 41 + a6 * 23 + a7 * 20 - a8 * 66 + a9 * 31 + a10 * 8 + a11 * 2 - a12 * 41 - a13 * 39 + a14 * 17 == 7258)
x.add(a1 * 28 + a2 * 35 + a3 * 16 - a4 * 65 + a5 * 53 + a6 * 39 + a7 * 27 + a8 * 15 - a9 * 33 + a10 * 13 + a11 * 101 + a12 * 90 - a13 * 34 + a14 * 23 == 26190)
x.add(a1 * 23 + a2 * 34 + a3 * 35 - a4 * 59 + a5 * 49 + a6 * 81 + a7 * 25 + a8 * 128 - a9 * 32 + a10 * 75 + a11 * 81 + a12 * 47 - a13 * 60 + a14 * 29 == 37136)
x.add(a1 * 38 + a2 * 97 + a3 * 35 - a4 * 52 + a5 * 42 + a6 * 79 + a7 * 90 + a8 * 23 - a9 * 36 + a10 * 57 + a11 * 81 + a12 * 42 - a13 * 62 - a14 * 11 == 27915)
x.add(a1 * 22 + a2 * 27 + a3 * 35 - a4 * 45 + a5 * 47 + a6 * 49 + a7 * 29 + a8 * 18 - a9 * 26 + a10 * 35 + a11 * 41 + a12 * 40 - a13 * 61 + a14 * 28 == 17298)
x.add(a1 * 12 + a2 * 45 + a3 * 35 - a4 * 9 - a5 * 42 + a6 * 86 + a7 * 23 + a8 * 85 - a9 * 47 + a10 * 34 + a11 * 76 + a12 * 43 - a13 * 44 + a14 * 65 == 19875)
x.add(a1 * 79 + a2 * 62 + a3 * 35 - a4 * 85 + a5 * 33 + a6 * 79 + a7 * 86 + a8 * 14 - a9 * 30 + a10 * 25 + a11 * 11 + a12 * 57 - a13 * 50 - a14 * 9 == 22784)
x.add(a1 * 8 + a2 * 6 + a3 * 64 - a4 * 85 + a5 * 73 + a6 * 29 + a7 * 2 + a8 * 23 - a9 * 36 + a10 * 5 + a11 * 2 + a12 * 47 - a13 * 64 + a14 * 27 == 9710)
x.add(a1 * 67 - a2 * 68 + a3 * 68 - a4 * 51 - a5 * 43 + a6 * 81 + a7 * 22 - a8 * 12 - a9 * 38 + a10 * 75 + a11 * 41 + a12 * 27 - a13 * 52 + a14 * 31 == 13376)
x.add(a1 * 85 + a2 * 63 + a3 * 5 - a4 * 51 + a5 * 44 + a6 * 36 + a7 * 28 + a8 * 15 - a9 * 6 + a10 * 45 + a11 * 31 + a12 * 7 - a13 * 67 + a14 * 78 == 24065)
x.add(a1 * 47 + a2 * 64 + a3 * 66 - a4 * 5 + a5 * 43 + a6 * 112 + a7 * 25 + a8 * 13 - a9 * 35 + a10 * 95 + a11 * 21 + a12 * 43 - a13 * 61 + a14 * 20 == 27687)
x.add(a1 * 89 + a2 * 67 + a3 * 85 - a4 * 25 + a5 * 49 + a6 * 89 + a7 * 23 + a8 * 56 - a9 * 92 + a10 * 14 + a11 * 89 + a12 * 47 - a13 * 61 - a14 * 29 == 29250)
x.add(a1 * 95 + a2 * 34 + a3 * 62 - a4 * 9 - a5 * 43 + a6 * 83 + a7 * 25 + a8 * 12 - a9 * 36 + a10 * 16 + a11 * 51 + a12 * 47 - a13 * 60 - a14 * 24 == 15317)

print(x.check())
print(x.model())

得到下面这些玩意

[a13 = 88,
 a3 = 10,
 a4 = 7,
 a10 = 108,
 a12 = 74,
 a1 = 119,
 a7 = 28,
 a6 = 43,
 a9 = 52,
 a14 = 33,
 a5 = 104,
 a8 = 91,
 a2 = 24,
 a11 = 88]

也就是[119, 24, 10, 7, 104, 43, 28, 91, 52, 108, 88, 74, 88, 33]

按照ord(input1[i]) ^ ord(input1[i + 1])进行异或, 反推回input

然后反推一个异或:注意这里源代码中code和a不是一一对应的…就是源码中a1=code[2]这些的

aim = [119, 24, 10, 7, 104, 43, 28, 91, 52, 108, 88, 74, 88, 33] #a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14
aim = [10, 24, 119, 7, 104, 43, 28, 91, 108, 52, 88, 74, 88, 33] #code[0]=a3,code[1]=a2.....源码中的....
print(len(aim))
flag = [0 for i in range(14)]
flag[13] = aim[13]

for i in range(12, -1, -1):#从12开始 到-1终止,每一步是-1
	flag[i] = aim[i] ^ flag[i+1]
	
print(flag)

[85, 95, 71, 48, 55, 95, 116, 104, 51, 95, 107, 51, 121, 33], 转换为字符串

c = [85, 95, 71, 48, 55, 95, 116, 104, 51, 95, 107, 51, 121, 33]
for i in c:
    print(chr(i), end="")
print("\n")

最后得到U_G07_th3_k3y! 交上去是不对的,还需要将其转换为md5才可以

加花指令的pyc

解法:

  • 根据uncompyle6和字节码判断花
  • 读取co_code的长度
  • 去掉花 并修改co_code长度
  • 保存uncompyle6即可

不加花指令的代码

def check():
	flag=5+5
	if(flag==10):
		return "right"
	return "Wrong"
#input=raw_input("Input something")
print(check())

我们先分别运行一下加了花指令和没有加花指令的pyc文件,看看加了花指令的pyc文件是否能输出right

image test

我们可以看到 加了花指令的pyc文件 即Pz_error.pyc也是可以输出right的

因为会把加了花指令的指令直接跳过去,就导致执行是没什么问题的

但是如果调用uncompyle6来反编译出源代码的话,加了花指令的pyc文件就会报错

image test

最下面报错的信息给我们的是 tuple index out of range 数组下标越界

我们先来得到Pz_error.pyc的字节码,然后用code.marshal,loads(f[8:])读取魔术字8位后的东西,并将其反编译,看看花指令是加在哪条指令上了

image test

我们可以看到 JUMP_ABSOLUTE将字节码计数器设置为目标 强制跳转到18

18也就是正常指令之后

LOAD_CONST 255 我们没有加花指令的py文件 就那么点代码 哪来的255,所以明显花指令就是在这里了。

所以我们现在定位到了花指令的地方,我们要做的就是把花指令去掉 并且修改co_code长度 也就是整个pyc文件的长度

那么我们来进行第二步 读取co_code的长度

len(code.co_code)

image test

我们可以看到是27个字节

我们把Pz_error.pyc丢进Winhex里面 那么我们该怎么在Winhex里面定位到我们所要找的花指令呢?

我们要借助python2程序里面的opcode.h工具

已知花指令是JUMP_ABSOLUTE 在opcode.h里面搜索JUMP_ABSOLUTE

image test

113转换为16进制就是71

在Winhex里面找的71那条 因为python2是3个字节码为1个指令

image test

接下来 我们找LOAD_CONST这条指令在opcode.h对应的数字

image test

100对应的16进制数字就是64 255对应的16进制数就是FF

所以基本可以确定 71 12 00 64 FF 00 这六个就是我们要找的花指令

image test

然后delete键删掉

现在我们到了第三步 修改co_code的长度

我们刚读出来Pz_error.pyc字节码长度是27 27-6=21

27对应的16进制数是1B

21对应的16进制数是15

我们在winhex里面将找的1B修改为15,然后保存,这样我们的Pz_error.pyc就修好了

现在我们来试试看uncompyle6能否反编译出来

image test

大功告成!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/555406.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

文件IO基础

一、文件描述符 调用 open 函数会有一个返回值&#xff0c;该返回值就是一个文件描述符&#xff08; file descriptor&#xff09;&#xff0c;这说明文件描述符是一个 非负整数&#xff1b;对于 Linux 内核而言&#xff0c;所有打开的文件都会通过文件描述符进行索引。 当调用…

2024年第十六届“华中杯”(B题)大学生数学建模挑战赛| 时间序列,滑动窗口 | 数学建模完整代码+建模过程全解全析

当大家面临着复杂的数学建模问题时&#xff0c;你是否曾经感到茫然无措&#xff1f;作为2022年美国大学生数学建模比赛的O奖得主&#xff0c;我为大家提供了一套优秀的解题思路&#xff0c;让你轻松应对各种难题。 让我们来看看华中杯 (B题&#xff09;&#xff01; CS团队倾…

《四月女友》定档5月18日 佐藤健、长泽雅美演绎唯美爱情

由川村元气担任编剧&#xff0c;山田智和导演&#xff0c;佐藤健、长泽雅美、森七菜主演的唯美爱情电影《四月女友》今日正式宣布定档5月18日&#xff0c;并发布了“相恋”版预告和“相拥”版海报。预告中&#xff0c;优美宁静的风景令人心生向往&#xff0c;藤代俊&#xff08…

【深度学习实战(8)】如何绘制loss曲线图

一、步骤 我们先定义一个dict&#xff0c;每一个key对应的value都是一个list。 loss_history dict((k, []) for k in ["epoch", "train_loss", "val_loss"])每一轮或者每一次迭代的损失都通过list记录下来。 loss_history["epoch"…

改手机IP地址的软件推荐

随着移动互联网的普及&#xff0c;手机已成为人们日常生活中不可或缺的一部分。而在使用手机的过程中&#xff0c;IP地址作为一个重要的网络标识&#xff0c;有时也需要进行修改或更改。为了满足这一需求&#xff0c;市面上涌现出了许多改手机IP地址的软件。虎观代理将对这些软…

2024年腾讯云服务器价格一览表

随着云计算技术的快速发展&#xff0c;越来越多的企业和个人开始选择使用云服务器来满足他们的数据存储和计算需求。腾讯云作为国内领先的云服务提供商&#xff0c;其服务器产品因性能稳定、安全可靠而备受用户青睐。那么&#xff0c;2024年腾讯云服务器的价格情况如何呢&#…

Flattened Butterfly 扁平蝶形拓扑

Flattened Butterfly 扁平蝶形拓扑 1. 传统蝶形网络 Butterfly Topology2. 扁平蝶形拓扑 Flattened Butterfly3.On-Chip Flattened Butterfly 扁平蝶形拓扑应用于片上网络 Flattened Butterfly 扁平蝶形拓扑 扁平蝶形拓扑是一种经济高效的拓扑&#xff0c;适用于高基数路由器…

复合升降机器人教学科研平台——技术方案

一&#xff1a;功能概述 1.1 功能简介 复合升降机器人是一款集成移动底盘、机械臂、末端执行器、边缘计算平台等机构形成的教学科研平台&#xff0c;可实现机器人建图导航、路径规划&#xff0c;机械臂运动学、动力学、轨迹规划、视觉识别等算法功能和应用&#xff0c;提供例如…

前后端交互实例(javaweb05)

文章开始前,先给大家看一张图,这是黑马javaweb-day05请求响应实例,也是第一个实现了前后端交互,这是我画的流程图,搞懂了前后端是如何交互的.(文件的所有路径不能出现中文,否则会报错,这个我暂时不知道该怎么解决). 那么这里面涉及到的东西,除了emp.html这是已经提供了的前端页…

C++:深入理解operator new/operator delete

动态内存管理 1.语法层面1.基本语法注意点 2.new/delete和malloc/free的区别3.operator new和operator delete函数&#xff08;底层重点&#xff09;1.operator new/delete原理2.图解1.new/new[]2.delete/delete[] 3.new[n]和delete[] 4.定位new1.定义2.使用格式 1.语法层面 1…

【前端面试3+1】13 JS特性、JS是单线程还是多线程、JS中的一部和同步、【合并两个有序数组】

一、JavaScript特性 弱类型&#xff1a;JavaScript是一种弱类型语言&#xff0c;变量的类型可以动态改变&#xff0c;不需要事先声明类型。动态性&#xff1a;JavaScript是一种动态语言&#xff0c;可以在运行时修改对象的结构和属性。基于原型的&#xff1a;JavaScript是一种基…

WdatePicker异常,无法弹出日期选择框

官网&#xff1a;My97日期控件官方网站 My97 DatePickerhttp://www.my97.net/ 可能使版本太老了&#xff0c;可以更新一下&#xff0c;然后根据官方的文件进行使用。 我的异常是因为在网上找的包里面缺少文件&#xff0c;去官网拉了一下最新的就行了。

状态压缩DP题单

P1433 吃奶酪&#xff08;最短路&#xff09; dp(i, s) 表示从 i 出发经过的点的记录为 s 的路线距离最小值 #include<bits/stdc.h> #define int long long using namespace std; const int N 20; signed main() { int n; cin >> n;vector<double>x(n 1),…

FreeRTOS之动态创建任务与删除任务

1.本文是利用FreeRTOS来动态创建任务和删除任务。主要是使用FreeRTOS的两个API函数&#xff1a;xTaskCreate()和vTaskDelete()。 任务1和任务2是让LED0、LED1闪烁。任务3是当按键按下时删除任务1。 使用动态创建任务时&#xff0c;需要动态的堆中申请任务所需的内存空间&…

OpenHarmony多媒体-ohos_videocompressor

介绍 videoCompressor是一款ohos高性能视频压缩器。 目前实现的能力&#xff1a; 支持视频压缩 使用本工程 有两种方式可以下载本工程&#xff1a; 开发者如果想要使用本工程,可以使用git命令 git clone https://gitee.com/openharmony-sig/ohos_videocompressor.git --…

Redis学习记录

Redis安装 首先是Redis的下载地址&#xff0c;事实上&#xff0c;Redis已经出到7的版本了&#xff0c;我们这里使用的是5的版本。&#xff08;3也能用&#xff09; Redis下载地址 我们将Redis下载下来并解压&#xff1a; 我们如何启动呢? redis-server.exe redis.windows.…

单分支:if语句

示例&#xff1a; /*** brief how about if? show you here.* author wenxuanpei* email 15873152445163.com(query for any question here)*/ #define _CRT_SECURE_NO_WARNINGS//support c-library in Microsoft-Visual-Studio #include <stdio.h>#define if_state…

学习笔记------约束的管理

此篇记录FPGA的静态时序分析&#xff0c;在学习FPGA的过程中&#xff0c;越发觉得对于时序约束只是懂了个皮毛。现在记录一下自己的学习过程。 本文摘自《VIVADO从此开始》高亚军 为什么要进行约束&#xff1f;约束的目的是什么&#xff1f; 简单来说&#xff0c;就是需要在…

Unity(MVC思想)

MVC 一下演示使用MVC和不使用MVC的做法区别。 前两个没有使用MVC 主面板逻辑&#xff1a; mainPanel是该脚本名字 每个场景中不一定存在该面板&#xff0c;单纯的显隐需要去手动挂载过于麻烦。 所以自己读取创建面板出来(每个场景仅创建一次)&#xff0c;存下该面板&#xf…

OpenHarmony网络请求库-httpclient

简介 HTTP是现代应用程序通过网络交换数据和媒体的的主要方式。httpclient是OpenHarmony 里一个高效执行的HTTP客户端&#xff0c;使用它可使您的内容加载更快&#xff0c;并节省您的流量。httpclient以人们耳熟能详的OKHTTP为基础&#xff0c;整合android-async-http&#xf…