4.3. Insight Into Pack Command

Pyarmor 8.0 has no command pack, but --pack. It could specify an executable file generated by PyInstaller:

pyinstaller foo.py
pyarmor gen --pack dist/foo/foo foo.py

If no this option, pyarmor only obfuscates the scripts.

If this option is set, pyarmor first obfuscates the scripts, then does extra work:

  • Unpacking this executable to a temporary folder
  • Replacing the scripts in bundle with obfuscated ones
  • Appending runtime files to the bundle in this temporary folder
  • Repacking this temporary folder to an executable file and overwrite the old

4.3.1. Packing obfuscated scripts manually

If something is wrong with --pack, or the final bundle doesn’t work, try to pack the obfuscated scripts manually.

You need know how to using PyInstaller and using spec file, if not, learn it by yourself.

Here is an example to pack script foo.py in the path /path/to/src

  • First obfuscating the script by Pyarmor [1]:

    cd /path/to/src
    pyarmor gen -O obfdist -a foo.py
    
  • Moving runtime package to current path [2]:

    mv obfdist/pyarmor_runtime_000000 ./
    
  • Already have foo.spec, appending runtime package to hiddenimports

a = Analysis(
    ...
    hiddenimports=['pyarmor_runtime_000000'],
    ...
)
  • Otherwise generating foo.spec by PyInstaller [3]:

    pyi-makespec --hidden-import pyarmor_runtime_000000 foo.py
    
  • Patching foo.spec by inserting extra code after a = Analysis

a = Analysis(
    ...
)

# Patched by Pyarmor
_src = r'/path/to/src'
_obf = r'/path/to/src/obfdist'

_count = 0
for i in range(len(a.scripts)):
    if a.scripts[i][1].startswith(_src):
        x = a.scripts[i][1].replace(_src, _obf)
        if os.path.exists(x):
            a.scripts[i] = a.scripts[i][0], x, a.scripts[i][2]
            _count += 1
if _count == 0:
    raise RuntimeError('No obfuscated script found')

for i in range(len(a.pure)):
    if a.pure[i][1].startswith(_src):
        x = a.pure[i][1].replace(_src, _obf)
        if os.path.exists(x):
            if hasattr(a.pure, '_code_cache'):
                with open(x) as f:
                    a.pure._code_cache[a.pure[i][0]] = compile(f.read(), a.pure[i][1], 'exec')
            a.pure[i] = a.pure[i][0], x, a.pure[i][2]
# Patch end.

pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
  • Generating final bundle by this patched foo.spec:

    pyinstaller foo.spec
    

If following this example, please

  • Replacing all the /path/to/src with actual path
  • Replacing all the pyarmor_runtime_000000 with actual name

how to verify obfuscated scripts have been packed

Inserting some print statements in the foo.spec to print which script is replaced, or add some code only works in the obfuscated script.

For example, add one line in main script foo.py

print('this is __pyarmor__', __pyarmor__)

If it’s not obfuscated, the final bundle will raise error.

notes

[1]Do not use -i and --prefix to obfuscate the scripts
[2]Just let PyInstaller could find runtime package without extra pypath
[3]Most of other PyInstaller options could be used here