這個題目之前比賽寫的時候就是一臉懵。比賽之後看了大神們的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}