PyInstaller
如果exe
文件是使用PyInstaller 打包的。我们可以用PyInstaller Extractor 解包exe
文件。
python pyinstxtractor.py <文件名>.exe
但是必须要使用相同的Python
版本运行命令才能解包PYZ
文件。解包成功后我们会得到一系列的.pyc
文件。
我们可以使用uncompyle6 去反编译.pyc
文件
uncompyle6 xxx.pyc
加密
假如解压PYZ
文件的过程中出现下面的这些错误的话,
那这个文件在生成的时候是被加密了
...
[!] Error: Failed to decompress PYZ-00.pyz_extracted\xxx.pyc, probably encrypted. Extracting as is.
...
加密的密钥可以在pyimod00_crypto_key.pyc
这个文件找到
uncompyle6 pyimod00_crypto_key.pyc > pyimod00_crypto_key.py
Pyinstaller
的加密功能使用tinyaes 实现的。既然有了密钥只需要逆向解密成正常的.pyc
文件再用uncompyle6
转为.py
文件。
decompile-python-exe.py view raw
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
import tinyaesfrom pyimod00_crypto_key import keyCIPHER_BLOCK_SIZE = 16 CIPHER_KEY = bytes (key, 'utf-8' ) PYC_HEADER = b"\x42\x0D\x0D\x0A\x00\x00\x00\x00\x70\x79\x69\x30\x10\x01\x00\x00" ENCRYPTED_FILENAME_SUFFIX = ".pyc.encrypted" PYC_FILENAME_SUFFIX = ".pyc" def encrypted_to_pyc (input_path, output_path ): with open (input_path, "rb" ) as encrypted_pyc: with open (output_path, "wb" ) as pyc: cipher_iv = encrypted_pyc.read(CIPHER_BLOCK_SIZE) encrypted_data = encrypted_pyc.read() cipher = tinyaes.AES(CIPHER_KEY, cipher_iv) decrypted_data = cipher.CTR_xcrypt_buffer(encrypted_data) plaintext = zlib.decompress(decrypted_data) pyc.write(PYC_HEADER) pyc.write(plaintext) print (f"decrypt {input_path} and save result to {output_path} " ) def pyc_to_py_with_uncompyle6 (input_path ): try : return subprocess.call(["uncompyle6" , "-o" , os.path.dirname(input_path), input_path], timeout=30 ) except : return -1 def pyc_to_py_with_decompile3 (input_path ): try : return subprocess.call(["decompyle3" , "-o" , os.path.dirname(input_path), input_path], timeout=30 ) except : pass return pyc_to_py_with_uncompyle6(input_path) def main (): for root, dirs, files in os.walk("." , topdown=False ): for filename in files: filepath = os.path.join(root, filename) if filename.endswith(".pyc.encrypted" ): pyc_filepath = filepath[:-len (ENCRYPTED_FILENAME_SUFFIX)] + PYC_FILENAME_SUFFIX encrypted_to_pyc(filepath, pyc_filepath) failed_list = [] for root, dirs, files in os.walk("." , topdown=False ): for filename in files: filepath = os.path.join(root, filename) if filename.endswith(PYC_FILENAME_SUFFIX): if pyc_to_py_with_decompile3(filepath) != 0 : failed_list.append(filepath) print (failed_list)
在Pyinstaller
最新的版本中已经移除了加密的功能
有些文件用uncompyle6
会解析失败 因为对Python
3.7以上的版本支持并不完全 所以还可以用decompyle3 去尝试失败的文件
参考