說在前面:
什么是pyc文件?
pyc是一種二進(jìn)制文件,是由py文件經(jīng)過編譯后,生成的文件,是一種byte code,py文件變成pyc文件后,加載的速度有所提高,pyc?文件是 Python 編譯過的字節(jié)碼文件。它是 Python 程序在運(yùn)行過程中由源代碼(通常是 .py 文件)自動(dòng)或手動(dòng)編譯產(chǎn)生的二進(jìn)制文件。
而且pyc是一種跨平臺(tái)的字節(jié)碼,是由[Python]的虛擬機(jī)來執(zhí)行的,這個(gè)是類似于[Java]或者.NET的虛擬機(jī)的概念。pyc的內(nèi)容,是跟python的版本相關(guān)的,不同版本編譯后的pyc文件是不同的,2.5編譯的pyc文件,2.4版本的python是無法執(zhí)行的。
為什么需要pyc文件?
因?yàn)閜y文件是可以直接看到源碼的,如果你是開發(fā)商業(yè)軟件的話,不可能把源碼也泄漏出去吧?所以就需要編譯為pyc后,再發(fā)布出去。當(dāng)然,pyc文件也是可以反編譯的,不同版本編譯后的pyc文件是不同的,根據(jù)python源碼中提供的opcode,可以根據(jù)pyc文件反編譯出py文件源碼,網(wǎng)上可以找到一個(gè)反編譯python2.3版本的pyc文件的工具,不過該工具從python2.4開始就要收費(fèi)了,如果需要反編譯出新版本的pyc文件的話,就需要自己動(dòng)手了,不過你可以自己修改python的源代碼中的opcode文件,重新編譯python,從而防止不法分子的破解。
pyc文件
解法:uncompyle6直接反編譯
eg.
def check():
flag=1+1
if(flag==2):
return "right"
return "error"
print(check())
這是我們所寫的一個(gè)簡單的python例子
現(xiàn)在我們來生成pyc文件?這里用的是python3
pyhton -m test.py
pyc文件也是可以運(yùn)行的
我們?cè)趯?duì)應(yīng)的文件夾的搜索框下輸入powershell
然后輸入
python .\test.pyc
可以看到即使是運(yùn)行py文件也是可以運(yùn)行的
但區(qū)別的是我們沒法看到pyc文件里面是什么東西,即使拖進(jìn)IDA里面也無濟(jì)于事
所以這里我們需要下載一個(gè)工具
uncompyle6.exe
在終端打開并輸入
pip install uncompyle
安裝后包含uncompyle6 但是版本為3.8.0 會(huì)導(dǎo)致一些軟件的反編譯失敗建議使用下面命令回到3.7.4版本
pip install uncompyle6==3.7.4
安裝以后,我們回到我們的tmp目錄并打開powershell輸入
uncompyle6.exe .\test.py
這里我們就得到了源碼
接下來的操作就跟windows逆向別無二致了
【----幫助網(wǎng)安學(xué)習(xí),以下所有學(xué)習(xí)資料免費(fèi)領(lǐng)!加vx:dctintin,備注 “博客園” 獲取!】
?、?網(wǎng)安學(xué)習(xí)成長路徑思維導(dǎo)圖
?、?60+網(wǎng)安經(jīng)典常用工具包
?、?100+SRC漏洞分析報(bào)告
?、?150+網(wǎng)安攻防實(shí)戰(zhàn)技術(shù)電子書
?、?最權(quán)威CISSP 認(rèn)證考試指南+題庫
?、?超1800頁CTF實(shí)戰(zhàn)技巧手冊(cè)
⑦ 最新網(wǎng)安大廠面試題合集(含答案)
?、?APP客戶端安全檢測(cè)指南(安卓+IOS)
txt里面是pyc字節(jié)碼
解法:
-
讀py字節(jié)碼
-
根據(jù)opcode文件查詢意思
我們先來得到我們test.py例子的字節(jié)碼
在powershell下先輸入python 然后輸入
import dis,marshal
f=open("test.pyc", "rb").read()
f
dis和marshal庫 一個(gè)是裝載庫 一個(gè)是反編譯字節(jié)碼的庫
我們可以將我們的test.pyc導(dǎo)入010Editor中配合著一起看
python2的前八個(gè)字節(jié)是python2的魔術(shù)字
python3的前十六個(gè)字節(jié)是python3的魔術(shù)字
所以我們可以不用讀前十六位 我們只需要讀后十六位的東西
code=marshal.loads(f[16:])
code
現(xiàn)在我們讀進(jìn)來的是二進(jìn)制數(shù)據(jù),我們可以用dis來進(jìn)行反編譯 就會(huì)得到python的字節(jié)碼 可以理解成python的匯編讀出來了
dis.dis(code)
這就是python的匯編
我們這里是直接將pyc文件uncompyle6回去的,但是在CTF中一般都是把python的匯編直接復(fù)制粘貼出來丟給我們...
所以我們只能硬看...或者配合點(diǎn)東西 比如python的opcode一起看
補(bǔ)充一點(diǎn):python2是3個(gè)字節(jié)為1個(gè)指令 python3是2個(gè)字節(jié)為1個(gè)指令
比如test.pyc中LOAD_CONST 指令就占用了0,1兩個(gè)字節(jié)
所以下條指令就從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內(nèi)
?
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個(gè)數(shù)據(jù)為text數(shù)組的數(shù)值
?
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,不是則跳轉(zhuǎn)到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{',不是則跳轉(zhuǎn)到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位是否為'}',不是則跳轉(zhuǎn)到154
#因?yàn)槿绻惶D(zhuǎn)繼續(xù)執(zhí)行的話就會(huì)執(zhí)行到輸入字符串符合的一段代碼使程序 ? #退出
?
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) ? ? ?#創(chuàng)建列表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) ? ? ? ? #相當(dāng)于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中的元素進(jìn)行異或處理后存入st
220 JUMP_ABSOLUTE ? ? ? ? ?184 ? ? ? ? ? ? #相當(dāng)于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 ? ? ? ? ? ? #對(duì)比st數(shù)組和text數(shù)組,不相等則跳轉(zhuǎn)到地址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) ? ? ? ? #相當(dāng)于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數(shù)組進(jìn)行異或加密后存入st數(shù)組并于text數(shù)組進(jìn)行對(duì)比我們可以從代碼中得之text數(shù)組的元素值也可以知道str的前七位必為’DASCTF{’,最后一位必為’}’,而key數(shù)組只有8位,所以對(duì)str的加密是8位8位的進(jìn)行的又因?yàn)楫惢蚓哂凶苑葱?,所以可以?jù)’DASCTF{‘字符串與text前7個(gè)元素做異或處理得出前7位,再將’}'與text最后一位進(jìn)行異或 處理得出第8位,就可以得到key的整個(gè)數(shù)組
這段匯編最關(guān)鍵的部分如下
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) ? ? ? ? #相當(dāng)于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
首先就是一個(gè)num的初始化 因?yàn)長OAD_CONST推送到堆棧
然后STORE_FAST將TOS(python的棧)存儲(chǔ)到本地中
這兩條結(jié)合起來 其實(shí)意思就是
num=0
接下來?SETUP_LOOP
(delta)
將一個(gè)循環(huán)的塊推送到塊堆棧。該塊跨越當(dāng)前指令,大小為delta字節(jié)。
LOAD_GLOBAL 定義一個(gè)全局變量 range LOAD_CONST定義一個(gè)常量 8
配合著utools里面程序員手冊(cè)里面的Python庫硬看
for i in range(8):
然后關(guān)鍵的這一步
首先LOAD_FAST 將num壓入棧堆,然后又把一個(gè)常量(7508399208111569251)推送到堆棧中
然后又執(zhí)行BINARY_SUBTRACT 也就是減操作 即棧的后一位減去棧頂,對(duì)應(yīng)到代碼中也就是num減去這個(gè)常量
然后又推了個(gè)值4294967295 然后進(jìn)行BINARY_MODULO操作 這是棧頂后一位取余棧頂?shù)闹?/p>
最后STORE_FAST 存儲(chǔ)到num這個(gè)變量
所以這關(guān)鍵的一步python代碼應(yīng)該是
num=(num-7508399208111569251)%4294967295
后面干的操作大體就是 LOAD_FAST num 然后LOAD_CONST 24 然后BINARY_RSHIFT 主要是就是棧頂后一位右移棧頂數(shù)據(jù)的值 然后存儲(chǔ)到key里面 大概就是這么個(gè)意思
print(num>>24)
結(jié)合起來就是這樣的
num=0
for i in range(8):
? ?num=(num-7508399208111569251)%4294967295
? ?print(num>>24)
這樣我們就得到這道題的密鑰
40
80
121
161
202
242
27
67
然后采用每八個(gè)字節(jié)都去異或一下這個(gè)密鑰,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文件
解法:
-
通過腳本變成結(jié)構(gòu)體和一個(gè)文件
-
重點(diǎn):再把時(shí)間屬性和版本的魔術(shù)字放回去保存
-
uncompyle6即可
下載完題目發(fā)現(xiàn)這是個(gè)exe文件 但是圖標(biāo)又是很明顯的pyc文件
所以這是個(gè)打包成exe的py文件
這里我們需要用到一個(gè)工具pyinstxtractor.py
把這個(gè)py文件復(fù)制到我們的題目文件夾里面
在搜索框中輸入powershell 在打開的終端中輸入
python .\pyinstxtractor.py .\attachment.exe
運(yùn)行后生成attachment.exe_extracted文件夾,進(jìn)入之后看到一些源文件,由于我電腦上的python是3.8版本,解包要3.6版本,所以生成了不正常的入口文件login而不是login.pyc,想要變成正常的可反編譯的pyc文件就要對(duì)生成文件進(jìn)行修改。(如果不嫌麻煩可以換一下python3.6的環(huán)境)
現(xiàn)在開始修改login入口文件,這里用的是winhex。
修改之前需要了解一點(diǎn),在將python文件打包成exe文件的過程中,會(huì)抹去pyc文件前面的部分信息,所以在反編譯之前需要檢查并添加上這部分信息,這部分信息可以通過struct文件獲取。
windex中打開struct文件后,把struct文件前幾個(gè)字節(jié)插入login開頭。(具體要插入幾個(gè)字節(jié)還是要看解包后的文件,我的文件是E3字節(jié)碼前面的丟失,那么就只需要看struct中E3之前的字節(jié)碼有哪些,ctrl + c復(fù)制,然后在login開頭ctrl + v 粘貼即可。)
修改后如下
保存后 將login后綴名修改為.pyc即可
將login.pyc復(fù)制粘貼到題目的文件夾后打開powershell終端
并且調(diào)用uncompyle6.exe
uncompyle6.exe .\login.pyc
就可以看到源碼了
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...')
看到一堆數(shù)字 就明白這是要解方程 用z3庫來寫方便點(diǎn)
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()
#根據(jù)題目來添加限制
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])
進(jìn)行異或, 反推回input
然后反推一個(gè)異或:注意這里源代碼中code和a不是一一對(duì)應(yīng)的....就是源碼中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]
, 轉(zhuǎn)換為字符串
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! 交上去是不對(duì)的,還需要將其轉(zhuǎn)換為md5才可以
加花指令的pyc
解法:
-
根據(jù)uncompyle6和字節(jié)碼判斷花
-
讀取co_code的長度
-
去掉花 并修改co_code長度
-
保存uncompyle6即可
不加花指令的代碼
def check():
flag=5+5
if(flag==10):
return "right"
return "Wrong"
#input=raw_input("Input something")
print(check())
我們先分別運(yùn)行一下加了花指令和沒有加花指令的pyc文件,看看加了花指令的pyc文件是否能輸出right
我們可以看到 加了花指令的pyc文件 即Pz_error.pyc也是可以輸出right的
因?yàn)闀?huì)把加了花指令的指令直接跳過去,就導(dǎo)致執(zhí)行是沒什么問題的
但是如果調(diào)用uncompyle6來反編譯出源代碼的話,加了花指令的pyc文件就會(huì)報(bào)錯(cuò)
最下面報(bào)錯(cuò)的信息給我們的是 tuple index out of range 數(shù)組下標(biāo)越界
我們先來得到Pz_error.pyc的字節(jié)碼,然后用code.marshal,loads(f[8:])讀取魔術(shù)字8位后的東西,并將其反編譯,看看花指令是加在哪條指令上了
我們可以看到?JUMP_ABSOLUTE
將字節(jié)碼計(jì)數(shù)器設(shè)置為目標(biāo) 強(qiáng)制跳轉(zhuǎn)到18
18也就是正常指令之后
LOAD_CONST 255 我們沒有加花指令的py文件 就那么點(diǎn)代碼 哪來的255,所以明顯花指令就是在這里了。
所以我們現(xiàn)在定位到了花指令的地方,我們要做的就是把花指令去掉 并且修改co_code長度 也就是整個(gè)pyc文件的長度
那么我們來進(jìn)行第二步 讀取co_code的長度
len(code.co_code)
我們可以看到是27個(gè)字節(jié)
我們把Pz_error.pyc丟進(jìn)Winhex里面 那么我們?cè)撛趺丛赪inhex里面定位到我們所要找的花指令呢?
我們要借助python2程序里面的opcode.h工具
已知花指令是JUMP_ABSOLUTE
?在opcode.h里面搜索JUMP_ABSOLUTE
113轉(zhuǎn)換為16進(jìn)制就是71
在Winhex里面找的71那條 因?yàn)閜ython2是3個(gè)字節(jié)碼為1個(gè)指令
接下來 我們找LOAD_CONST這條指令在opcode.h對(duì)應(yīng)的數(shù)字
100對(duì)應(yīng)的16進(jìn)制數(shù)字就是64 255對(duì)應(yīng)的16進(jìn)制數(shù)就是FF
所以基本可以確定 71 12 00 64 FF 00 這六個(gè)就是我們要找的花指令
然后delete鍵刪掉
現(xiàn)在我們到了第三步 修改co_code的長度
我們剛讀出來Pz_error.pyc字節(jié)碼長度是27 27-6=21
27對(duì)應(yīng)的16進(jìn)制數(shù)是1B
21對(duì)應(yīng)的16進(jìn)制數(shù)是15
我們?cè)趙inhex里面將找的1B修改為15,然后保存,這樣我們的Pz_error.pyc就修好了
現(xiàn)在我們來試試看uncompyle6能否反編譯出來
大功告成!
更多網(wǎng)安技能的在線實(shí)操練習(xí),請(qǐng)點(diǎn)擊這里>>文章來源:http://www.zghlxwxcb.cn/news/detail-855158.html
??文章來源地址http://www.zghlxwxcb.cn/news/detail-855158.html
到了這里,關(guān)于CTF中常見的四種python逆向的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!