这个题目之前比赛写的时候就是一脸懵。比赛之后看了大神们的wp才知道还是我太弱了,学习的太少了。
1.查壳
下载附件后先查壳。
发现带有upx壳,我么先脱壳。
2.将python打包的exe文件转化为pyc文件
通过观察文件图标可以发现,这个文件是使用python打包的一个exe文件我们需要使用pyinstxtractor.py
将python打包的.exe文件还原成.py文件
python pyinstxtractor.py notsudoku.exe
//注意:要将被还原文件和 pyinstxtractor.py放在同一文件夹下哦!
转化出来后会得到一个notsudoku.exe_extracted
文件夹
红圈标出的就是等下要还原的文件。
3.将文件还原成可阅读的.py文件
使用010editor
打开上图红圈标记的文件
在notsudoku.exe_extracted
文件夹找到struct
文件也用010editor
打开
新建一页:复制上图红圈里的内容到新建的页面中。再将2
文件的所有内容复制到新建的页面中 ,保存并命名为xxx.pyc
文件
3.2 使用uncompyle6对.pyc文件进行反编译。
使用这条命令
uncompyle6.exe 111.pyc > 111.py
这样就得到了可以直接阅读的.py 文件。
4.代码分析
通过分析代码可以看出,这个题其实就是一个五阶幻方。
import time, sys, hashlib
class あ:
def __init__(self):
self.う = {}
self.な = []
self.に = ''
self.ぬ = []
self.ね = 65
def え(self, えひ):
def の(f):
self.う[えひ] = f
return f
return の
def お(self, は):
return self.う.get(は)
def か(self):
き = 0
while True:
く = self.な[き][0]
け = self.な[き][1]
こ = self.な[き][2]
さ = self.お(く)
さ(け, こ)
き += 1
い = あ()
@い.え('し')
def f(a, b):
if a == 1:
い.ぬ += b
@い.え('す')
def f(a, b):
if a == 1:
print(い.に)
else:
if a == 2:
print(い.ぬ)
else:
if a == 3:
print((い.flag), end='')
else:
print(a, end='')
@い.え('せ')
def f(a, b):
sys.exit()
@い.え('そ')
def f(a, b):
い.に = input() #输入flag
@い.え('た')
def f(a, b):
time.sleep(a)
@い.え('ち')
def f(a, b):
if len(い.に) % 2 != 0:
sys.exit()
for i in い.に:
if ord(i) > 52 or ord(i) < 48:
sys.exit()
x = str(hashlib.new('md5', bytes((い.に), encoding='utf8')).hexdigest())#对输入的flag进行MD5操作
if x[:6] != 'e3a912': #对MD5后的flag前六位进行判断,判断是否为正确的flag
sys.exit()
い.flag = x
@い.え('と')
def f(a, b): #对五阶幻方进行赋值,从1开始赋值到25结束
ふ = 0
for i in range(0, len(い.に), 2):#这里也可以看出输入的flag其实就是坐标,变量a是横坐标,变量b是纵坐标。flag每两位是一组
ふ += 1
a = int(い.に[i])
b = int(い.に[(i + 1)])
い.ぬ[a][b] = ふ
@い.え('つ')
def f(a, b): #对赋值了的五阶幻方进行判断。判断是否为正确的五阶幻方。其实,这里也就是给出了五阶幻方中的几个值方便之后逆向破解。
if い.ぬ[0][1] != 24 or い.ぬ[4][3] != 2:
sys.exit()
if い.ぬ[0][2] != 1 or い.ぬ[2][3] != 20:
sys.exit()
if い.ぬ[1][0] != 23 or い.ぬ[3][4] != 3:
sys.exit()
@い.え('て')
def f(a, b):
ね = 0
if b == -1:
for i in range(5):
ね += い.ぬ[a][i]
if ね != い.ね:
sys.exit()
else:
for i in range(5):
ね += い.ぬ[i][b]
if ね != い.ね:
sys.exit()
い.な = [
[
'す', 'welcome baby~ ', 0],
[
'す', 'input your flag~:', 0],
[
'そ', 0, 0],
[
'す', 'your input is:', 0],
[
'す', 1, 0],
[
'す', "let's check......", 0],
[
'た', 0.5, 0],
[
'し', 1, [[0 for i in range(5)]]],
[
'し', 1, [[0 for i in range(5)]]],
[
'し', 1, [[0 for i in range(5)]]],
[
'し', 1, [[0 for i in range(5)]]],
[
'し', 1, [[0 for i in range(5)]]],
[
'ち', 0, 0],
[
'と', 0, 0],
[
'つ', 0, 0],
[
'て', 0, -1],
[
'て', 1, -1],
[
'て', 2, -1],
[
'て', 3, -1],
[
'て', 4, -1],
[
'て', 0, 0],
[
'て', 0, 1],
[
'て', 0, 2],
[
'て', 0, 3],
[
'て', 0, 4],
[
'す', 'Goodjob!', 0],
[
'す', 'The flag is vnctf{', 0],
[
'す', 3, 0],
[
'す', '}', 0],
[
'せ', 0, 0]]
い.か()
解出来的五阶幻方是这样子的
附上解密脚本:
#include<cstdio>
#include<cmath>
#include<string>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<vector>
#include<queue>
#include<map>
using namespace std;
int main()
{
int a[100][100];
for (int i = 0; i <= 4; i++)
{
for (int j = 0; j <= 4; j++)
{
cin >> a[i][j];
}
}
int k = 1;
while (k <= 25)
{
for (int i = 0; i <= 4; i++)
{
for (int j = 0; j <= 4; j++)
{
if (a[i][j] == k)
{
cout << i << j;
k++;
}
}
}
}
return 0;
system("pause");
}
//这里的数字需要自己输入哦我比较懒就没有直接给数组初始化了
//嘻嘻,其实我就是菜忘记怎么二维数组初始化了,有会的大佬欢迎在评论区教我!!
将输出的数字MD5就得到了flag:e3a912c1e911ad82544af0c3d753f44f
最终就是vnctf{e3a912c1e911ad82544af0c3d753f44f}