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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
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')
check_something()

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')
__assert_armored__(m)

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
[runtime.message]

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

[runtime.message.zh_CN]

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

[runtime.message.zh_TW]

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