1.4. Advanced Tutorials

1.4.1. Using rftmode pro

RFT mode could rename most of builints, functions, classes, local variables. It equals rewritting scripts in source level.

For example, the following Python script

import sys

def sum2(a, b):
    return a + b

def main(msg):
    a = 2
    b = 6
    c = sum2(a, b)
    print('%s + %s = %d' % (a, b, c))

if __name__ == '__main__':
    main('pass: %s' % data)

will be reformed to

pyarmor__17 = __assert_armored__(b'\x83\xda\x03sys')

def pyarmor__22(a, b):
    return a + b

def pyarmor__16(msg):
    pyarmor__23 = 2
    pyarmor__24 = 6
    pyarmor__25 = pyarmor__22(pyarmor__23, pyarmor__24)
    pyarmor__14('%s + %s = %d' % (pyarmor__23, pyarmor__24, pyarmor__25))

if __name__ == '__main__':
    pyarmor__16('pass: %s' % pyarmor__20)

Using --enable-rft to enable RTF mode:

$ pyarmor gen --enable-rft foo.py

This feature is only available for Pyarmor Pro.

1.4.2. Using bccmode pro

BCC mode could convert most of functions and methods in the scripts to equivalent C functions, those c functions will be comipled to machine instructions directly, then called by obfuscated scripts.

Note that the code in model level is not converted to C function.

Using --enable-bcc to enable BCC mode:

$ pyarmor gen --enable-bcc foo.py

This feature is only available for Pyarmor Pro.

1.4.3. Customization error handler

By default when something is wrong with obfuscated scripts, a RuntimeError with error message is raised.

If prefer to show error message only:

$ pyarmor cfg on_error=1

If prefer to quit directly without any message:

$ pyarmor cfg on_error=2

Restore the default handler:

$ pyarmor cfg on_error=0

Or reset this option:

$ pyarmor cfg --reset on_error

After the option is changed, obfuscating the script again to make it effects.

1.4.4. Patching source by plugin marker

Before obfuscating a script, Pyarmor scans each line, remove plugin marker plus the following one whitespace, leave the rest as it is.

The default plugin marker is # pyarmor:, any comment line with this prefix will be as a plugin marker.

For example, these lines

print('start ...')

# pyarmor: print('this is plugin code')
# pyarmor: check_something()

will be changed to

print('start ...')

print('this is plugin code')

One real case: protecting hidden imported modules

By default --assert-import could only protect modules imported by statement import, it doesn’t handle modules imported by other methods.

For example,

m = __import__('abc')

In obfuscated script, there is a builtin function __assert_armored__ could be used to check m is obfuscated. In order to make sure m could not be replaced by others, check it manually:

m = __import__('abc')

But this results in a problem, The plain script could not be run because __assert_armored__ is only available in the obfuscated script.

The plugin marker is right solution for this case. Let’s make a little change

m = __import__('abc')
# pyarmor: __assert_armored__(m)

By plugin marker, both the plain script and the obfsucated script work as expected.

1.4.5. Using hooks

New in version 8.1: This feature is not implemented in 8.0

Hooks is used to do some extra checks when running obfuscated scripts.

A hook is a Python script called in any of

  • boot: when importing the runtime package pyarmor_runtime
  • period: only called when runtime key is in period mode
  • import: when imporing an obfuscated module

An example of hook script hook.py

   'boot': '''def boot_hook(*args):
   print('hello, boot hook')''',

   'import': '''def import_hook(*args):
   print('hello, import hook')''',

   'period': '''def period_hook(*args):
   print('hello, period hook')''',

Save it to global or local configuration path

1.4.6. Internationalization runtime error message

Create messages.cfg in the path .pyarmor:

$ mkdir .pyarmor
$ vi .pyarmor/message.cfg

It’s a .ini format file, add a section runtime.message with option languages. The language code is same as environment variable LANG, assume we plan to support 2 languages, and only customize 2 errors:

  • error_1: license is expired
  • error_2: license is not for this machine

languages = zh_CN zh_TW

error_1 = invalid license
error_2 = invalid license

error_1 and error_2 is default message for any non-matched language.

Now add 2 extra sections runtime.message.zh_CN and runtime.message.zh_TW


error_1 = 脚本超期
error_2 = 未授权设备


error_1 = 腳本許可證已經過期
error_2 = 腳本許可證不可用於當前設備

Then obfuscate script again to make it works.

PYARMOR_LANG could be used to set runtime language. If it’s set, the obfuscated scripts ignore LANG.

1.4.7. Generating cross platform scripts

New in version 8.1: This feature is not implemented in 8.0

Use --platform

1.4.8. Obfuscating scripts for multiple Pythons

New in version 8.1: This feature is not implemented in 8.0

Use helper script merge.py