目录
一.获取pyc文件
二.反编译出.py源码
三.程序逻辑
1.第一个限制条件
2.第二段
3.第三段
这题是对python打包成的可执行程序逆向
如果对如何反编译.pyc和.py文件有疑问可以参考:
Python逆向基本操作步骤——以杭电新生赛hgame week2 reverse stream(python3.10逆向)为例
一.获取pyc文件
在qaq.exe所在目录打开cmd使用命令:python pyinstxtractor.py qaq.exe
成功后会输出一个_extracted文件夹
在文件夹内找到qaq.pyc和struct.pyc这两个文件
用winhex或者其他软件打开,查看16进制下的信息,前两行是magic head,这里qaq.pyc和struct.pyc文件的前两行相同,所以不需要修复
二.反编译出.py源码
在qaq.pyc文件所在目录打开cmd,使用命令: pycdc.exe qaq.pyc,成功输出源码
en = [
3,
37,
72,
11,
6,
132]
output = [
105,
97,
23,
78,
121,
48,
108,
65,
99,
52,
181,
177,
98,
53,
67,
29,
41,
120,
60,
101,
51,
103,
105,
109,
121,
50]
print('welcome to SDPCSEC2023')
flag = input('please input your flag:')
str = flag
a = len(str) #flag长度>=37
if a < 37:
print('lenth wrong!')
exit(0) #前四个字符的限制条件
if ord(str[0]) + 2023 * ord(str[1]) + 2023 * ord(str[2]) + 2023 * ord(str[3]) == 623186: #第一个限制条件
print('good!continue')
else:
print('bye~')
exit(0)
# f}Mj bjk2x1Jg2e516a8f0an7a5éí1z
x = []
k = 5 #中间26个,所以flag[4]需要爆破
for i in range(13): #0到12
b = ord(str[k]) #第六个字符,8,10,12
c = ord(str[k + 1]) #第七个字符,9,11,13
a11 = c ^ en[i % 6]
a22 = b ^ en[i % 6]
x.append(a11) #先添加奇数下标对应字符
x.append(a22) #每次循环添加两个字符
k += 2
if x == output:
print('good!continue')
else:
print('oh,you are wrong!')
exit(0)
l = len(str)
v1 = ord(str[l - 7]) #倒数第七个
v2 = ord(str[l - 6])
v3 = ord(str[l - 5])
v4 = ord(str[l - 4])
v5 = ord(str[l - 3])
v6 = ord(str[l - 2]) #倒数第二个
#aef_75
if v1 * 3 + v2 * 2 + v3 * 5 == 1003 and v1 * 4 + v2 * 7 + v3 * 9 == 2013 and v1 + v2 * 8 + v3 * 2 == 1109 and v1 * 3 + v5 * 2 + v6 * 5 == 671 and v4 * 4 + v5 * 7 + v6 * 9 == 1252 and v4 + v5 * 8 + v6 * 2 == 644:
print('you get the right flag!')
三.程序逻辑
1.第一个限制条件
if ord(str[0]) + 2023 * ord(str[1]) + 2023 * ord(str[2]) + 2023 * ord(str[3]) == 623186:
当时很奇怪的是只有对前四个字符这一条限制条件,这一条方程组如果爆破的话会有很多组解
而且对第五个字符并没有任何操作,后续突然明白前五个字符和最后一个字符是 flag{}
2.第二段
这段代码的作用是对flag[]下标为5到30的共26个字符进行加密,仔细看是简单的异或操作
x = []
k = 5 #中间26个,所以flag[4]需要爆破
for i in range(13): #0到12
b = ord(str[k]) #第六个字符,8,10,12
c = ord(str[k + 1]) #第七个字符,9,11,13
a11 = c ^ en[i % 6]
a22 = b ^ en[i % 6]
x.append(a11) #先添加奇数下标对应字符
x.append(a22) #每次循环添加两个字符
k += 2
if x == output:
脚本:
out = [
105,
97,
23,
78,
121,
48,
108,
65,
99,
52,
181,
177,
98,
53,
67,
29,
41,
120,
60,
101,
51,
103,
105,
109,
121,
50]
en = [
3,
37,
72,
11,
6,
132]
k=0
s=""
for i in range(13):
a11=out[k]
a22=out[k+1]
b=a22^en[i%6]
c=a11^en[i%6]
s+=chr(b)
s+=chr(c)
k+=2
print(s)
得到中间26个字符:bjk2x1Jg2e516a8f0an7a5éí1z
3.第三段
这段是对倒数第七个到倒数第二个共6个字符进行限制,这里给了6个判断式也就是6组方程组,所以可以解出这6个字符
不过值得一提的是,题目有点问题: 第四个判断条件 v1 * 3 + v5 * 2 + v6 * 5 == 671 是有问题的,询问过出题人得知v1应该改成v4(这里已经修改过了)
l = len(str)
v1 = ord(str[l - 7]) #倒数第七个
v2 = ord(str[l - 6])
v3 = ord(str[l - 5])
v4 = ord(str[l - 4])
v5 = ord(str[l - 3])
v6 = ord(str[l - 2]) #倒数第二个
#aef_75
if v1 * 3 + v2 * 2 + v3 * 5 == 1003 and v1 * 4 + v2 * 7 + v3 * 9 == 2013 and v1 + v2 * 8 + v3 * 2 == 1109 and v4 * 3 + v5 * 2 + v6 * 5 == 671 and v4 * 4 + v5 * 7 + v6 * 9 == 1252 and v4 + v5 * 8 + v6 * 2 == 644:
print('you get the right flag!')
系数矩阵:
3 2 5 0 0 0 |1003
4 7 9 0 0 0 |2013
1 8 2 0 0 0 |1109
0 0 0 3 2 5 |671
0 0 0 4 7 9 |1252
0 0 0 1 8 2 |644
在线解方程:
这六个字符是:aeff73
所以最后的flag是:flag{bjk2x1Jg2e516a8f0an7a5éí1zaeff73}