Wednesday, 15 February 2017

Extracting encrypted pyinstaller executables

It has been more than a quarter since the last post, and in the meantime, I was very busy and did not have the time to write a proper post. The good news is at the moment, I am comparatively free and can put in a quick post. 

As said earlier, PyInstaller provides an option to encrypt the embedded files within the executable. This feature can be used by supplying an argument --key=key-string while generating the executable. 

Detecting encrypted pyinstaller executables is simple. If  pyinstxtractor is used, it would indicate this as shown in Figure 1.

Trying to extract encrypted pyinstaller archive
Figure 1: Trying to extract encrypted pyinstaller archive

The other tell-tale sign is the presence of the file pyimod00_crypto_key in the extracted directory as shown in Figure 2.
The file pyimod00_crypto_key indicates usage of crypto
Figure 2: The file pyimod00_crypto_key indicates usage of crypto

If encryption is used, pyinstaller AES encrypts all the embedded files present within ZLibArchive i.e. the out00-PYZ.pyz file. When pyinstxtractor encounters an encrypted pyz archive, it would extract the contents as-is without decrypting the individual files as shown in Figure 3.

Contents of an an encrypted pyz archive
Figure 3: Contents of an encrypted pyz archive

To decrypt the files, you would need the key, and the key is present right within the file pyimod00_crypto_key. This is just a pyc file, and can be fed to a decompiler to retrieve the key. 

With the key in hand, it is a matter of another script to decrypt.

from Crypto.Cipher import AES
import zlib

CRYPT_BLOCK_SIZE = 16

# key obtained from pyimod00_crypto_key
key = 'MySup3rS3cr3tK3y'

inf = open('_abcoll.pyc.encrypted', 'rb') # encrypted file input
outf = open('_abcoll.pyc', 'wb') # output file 

# Initialization vector
iv = inf.read(CRYPT_BLOCK_SIZE)

cipher = AES.new(key, AES.MODE_CFB, iv)

# Decrypt and decompress
plaintext = zlib.decompress(cipher.decrypt(inf.read()))

# Write pyc header
outf.write('\x03\xf3\x0d\x0a\0\0\0\0')

# Write decrypted data
outf.write(plaintext)

inf.close()
outf.close()
The above snippet can be used for decrypting the encrypted files. Afterward, you can run a decompiler to get back the source.