1.5. Customization and Extension
Contents
Pyarmor provides the following ways to extend:
Using pyarmor cfg to change default configurations
Using plugin script to customize all generated files
Using hook script to extend features in obfuscated scripts
1.5.1. Changing runtime package name
New in version 8.2: 1
By default the runtime package name is pyarmor_runtime_xxxxxx
This name is variable with any valid package name. For example, set it to my_runtime
:
pyarmor cfg package_name_format "my_runtime"
- 1
Pyarmor trial version could not change runtime package name
1.5.2. Appending assert functions and modules
New in version 8.2.
Pyarmor 8.2 introduces configuration item auto_mode
to protect more functions and modules. The default value is and
, --assert-call
and --assert-import
only protect modules and functions which Pyarmor make sure they’re obfuscated.
If set its value to or
, then all the names in the configuration item includes
are also protected. For example, appending function foo
和 koo
to assert list:
$ pyarmor cfg ast.call:auto_mode "or"
$ pyarmor cfg ast.call:includes "foo koo"
$ pyarmor gen --assert-call foo.py
For example, also protect hidden imported module joker.card
:
$ pyarmor cfg ast.import:auto_mode "or"
$ pyarmor cfg ast.import:includes "joker.card"
$ pyarmor gen --assert-import joker/
1.5.3. Using plugin to fix loading issue in darwin
New in version 8.2.
In darwin, if Python is not installed in the standard path, the obfuscated scripts may not work because extension module pyarmor_runtime
in the runtime package could not be loaded.
Let’s check the dependencies of pyarmor_runtime.so
:
$ otool -L dist/pyarmor_runtime_000000/pyarmor_runtime.so
dist/pyarmor_runtime_000000/pyarmor_runtime.so:
pyarmor_runtime.so (compatibility version 0.0.0, current version 1.0.0)
...
@rpath/lib/libpython3.9.dylib (compatibility version 3.9.0, current version 3.9.0)
...
Suppose target device has no @rpath/lib/libpython3.9.dylib
, but @rpath/lib/libpython3.9.so
, in this case pyarmor_runtime.so
could not be loaded.
We can create a plugin script .pyarmor/myplugin.py
to fix this problem
__all__ = ['CondaPlugin']
class CondaPlugin:
def _fixup(self, target):
from subprocess import check_call
check_call('install_name_tool -change @rpath/lib/libpython3.9.dylib @rpath/lib/libpython3.9.so %s' % target)
check_call('codesign -f -s - %s' % target)
@staticmethod
def post_runtime(ctx, source, target, platform):
if platform.startswith('darwin.'):
print('using install_name_tool to fix %s' % target)
self._fixup(target)
Enable this plugin and generate the obfuscated script again:
$ pyarmor cfg plugins + "myplugin"
$ pyarmor gen foo.py
See also
1.5.4. Using hook to bind script to docker id
New in version 8.2.
Suppose we need bind script app.py
to 2 dockers which id are docker-a1
and docker-b2
First create hook script .pyarmor/hooks/app.py
def _pyarmor_check_docker():
cid = None
with open("/proc/self/cgroup") as f:
for line in f:
if line.split(':', 2)[1] == 'name=systemd':
cid = line.strip().split('/')[-1]
break
docker_ids = __pyarmor__(0, None, b'keyinfo', 1).decode('utf-8')
if cid is None or cid not in docker_ids.split(','):
raise RuntimeError('license is not for this machine')
_pyarmor_check_docker()
Then generate the obfuscated script, store docker ids to runtime key as private data at the same time:
$ pyarmor gen --bind-data "docker-a1,docker-b2" app.py
Run the obfuscated script to check it, please add print statements in the hook script to debug it.
See also
1.5.5. Using hook to check network time by other service
New in version 8.2.
If NTP is not available in the target device and the obfuscated scripts has expired date, it may raise RuntimeError: Resource temporarily unavailable
.
In this case, using hook script to verify expired data by other time service.
First create hook script in the .pyarmor/hooks/foo.py
:
def _pyarmor_check_worldtime(host, path):
from http.client import HTTPSConnection
expired = __pyarmor__(1, None, b'keyinfo', 1)
conn = HTTPSConnection(host)
conn.request("GET", path)
res = conn.getresponse()
if res.code == 200:
data = res.read()
s = data.find(b'"unixtime":')
n = data.find(b',', s)
current = int(data[s+11:n])
if current > expire:
raise RuntimeError('license is expired')
else:
raise RuntimeError('got network time failed')
_pyarmor_check_worldtime('worldtimeapi.org', '/api/timezone/Europe/Paris')
Then generate script with local expired date:
$ pyarmor gen -e .30 foo.py
Thus the obfuscated script could verify network time by itself.
See also
1.5.6. Protecting extension module pyarmor_runtime
New in version 8.2.
This example shows how to check the file content of an extension module to make sure it’s not changed by others.
First create a hook script .pyarmor/hooks/foo.py
:
1def check_pyarmor_runtime(value):
2 from pyarmor_runtime_000000 import pyarmor_runtime
3 with open(pyarmor_runtime.__file__, 'rb') as f:
4 if sum(bytearray(f.read())) != value:
5 raise RuntimeError('unexpected %s' % filename)
6
7check_pyarmor_runtime(EXCEPTED_VALUE)
Line 7 EXCEPTED_VALUE
need to be replaced with real value, but it doesn’t work to get the sum value of pyarmor_runtime.so
after building, because each build the sum value is different. We need use a post-runtime plugin to get the expected value and update the hook script automatically
# Plugin script: .pyarmor/myplugin.py
__all__ = ['CondaPlugin', 'RuntimePlugin']
class RuntimePlugin:
@staticmethod
def post_runtime(ctx, source, target, platform):
with open(target, 'rb') as f:
value = sum(bytearray(f.read()))
with open('.pyarmor/hooks/foo.py', 'r') as f:
source = f.read()
source = source.replace('EXPECTED_VALUE', str(value))
with open('.pyarmor/hooks/foo.py', 'r') as f:
f.write(source)
class CondaPlugin:
...
Then enable this plugin:
$ pyarmor cfg plugins + "myplugin"
Finally generate the obfuscated script, and verify it:
$ pyarmor gen foo.py
$ python dist/foo.py
This example is only guide how to do, it’s not safe enough to use it directly. There is always a way to bypass open source check points, please write your private check code. There are many other methods to prevent binary file from hacking, please learn and search these methods by yourself.
See also
1.5.7. Comments within outer key
New in version 8.2.
The outer key ignores all the printable text at the header, so it’s possible to insert some readable text in the outer key as comments.
Post-key plugin is designed to do this. The following example plugin will print all the key information in the console, and write expired date to outer key file:
Enable this plugin and generate an outer key:
Check comment:
See also
Plugins