說在前面:
什麼是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
可以看到即使是運行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
這裏我們就得到了源碼
接下來的操作就跟windows逆向別無二致了
【---- 幫助網安學習,以下所有學習資料免費領!領取資料加 we~@x:dctintin,備註 “開源中國” 獲取!】
① 網安學習成長路徑思維導圖
② 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庫 一個是裝載庫 一個是反編譯字節碼的庫
我們可以將我們的test.pyc導入010Editor中配合着一起看
python2的前八個字節是python2的魔術字
python3的前十六個字節是python3的魔術字
所以我們可以不用讀前十六位 我們只需要讀後十六位的東西
code=marshal.loads(f[16:]) code
現在我們讀進來的是二進制數據,我們可以用dis來進行反編譯 就會得到python的字節碼 可以理解成python的彙編讀出來了
dis.dis(code)
這就是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即可
下載完題目發現這是個exe文件 但是圖標又是很明顯的pyc文件
所以這是個打包成exe的py文件
這裏我們需要用到一個工具pyinstxtractor.py
把這個py文件複製到我們的題目文件夾裏面
在搜索框中輸入powershell 在打開的終端中輸入
python .\pyinstxtractor.py .\attachment.exe
運行後生成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 粘貼即可。)
修改後如下
保存後 將login後綴名修改爲.pyc即可
將login.pyc複製粘貼到題目的文件夾後打開powershell終端
並且調用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...')
看到一堆數字 就明白這是要解方程 用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
我們可以看到 加了花指令的pyc文件 即Pz_error.pyc也是可以輸出right的
因爲會把加了花指令的指令直接跳過去,就導致執行是沒什麼問題的
但是如果調用uncompyle6來反編譯出源代碼的話,加了花指令的pyc文件就會報錯
最下面報錯的信息給我們的是 tuple index out of range 數組下標越界
我們先來得到Pz_error.pyc的字節碼,然後用code.marshal,loads(f[8:])讀取魔術字8位後的東西,並將其反編譯,看看花指令是加在哪條指令上了
我們可以看到 JUMP_ABSOLUTE
將字節碼計數器設置爲目標 強制跳轉到18
18也就是正常指令之後
LOAD_CONST 255 我們沒有加花指令的py文件 就那麼點代碼 哪來的255,所以明顯花指令就是在這裏了。
所以我們現在定位到了花指令的地方,我們要做的就是把花指令去掉 並且修改co_code長度 也就是整個pyc文件的長度
那麼我們來進行第二步 讀取co_code的長度
len(code.co_code)
我們可以看到是27個字節
我們把Pz_error.pyc丟進Winhex裏面 那麼我們該怎麼在Winhex裏面定位到我們所要找的花指令呢?
我們要藉助python2程序裏面的opcode.h工具
已知花指令是JUMP_ABSOLUTE
在opcode.h裏面搜索JUMP_ABSOLUTE
113轉換爲16進制就是71
在Winhex裏面找的71那條 因爲python2是3個字節碼爲1個指令
接下來 我們找LOAD_CONST這條指令在opcode.h對應的數字
100對應的16進制數字就是64 255對應的16進制數就是FF
所以基本可以確定 71 12 00 64 FF 00 這六個就是我們要找的花指令
然後delete鍵刪掉
現在我們到了第三步 修改co_code的長度
我們剛讀出來Pz_error.pyc字節碼長度是27 27-6=21
27對應的16進制數是1B
21對應的16進制數是15
我們在winhex裏面將找的1B修改爲15,然後保存,這樣我們的Pz_error.pyc就修好了
現在我們來試試看uncompyle6能否反編譯出來
大功告成!