Python 雖然是直譯式語言(Interpreted Language),但在執行 .py 檔案時,Python 解譯器(CPython)會先將原始碼編譯成位元組碼(Bytecode),並快取為 .pyc 檔案(通常放在 __pycache__ 資料夾中)以加速後續載入。
在 CTF 競賽的 Reverse 題目中,經常會拿到一個 .pyc 檔案,或是被打包成單一執行檔的 Python 程式(如 PyInstaller)。
雖然 .pyc 檔是二進位格式,但它保留了豐富的抽象語法樹(AST)與指令結構。透過反編譯器 (Decompiler),我們能輕鬆將位元組碼還原回人類可讀的高階 Python 原始碼!
不同的 Python 版本(如 3.8, 3.10, 3.11+)其位元組碼結構會有些許差異,常用解題工具如下:
uncompyle6 / decompyle++ (pycdc):
uncompyle6:經典的 Python 反編譯器,對 Python 2.7 至 3.8 的支援度極佳。pycdc (decompyle++):由 C++ 撰寫的強大反編譯工具,支援較新版本的 Python(如 3.10+)。pyinstxtractor:
.exe 或二進位檔,需先用此腳本解包出內含的 .pyc 檔案。我們在 WSL 中親自建立一個含 Flag 的 Python 腳本,將其編譯為 .pyc,並使用 Python 內建的 dis 模組與反編譯邏輯來進行逆向還原!
在 WSL 終端機執行以下命令,生成 .pyc 編譯檔:
# 1. 建立包含 Flag 的範例 Python 腳本
cat << 'EOF' > target_script.py
def check_flag():
secret_flag = "picoCTF{pyc_d3c0mp1l3_1s_s0_34sy_99f2a}"
user_input = input("Enter password: ")
if user_input == "python_master_2026":
print(f"Correct! Flag: {secret_flag}")
else:
print("Wrong password!")
if __name__ == "__main__":
check_flag()
EOF
# 2. 將 .py 檔編譯為 .pyc 位元組碼檔
python3 -m py_compile target_script.py
# 3. 複製編譯好的 .pyc 檔至當前目錄
cp __pycache__/target_script.cpython-*.pyc challenge.pyc
dis 檢視 Bytecode若不使用第三方工具,Python 內建的 dis 模組可以將 .pyc 檔案反組譯為位元組碼指令(Opcode):
python3 -m dis challenge.pyc
在輸出結果中,你可以看到 LOAD_CONST 指令後直接帶出了字串常數 "picoCTF{pyc_d3c0mp1l3_1s_s0_34sy_99f2a}"!
在 CTF 中,即便不進行完整的程式碼結構還原,我們也能寫腳本直接剖析 .pyc 的 Code Object(co_consts),把裡面所有的字串常數硬挖出來:
# 創建 day24_pyc_reverse.py
cat << 'EOF' > day24_pyc_reverse.py
import marshal
import dis
def extract_consts_from_pyc(pyc_path):
print(f"[*] 正在分析 .pyc 檔案: '{pyc_path}'")
with open(pyc_path, "rb") as f:
# 跳過 .pyc 標頭 (Header) 讀取 Code Object
# Python 3.7+ 標頭長度通常為 16 bytes
f.seek(16)
code_obj = marshal.load(f)
print("[*] 成功讀取 Code Object,開始提取字串常數 (co_consts)...")
# 遞迴搜尋 Code Object 中的常數
def search_consts(code):
for const in code.co_consts:
if isinstance(const, str) and "picoCTF" in const:
print(f"[🎯] 成功挖到硬編碼 Flag: {const}")
elif hasattr(const, 'co_consts'):
search_consts(const)
search_consts(code_obj)
if __name__ == "__main__":
extract_consts_from_pyc("challenge.pyc")
EOF
python3 day24_pyc_reverse.py
在 WSL 終端機執行腳本後,即可直接解析 .pyc 檔案的內部架構,精準印出隱藏的 Flag:
[*] 正在分析 .pyc 檔案: 'challenge.pyc'
[*] 成功讀取 Code Object,開始提取字串常數 (co_consts)...
[🎯] 成功挖到硬編碼 Flag: picoCTF{pyc_d3c0mp1l3_1s_s0_34sy_99f2a}

為了防止 .pyc 檔案被輕易反編譯還原,常見的保護手段包括: