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.

6 comments:

  1. Thank you very much! :)

    ReplyDelete
  2. What about when there is not a pymod00_crypto_key file at all?

    ReplyDelete
    Replies
    1. This probably means a modified version of pyinstaller has been used. Look, if the application runs without asking for a key it must have the key embedded within it somewhere. You would need to find where.

      Delete
  3. I have the key and the files. But then I am stuck. Am a windows user, but have python 3 installed. But above snip fails with me. I need the python code for a very small program I use, but the HDD drive with the source py files are dead.

    Can we contact eachother, or can you help me here?

    ReplyDelete
    Replies
    1. You should elaborate a bit about the exact error you are facing.
      For contact, you can use my email on the "About Me" page.

      Delete
    2. I would use your "About me", but it is empty. :-)

      I get stuck at your last picture. I dont know have to use that code. I have a folder with *.pyc.encryptet files I need decryptet.

      Delete