4.2. Understanding Obfuscated Script
Remain as standard .py files
The obfuscated scripts are normal Python scripts, it’s clear by checking the content of dist/foo.py
:
1from pyarmor_runtime_000000 import __pyarmor__
2__pyarmor__(__name__, __file__, b'\xa...')
It’s a simple script, first imports function __pyarmor__
from package pyarmor_runtime_000000
, then call this function.
Runtime package
This package pyarmor_runtime_000000
is generated by Pyarmor, it’s also a normal Python package, here it’s package content:
$ ls dist/pyarmor_runtime_000000
... __init__.py
... pyarmor_runtime.so
There is binary extension module pyarmor_runtime
, this is a big difference from plain Python script. Generally using binary extensions means the obfuscated scripts
may not be compatible with different builds of CPython interpreter.
often will not work correctly with alternative interpreters such as PyPy, IronPython or Jython
For example, when obfuscating scripts by Python 3.8, they can be run by any Python 3.8.x, but can’t be run by Python 3.7, 3.9 etc.
For example, packaging pure .py script is easy, but packaging binary extension need more work.
For example, in Android pure .py script can be run in any location, but binary extensions must be in special system paths.
The runtime package pyarmor_runtime_000000
could be in any path, it can be taken as a third-party package, save it in any location, and import it following Python import system.
pyarmor provides several options -i
, --prefix
to help generating right code to import it. Also refer to option --use-runtime
and command pyarmor gen runtime for using shared runtime package.
See also
Changing runtime package name in the chapter Customization and Extension
Using shared runtime package in the chapter Advanced Tutorial
Runtime key
The runtime key generally is embedded into extension module pyarmor_runtime
, it also could be an outer file. It stores expire date, bind devices, and user private data etc.
Extension module pyarmor_runtime
will not load the obfuscated script unless the runtime key exists and is valid.
User also could store any private data in the runtime key, then use hook script to check private data in the obfuscated scripts.
If runtime key is stored in an outer file, any readable text in the header will be ignored. User can add comment at the header of runtime key file, the rest part are bytes data, only in the obfuscated scripts they could be read.
See also
command pyarmor gen key
4.2.1. Restrict modes
By default the obfuscated scripts can’t be changed.
After using --private
, the attributes of the obfuscated scripts could not be seen by plain script or Python interpreter.
After using --restrict
, the attributes of the obfuscated scripts are protected as --private
, and the obfuscated scripts could not be imported by plain script or Python interpreter,
Disable all the restrictions by this command:
$ pyarmor cfg restrict_module 0
Generally only disable all the restrictions for specified module. For example, only no restrictions for module NAME
:
$ pyarmor cfg -p NAME restrict_module 0
4.2.2. The differences of obfuscated scripts
Although use obfuscated scripts as they’re normal Python scripts, but the obfuscated scripts are still different from pure Python scripts, they changes a few Python features and results in some third party packages could not work.
Here are major changed features:
The obfuscated scripts are bind to Python major/minor version. For example, if it’s obfuscated by Python 3.6, it must run by Python 3.6. It doesn’t work for Python 3.5
The obfuscated scripts are platform-dependent, supported platforms and Python versions refer to Building Environments
If Python interpreter is compiled with Py_TRACE_REFS or Py_DEBUG, it will crash to run obfuscated scripts.
Any module may not work if it try to visit the byte code, or some attributes of code objects in the obfuscated scripts. For example most of
inspect
function are broken.Pass the obfuscated code object by
cPickle
or any third serialize tool may not work.sys._getframe([n])
may get the different frame. Note that many third packages uses this feature to get local variable and broken. For example,pandas
,cherrypy
.The code object attribute
__file__
is<frozen name>
other than real filename.Note that module attribute
__file__
is still filename. For example, obfuscate the scriptfoo.py
and run it:def hello(msg): print(msg) # The output will be 'foo.py' print(__file__) # The output will be '<frozen foo>' print(hello.__file__)
A few options may also change something:
pyarmor cfg mix_argname=1
hides annotations.Importing obfuscated module by importlib.util.spec_from_file_location need extra handle, refer to issue 846
See also
Work with Third-Party Libraries
Generating cross platform scripts and Obfuscating scripts for multiple Python versions in the chapter Advanced Tutorial
4.2.3. Supported Third-Party Interpreter
About third-party interpreter, for example Jython, and any embedded Python C/C++ code, only they could work with CPython extension module, they could work with Pyarmor. Check third-party interpreter documentation to make sure this.
A few known issues
On Linux, RTLD_GLOBAL must be set as loading libpythonXY.so by dlopen, otherwise obfuscated scripts couldn’t work.
Boost::python does not load libpythonXY.so with RTLD_GLOBAL by default, so it will raise error “No PyCode_Type found” as running obfuscated scripts. To solve this problem, try to call the method sys.setdlopenflags(os.RTLD_GLOBAL) as initializing.
PyPy could not work with pyarmor, it’s total different from CPython
WASM is not supported.
See also