diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 15303171..74544083 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,89 +1,22 @@ --- name: Bug report about: Report a bug of pyarmor -title: '' +title: "[BUG] `Error message`" labels: bug -assignees: jondy +assignees: '' --- -It's more readable in the issue Preview mode for the hints +**Please remove this line and all the following lines when submit issue** -**IMPORTANT** -Please provide the necessary information according to this template when reporting bug. If missing necessay information, the issue will be marked as `invalid` and be closed directly. - -**Hints** -Before report a bug, please read these common questions and solutions first +In order to solve your issue quickly, please read this page and try common solutions in the section `Asking questions in GitHub` https://pyarmor.readthedocs.io/en/latest/questions.html -If there is the exact solution in the documentation, the issue will be marked as `invalid` and be closed directly. - -And please remove all the above hints. - -**Title** -A clear and concise description of what the bug is. - -**Description** -1. The full pyarmor command and full output log (required) -2. If distributing the obfuscated script to other machine, which files are copied (optional) -3. The command to run the obfuscated scripts and full traceback when something is wrong - -The output log could be redirected to a file by this way. For example, +If it's really a bug, please following the guide in the section `Reporting issues` - pyarmor obfuscate foo.py >log.txt 2>&1 - -Here is an issue instance - -*Title*: - - cannot import name 'pyarmor' from 'pytransform' +为了快速解决你遇到的问题,请务必打开下面的链接并尝试其中关于常见问题的通用解决方案 +https://pyarmor.readthedocs.io/en/latest/questions.html -*Description*: +如果确认是一个 Bug,建议按照上面链接中的章节 `报告问题` 的规范提交问题报告 -1. On MacOS 10.14 run pyarmor to obfuscate the script -``` -$ pyarmor obfuscate --exact main.py -INFO Create pyarmor home path: /Users/jondy/.pyarmor -INFO Create trial license file: /Users/jondy/.pyarmor/license.lic -INFO Generating public capsule ... -INFO PyArmor Trial Version 7.0.1 -INFO Python 3.7.10 -INFO Target platforms: Native -INFO Source path is "/Users/jondy/workspace/pyarmor-webui/test/__runner__/__src__" -INFO Entry scripts are ['main.py'] -INFO Use cached capsule /Users/jondy/.pyarmor/.pyarmor_capsule.zip -INFO Search scripts mode: Exact -INFO Save obfuscated scripts to "dist" -INFO Read product key from capsule -INFO Obfuscate module mode is 2 -INFO Obfuscate code mode is 1 -INFO Wrap mode is 1 -INFO Restrict mode is 1 -INFO Advanced value is 0 -INFO Super mode is False -INFO Super plus mode is not enabled -INFO Generating runtime files to dist/pytransform -INFO Extract pytransform.key -INFO Generate default license file -INFO Update capsule to add default license file -INFO Copying /Users/jondy/workspace/pyarmor-webui/venv/lib/python3.7/site-packages/pyarmor/platforms/darwin/x86_64/_pytransform.dylib -INFO Patch library dist/pytransform/_pytransform.dylib -INFO Patch library file OK -INFO Copying /Users/jondy/workspace/pyarmor-webui/venv/lib/python3.7/site-packages/pyarmor/pytransform.py -INFO Rename it to pytransform/__init__.py -INFO Generate runtime files OK -INFO Start obfuscating the scripts... -INFO /Users/jondy/workspace/pyarmor-webui/test/__runner__/__src__/main.py -> dist/main.py -INFO Insert bootstrap code to entry script dist/foo.py -INFO Obfuscate 1 scripts OK. -``` -2. Copy the whole folder `dist/` to target machine Ubuntu -3. Failed to run the obfuscated script by Python 3.7 in Unbutu -``` -$ cd dist/ -$ python3 main.py -Traceback (most recent call last): -File "main.py", line 1, in - from pytransform import pyarmor -ImportError: cannot import name 'pyarmor' from 'pytransform' (/home/jondy/dist/pytransform/__init__.py) -``` +**在提交报告时候请删除当前行以及上面的所有行** diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index 74fffcd0..00000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -name: Feature request -about: 'Request for new platform or suggest an idea for this project ' -title: '' -labels: enhancement -assignees: jondy - ---- - -**Is your feature request related to a problem? Please describe.** -A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] - -**Describe the solution you'd like** -A clear and concise description of what you want to happen. - -**Describe alternatives you've considered** -A clear and concise description of any alternative solutions or features you've considered. - -**Additional context** -Add any other context or screenshots about the feature request here. - -If request for new platform, first check all the supported platforms by `pyarmor download`. If it's really not in the list, run the following script in this platform and provide the output by text not snapshot image - -```python -from platform import * -print('system name: %s' % system()) -print('machine: %s' % machine()) -print('processor: %s' % processor()) -print('aliased terse platform: %s' % platform(aliased=1, terse=1)) - -if system().lower().startswith('linux'): - print('libc: %s' % libc_ver()) - print('distribution: %s' % linux_distribution()) -``` diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md deleted file mode 100644 index b57ff5f7..00000000 --- a/.github/ISSUE_TEMPLATE/question.md +++ /dev/null @@ -1,62 +0,0 @@ ---- -name: Question -about: Something you're not sure but nothing found in documentation, or it's not clear -title: '' -labels: question -assignees: jondy - ---- - -It's more readable in the issue Preview mode for all the hints - -**IMPORTANT** - -If ask a question or describe the problem, please provide the following information: - -1. A clear and concise description of the question (required) -2. The full pyarmor command and full output log (required if something is wrong) -3. If distributing the obfuscated script to other machine, which files are copied (optional) -4. The command to run the obfuscated scripts and full traceback when something is wrong - -The output log could be redirected to a file by this way. For example, - - pyarmor obfuscate foo.py >log.txt 2>&1 - -If missing the necessary information, or the question has been described in the document, the issue will be marked as "invalid" and be closed direclty. - -Please first read the document list in the below before asking a question. - -**Hint 1** -[Basic usage](https://pyarmor.readthedocs.io/en/latest/usage.html) -[Advanced usage](https://pyarmor.readthedocs.io/en/latest/advanced.html) -[Command usage](https://pyarmor.readthedocs.io/en/latest/man.html) -[Support platforms](https://pyarmor.readthedocs.io/en/latest/platforms.html) -[Performance](https://pyarmor.readthedocs.io/en/latest/performance.html) -[Security](https://pyarmor.readthedocs.io/en/latest/security.html) -[License](https://pyarmor.readthedocs.io/en/latest/license.html) -[Questions](https://pyarmor.readthedocs.io/en/latest/questions.html) - -**Hint 2** -If something you're not sure, but it could be verified by doing a test in five minutes, just do it to save time for both of us. For example, understand the difference of super mode and non-super mode scripts by a simple test -``` -mkdir test-non-super-mode -cd test-non-super-mode -echo "print('Hello')" > foo.py -pyarmor obfuscate foo.py - -# check the output file list and the content of obfuscated scripts in non-super mode -ls dist/ -cat dist/foo.py - -mkdir test-super-mode -cd test-super-mode -echo "print('Hello')" > foo.py -pyarmor obfuscate --advanced 2 foo.py - -# check the output file list and the content of obfuscated scripts in super mode -ls dist/ -cat dist/foo.py -``` - -**Hint 3** -Please remove all the above hints before your question diff --git a/.gitignore b/.gitignore index 83ac52fd..53fa5280 100644 --- a/.gitignore +++ b/.gitignore @@ -102,4 +102,4 @@ ENV/ # pip .pypirc -pypi-upload.sh +scripts/pypi-upload.sh diff --git a/.readthedocs.yaml b/.readthedocs.yaml index b30a3317..043e2783 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -1,7 +1,17 @@ version: 2 +build: + os: ubuntu-22.04 + tools: + python: "3.7" + +sphinx: + configuration: docs/conf.py + python: - version: "3.7" install: - requirements: docs/requirements.txt - system_packages: true + +formats: + - pdf + - epub diff --git a/LICENSE b/LICENSE index fb6e3840..e96ef11b 100755 --- a/LICENSE +++ b/LICENSE @@ -1,145 +1,277 @@ - END USER LICENSE AGREEMENT +Pyarmor 8.0 End User License Agreement - The following agreement regarding PyArmor - referred to as "software" - is - made between Jondy Zhao - referred to as "licensor" - and anyone who is - installing, accessing or in any other way using the software - referred to - as "user". +The following agreement regarding Pyarmor is made between Jondy Zhao - +referred to as "licensor" - and anyone or organization who is installing, +accessing or in any other way using Pyarmor - referred to as "user" or +"you". - 1. The author and holder of the copyright of the software is Jondy Zhao. +1. Definitions +-------------- - 2. The software is distributed as Free To Use But Restricted: +1.1. "This Software" - a. The trial version could not obfuscate the big scripts. + means Pyarmor 8.0+, it doesn't include Pyarmor prior to 8.0. Pyarmor 8.0 is + rewritten, and provides new features. For compatibility, Pyarmor 8.0 also + includes most of the old features, this license doesn't apply to those old + features. It's only for new features. - b. The scripts obfuscated by trial version are not private. It - means anyone could generate the license file which works for - these obfuscated scripts. +1.2. "Product" + means any application or software for sale. - c. The trial version could not download the latest dynamic library - of extra platforms, the old versions still are available. +1.3. "One Product" + means a product name and everything that makes up this name. It includes + all the devices to develop, build, debug, test product. It also includes + product current version, history versions and all the future versions. - d. The super plus mode is not availaible in the trial version. + One product may has several variants, each variant name is composed of + product name plus feature name. As long as the proportion of the + variable part is far less than that of the common part, they're + considered as "one product". - e. Without permission the trial version may not be used for the Python - scripts of any commercial product. +1.4. "Customer" + means anyone who uses user's product. - 3. There are 2 basic types of licenses issued for the software. These are: +1.5 "Script" + means Python script. - a. A personal license for home users. The user purchases one license to use - the software on his own computer. When placing an order of this kind of - license, please fill real name as registration name, this software is - only authorized to this registration name. +1.6 "User Script" + means user owns this script. - Home users may use their personal license to obfuscate all the python - scripts which are property of the license owner, to generate private - license files for the obfuscated scripts and distribute them and all the - required files to any other machine or device. +1.7 "Other Script" + means user doesn't own this script. - Home users could NOT obfuscate any python script which is NOT property - of the license owner. +1.8 "Mix Str" + means a feature of this software to obfuscate string constant in script - b. A enterprise license for business users. The user purchases one license - to use the software for one product serials of an organization. When - placing an order of this kind of license, please fill orginization name - plus product name, this software is only authorized to this registration - name. +1.9 "BCC Mode" + means an irreversible obfuscation method, a feature of this software - One product serials include the current version and any other latter - versions of the same product. +1.10 "RFT Mode" + means an irreversible obfuscation method, a feature of this software - Business users may use their enterprise license on all computers and - embedded devices to obfuscate all the python scripts of this product - serials, to generate private license files for these obfuscated scripts - and distribute them and all the required files to any other machine and - device. +1.11 "Big Script" + means script size exceeds a certain value - Without permission of "licensor" the license purchased for one product - serials should not be used for other product serials. Business users - should purchase new license for different product serials. +1.12 "Pyarmor License" + means a file issued by licensor to unlock this software limitations - A user who purchased a license, is granted a non-exclusive right to use - the software on as many computers as defined by the licensing terms above - according to the number of licenses purchased, for any legal purpose. +1.13 "Basic License" + means a kind of Pyarmor License - In any case, the software is only used to obfuscate the Python scripts - owned by the authorized person or enterprise. +1.13 "Pro License" + means a kind of Pyarmor License - 4. There are no additional license fees, apart from the cost of the license, - associated with the creation and distribution of obfuscated python scripts. - Owners of a license may use their copies of the software to produce - obfuscated python scripts and to distribute those files free of any - additional royalties. +1.14 "Group License" + means a kind of Pyarmor License - 5. To buy a license, please run command +1.15 "License No." + means a string with format "pyarmor-vax-xxxxxx", "x" stands for a + digital, each Pyarmor License has an unique License No. - pyarmor register --buy +1.16 "Licensed Product" + means product has been registered to one Pyarmor License. - Or open the following url in any web browser +2. License Grants and Conditions +-------------------------------- - https://order.shareit.com/cart/add?vendorid=200089125&PRODUCT[300871197]=1 +Installing and using this software signifies acceptance of these terms and +conditions of the license. If you do not agree with the terms of this +license, you must remove all of this software files from your storage +devices and cease to use this software. - For personal license, please fill the registeration name with real name - when placing an order. +2.1. Trial Version Limitations - For enterprise license, please fill the registeration name with enterprise - name, and also fill "License To Product" with the product name which will - use this software. +This software is published as shareware, free trial version never expires, +but there are some limitations. - A registration file generally named "pyarmor-regcode-xxxx.txt" will be sent - by email immediately after payment is completed successfully. +2.1.1 Can not obfuscate big script +2.1.2 Can not use feature Mix Str +2.1.3 Can not use feature RFT Mode, BCC Mode +2.1.4 Can not be used to obfuscate any commercial product. If the total sale + income of this product is less than 100 x license fees, this software + could be used temporarily. +2.1.5 Can not be used to provide obfuscation service in any form, in short + this software can't be used to obfuscate the scripts of others - Save it to disk, then run the following command to register PyArmor +2.2. Grants - pyarmor register /path/to/pyarmor-regcode-xxxx.txt +User purchases Pyarmor License to unlock these limitations except 2.1.5. - Check the registration information: +Licensor issues 3 kind of Pyarmor License. - pyarmor register +2.2.1 Pyarmor Basic, unlock limitations 2.1.1, 2.1.2, 2.1.4 +2.2.2 Pyarmor Pro, unlock limitations 2.1.1, 2.1.2, 2.1.3, 2.1.4 +2.2.3 Pyarmor Group, unlock limitations 2.1.1, 2.1.2, 2.1.3, 2.1.4 - After registration successfully, remove all the obfuscated scripts by trial - version, then obfuscate them again. +Pyarmor Basic and Pyarmor Pro need internet connection to verify, it doesn't +work in the device without internet connection. - The registration code is valid forever, it can be used permanently. But it - may not be rented or leased. +Pyarmor Pro can not be used in CI/CD pipeline. - 6. The software's free version may be freely distributed, with exceptions - noted below, provided the distribution package is not modified in any way. +Pyarmor Group need not internet connection. - a. Nobody may distribute separate parts of the package, without written - permission. +Each Pyarmor License only need pay once, not periodically. - b. The software's unlicensed free version may not be distributed inside of - any other software package without written permission. The software - must remain in the original unmodified installation file for download - without any barrier and conditions to the user such as collecting fees - for the download or making the download conditional on the user giving - his contact data. +2.3 Conditions - c. The unmodified installation file of PyArmor must be provided pure and - unpaired. Any bundling is interdicted. In particular the use of any - install or download software which is providing any kind of download - bundles is prohibited unless granted by Jondy Zhao written form. +2.3.1 Each Pyarmor License can only be used to register One Product. Each + Licensed Product has an unique License No. in global. If user has many + products, each product need purchase one Pyarmor License. Except 2.6.1 - d. Hacks/cracks, keys or key generators may not be included, pointed to - or referred to by the distributor of the free version. +2.3.2 Pyarmor License could be installed in limited machines and devices which + used to develop, build, debug, test and support Licensed Product. - e. In case of violation of the precedent conditions the allowance - lapses immediately and automatically. +2.3.3 Pyarmor Basic and Pro License can only be used in no more than 100 + devices. Pyarmor License be used means use any feature of Pyarmor in + one machine. Running obfuscated scripts generated by Pyarmor is not + considered as Pyarmor License be used. - 7. The software is distributed "as is". No warranty of any kind is expressed - or implied. You use at your own risk. Neither the author, the licensor - nor the agents of the licensor will be liable for data loss, damages, - loss of profits or any other kind of loss while using or misusing - this software. +2.3.4 Pyarmor Basic and Pro License need internet connection to verify. - 8. The dynamic library of the software may not be used for reverse engineer to - re-create the PyArmor obfuscated algorithm. +2.3.5 Pyarmor License could not be installed in customer's devices. - 9. The licensor shall be responsible for interpretation of the - agreement. Anytime the licensor made any modifications to the agreement, - the modified version shall be applicable to the user automatically. +2.3.6 Pyarmor License could not be used to obfuscate any other scripts. - 10. Installing and using the software signifies acceptance of these terms - and conditions of the license. If you do not agree with the terms of this - license, you must remove all software files from your storage devices - and cease to use the software. +2.3.7 Pyarmor License could not be transferred to other product. + +2.4 Special Cases + +2.4.1 When product name is changed, but the product features are same, + Pyarmor License could be used without changing registration product + name. + +2.4.2 When organization user is renamed, or acquired by others, only product + name is not changed, Pyarmor License need change nothing, and still + could be used. But if product name is changed, even product functions + are same, Pyarmor License can't be used again. + +2.4.3 When product in developing and its name is not defined, the initial + registration could use "TBD" as the product name. In this case, + product name can be changed once. Before this product is sold, user + must change registration name to real product name. + +2.5 Modifying Software + +User could modify Python scripts of this software to meet needs. But this +modified software could only be used in Licensed Product, it could not be +distributed to others. + +2.6 Fair Use + +This License is not intended to limit any rights users have under applicable +copyright doctrines of fair use, fair dealing, or other equivalents. + +2.6.1 If user has many products, and has purchased one license for the first + product. The second product could use first product license only if sale + income of the second product less than 100x license fees. Once greater + than 100x license fees, the second product need purchase its own license. + It's same to user's other products. + +3. Responsibilities and Commitments +----------------------------------- + +3.1 This software does nothing except the features described in the software + documentation. + +3.2 Using Pyarmor Basic and Pro License, this software need internet + connection to request authorization. And only the related files of + Pyarmor License, serial number of hard disk, Ethernet address, IPv4/IPv6 + address, hostname will be sent to remote server for verification. + +3.3 Except 3.2, this software does not record and collect any other device + information, and need not connect to internet. + +3.4 Regarding to obfuscated scripts generated by this software + +3.4.1 this software has no any control or limitation to obfuscated scripts, + the behaviors of obfuscated scripts are totally defined by user. + +3.4.2 License No. and product name will be embedded into obfuscated scripts, + user's regname, email and other information are not. + +4. Termination +-------------- + +The rights granted under this License will terminate automatically if You +fail to comply with any of its terms. + +4.1 You issue chargeback after purchased Pyarmor License has been registered + to one product. Even chargeback is rejected by bank or user cancels + chargeback, this Pyarmor License can't be used again. + +4.2 You let others got Pyarmor License intentionally or unintentionally + +4.3 You lost Pyarmor License by hacker + +4.4 In any cases include 4.2, 4.3, once licensor find too many machines use + one Pyarmor License exceeds permission, this Pyarmor License will be + ceased, and never could be used again. + +4.5 In any cases if licensor finds one Pyarmor License is used to other + product, even it's caused by 4.3, this Pyarmor License will be ceased. A + notify email will be sent to registration email of this Pyarmor License. + +4.6 Pyarmor license must be installed in your build enviornments, you can't + install Pyarmor License in customer's device to obfuscate your customer's + scripts + +5. Restrictions +--------------- + +You shall not (and shall not allow any third party to): + +5.1 decompile, disassemble, or otherwise reverse engineer any binary files + of this software; + +5.2 distribute, sell, sublicense, rent, lease, or use this software (or any + portion thereof) for time sharing, hosting, service provider, or like + purposes; + +5.3 remove any product identification, proprietary, copyright, or other + notices contained in this software; + +5.4 copy, modify (except as expressly permitted in this Agreement) or + translate any part of this software, create a derivative work of any + part of this software, or incorporate this software into or with other + software, except to the extent expressly authorized in writing by + licensor; + +5.5 attempt to circumvent or disable the security key mechanism that + protects this software against unauthorized use (except and only to the + extent that applicable law prohibits or restricts such restrictions); + +6. Disclaimer of Warranty +------------------------- + +This Software is provided under this License on an "as is" basis, without +warranty of any kind, either expressed, implied, or statutory, including, +without limitation, warranties that this software is free of defects, fit +for a particular purpose or non-infringing. The entire risk as to the +quality and performance of this software is with users. Neither the licensor +nor the agents of the licensor will be liable for data loss, damages, loss +of profits or any other kind of loss while using or misusing this +software. This disclaimer of warranty constitutes an essential part of this +License. No use of this software is authorized under this License except +under this disclaimer. + +7. Changes to this Agreement +---------------------------- + +Licensor reserves the right, at its sole discretion, to modify or replace +this Agreement at any time. If a revision is material licensor will provide +at least 30 days' notice prior to any new terms taking effect. What +constitutes a material change will be determined at the sole discretion of +the licensor. + +By continuing to access or use this software after any revisions become +effective, You agree to be bound by the revised terms. If You do not agree +to the new terms, You are no longer authorized to use this software. + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the courts of +a jurisdiction where the defendant maintains its principal place of business +and such litigation shall be governed by laws of that jurisdiction, without +reference to its conflict-of-law provisions. Nothing in this Section shall +prevent a party's ability to bring cross-claims or counter-claims. diff --git a/LICENSE-ZH b/LICENSE-ZH index e32e9ed7..e86b8122 100755 --- a/LICENSE-ZH +++ b/LICENSE-ZH @@ -1,102 +1,285 @@ - PyArmor 最终用户许可协议 +Pyarmor 8.0 最终用户许可协议 - 以下关于 PyArmor(下称“本软件”)的协议在 赵俊德(下称“许可人”)和任何安装、 - 访问或以其它方式使用本软件的自然人(下称“用户”)之间订立。 +重要!请仔细阅读本协议。下载、安装或使用整个软件或其任何部分,即表示您接受本协议 +的所有条款和条件。您同意本协议与任何书面协议一样具有法律效力。 - 1. 本软件的作者及版权持有人为 赵俊德。 +如果您不同意所有这些条款和条件,请不要使用或访问该软件。如果您已经支付了使用该软 +件的许可费,但不同意这些条款,您可以退还该软件以获得全额退款,但前提是您 (A) 未 +使用该软件,并且 (B) 在初次购买该软件的三十 (30) 天内退还该软件。 - 2. 本软件可以自由试用但是功能上有一定限制: +如果您同意这些条款和条件,即表示您确认自己已年满或超过 18 岁并且具有足够的民事行 +为能力签订在法律上有约束力的协议。 - a. 试用版本可以加密的脚本大小有限制,超过限制的脚本无法进行加密。 +以下协议在 Pyarmor 开发者 赵俊德(下称“许可人”)和任何安装、访问或以其它方式使用 +本软件的自然人或者团体机构(下称“用户”)之间订立。 - b. 在试用版中中生成的加密脚本不是私有的,也就是说,其他任何人也可以为这些加 - 密脚本生成新的许可文件。 +1. 概念和定义 - c. 试用版本不能下载其他平台最新版本的动态库,以前版本的动态库依旧可以使用。 +本软件 +下文中特指 Pyarmor 8.0 之后的版本,不包含 Pyarmor 8.0 之前的版本。虽然为了兼 +容性,Pyarmor 8.0 中包含老版本中功能,但是本协议中的许可协议并不适用于部分功能。 - d. 终极加密模式(SPP)在试用版本中不可用。 +产品 +下文中特指的是部分或者全部使用 Python 脚本实现功能的软件,该软件同时还通过销售进 +行盈利,除非特别说明,下文中的产品不包含非盈利的任何软件。 - e. 任何人都可以使用本软件加密非商业用途的Python脚本,未经许可不得用于商业用 - 途。 +一种产品 +一种产品在本协议中指的是独立销售的一个软件的所有组成部分,包括开发需要的各种设备, +以及提供支持的服务器,云服务器等。一种产品也包括产品的当前版本,历史版本,以及将 +来的升级版本。一种产品也包括基础功能相同,组合不同特殊功能而形成的不同版本的产品, +这种产品的特征是不同版本对外销售名称一样,只是通过辅助名称等来进行区分。 - 3. 本软件有两种基本类型的许可方式: +用户 +使用本软件的个人或者组织机构。 - a. 个人用户许可,适用于产品的所有权为个人所有。个人用户购买一个许可证可以在 - 自己所有的计算机和相关硬件设备上使用。购买这种类型的许可证的时候,注册名 - 称填写个人的真实姓名,本产品只授权于注册名称对应的个人使用。 +客户 +在本文中特指的是使用用户产品的个人或者组织机构。 - 个人用户许可证允许使用本软件加密任何属于自己的 Python 脚本,为加密脚本生 - 成私有许可文件,发布加密后的脚本和必要的辅助文件到任何其他设备。 +用户产品 +用户拥有完全或者部分产权的产品。 - 个人用户许可证不允许加密产权属于法人(公司)的 Python 脚本。 +其它产品 +产权不属于用户的产品。 - b. 企业用户许可,适用于产品的所有权为法人(公司)所有。企业用户购买一个软件 - 许可证可以在同一个产品系列的各个项目中使用。购买这种类型的许可证的时候, - 注册名称填写机构名称以及产品名称,例如,“西安德新软件的易科系统”,本软件 - 只授权于注册名称对应的产品使用。 +脚本 +下文中特指的 Python 脚本文件。 - 产品系列包括同一个产品升级之后的所有版本。 +加密脚本 +下文中指的是使用本软件加密之后输出的脚本。 - 企业用户许可证允许使用本软件在任何设备上,加密属于该产品系列的 Python 脚 - 本,为加密脚本生成私有许可文件,发布加密后的脚本和必要的辅助文件到任何其 - 他设备。 +用户脚本 +用户拥有完全或者部分产权的脚本。 - 除非有许可人的许可,否则企业用户许可证不可以用于其他的产品系列。如果需要 - 在其他产品系列中使用,必须为其他产品单独购买软件许可。 +其它脚本 +用户不具备任何产权的脚本 - 不管那一种许可方式,本软件都只可用于保护产品本身,不允许应用于产权不属于被 - 授权人的 Python 脚本。 +许可证 +下文中指的是由许可人生成并发送给用户的特殊格式的文件,本软件会根据该文件来决定某 +些功能是否可用 - 4. 除了购买软件许可的费用之外,没有其他任何费用。获得软件许可的用户可以使用本软 - 件在许可的范围之内加密任何Python脚本并自由发布,不需要在向许可人支付任何费用。 +激活许可证 +指的是用户第一次使用许可证运行本软件,称之为激活许可证。激活之后的状态称之为许可 +证已经激活;如果许可证从来没有被用来运行本软件,那么称之为许可证没有被激活。 - 5. 购买软件许可,可以通过下面任意一个链接的电子商务网站 +停用许可证 +指的是许可人在认证服务器对某一个许可证取消授权,取消授权之后的许可证等同于没有许 +可证,该许可证授予的相关功能无法在继续使用。 - https://order.shareit.com/cart/add?vendorid=200089125&PRODUCT[300871197]=1 - https://pyarmor.dashingsoft.com/cart/order.html +1.1 本软件的功能定义 - 对于类型为个人用户的许可,注册名称需要填写正确的姓名。 +基本加密功能 +是指没有使用任何选项的加密功能。 - 对于类型为企业用户的许可,除了注册名称需要填写正确的企业名称之外,还需要填 - 写被授权的产品名称,如果仅在企业内部使用,不会用于任何被销售的产品,可以填 - 写“内部使用”。 +JIT 保护 +是指使用动态代码生成机制对加密脚本进行保护的功能。 - 支付成功之后一个名为 "pyarmor-regcode-xxxx.txt" 的注册文件会自动通过电子邮 - 件发送过去,把注册文件保存到磁盘,然后使用下面的命令进行注册 +Themedia 保护 +是指使用第三方工具 Themedia 对 Widnows 动态库进行保护的功能。 - pyarmor register pyarmor-regcode-xxxx.txt +Assert 保护 +是指保护加密脚本不会被替换或者非法注入的保护功能。 - 运行下面的命令查看注册信息 +设置脚本有效期 +是指能够限制加密脚本运行有效期的功能。 - pyarmor register +绑定加密脚本到设备 +是指能够限制加密脚本运行在指定设备的功能。 - 注册成功之后,请彻底删除使用试用版本生成的所有文件,然后重新进行加密。 +混淆字符串功能 +是指对脚本中的字符串常量进行混淆保护的功能。 - 软件注册码永久有效,可以一直使用,但是不能转接或者租用。 +RFT 加密模式 +是指通过重命名脚本中的函数,类,方法和变量的名称来保护脚本的功能。 - 6. 本软件可免费分发(除下列例外),分发前提为分发包未以任何形式被修改: +BCC 加密模式 +是指把 Python 脚本中部分函数转换成为对应的 C 函数,通过编译直接生成机器指令代码, +从而对脚本进行保护的功能。 - a) 任何人未经书面许可的情况下,不得再分发软件包的任何单独部分。 +2. 本软件的许可模式 - b) 在未经书面许可的情况下,本软件不得在任何其它软件包中发布。本软件必须保持 - 以未经修改的原始安装文件而供下载,而不得对用户附带任何障碍或条件,例如收 - 取下载费用,或以用户提供联系信息为前提来提供下载。 +2.1 试用版本 - c) 本软件未经修改的安装文件必须以纯净而独立的形式提供。禁止任何形式的捆绑。 - 尤其禁止使用任何安装或下载软件来提供任何下载捆绑,除非获得书面形式同意。 +下载和安装本软件表示自动接受试用许可协议,试用版本有如下的限制 - d) 本软件的分发者不得包含、指向或引用黑客/破解、注册文件及注册文件生成器。 +(1) 加密功能对脚本大小有限制,不能加密超过限制的大脚本。 +(2) 混淆字符串功能在试用版中无法使用。 +(3) RFT 加密模式,BCC 加密模式在试用版无法使用。 +(4) 不可以应用于加密商用产品。特别的,如果商用产品的累计销售额小于基础版许 + 可证费用乘以 100,可以暂时使用;但是一旦累计销售额超过阀值,就不可以在 + 继续使用。 +(5) 运行辅助包的名称 "pyarmor_runtime_000000" 不可以被设置和修改 +(6) 不可以使用本软件提供任何形式的加密服务,不管是通过应用程序还是网络服务。 + 总之在任何情况下都不允许使用本软件加密其他人的脚本。 +(7) 不支持 obf-code 大于 1 的任何加密模式 - e) 违反上述条件的情况下,许可自动立即失效。 +试用版本中功能限制,需要通过许可授权来解锁相关功能。 - 7. 本软件“按原样”发布。不提供任何明示或暗示的担保。您的使用需要自己承担风险。无 - 论作者、许可人或许可人的经销商,均不对使用或误用本软件时发生的数据丢失、损坏、 - 利润损失或其它任何形式的损失而负责。 +2.2 许可授权 - 8. 本软件的二进制代码不得被进行反向工程来重新创建本软件专用的加密算法。 +许可授权需要通过购买相应的许可证来获取,购买许可证可以通过指定的网站购买。 - 9. 本协议由许可人负责解释。任何时候许可人对本协议做出任何修改,修改版本自动适用 - 于用户。 +每一个许可证都有一个 18 位字符长度唯一的编号,并授权给有且只有一种产品使用。也就 +是说,任何一种使用本软件进行保护的产品都有自己唯一的许可证编号,不允许两种不同产 +品使用相同的许可证编号。 - 10. 安装并使用本软件意味着接受本许可的这些条款和条件。如果您不同意本许可的条款,您必 - 须从您的存储设备中删除本软件全部文件并终止使用本软件。 +本软件提供三种许可证,分别解锁不同的功能 + +2.2.1 基础版许可证 + +基础版许可证解锁限制 2.1 中的 (1) (2) (4) (5) (7). + +基础版许可证加密脚本的时候需要在线验证许可证 + +2.2.2 专家版许可证 + +专家版许可证解锁限制 2.1 中的 (1) (2) (3) (4) (5) (7). + +专家版许可证加密脚本的时候需要在线验证许可证 + +2.2.3 集团版许可证 + +集团版许可证解锁限制 2.1 中的 (1) (2) (3) (4) (5) (7). + +集团版许可证加密脚本的时候不需要在线验证许可证 + +不管哪一种许可证,运行加密脚本的时候都无需验证许可证,本软件对于加密脚本的运行没 +有任何控制和限制。 + +2.3 购买和退款 + +除了购买软件许可的费用之外,没有其它任何费用。获得许可的用户可以使用本软件在许可 +的范围之内加密任何脚本并自由发布,不需要在向许可人支付任何费用。 + +购买软件许可的费用是一次性收费,可以永久在购买本软件时候的版本中使用,但是许可证 +可能在任何一个升级版本中失效,许可人不承诺许可证可以在今后所有的升级版本中使用。 + +一旦激活许可证之后,不在支持退款,购买之后没有激活许可证,在三十天之内支持取消许 +可证并退款。但是如果购买时间超过三十天,没有激活的许可证也不再支持退款。 + +2.4 合理使用原则 + +如果用户有多个产品并且已经为第一个产品购买许可证,其他产品满足下列条件可以使用第 +一个产品的许可证: + +(1) 如果该产品的销售收入在当前许可证费用的 100 倍之内,那么该产品可以使用第一个 + 产品的许可证。 + +(2) 如果该产品的销售输入超过当前许可证费用的 100 倍,那么该产品不可以继续使用 + 第一个产品的许可证,需要单独购买新的许可证。 + +本许可证并不旨在限制用户根据适用条款享有的任何权利、公平交易或其他等效的合理使用。 + +3. 许可人的承诺 + +3.1 对于在开发设备运行本软件进行加密 + +本软件没有任何后门以及和加密无关的功能代码,本软件在加密过程中不会记录和访问无关 +的设备数据和信息,下面列出的资源除外: + +(1) 基础版和专家版许可模式下使用本软件需要通过互联网进行许可认证 +(2) 本软件会访问设备名称,硬盘序列号,以太网卡地址,IPv4/IPv6 地址,并且仅用于 + 许可认证 + +3.2 对于在客户设备运行的加密脚本 + +(1) 本软件对于加密脚本的运行没有任何控制和限制,加密脚本的运行限制完全由用户控制 +(2) 除非加密脚本设置了网络时间的有效期,否则本软件不会访问互联网 +(3) 除非加密脚本设置了绑定到设备,否则本软件不会访问任何设备和系统信息 +(4) 许可证编号和被授权的产品名称会嵌入到加密脚本中,除此之外,加密脚本中没有任何用 + 户相关的注册信息,例如注册名称和邮箱等。 + +4. 使用本软件的限制和约束 + +4.1 本软件只能用于加密用户脚本,不能以任何方式加密其它脚本,包含但不限于如下方式是 + 许可禁止的行为 + + (1) 在用户发布的产品中包含本软件自身去加密其他人的脚本 + (2) 在服务器上运行本软件,通过网络向其它人提供基于本软件的加密脚本服务 + (3) 接受其它人的请求,在自己的设备上加密其他人的脚本 + +4.2 用户可以修改本软件相关的 Python 脚本以满足使用方面的额外需求,但是修改后的脚 + 本只能在许可证范围内使用,不得分发给其它人 + +4.3 同一个许可下面,使用本软件的设备数目有限制。这里的设备是指安装本软件并使用本 + 软件对脚本进行加密的设备,不是指运行加密脚本的客户机器。 + +4.4 每一个许可证绑定到一种产品,不可以转移给其它产品。 + +4.5 用户注册信箱被盗用视同用户自己使用。 + +4.6 本软件只能安装在用户的开发环境,只能用于加密客户的脚本,不能安装在客户设备或者 + 生产环境去加密客户的脚本 + +5. 导致许可证被停用的行为 + +5.1 使用 Paypal,信用卡等网络支付的用户,一旦在激活许可证之后进行撤单,该许可证 + 会被永久停用。即便撤单行为失败或者用户主动取消撤单,该许可证也无法在重新使用。 + +5.2 用户丢失许可,许可人在发现使用设备数量显著超过许可之后,可以在不通知用户的情 + 况下停用该许可证。包含但不限于下列方式 + + (1) 用户主动把许可文件提供给其他用户 + (2) 用户保管不妥造成许可文件无意被其它人获取 + (3) 用户数据被黑客窃取 + +5.3 当许可人发现许可证的注册产品和实际的用户产品不一致的时候,可以在不通知用户的 + 情况下停用该许可证。 + + 但是在下列情况下,即便实际的产品名称和许可证绑定的产品名称不一致,该许可证可 + 以继续合法使用,不会被停用: + + (1) 公司被收购或者更改名称,只要产品名称不发生变化,无需修改注册名称,可以继 + 续合法使用在原来的产品 + + (2) 产品名称发生变化,只要产品能很明显的证明和原来的产品是同一个,可以继续合 + 法使用许可证在修改名称后的产品。如果不能很明显的证明,需要购买新的许可证。 + + (3) 对于开发早期产品名称没有确定的情况,本软件允许在第一次许可登记的时候使用 + 三个英文字符 “TBD“ 来指定产品名称,允许在其后修改为真正的产品名称。对于产 + 品名称为 “TBD“ 的许可证,一旦开始销售,必须修改为真正的产品名称,否则也会 + 被认为是非授权使用。 + +6. 许可限制 + +用户不得(且不得允许任何第三方): + +6.1 以任何方式对本软件的二进制文件进行反编译、反汇编或逆向工程; + +6.2 分发、出售、转让许可、出租、租赁该软件(或其任何部分),也不得将其用于分时、 + 托管、服务提供商等目的; + +6.3 去除软件中包含的任何产品标识、专有权、版权或其他通知; + +6.4 复制、修改(本协议中明确允许的除外),创建软件任何部分的衍生作品,或者将该软 + 件并入其他软件或与之合并,有许可人以书面形式明确授权的除外; + +6.5 尝试规避或禁用防止擅自使用该软件的安全密钥机制(除非且仅当适用的法律禁止或限 + 制此类限制)。 + +7. 担保免责声明 + +本软件和所有服务均“按原样”提供。许可人不做任何其他保证、条件或承诺(无论是明示还 +是默示的,是法定还是其他形式的),包括但不限于有关所有权、适销性、针对特定目的的 +适用性或不侵权的保证。许可人不提供以下保证: + +(1) 该软件符合您的要求; +(2) 该软件不含错误或缺陷; +(3) 该软件的安全性、可靠性、及时性或性能达到特定水平; +(4) 该软件中的任何错误都将得到纠正; +(5) 该软件可产生特定结果或输出。 + +8. 对高风险活动的免责声明 + +本软件不具有容错性,其设计、制造或预定用途并非为了生命援助、医疗、急救、关键任务 +或其他严格责任或危险活动(以下简称“高风险活动”)。许可人特此否认对高风险活动的适 +用性的任何明示或默示的保证。 + +9. 遵守法律 + +在使用该软件以及从软件产生的任何结果时,用户有责任遵守所有适用的法律、法规和业务 +守则。 + +10. 协议的修订和解释 + +本协议由许可人负责解释。任何时候许可人对本协议做出任何修改,修改版本自动适用于用 +户。 diff --git a/LICENSE-ZH.7 b/LICENSE-ZH.7 new file mode 100755 index 00000000..da1de705 --- /dev/null +++ b/LICENSE-ZH.7 @@ -0,0 +1,102 @@ + PyArmor 最终用户许可协议 + + 以下关于 PyArmor(下称“本软件”)的协议在 赵俊德(下称“许可人”)和任何安装、 + 访问或以其它方式使用本软件的自然人(下称“用户”)之间订立。 + + 1. 本软件的作者及版权持有人为 赵俊德。 + + 2. 本软件可以自由试用但是功能上有一定限制: + + a. 试用版本可以加密的脚本大小有限制,超过限制的脚本无法进行加密。 + + b. 在试用版中中生成的加密脚本不是私有的,也就是说,其他任何人也可以为这些加 + 密脚本生成新的许可文件。 + + c. 试用版本不能下载其他平台最新版本的动态库,以前版本的动态库依旧可以使用。 + + d. 终极加密模式(SPP)在试用版本中不可用。 + + e. 任何人都可以使用本软件加密非商业用途的Python脚本,未经许可不得用于商业用 + 途。 + + 3. 本软件有两种基本类型的许可方式: + + a. 个人用户许可,适用于产品的所有权为个人所有。个人用户购买一个许可证可以在 + 自己所有的计算机和相关硬件设备上使用。购买这种类型的许可证的时候,注册名 + 称填写个人的真实姓名,本产品只授权于注册名称对应的个人使用。 + + 个人用户许可证允许使用本软件加密任何属于自己的 Python 脚本,为加密脚本生 + 成私有许可文件,发布加密后的脚本和必要的辅助文件到任何其他设备。 + + 个人用户许可证不允许加密产权属于法人(公司)的 Python 脚本。 + + b. 企业用户许可,适用于产品的所有权为法人(公司)所有。企业用户购买一个软件 + 许可证可以在同一个产品的各个项目中使用。购买这种类型的许可证的时候,注册 + 名称填写机构名称以及产品名称,例如,“西安德新软件的易科系统”,本软件只授 + 权于注册名称对应的产品使用。 + + 同一个产品包括产品升级之后的所有版本。 + + 企业用户许可证允许使用本软件在任何设备上,加密属于该产品系列的 Python 脚 + 本,为加密脚本生成私有许可文件,发布加密后的脚本和必要的辅助文件到任何其 + 他设备。 + + 除非有许可人的许可,否则企业用户许可证不可以用于其他的产品。如果需要在其 + 他产品中使用,必须为其他产品单独购买软件许可。 + + 不管那一种许可方式,本软件都只可用于保护产品本身,不允许应用于产权不属于被 + 授权人的 Python 脚本。 + + 4. 除了购买软件许可的费用之外,没有其他任何费用。获得软件许可的用户可以使用本软 + 件在许可的范围之内加密任何Python脚本并自由发布,不需要在向许可人支付任何费用。 + + 5. 购买软件许可,可以通过下面任意一个链接的电子商务网站 + + https://order.shareit.com/cart/add?vendorid=200089125&PRODUCT[300871197]=1 + https://pyarmor.dashingsoft.com/cart/order.html + + 对于类型为个人用户的许可,注册名称需要填写正确的姓名。 + + 对于类型为企业用户的许可,除了注册名称需要填写正确的企业名称之外,还需要填 + 写被授权的产品名称,如果仅在企业内部使用,不会用于任何被销售的产品,可以填 + 写“内部使用”。 + + 支付成功之后一个名为 "pyarmor-regcode-xxxx.txt" 的注册文件会自动通过电子邮 + 件发送过去,把注册文件保存到磁盘,然后使用下面的命令进行注册 + + pyarmor register pyarmor-regcode-xxxx.txt + + 运行下面的命令查看注册信息 + + pyarmor register + + 注册成功之后,请彻底删除使用试用版本生成的所有文件,然后重新进行加密。 + + 软件注册码永久有效,可以一直使用,但是不能转接或者租用。 + + 6. 本软件可免费分发(除下列例外),分发前提为分发包未以任何形式被修改: + + a) 任何人未经书面许可的情况下,不得再分发软件包的任何单独部分。 + + b) 在未经书面许可的情况下,本软件不得在任何其它软件包中发布。本软件必须保持 + 以未经修改的原始安装文件而供下载,而不得对用户附带任何障碍或条件,例如收 + 取下载费用,或以用户提供联系信息为前提来提供下载。 + + c) 本软件未经修改的安装文件必须以纯净而独立的形式提供。禁止任何形式的捆绑。 + 尤其禁止使用任何安装或下载软件来提供任何下载捆绑,除非获得书面形式同意。 + + d) 本软件的分发者不得包含、指向或引用黑客/破解、注册文件及注册文件生成器。 + + e) 违反上述条件的情况下,许可自动立即失效。 + + 7. 本软件“按原样”发布。不提供任何明示或暗示的担保。您的使用需要自己承担风险。无 + 论作者、许可人或许可人的经销商,均不对使用或误用本软件时发生的数据丢失、损坏、 + 利润损失或其它任何形式的损失而负责。 + + 8. 本软件的二进制代码不得被进行反向工程来重新创建本软件专用的加密算法。 + + 9. 本协议由许可人负责解释。任何时候许可人对本协议做出任何修改,修改版本自动适用 + 于用户。 + + 10. 安装并使用本软件意味着接受本许可的这些条款和条件。如果您不同意本许可的条款,您必 + 须从您的存储设备中删除本软件全部文件并终止使用本软件。 diff --git a/LICENSE.7 b/LICENSE.7 new file mode 100755 index 00000000..2a289245 --- /dev/null +++ b/LICENSE.7 @@ -0,0 +1,145 @@ + END USER LICENSE AGREEMENT + + The following agreement regarding PyArmor - referred to as "software" - is + made between Jondy Zhao - referred to as "licensor" - and anyone who is + installing, accessing or in any other way using the software - referred to + as "user". + + 1. The author and holder of the copyright of the software is Jondy Zhao. + + 2. The software is distributed as Free To Use But Restricted: + + a. The trial version could not obfuscate the big scripts. + + b. The scripts obfuscated by trial version are not private. It + means anyone could generate the license file which works for + these obfuscated scripts. + + c. The trial version could not download the latest dynamic library + of extra platforms, the old versions still are available. + + d. The super plus mode is not availaible in the trial version. + + e. Without permission the trial version may not be used for the Python + scripts of any commercial product. + + 3. There are 2 basic types of licenses issued for the software. These are: + + a. A personal license for home users. The user purchases one license to use + the software on his own computer. When placing an order of this kind of + license, please fill real name as registration name, this software is + only authorized to this registration name. + + Home users may use their personal license to obfuscate all the python + scripts which are property of the license owner, to generate private + license files for the obfuscated scripts and distribute them and all the + required files to any other machine or device. + + Home users could NOT obfuscate any python script which is NOT property + of the license owner. + + b. A enterprise license for business users. The user purchases one license + to use the software for one product of an organization. When placing an + order of this kind of license, please fill orginization name plus + product name, this software is only authorized to this registration + name. + + One product include the current version and any other latter versions of + the same product. + + Business users may use their enterprise license on all computers and + embedded devices to obfuscate all the python scripts of this product, to + generate private license files for these obfuscated scripts and + distribute them and all the required files to any other machine and + device. + + Without permission of "licensor" the license purchased for one product + should not be used for other product. Business users should purchase new + license for different product. + + A user who purchased a license, is granted a non-exclusive right to use + the software on as many computers as defined by the licensing terms above + according to the number of licenses purchased, for any legal purpose. + + In any case, the software is only used to obfuscate the Python scripts + owned by the authorized person or enterprise. + + 4. There are no additional license fees, apart from the cost of the license, + associated with the creation and distribution of obfuscated python scripts. + Owners of a license may use their copies of the software to produce + obfuscated python scripts and to distribute those files free of any + additional royalties. + + 5. To buy a license, please run command + + pyarmor register --buy + + Or open the following url in any web browser + + https://order.shareit.com/cart/add?vendorid=200089125&PRODUCT[300871197]=1 + + For personal license, please fill the registeration name with real name + when placing an order. + + For enterprise license, please fill the registeration name with enterprise + name, and also fill "License To Product" with the product name which will + use this software. + + A registration file generally named "pyarmor-regcode-xxxx.txt" will be sent + by email immediately after payment is completed successfully. + + Save it to disk, then run the following command to register PyArmor + + pyarmor register /path/to/pyarmor-regcode-xxxx.txt + + Check the registration information: + + pyarmor register + + After registration successfully, remove all the obfuscated scripts by trial + version, then obfuscate them again. + + The registration code is valid forever, it can be used permanently. But it + may not be rented or leased. + + 6. The software's free version may be freely distributed, with exceptions + noted below, provided the distribution package is not modified in any way. + + a. Nobody may distribute separate parts of the package, without written + permission. + + b. The software's unlicensed free version may not be distributed inside of + any other software package without written permission. The software + must remain in the original unmodified installation file for download + without any barrier and conditions to the user such as collecting fees + for the download or making the download conditional on the user giving + his contact data. + + c. The unmodified installation file of PyArmor must be provided pure and + unpaired. Any bundling is interdicted. In particular the use of any + install or download software which is providing any kind of download + bundles is prohibited unless granted by Jondy Zhao written form. + + d. Hacks/cracks, keys or key generators may not be included, pointed to + or referred to by the distributor of the free version. + + e. In case of violation of the precedent conditions the allowance + lapses immediately and automatically. + + 7. The software is distributed "as is". No warranty of any kind is expressed + or implied. You use at your own risk. Neither the author, the licensor + nor the agents of the licensor will be liable for data loss, damages, + loss of profits or any other kind of loss while using or misusing + this software. + + 8. The dynamic library of the software may not be used for reverse engineer to + re-create the PyArmor obfuscated algorithm. + + 9. The licensor shall be responsible for interpretation of the + agreement. Anytime the licensor made any modifications to the agreement, + the modified version shall be applicable to the user automatically. + + 10. Installing and using the software signifies acceptance of these terms + and conditions of the license. If you do not agree with the terms of this + license, you must remove all software files from your storage devices + and cease to use the software. diff --git a/README.md b/README.md index cab6f0d1..b5cc188b 100644 --- a/README.md +++ b/README.md @@ -1,92 +1,121 @@ -# PyArmor +# Pyarmor -* [Homepage](https://pyarmor.dashingsoft.com) ([中文版网站](https://pyarmor.dashingsoft.com/index-zh.html)) -* [Documentation](https://pyarmor.readthedocs.io/en/latest/)([中文版](https://pyarmor.readthedocs.io/zh/latest/)) +Pyarmor is a command-line tool designed for obfuscating Python scripts, binding obfuscated scripts to specific machines, and setting expiration dates for obfuscated scripts. -PyArmor is a command line tool used to obfuscate python scripts, bind -obfuscated scripts to fixed machine or expire obfuscated scripts. It -protects Python scripts by the following ways: +## Key Features -* Obfuscate code object to protect constants and literal strings. -* Obfuscate co_code of each function (code object) in runtime. -* Clear f_locals of frame as soon as code object completed execution. -* Verify the license file of obfuscated scripts while running it. +- **Seamless Replacement**: Obfuscated scripts remain as standard `.py` files, allowing them to seamlessly replace the original Python scripts in most cases. +- **Balanced Obfuscation**: Offers multiple ways to obfuscate scripts to balance security and performance. +- **Irreversible Obfuscation**: Renames functions, methods, classes, variables, and arguments. +- **C Function Conversion**: Converts some Python functions to C functions and compiles them into machine instructions using high optimization options for irreversible obfuscation. +- **Script Binding**: Binds obfuscated scripts to specific machines or sets expiration dates for obfuscated scripts. +- **Themida Protection**: Protects obfuscated scripts using Themida (Windows only). -Also refer to [The Security of PyArmor](https://pyarmor.readthedocs.io/en/latest/security.html) +## Supported Platforms -## Support Platforms +- Python 2 and Python 3[^1] +- Windows +- Various Linux distributions, including embedded systems and Raspberry Pi +- Apple Intel and Apple Silicon +- Supported architectures: x86_64, aarch64, armv7, etc.[^2] -- Python 2.7 and Python3.0~Python3.10 -- Prebuilt Platform: win32, win_amd64, linux_i386, linux_x86_64, macosx_x86_64 -- Embedded Platform: Raspberry Pi, Banana Pi, Orange Pi, TS-4600 / TS-7600 and more +For more information, check out the [Pyarmor Environments][encironments]. -Refer to [support platforms](https://pyarmor.readthedocs.io/en/latest/platforms.html) +[^1]: Some features may be exclusive to Python 3. +[^2]: Some features may be exclusive to specific architectures. -## Quick Start +[encironments]: https://pyarmor.readthedocs.io/en/stable/reference/environments.html -Installation +## Quick start - pip install pyarmor +1. **Install Pyarmor**: +```shell +pip install pyarmor +``` -Obfuscate scripts +2. **Obfuscate the `foo.py` script**: +```shell +pyarmor gen foo.py +``` - pyarmor obfuscate foo.py +This command generates an obfuscated script like this at `dist/foo.py`: -Run obfuscated scripts +```python +from pyarmor_runtime import __pyarmor__ +__pyarmor__(__name__, __file__, b'\x28\x83\x20\x58....') +``` - python dist/foo.py +3. **Run the obfuscated script**: +```shell +python dist/foo.py +``` -Pack obfuscated scripts into one bundle +For more information, check out the [getting started tutorial][tutorial]. - pip install pyinstaller - pyarmor pack foo.py +[tutorial]: https://pyarmor.readthedocs.io/en/stable/tutorial/getting-started.html -Obfuscate scripts with an expired license +## License - pyarmor licenses --expired 2018-12-31 r001 - pyarmor obfuscate --with-license licenses/r001/license.lic foo.py +Pyarmor is published as shareware. The free trial version never expires, but has some limitations. -There is also a web-ui package [pyarmor-webui](https://github.com/dashingsoft/pyarmor-webui) +Refer to [Pyarmor licenses][licenses] for information on license types, features, limitations, and purchasing a Pyarmor license. - pip install pyarmor-webui +Please read the [Pyarmor EULA](LICENSE). -Start webui, open web page in browser ([snapshots](https://github.com/dashingsoft/pyarmor-webui/tree/master/snapshots)) +[licenses]: https://pyarmor.readthedocs.io/en/latest/licenses.html - pyarmor-webui +## Getting Help -More usage, refer to +- **[Ask in learning system][askeke] or [look through check list][checklist]** +- **Consult the [Pyarmor Documentation][doc].** +- **Check the [FAQ][faq] for answers to common questions.** +- **Try the documentation [index][genindex] or the [detailed table of contents][mastertoc].** +- **If you still can't find the information you need, see [asking questions on GitHub][asking].** +- **[Report bugs][issues] following the issue template.** +- **For business and security inquiries, send an email to .** -* [Examples](https://pyarmor.readthedocs.io/en/latest/examples.html) -* [Using PyArmor](https://pyarmor.readthedocs.io/en/latest/usage.html) -* [Advanced Usage](https://pyarmor.readthedocs.io/en/latest/advanced.html) -* [Man Page](https://pyarmor.readthedocs.io/en/latest/man.html) -* [Sample Shell Scripts](src/examples/README.md) +There is also one third-party learn platform -## License & Purchase +- **[Ask Pyarmor Guru][gurubase], it is a Pyarmor-focused AI to answer your questions** (not made by Pyarmor Team, the answer doesn't stand for Pyarmor Team's opinion) -PyArmor is published as shareware, free trial version never expires, but there are -some limitations: +## Resources -* The trial version could not obfuscate the big scripts -* The trial version uses same public capsule other than private capsule -* The trial version could not download the latest dynamic library of extra platforms -* The super plus mode is not available in the trial version +* [Website](https://pyarmor.dashingsoft.com) +* [Documentation][doc] +* [Documentation 8.x](https://pyarmor.readthedocs.io/en/v8.5.12/) +* [Documentation 7.x](https://pyarmor.readthedocs.io/en/v7.7/) +* [Pyarmor 9.1 new features](https://eke.dashingsoft.com/pyarmor/docs/en/index.html) +* [Pyarmor Learning System](https://eke.dashingsoft.com/pyarmor/) -For details, refer to [PyArmor License](https://pyarmor.readthedocs.io/en/latest/license.html). +中文资源 -## [Change Logs](docs/change-logs.rst) +* [Pyarmor 网站](https://pyarmor.dashingsoft.com/index-zh.html) +* [Pyarmor 在线文档](https://pyarmor.readthedocs.io/zh/latest/) +* [Pyarmor 8.x 在线文档](https://pyarmor.readthedocs.io/zh/v8.5.12/) +* [Pyarmor 7.x 在线文档](https://pyarmor.readthedocs.io/zh/v7.x/) +* [Pyarmor 9.1 新功能](https://eke.dashingsoft.com/pyarmor/docs/zh/index.html) +* [Pyarmor 学习系统](https://eke.dashingsoft.com/pyarmor/) -It describes the fixed issues, new features, incompatible issues in different -versions. +## Changelog -It's recommended to read this carefully before upgrading pyarmor. +Each major version comes with a separate changelog file, detailing fixed issues, new features, and compatibility issues between different versions. -## [Report issuses](https://github.com/dashingsoft/pyarmor/issues) +Make sure to read the changelog carefully before upgrading Pyarmor: -If there is any question, first check these [questions and -solutions](https://pyarmor.readthedocs.io/en/latest/questions.html), it may help -you solve the problem quickly. +- [Pyarmor 8.x Changelog](docs/ChangeLogs.8) +- [Pyarmor 9.x Changelog](docs/ChangeLogs.9) -If there is no solution, for technical issue, click here to [report an -issue](https://github.com/dashingsoft/pyarmor/issues) according to the issue -template, for business and security issue send email to . +**Full changelogs** at [releases][releases] + +**Upcoming features** at [Pyarmor Release Plan](docs/ReleasePlan.md) + +[releases]: https://github.com/dashingsoft/pyarmor/releases +[faq]: https://pyarmor.readthedocs.io/en/latest/questions.html +[issues]: https://github.com/dashingsoft/pyarmor/issues +[genindex]: https://pyarmor.readthedocs.io/en/stable/genindex.html +[mastertoc]: https://pyarmor.readthedocs.io/en/stable/index.html#table-of-contents +[asking]: https://pyarmor.readthedocs.io/en/latest/questions.html#asking-questions-in-github +[doc]: https://pyarmor.readthedocs.io/ +[gurubase]: https://gurubase.io/g/pyarmor +[askeke]: https://eke.dashingsoft.com/pyarmor/ask +[checklist]: https://pyarmor.readthedocs.io/en/latest/reference/solutions.html diff --git a/docs.7/Makefile b/docs.7/Makefile new file mode 100644 index 00000000..876e4be5 --- /dev/null +++ b/docs.7/Makefile @@ -0,0 +1,216 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# User-friendly check for sphinx-build +ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) +$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) +endif + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " applehelp to make an Apple Help Book" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " xml to make Docutils-native XML files" + @echo " pseudoxml to make pseudoxml-XML files for display purposes" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + @echo " coverage to run coverage check of the documentation (if enabled)" + +.PHONY: clean +clean: + rm -rf $(BUILDDIR)/* + +.PHONY: html +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +.PHONY: dirhtml +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +.PHONY: singlehtml +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +.PHONY: pickle +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +.PHONY: json +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +.PHONY: htmlhelp +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +.PHONY: qthelp +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/PyArmor.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/PyArmor.qhc" + +.PHONY: applehelp +applehelp: + $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp + @echo + @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." + @echo "N.B. You won't be able to view it unless you put it in" \ + "~/Library/Documentation/Help or install it in your application" \ + "bundle." + +.PHONY: devhelp +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/PyArmor" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/PyArmor" + @echo "# devhelp" + +.PHONY: epub +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +.PHONY: latex +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +.PHONY: latexpdf +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +.PHONY: latexpdfja +latexpdfja: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through platex and dvipdfmx..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +.PHONY: text +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +.PHONY: man +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +.PHONY: texinfo +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +.PHONY: info +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +.PHONY: gettext +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +.PHONY: changes +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +.PHONY: linkcheck +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +.PHONY: doctest +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." + +.PHONY: coverage +coverage: + $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage + @echo "Testing of coverage in the sources finished, look at the " \ + "results in $(BUILDDIR)/coverage/python.txt." + +.PHONY: xml +xml: + $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml + @echo + @echo "Build finished. The XML files are in $(BUILDDIR)/xml." + +.PHONY: pseudoxml +pseudoxml: + $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml + @echo + @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." diff --git a/docs.7/_common_definitions.txt b/docs.7/_common_definitions.txt new file mode 100644 index 00000000..a8eed720 --- /dev/null +++ b/docs.7/_common_definitions.txt @@ -0,0 +1,18 @@ +.. _PyArmor: https://pyarmor.dashingsoft.com/ + +.. |Homepage| replace:: https://pyarmor.dashingsoft.com/ +.. |Manual| replace:: https://pyarmor.readthedocs.io/ +.. |PyArmor| replace:: `PyArmor` +.. |PyArmorVersion| replace:: PyArmor |version| + +.. _issues: https://github.com/dashingsoft/pyarmor/issues/ +.. _pypi: https://pypi.python.org/pypi/pyarmor/ +.. _pyarmor-webui: https://pypi.python.org/pypi/pyarmor-webui/ + +.. _PyInstaller: https://www.pyinstaller.org/ +.. _Cython: https://cython.org/ +.. _PyUpdater: https://www.pyupdater.org/ +.. _pip: http://www.pip-installer.org/ +.. _ASProtect: http://www.aspack.com/ +.. _VMProtect: https://vmpsoft.com/ +.. _NTP: http://www.ntp.org diff --git a/docs/advanced.rst b/docs.7/advanced.rst similarity index 94% rename from docs/advanced.rst rename to docs.7/advanced.rst index 31a64103..1fba67f2 100644 --- a/docs/advanced.rst +++ b/docs.7/advanced.rst @@ -573,6 +573,12 @@ the plugin script:: pyarmor obfuscate --with-license licenses/rcode-001/license.lic \ --plugin check_ntp_time foo.py +For command :ref:`pack`:: + + pyarmor licenses -x 20190501 rcode-001 + pyarmor pack --with-license licenses/rcode-001/license.lic \ + -x " --plugin check_ntp_time" foo.py + More examples, refer to https://github.com/dashingsoft/pyarmor/tree/master/plugins About how plugins work, refer to :ref:`How to Deal With Plugins` @@ -986,7 +992,7 @@ work. For example, first obfustate the scripts:: Then translate the obfuscated one as normal python scripts by Nuitka:: cd ./dist - python -m nuitka --include-package pytransform foo.py + python -m nuitka --include-package=pytransform foo.py ./foo.bin There is one problem is that the imported modules (packages) in the obfuscated @@ -1712,4 +1718,97 @@ It also works for super mode:: python -m pyarmor.helper.merge ... + +How to customize error message +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +I have started to play around with pyarmor. When using a license file that +expires you get the message “License is expired”. Is there a way to change this +message? + +From pyarmor v7.8.0 (it's not released now), there are 2 license error messages +could be customized by runtime configure file `~/.pyarmor/runtime.cfg` with json +format: + +* License is expired +* License is not for this machine + +In order to customize the error message, first create the file +`~/.pyarmor/runtime.cfg`, edit it, then obfuscate the scripts. + +There are 3 kind of ways to customize error handlers by editing the content of +this file: + +1. Quit directly if "errors" is set to keyword "exit" + +.. code:: json + { + "errors": "exit" + } + +2. Show same message for any license error + +.. code:: json + { + "errors": "something is wrong" + } + +3. Show customized message for different errors + +.. code:: json + { + "errors": ["this license is expired", "this license is not for this machine"] + } + +For old version, you need patch the source script `pytransform.py` in the pyarmor +package. There is a function `pyarmor_runtime` + +.. code:: python + + def pyarmor_runtime(path=None, suffix='', advanced=0): + ... + try: + pyarmor_init(path, is_runtime=1, suffix=suffix, advanced=advanced) + init_runtime() + except Exception as e: + if sys.flags.debug or hasattr(sys, '_catch_pyarmor'): + raise + sys.stderr.write("%s\n" % str(e)) + sys.exit(1) + +Change the hanler of the exception as you desired. + +If the scripts are obfuscated by super mode, this solution doesn't work. You may +create a script to catch exceptions raised by obfuscated script `foo.py`. For +example + +.. code:: python + + try: + import foo + except Exception as e: + print('something is wrong') + +By this way not only the exceptions of pyarmor but also of normal scripts are +catched. In order to handle the exceptions of pyarmor only, first create runtime +package by :ref:`runtime`, and obfuscate the scripts with it:: + + pyarmor runtime --advanced 2 -O dist + pyarmor obfuscate --advanced 2 --runtime @dist foo.py + +Then create a boot script ``dist/foo_boot.py`` like this + +.. code:: python + + try: + import pytransform_bootstrap + except Exception as e: + print('something is wrong') + else: + import foo + +The script ``dist/pytransform_bootstrap.py`` is created by :ref:`runtime`, it's +obfuscated from an empty script, so only pyarmor bootstrap exceptions are raised +by it. + .. include:: _common_definitions.txt diff --git a/docs/build-wheel.rst b/docs.7/build-wheel.rst similarity index 97% rename from docs/build-wheel.rst rename to docs.7/build-wheel.rst index 5117d221..42ae4c2e 100755 --- a/docs/build-wheel.rst +++ b/docs.7/build-wheel.rst @@ -90,7 +90,7 @@ About the details, please refer to function `bdist_wheel` in the `_ It's a simple script, and only implements basic functions, if something is wrong -with the script, do it manully or writeh a shell script as the following steps: +with the script, do it manully or write a shell script as the following steps: 1. Build wheel with original package 2. Unpack this wheel to temporary path by this command:: diff --git a/docs/change-logs.rst b/docs.7/change-logs.rst similarity index 93% rename from docs/change-logs.rst rename to docs.7/change-logs.rst index 71b372a3..93a54f86 100644 --- a/docs/change-logs.rst +++ b/docs.7/change-logs.rst @@ -3,8 +3,9 @@ Change Logs =========== -**Since v7.3.1, some features may be not available for old license. pyarmor will - report "This license may be expired" when using those features** +**Since v7.3.1, some features may be not available for old pyarmor + license. pyarmor will report "This license may be expired" when using + those features** Incompatible issues ------------------- @@ -16,7 +17,7 @@ Incompatible issues The license file generated by these versions doesn't work with the old obfuscated scripts. There are 2 solutions for this case, still generating the - license file with old version pyarmor, or obfuscating the scrips again by new + license file with old version pyarmor, or obfuscating the scripts again by new version pyarmor. However ``license.lic`` generated by old version still works. That is to say, @@ -39,12 +40,127 @@ Incompatible issues .. The dev version could be installed by this command:: - pip install https://pyarmor.dashingsoft.com/downloads/temp/pyarmor-7.2.0.zip + pip install https://pyarmor.dashingsoft.com/downloads/temp/pyarmor-7.7.0.zip It may be changed from time to time to fix new bugs, please update it once it doesn't work. If the new version has been released in PyPi, please remove the dev version, install the stable version from PyPi. +8.0.1 (developing) +------------------ + +From PyArmor 8.0.1, there are some incompatible changes + +* SPP mode doesn't work prior to PyArmor 8.0, please upgrade pyarmor to 8.0+ to + use it. And it only works in arch x86_64 and aarch64, no plan to support new + platforms for SPP mode. For other platforms, use BCC mode instead. BCC mode is + an enhancement of SPP mode, and has schedule to support X86, armv7. + +* In previous versions, querying registration information by command `pyarmor + register` will not work after 2023-12-31. It still works in PyArmor 8.0+ + +New big features: + +* Customize and localize runtime error messages +* Introduce irreversible obfuscation mode BCC, it's an enhancement of SPP mode. +* Introduce irreversible obfuscation mode RFT, it could rename all the + function/class/method/argument/variable. + +Fixed bugs: + +* Fix bug (#908): `--mix-str` doesn't work if there is encoding line +* Fix spp mode bug: the decorators of nest function are ignored + +.. important:: + + In order to improve security and support Python 3.11, there are + significant changes from Pyarmor 8.0, more information refer to + https://github.com/dashingsoft/pyarmor#release-plan + +7.7.4 +----- +* Fix bug: pyinstaller option `--upx-dir` doesn't work in the command `pack` +* Fix bug (#884): "insert one redundant line" doesn't work in Python 3.10 for super mode + +7.7.3 +----- +* Fix bug(#882, #883): `pack` command fails when using pyinstaller option `--onefile` + +7.7.2 +----- +* Fix bug(#882): `pack` command fails when using pyinstaller option `--onefile` + +7.7.1 +----- +* Fix bug(#853): `pack` command fails when passing the `--upx-dir` flag to PyInstaller +* Fix bug(#860): `--exact` flag doesn't work in the option `-x` of `pack` command +* Fix bug(#878): For pyinstaller 5.6.2, `pack` command fails with error + `win32ctypes.pywin32.pywintypes.error: (2, 'LoadLibraryEx', 'The system cannot + find the file specified')` + +7.7.0 +----- +* Fix bug(#814): `--mix-str` results in `from __future__ import xxx` error +* Change core version to **r52.6** +* Remove duplicated mac addresses when printing all mac addresses +* Fix super mode crash bug in aarch64 platform +* Change spp build library version to **r4** +* Fix spp mode bug: `RuntimeError: Init spp mode failed` when function name + starts with `lambda_` +* Fix spp mode crash bugs + +7.6.1 +----- +* Change spp build library version to **r3** +* Fix bug: `ImportError: dynamic module does not define module export function + (PyInit_pytransform_vax_xxxxxx)` + +7.6.0 +----- +* For command `obfuscate` add option `--mix-str` to obfuscate the string value +* For command `config` add option `--mixin`, only avaliable mixin is `str` now +* Project add new attribute `mixins` to support option `--mixin` +* Change core version to **r51.5** +* Change spp build library version to **r2** +* Fix spp mode crash bugs because of the same list/dict/set constants conflicts +* Fix spp mode crash bugs because cellvars and freevars are freed even they are + still used by other lambda/functions. + +7.5.1 +----- +* Fix spp mode bug (#758): `from __future__ imports must occur at the beginning of the file` +* Fix spp mode bug (#760): 'Set' object has no attribute 'ctx' +* Fix spp mode bug (#760): redefinition of `_listcomp_326_1` +* Fix spp mode bug: `Can not import XXX from MMM` +* Fix spp mode bug: using `PYTHONOPTIMIZE` may results in sppmode fails. +* Fix spp mode bugs regarding to `with/lambda/comprehension` + +7.5.0 +----- +* Fix command `pack` issue: if using `--src` in the option `--xoptions`, pyarmor + raises excpetion "no entry script found" +* Change core version and spp library version to **r50.4** +* Support `sppmode` for Python3.7~3.10 in platforms: darwin.aarch64, linux.aarch64 +* Support `sppmode` for Python 3.10 in platforms: windows.x86_64, linux.x86_64, + darwin.x86_64, darwin.aarch64, linux.aarch64 +* Fix `sppmode` bug: the result of inplace op for attribute is not right. For + example, after `self.a += 1`, `self.a` isn't increased in old version. +* Fix `sppmode` bug: AnnAssign is ignored. For example, after `x: int = 3`, `x` + still is undefined in old version. + +7.4.3 +----- +* Fix `pack` issue: pass duplicated extra options to PyInstaller + +7.4.2 +----- +* Fix inline option `no-spp-mode` issue + +7.4.1 +----- +* Fix encoding issue when generating entry script (#712) +* Fix example scripts `obfuscate-app.bat` and `obfuscate-pkg.bat` bugs (#713) + 7.4.0 ----- * Change core version to **r49.3** @@ -915,7 +1031,7 @@ customer by email. For the previous purchased user, the old private capsules which are generated implicitly by PyArmor after registered PyArmor still work, -but maybe not supported later. Contact jondy.zhao@gmail.com if you'd +but maybe not supported later. Contact pyarmor@163.com if you'd like to use new `private capsule`. The other changes: @@ -953,7 +1069,7 @@ Then register this keyfile in the new version of pyarmor It's recommanded that you have not yet issued any customized "license.lic" to your customers. -Forward the purchased email received from MyCommerce to jondy.zhao@gmail.com, +Forward the purchased email received from MyCommerce to pyarmor@163.com, and the new key file will be sent to the registration email. If pyarmor license is purchased after 2017-10-10, no fee for this upgrading. Before 2017-10-10, please purchase a new license for latest pyarmor. diff --git a/docs.7/conf.py b/docs.7/conf.py new file mode 100644 index 00000000..6c5dc71f --- /dev/null +++ b/docs.7/conf.py @@ -0,0 +1,286 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# PyArmor documentation build configuration file, created by +# sphinx-quickstart on Sat Dec 1 18:07:32 2018. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys +import os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +#sys.path.insert(0, os.path.abspath('.')) + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'sphinx.ext.todo', +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# source_suffix = ['.rst', '.md'] +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = 'Pyarmor' +copyright = '2018 - 2023 Dashingsoft Corp.' +author = 'Jondy Zhao' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '8.0' +# The full version, including alpha/beta/rc tags. +release = '8.0.1' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['_build'] + +# The reST default role (used for this markup: `text`) to use for all +# documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + +# If true, keep warnings as "system message" paragraphs in the built documents. +#keep_warnings = False + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = True + + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'sphinx_rtd_theme' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (relative to this directory) to use as a favicon of +# the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# Add any extra paths that contain custom files (such as robots.txt or +# .htaccess) here, relative to this directory. These files are copied +# directly to the root of the documentation. +#html_extra_path = [] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Language to be used for generating the HTML full-text search index. +# Sphinx supports the following languages: +# 'da', 'de', 'en', 'es', 'fi', 'fr', 'h', 'it', 'ja' +# 'nl', 'no', 'pt', 'ro', 'r', 'sv', 'tr' +#html_search_language = 'en' + +# A dictionary with options for the search language support, empty by default. +# Now only 'ja' uses this config value +#html_search_options = {'type': 'default'} + +# The name of a javascript file (relative to the configuration directory) that +# implements a search results scorer. If empty, the default will be used. +#html_search_scorer = 'scorer.js' + +# Output file base name for HTML help builder. +htmlhelp_basename = 'Pyarmordoc' + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { +# The paper size ('letterpaper' or 'a4paper'). +#'papersize': 'letterpaper', + +# The font size ('10pt', '11pt' or '12pt'). +#'pointsize': '10pt', + +# Additional stuff for the LaTeX preamble. +#'preamble': '', + +# Latex figure (float) alignment +#'figure_align': 'htbp', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + (master_doc, 'Pyarmor.tex', 'Pyarmor Documentation', + 'Jondy Zhao', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + (master_doc, 'pyarmor', 'Pyarmor Documentation', + [author], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + (master_doc, 'Pyarmor', 'Pyarmor Documentation', + author, 'Pyarmor', 'Power tool to obfuscate Python scripts.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' + +# If true, do not generate a @detailmenu in the "Top" node's menu. +#texinfo_no_detailmenu = False diff --git a/docs/examples.rst b/docs.7/examples.rst similarity index 98% rename from docs/examples.rst rename to docs.7/examples.rst index 1c3bfe76..da5e3b93 100644 --- a/docs/examples.rst +++ b/docs.7/examples.rst @@ -52,7 +52,7 @@ options work with command :ref:`obfuscate`:: .. important:: The command :ref:`pack` will obfuscate the scripts automatically, do not try - to pack the obfuscated the scripts. + to pack the obfuscated scripts. .. note:: diff --git a/docs/how-to-do.rst b/docs.7/how-to-do.rst similarity index 99% rename from docs/how-to-do.rst rename to docs.7/how-to-do.rst index 30a719c2..aad52c1a 100755 --- a/docs/how-to-do.rst +++ b/docs.7/how-to-do.rst @@ -67,7 +67,7 @@ Then change code object as the following way POP_TOP END_FINALLY -* Append function names ``__armor_enter``, ``__armor_exit__`` to ``co_consts`` +* Append function names ``__armor_enter__``, ``__armor_exit__`` to ``co_consts`` * Increase ``co_stacksize`` by 2 diff --git a/docs.7/index.rst b/docs.7/index.rst new file mode 100644 index 00000000..380c047b --- /dev/null +++ b/docs.7/index.rst @@ -0,0 +1,63 @@ +.. pyarmor documentation master file, created by + sphinx-quickstart on Sat Dec 1 11:22:25 2018. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +PyArmor's Documentation +======================= + +:Version: |PyArmorVersion| +:Homepage: |Homepage| +:Contact: pyarmor@163.com +:Authors: Jondy Zhao +:Copyright: This document has been placed in the public domain. + + +|PyArmor| is a command line tool used to obfuscate python scripts, +bind obfuscated scripts to fixed machine or expire obfuscated +scripts. It protects Python scripts by the following ways: + +* Obfuscate code object to protect constants and literal strings. +* Obfuscate co_code of each function (code object) in runtime. +* Clear f_locals of frame as soon as code object completed execution. +* Verify the license file of obfuscated scripts while running it. + +|PyArmor| supports Python 2.6, 2.7 and Python 3. + +|PyArmor| is tested against ``Windows``, ``Mac OS X``, and ``Linux``. + +|PyArmor| has been used successfully with ``FreeBSD`` and embedded +platform such as ``Raspberry Pi``, ``Banana Pi``, ``Orange Pi``, ``TS-4600 / TS-7600`` etc. +but is not fullly tested against them. + +Contents: + +.. toctree:: + :maxdepth: 2 + + installation + usage + advanced + build-wheel + examples + project + man + understand-obfuscated-scripts + how-to-do + pytransform + platforms + mode + performance + security + questions + license + change-logs + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` + +.. include:: _common_definitions.txt diff --git a/docs/installation.rst b/docs.7/installation.rst similarity index 100% rename from docs/installation.rst rename to docs.7/installation.rst diff --git a/docs/license.rst b/docs.7/license.rst similarity index 90% rename from docs/license.rst rename to docs.7/license.rst index fcc2c846..44524ec2 100644 --- a/docs/license.rst +++ b/docs.7/license.rst @@ -36,24 +36,23 @@ There are 2 basic types of licenses issued for the software: property of the license owner. * A enterprise license for business users. The user purchases one - license to use the software for one product serials of an - organization. When placing an order of this kind of license, please - fill orginization name plus product name, this software is only - authorized to this registration name. + license to use the software for one product of an organization. When + placing an order of this kind of license, please fill orginization + name plus product name, this software is only authorized to this + registration name. - One product serials include the current version and any other latter + One product include the current version and any other latter versions of the same product. Business users may use their enterprise license on all computers and - embedded devices to obfuscate all the python scripts of this product - serials, to generate private license files for these obfuscated - scripts and distribute them and all the required files to any other - machine and device. + embedded devices to obfuscate all the python scripts of this product, + to generate private license files for these obfuscated scripts and + distribute them and all the required files to any other machine and + device. - Without permission of the software owner the license purchased for - one product serials should not be used for other product - serials. Business users should purchase new license for different - product serials. + Without permission of the software owner the license purchased for one + product should not be used for other product. Business users should + purchase new license for different product. In any case, the software is only used to obfuscate the Python scripts owned by the authorized person or enterprise. @@ -106,7 +105,12 @@ Upgrade Notes ------------- The license purchased before **2019-10-10** don't support to upgrade the latest -version. A new license is required to use the latest version. +version. If need use the latest version, purchasing a new license, and run +`pyarmor register` command. If you have issued any `license.lic` generated by +old license, and still want it works, run `pyarmor register` with option `-u` to +register new license. For example, + + pyarmor register -u /path/to/pyarmor-regcode-xxxx.txt Technical Support ----------------- diff --git a/docs/man.rst b/docs.7/man.rst similarity index 98% rename from docs/man.rst rename to docs.7/man.rst index 4b52c725..1f067254 100644 --- a/docs/man.rst +++ b/docs.7/man.rst @@ -104,6 +104,7 @@ Obfuscate python scripts. --wrap-mode <0,1> Disable or enable wrap mode --with-license FILENAME Use this licese, special value `outer` means no license --cross-protection FILENAME Specify customized protection script +--mix-str Obfuscate the string value **DESCRIPTION** @@ -430,7 +431,7 @@ For binding other hard disk card, specify a name for it. For example:: pyarmor licenses --bind-disk "/dev/vda2:KDX3298FS6P5AX380" r008 By option `-x` any data could be saved into the license file, it's mainly used -to extend license tyoe. For example:: +to extend license type. For example:: pyarmor licenses -x "2019-02-15" r005 @@ -573,6 +574,17 @@ When something is wrong, turn on PyArmor debug flag to print traceback:: pyarmor -d pack ... +.. important:: + + For option `-e` and `-x`, it need an extra whitespace in option value, + otherwise it will complain of `error: unrecognized arguments`. For exmaple:: + + # Wrong, no heading whitespace before --advanced 2 + pyarmor pack -x "--advanced 2" ... + + # Right + pyarmor pack -x " --advanced 2" ... + **EXAMPLES** * Obfuscate `foo.py` and pack them into the bundle `dist/foo`:: @@ -774,6 +786,7 @@ Update project settings. --bootstrap <0,1,2,3> How to insert bootstrap code to entry script --enable-suffix <0,1> Generate the runtime package with unique name --with-license FILENAME Use this license file, special value `outer` means no license +--mixin NAME Available mixin `str`, used to obfuscate string value **DESCRIPTION** @@ -818,6 +831,13 @@ For the details of each option, refer to :ref:`Project Configuration File` pyarmor config --wrap-mode 0 +* Obfuscate all string value in the scripts:: + + pyarmor config --mixin str + + # Restore default value, no obfuscating strings + pyarmor config --mixin '' + * Set plugin for entry script. The content of `check_ntp_time.py` will be insert into entry script as building project:: diff --git a/docs/mode.rst b/docs.7/mode.rst similarity index 94% rename from docs/mode.rst rename to docs.7/mode.rst index 31e812eb..2fac586a 100644 --- a/docs/mode.rst +++ b/docs.7/mode.rst @@ -49,7 +49,8 @@ Super Plus Mode This is an enhancement of super mode, it will convert some functions to binary code. It's introduced in PyArmor 7.0.1, and now only works for arch X86_64 and -Python 3.7, 3.8, 3.9 +Python 3.7, 3.8, 3.9. From PyArmor 7.5.0, Python 3.10 with arch X86_64 works, +and Python 3.7~3.10 for arch AARCH64 in Darwin and Linux works. It requires ``c`` compiler. In Linux and Darwin, ``gcc`` and ``clang`` is OK. In Windows, only ``clang.exe`` works. It could be configured by one of these ways: @@ -58,7 +59,7 @@ Windows, only ``clang.exe`` works. It could be configured by one of these ways: * Download and install Windows version of `LLVM `_ * Download `https://pyarmor.dashingsoft.com/downloads/tools/clang-9.0.zip`, it's about 26M bytes, there is only one file in it. Unzip it and save ``clang.exe`` - to ``$HOME/.pyarmor``. ``$HOME`` is home path of current logon user, check the + to ``$HOME/.pyarmor/``. ``$HOME`` is home path of current logon user, check the environment variable ``HOME`` to get the real path. After ``c`` compiler works, enable super plus mode by ``--advanced 5``:: @@ -87,7 +88,7 @@ also works in the docstring to ignore ``function`` or ``class``, for example: There are a few differences in the spp mode: * Calling `raise` without argument not in the exception handler will raise - different exception + different exception. .. code-block:: python @@ -98,28 +99,39 @@ There are a few differences in the spp mode: >>> raise UnboundlocalError: local variable referenced before assignment +* Some exception messages may different from the plain script. + +* Most of function attributes which starts with `__` doesn't exists, + or the value is different from the original. + Unsupport features for spp mode: .. code-block:: python unsupport_nodes = ( - ast.Nonlocal, + ast.ExtSlice, + ast.AsyncFunctionDef, ast.AsyncFor, ast.AsyncWith, - ast.Await, ast.Yield, ast.YieldFrom, ast.GeneratorExp + ast.Await, ast.Yield, ast.YieldFrom, ast.GeneratorExp, + + ast.NamedExpr, + + ast.MatchValue, ast.MatchSingleton, ast.MatchSequence, + ast.MatchMapping, ast.MatchClass, ast.MatchStar, + ast.MatchAs, ast.MatchOr ) - if hasattr(ast, 'MatchValue'): - unsupport_nodes += ( - ast.MatchValue, ast.MatchSingleton, ast.MatchSequence, - ast.MatchMapping, ast.MatchClass, ast.MatchStar, - ast.MatchAs, ast.MatchOr - ) -And unsupport functions:: +And unsupport functions: - exec, eval, super, locals, sys._getframe +* exec, +* eval +* super +* locals +* sys._getframe +* sys.exc_info -For example, the following functions will not obfuscated by super plus -mode, because they use unsupport features or call unsupport functions: +For example, the following functions are not obfuscated by super plus +mode, because they use unsupported features or unsupported functions: .. code-block:: python @@ -170,7 +182,7 @@ It is recommended to upgrade in the next minor version. .. note:: In trial version the module could not be obfuscated by advanced - mdoe if there are more than about 30 functions in this module, (It + mode if there are more than about 30 functions in this module, (It still could be obfuscated by non-advanced mode). .. important:: @@ -191,7 +203,7 @@ Enable vm mode with advanced mode by this way:: pyarmor obfuscate --advanced 3 foo.py -Enable vm mode with super mdoe by this way:: +Enable vm mode with super mode by this way:: pyarmor obfuscate --advanced 4 foo.py @@ -475,7 +487,7 @@ For example, def fabico(): global var_b - # Wrong, remove item from module.__dict__ not in modul level + # Wrong, remove item from module.__dict__ not in model level del var_b # This is foo.py, obfuscated with mode 101 diff --git a/docs/performance.rst b/docs.7/performance.rst similarity index 100% rename from docs/performance.rst rename to docs.7/performance.rst diff --git a/docs/platforms.rst b/docs.7/platforms.rst similarity index 95% rename from docs/platforms.rst rename to docs.7/platforms.rst index 71b3983b..f619a336 100644 --- a/docs/platforms.rst +++ b/docs.7/platforms.rst @@ -92,7 +92,7 @@ example, ``linux/x86_64/11/py38``. - Linux with glibc < 2.14 and UCS2 * - windows - x86_64 - - 11, 25 + - 11 - 27, 37, 38, 39, 310 - * - windows @@ -100,6 +100,11 @@ example, ``linux/x86_64/11/py38``. - 11, 25 - 27, 37, 38, 39 - + * - windows + - x86_64 + - 25 + - 27, 37, 38, 39 + - In some platforms, `pyarmor` doesn't know its standard name, just download the right one and save it in the path ``~/.pyarmor/platforms/SYSTEM/ARCH/N/``. Run @@ -143,6 +148,13 @@ library. For security reason, the zero feature library uses different alogrithm to obfuscate the scripts. So the platform ``windows.x86_64.7`` can not share the same obfuscated scripts with platform ``linux.armv7.0``. +.. note:: + + In Apple M1, dynamic libraris with feature 2 `JIT` will be killed by Python + interpreter. Try to resign executable with `com.apple.security.cs.allow-jit` + entitlement, it may fix the problem. Refer to + + https://developer.apple.com/documentation/security/hardened_runtime .. _standard platform names: @@ -186,7 +198,7 @@ and run it in the target machine:: python get_platform_name.py -.. note:: New platforms in differnt versions +.. note:: New platforms in different versions * v5.9.3: android.armv7 * v5.9.4: uclibc.armv7 diff --git a/docs/project.rst b/docs.7/project.rst similarity index 100% rename from docs/project.rst rename to docs.7/project.rst diff --git a/docs/protect-python-scripts-by-pyarmor.md b/docs.7/protect-python-scripts-by-pyarmor.md similarity index 100% rename from docs/protect-python-scripts-by-pyarmor.md rename to docs.7/protect-python-scripts-by-pyarmor.md diff --git a/docs/pyarmor-history.md b/docs.7/pyarmor-history.md similarity index 100% rename from docs/pyarmor-history.md rename to docs.7/pyarmor-history.md diff --git a/docs/pytransform.rst b/docs.7/pytransform.rst similarity index 100% rename from docs/pytransform.rst rename to docs.7/pytransform.rst diff --git a/docs.7/questions.rst b/docs.7/questions.rst new file mode 100644 index 00000000..7698c359 --- /dev/null +++ b/docs.7/questions.rst @@ -0,0 +1,971 @@ +.. _questions: + +When Things Go Wrong +==================== + +Some necessary knowledges and technicals are required to used pyarmor. Check +this list, make sure you know them, and your question is not related to them. + +.. _Necessary Knowledges: + +Necessary Knowledges +-------------------- + +Shell +~~~~~ + +pyarmor is a command line tool, it must be run in the shell or terminal. If you +know nothing about shell command, use `pyarmor-webui`_ instead. + +When command `pyarmor` complains of argument error, unknown option etc. Please +use option ``-h`` to list all the available options, and fix command syntax +error by these hints. For example:: + + pyarmor obfuscate -h + +Python +~~~~~~ + +How to run Python +https://docs.python.org/3.8/tutorial/interpreter.html#using-the-python-interpreter + +Source Code Encoding +~~~~~~~~~~~~~~~~~~~~ + +If the obfuscated scripts print unexpected output, you need learn this + +https://docs.python.org/3.8/tutorial/interpreter.html#source-code-encoding + +Then set the right source code encoding in the scripts, first run the plain +script to make sure everything is fine, then obfuscate the scripts again. + + +Python Import System +~~~~~~~~~~~~~~~~~~~~ + +The obfuscated scripts need an extra :ref:`Runtime Package` to run, it's a +common Python package, which could be imported as normal Python module or +package. If it can't be imported correctly, for example, not distributed with +the obfuscated scripts or stored in the wrong place, the obfuscated scripts may +raise exceptions like this:: + + ModuleNotFoundError: No module named 'app.pytransform' + +This is not PyArmor's error, just Python can not find it. In this case, you need +know Python how to import module, package, what's absolute import and relative +import, you must know what's ``sys.path`` + +https://docs.python.org/3.8/library/sys.html#sys.path + +The obfuscated script is a very simple Python script, the first line is an +import statement, the second line is a function call. For any import or no +module found error, for example:: + + ImportError: No module named model.NukepediaDB + +Just think it as a common python script, check whether the module, package or +extension file locates in the right place according to Python Import System. If +not, move the module, package or extension file to right path. + +Refer to the following official document or by search engineer to understand +Python Import System + +https://docs.python.org/3.8/reference/simple_stmts.html#the-import-statement + + +PyInstaller +~~~~~~~~~~~ + +If you'd like to pack the obfuscated scripts to one executable, and your project +structure is complex, you must know `PyInstaller`_ and could pack your project +by `PyInstaller`_ directly. + +https://pyinstaller.readthedocs.io/en/stable/usage.html + + +Common Solutions +---------------- + +I have receive a lot of issues, most of them aren't pyarmor's defect, but use +pyarmor in wrong way. So when you're in trouble with pyarmor, spending a few +hours to understand pyarmor may solve the problem quickly. Self-help is better +than help from others, it also could save time for both of us. + +First make sure you have read the basic guide :ref:`Using PyArmor`. + +Look through :ref:`Understanding Obfuscated Scripts`, especially the section +:ref:`The Differences of Obfuscated Scripts` + +If you don't know how to use pyarmor in a special case, have a glance at the toc +of :ref:`Advanced Topics`. + +Here are several common solutions + +* Upgrade pyarmor to latest stable version, please check :ref:`change logs` + before upgrading. If pyarmor works fine before, but now doesn't work, also + make a :ref:`clean uninstallation`, re-install pyarmor, and start everything + from refresh state. + +* As obfuscating the script by ``pyarmor``, check not only the last error + message, but also each log carefully to understand what pyarmor is doing, it's + very helpful to find the problem. And try to get more information by common + option ``-d``. For example:: + + pyarmor -d obfuscate --recursive foo.py + +* As running the obfuscated scripts, turn on Python debug option by ``-d`` to + print more information. If there is line number and script name in the + traceback, check the source script around this line. Make sure it doesn't use + any feature changed by obfuscated scripts. For example:: + + python -d obf_foo.py + +* If you distribute the obfuscated scripts in different platform or docker, make + sure the related cross platform options are set. Because the obfuscated + scripts include binary library, it's platform dependent, and Python version in + target must be same as the version to obfuscate the scripts. + +* If you are using command :ref:`pack`, make sure PyInstaller could pack the + plain scripts directly and the final bundle works. + +* If you are using the scripts obfuscated by :ref:`Restrict mode` 3 or more, try + to use the default restrict mode. If low restrict mode works, check the + scripts make sure they don't violate the restrict mode. + +* If you are using complex scripts or packages, try a simple script or package + to check it works or not. + +* Understanding pyarmor by doing a test in a few minutes if something you're not + sure. + +The default option of pyarmor works for common cases, but for complex cases, you +need understand the different options for each command. First list all available +options of ``obfuscate`` by option ``-h``:: + + pyarmor obfuscate -h + +You may find the desired option by its short description. If you're not sure, go +to :ref:`Man Page` to read the details of each option. + +Maybe the simplest way to understand an option is, do a test in one minute. For +example, the option ``--bootstrap`` is used to control how to generate the +bootstrap code for obfuscated scripts, do tests in a fresh path like this:: + + cd /path/to/test + mkdir case-1 + cd case-1 + echo "print('Hello')" > foo.py + pyarmor obfuscate --bootstrap 2 foo.py + ls dist/ + cat dist/foo.py + + cd /path/to/test + mkdir case-2 + cd case-2 + echo "print('Hello')" > foo.py + pyarmor obfuscate --bootstrap 3 foo.py + ls dist/ + cat dist/foo.py + +You can combine different options to do similar tests, it could help you +understand pyarmor quickly. + +.. note:: + + There are a lot of reporeted `issues`_, search here first try to find same + issue. + +.. _reporting an issue: + +Reporting an issue +------------------ + +When there is no solution in the document, about security issue, send email to +pyarmor@163.com, all the others please click `issues`_ to report, and +provide the necessary information + +1. The full pyarmor command and full output log (required) +2. If distributing the obfuscated script to other machine, which files are copied (optional) +3. The command to run the obfuscated scripts and full traceback when something is wrong + +The output log could be redirected to a file by this way:: + + pyarmor obfuscate foo.py >log.txt 2>&1 + +Here it's an example, the title of issue:: + + cannot import name 'pyarmor' from 'pytransform' + +The content of issue (copy all of these to github and modify it):: + + 1. On MacOS 10.14 run pyarmor to obfuscate the script + ``` + $ pyarmor obfuscate --exact main.py + INFO Create pyarmor home path: /Users/jondy/.pyarmor + INFO Create trial license file: /Users/jondy/.pyarmor/license.lic + INFO Generating public capsule ... + INFO PyArmor Trial Version 7.0.1 + INFO Python 3.7.10 + INFO Target platforms: Native + INFO Source path is "/Users/jondy/workspace/pyarmor-webui/test/__runner__/__src__" + INFO Entry scripts are ['main.py'] + INFO Use cached capsule /Users/jondy/.pyarmor/.pyarmor_capsule.zip + INFO Search scripts mode: Exact + INFO Save obfuscated scripts to "dist" + INFO Read product key from capsule + INFO Obfuscate module mode is 2 + INFO Obfuscate code mode is 1 + INFO Wrap mode is 1 + INFO Restrict mode is 1 + INFO Advanced value is 0 + INFO Super mode is False + INFO Super plus mode is not enabled + INFO Generating runtime files to dist/pytransform + INFO Extract pytransform.key + INFO Generate default license file + INFO Update capsule to add default license file + INFO Copying /Users/jondy/workspace/pyarmor-webui/venv/lib/python3.7/site-packages/pyarmor/platforms/darwin/x86_64/_pytransform.dylib + INFO Patch library dist/pytransform/_pytransform.dylib + INFO Patch library file OK + INFO Copying /Users/jondy/workspace/pyarmor-webui/venv/lib/python3.7/site-packages/pyarmor/pytransform.py + INFO Rename it to pytransform/__init__.py + INFO Generate runtime files OK + INFO Start obfuscating the scripts... + INFO /Users/jondy/workspace/pyarmor-webui/test/__runner__/__src__/main.py -> dist/main.py + INFO Insert bootstrap code to entry script dist/foo.py + INFO Obfuscate 1 scripts OK. + ``` + 2. Copy the whole folder `dist/` to target machine Ubuntu + 3. Failed to run the obfuscated script by Python 3.7 in Unbutu + ``` + $ cd dist/ + $ python3 main.py + Traceback (most recent call last): + File "main.py", line 1, in + from pytransform import pyarmor + ImportError: cannot import name 'pyarmor' from 'pytransform' (/home/jondy/dist/pytransform/__init__.py) + ``` + +.. important:: + + The issue may be marked as ``invalid`` and be closed directly in any of: + + * Not reported as template or missing necessary information + * There is the exact solution in the documentation + + +Segment fault +------------- + +In the following cases, obfuscated scripts may crash + +* Running obfuscated script by debug version Python +* Obfuscating scripts by Python X.Y but running the obfuscated scripts by + different Python version M.N +* Running the scripts in different platform but obfuscate them without option + ``--platform`` + + - Docker, it's Alpine Linux, in PyArmor, the platform name is `musl.x86_64`, + not `linux.x86_64` + - In Windows, 32-bit Windows is different from 64-bit Windows + - In 64-bit Windows, 32-bit Python is different from 64-bit Python + +* Read ``co_code`` or other attributes of the obfuscated code object by any way, + some third packages may analysis the byte code to do something. +* Importing the scripts obfuscated by restrict mode 3 and more in non-obfuscated + script may crash. It also may crash if it's obfuscated by ``obf-code=0`` +* Mixing the scripts obfuscated by different option ``--advanced`` +* In MacOS, the core library of pyarmor is linked to standard system Python, for + others, use ``install_name_tool`` to change ``rpath`` to adapt this machine. + +For PyArmor 5.5.0 ~ 6.6.0, some machines may be crashed because of advanced +mode. A quick workaround is to disable advanced mode by editing the file +:file:`pytransform.py` which locates in the installed path of ``pyarmor`` , in +the function ``_load_library``, uncomment about line 202. The final code looks +like this:: + + # Disable advanced mode if required + m.set_option(5, c_char_p(1)) + + +Bootstrap Problem +----------------- + +Could not find `_pytransform` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Generally, the dynamic library `_pytransform` is in the :ref:`runtime package`, +before v5.7.0, it's in the same path of obfuscated scripts. It may be: + +* `_pytransform.so` in Linux +* `_pytransform.dll` in Windows +* `_pytransform.dylib` in MacOS + +First check whether the file exists. If it exists: + +* Check the permissions of dynamic library + + If there is no execute permissions in Windows, it will complain: + `[Error 5] Access is denied` + +* Check whether `ctypes` could load `_pytransform`:: + + from pytransform import _load_library + m = _load_library(path='/path/to/dist') + +* Try to set the runtime path in the :ref:`Bootstrap Code` of entry + script:: + + from pytransform import pyarmor_runtime + pyarmor_runtime('/path/to/dist') + +Still doesn't work, report an issues_ + + +ERROR: Unsupport platform linux.xxx +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Please refer to :ref:`Support Platforms` + + +/lib64/libc.so.6: version 'GLIBC_2.14' not found +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +In some machines there is no `GLIBC_2.14`, it will raise this exception. + +One solution is patching `_pytransform.so` by the following way. + +First check version information:: + + readelf -V /path/to/_pytransform.so + ... + + Version needs section '.gnu.version_r' contains 2 entries: + Addr: 0x00000000000056e8 Offset: 0x0056e8 Link: 4 (.dynstr) + 000000: Version: 1 File: libdl.so.2 Cnt: 1 + 0x0010: Name: GLIBC_2.2.5 Flags: none Version: 7 + 0x0020: Version: 1 File: libc.so.6 Cnt: 6 + 0x0030: Name: GLIBC_2.7 Flags: none Version: 8 + 0x0040: Name: GLIBC_2.14 Flags: none Version: 6 + 0x0050: Name: GLIBC_2.4 Flags: none Version: 5 + 0x0060: Name: GLIBC_2.3.4 Flags: none Version: 4 + 0x0070: Name: GLIBC_2.2.5 Flags: none Version: 3 + 0x0080: Name: GLIBC_2.3 Flags: none Version: 2 + +Then replace the entry of `GLIBC_2.14` with `GLIBC_2.2.5`: + +* Copy 4 bytes at 0x56e8+0x10=0x56f8 to 0x56e8+0x40=0x5728 +* Copy 4 bytes at 0x56e8+0x18=0x5700 to 0x56e8+0x48=0x5730 + +Here are sample commands:: + + xxd -s 0x56f8 -l 4 _pytransform.so | sed "s/56f8/5728/" | xxd -r - _pytransform.so + xxd -s 0x5700 -l 4 _pytransform.so | sed "s/5700/5730/" | xxd -r - _pytransform.so + +.. note:: + + From v5.7.9, this patch is not required. In cross-platform all you need to do + is specify the platform to `centos6.x86_64` to fix this issue. For example:: + + pyarmor obfuscate --platform centos6.x86_64 foo.py + +'pyarmor' is not recognized issue +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +If `pyarmor` is installed by pip, please search "pyarmor" in the computer, then +run full path pyarmor, or add path of pyarmor to environment variable PATH. + +If not by pip, the equivalent of the pyarmor command is running Python script +"pyarmor.py" found in the distribution folder. + +__snprintf_chk: symbol not found +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +When run pyarmor in some dockers, it may raise this exception. Because these +dockers are built with musl-libc, but the default ``_pytransform.so`` is built +with glibc, ``__snprintf_chk`` is missed in the musl-libc. + +In this case, try to download the corresponding dynamic library:: + + # For x86_64 + pyarmor download musl.x86_64.7 + + # For arm64 + pyarmor download musl.aarch64.3 + + # For armv7l + pyarmor download musl.arm.0 + +And overwrite the old one which filename could be found in the traceback. + +Before pyarmor v6.7.0, download the latest version by this way: + +For x86/64 +http://pyarmor.dashingsoft.com/downloads/latest/alpine/_pytransform.so + +For ARM +http://pyarmor.dashingsoft.com/downloads/latest/alpine.arm/_pytransform.so + +Apple M1 Hangs Issue +~~~~~~~~~~~~~~~~~~~~ +When pyarmor hangs in Apple M1, try these solutions: + +* Remove the whole folder of `~/.pyarmor/platforms`, refer to :ref:`Clean uninstallation` +* Upgrade pyarmor to latest version and use paid version. +* For trial version, export enviornment variable `PYARMOR_PLATFORM=darwin.aarch64.0` + +Because the default core library of `_pytransform.dylib` uses some features like +JIT-compile which may be blocked by Apple M1. + +Signing python interpreter with the corresponding entitlement may fix this +problem, but I'm not sure. Refer to +https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_cs_allow-jit + +Obfuscating Scripts Problem +--------------------------- + +Warning: code object xxxx isn't wrapped +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +It means this function isn't been obfuscated, because it includes some +special instructions. + +For example, there is 2-bytes instruction `JMP 255`, after the code +object is obfuscated, the operand is increased to `267`, and the +instructions will be changed to:: + + EXTEND 1 + JMP 11 + +In this case, it's complex to obfuscate the code object with wrap +mode. So the code object is obfuscated with non wrap mode, but all the +other code objects still are obfuscated with wrap mode. + +In current version add some unused code in this function so that the +operand isn't the critical value may avoid this warning. + +.. note:: + + Before v5.5.0, in this case the code object is left as it is. + +Code object could not be obufscated with advanced mode 2 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Because this function includes some jump instructions that couldn't be +handled. In this case, just refine this function, make sure the first statement +will not generate jump instruction. For example, assignment, function call or +any simple statement. However, the compound statements, for examples, `try`, +`for`, `if`, `with`, `while` etc. will generate the jump instructions. If there +is no anyway to refactor the function, insert the following statement at the +beginning of this function:: + + [None, None] + +It will generate some instructions but doesn't change anything. + +Error: Try to run unauthorized function +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +If there is any file `license.lic` or `pytransform.key` in the current +path, pyarmor maybe reports this error. One solution is to remove all +of that files, the other solution to upgrade PyArmor to v5.4.5 later. + + +'XXX' codec can't decode byte 0xXX +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Add the exact source encode at the begin of the script. For example:: + + # -*- coding: utf-8 -*- + +Refer to https://docs.python.org/2.7/tutorial/interpreter.html#source-code-encoding + +If the source encode has been added into main script, it still raises this +issue. Please check the output log to find the exact script name, it may not the +main script. + + +Why plugin doesn't work +~~~~~~~~~~~~~~~~~~~~~~~ + +If the plugin script doesn't work as expected, first check the plugin script +could be injected into the entry script by set Python debug flag:: + + # In linux + export PYTHONDEBUG=y + # In Windows + set PYTHONDEBUG=y + + pyarmor obfuscate --exact --plugin check_ntp_time foo.py + +It will generate patched file ``foo.py.pyarmor-patched``, make sure the content +of plugin script has been inserted into the right place, and the verify function +will be executed. + + +Running Obfuscated Scripts Problem +---------------------------------- + +The `license.lic` generated doesn't work +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The key is that the capsule used to obfuscate scripts must be same as +the capsule used to generate licenses. + +The :ref:`Global Capsule` will be changed if the trial license file of +|PyArmor| is replaced with normal one, or it's deleted occasionally +(which will be generated implicitly as running command `pyarmor +obfuscate` next time). + +In any cases, generating new license file with the different capsule +will not work for the obfuscated scripts before. If the old capsule is +gone, one solution is to obfuscate these scripts by the new capsule +again. + + +NameError: name '__pyarmor__' is not defined +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +No :ref:`Bootstrap Code` are executed before importing obfuscated scripts. + +* When creating new process by `Popen` or `Process` in mod `subprocess` or + `multiprocessing`, to be sure that :ref:`Bootstrap Code` will be called before + importing any obfuscated code in sub-process. Otherwise it will raise this + exception. +* If `pytransform.py` or `pytransform/__init__.py` raises this exception. Make + sure it is not obfuscated, it must be plain script. +* Also check system module `os`, `ctypes`, make sure they're not obfuscated, try + to use option ``--exclude`` to exclude the whole Python system library path. + +How to check :ref:`Bootstrap Code` executed or not? One simple way is to insert +one print statement before them. For example + +.. code:: python + + print('Start to run bootstrap code') + from pytransfrom import pyarmor_runtime + pyarmor_runtime() + +If the message is printed, then it's OK. Removing the print statement and check +other causes. + +The other solution for this issue is :ref:`using super mode` to obfuscate the +scripts. + +Marshal loads failed when running xxx.py +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +1. Check whether the version of Python to run obfuscated scripts is same as the + version of Python to obfuscate script + +2. Run obfuscated script by `python -d` to show more error message. + +3. Be sure the capsule used to generated the license file is same as the capsule + used to obfuscate the scripts. The filename of the capsule will be shown in + the console when the command is running. + +4. For cross platform obfuscation, make sure the dynamic library feature is set + correctly, refer to :ref:`Obfuscating scripts with different features` + +_pytransform can not be loaded twice +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +When the function `pyarmor_runtime` is called twice, it will complaint +`_pytransform can not be loaded twice` + +For example, if an obfuscated module includes the following lines:: + + from pytransform import pyarmor_runtime + pyarmor_runtime() + __pyarmor__(....) + +When importing this module from entry script, it will report this +error. The first 2 lines should be in the entry script only, not in +the other module. + +This limitation is introduced from v5.1, to disable this check, just +edit `pytransform.py` and comment these lines in function +`pyarmor_runtime`:: + + if _pytransform is not None: + raise PytransformError('_pytransform can not be loaded twice') + +.. note:: + + This limitation has been removed from v5.3.5. + + +Check restrict mode failed +~~~~~~~~~~~~~~~~~~~~~~~~~~ +Use obfuscated scripts in wrong way, by default all the obfuscated +scripts can't be changed any more. + +Besides packing the obfuscated scripts will report this error +either. Do not pack the obfuscated scripts, but pack the plain scripts +directly. + +For more information, refer to :ref:`Restrict Mode` + + +Protection Fault: unexpected xxx +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Use obfuscated scripts in wrong way, by default, all the runtime files +can't be changed any more. Do not touch the following files + +* pytransform.py +* _pytransform.so/.dll/.dylib + +If the entry script is obfuscated by new version, but the runtime files are +still old, it may raise this exception. Using option ``--no-cross-protection`` +to disable this protection, or using option ``--runtime`` to specify the same +runtime files when obfuscating the scrpits, could fix this issue. + +For more information, refer to :ref:`Special Handling of Entry Script` + + +Run obfuscated scripts reports: Invalid input packet +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Mixing trial version and purchased version to obfuscate scripts and generate +`license.lic` also may raise this exception. Make sure all the files generated +by trial version, for example, obfusbcated script, license file and runtime +files, are removed. + +Make sure the runtime module or package `pytransform` imported by the obfuscated +scripts is the one distributed with the obfuscated scripts. For example, running +the obfuscated scripts `python dist/foo.py` in the source path of pyarmor +package may rasie this exception, because `pytransform.py` of pyarmor will be +imported by the `dist/foo.py` unexpectedly. + +If the scripts are obfuscated in different platform, check the notes in +:ref:`Distributing Obfuscated Scripts To Other Platform` + +Before v5.7.0, check if there is any of `license.lic` or `pytransform.key` in +the current path. Make sure they're generated for the obfuscated scripts. If +not, rename them or move them to other path. + +Because the obfuscated scripts will first search the current path, then search +the path of runtime module `pytransform.py` to find the file `license.lic` and +`pytransform.key`. If they're not generated for the obfuscated script, this +error will be reported. + + +OpenCV fails because of `NEON - NOT AVAILABLE` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +In some Raspberry Pi platform, run the obfuscated scripts to import +OpenCV fails:: + + ************************************************** **************** + * FATAL ERROR: * + * This OpenCV build doesn't support current CPU / HW configuration * + * * + * Use OPENCV_DUMP_CONFIG = 1 environment variable for details * + ************************************************** **************** + + Required baseline features: + NEON - NOT AVAILABLE + terminate called after throwing an instance of 'cv :: Exception' + what (): OpenCV (3.4.6) /home/pi/opencv-python/opencv/modules/core/src/system.cpp:538: error: + (-215: Assertion failed) Missing support for required CPU baseline features. Check OpenCV build + configuration and required CPU / HW setup. in function 'initialize' + +One solution is to specify optioin ``--platform`` to `linux.armv7.0`:: + + pyarmor obfuscate --platform linux.armv7.0 foo.py + pyarmor build --platform linux.armv7.0 + pyarmor runtime --platform linux.armv7.0 + +The other solution is to set environment variable `PYARMOR_PLATFORM` +to `linux.armv7.0`. For examples:: + + PYARMOR_PLATFORM=linux.armv7.0 pyarmor obfuscate foo.py + PYARMOR_PLATFORM=linux.armv7.0 pyarmor build + + Or, + + export PYARMOR_PLATFORM=linux.armv7.0 + pyarmor obfuscate foo.py + pyarmor build + +How to customize error message +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Refer to :ref:`How to customize error message` + + +undefined symbol: PyUnicodeUCS4_AsUTF8String +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If Python interpreter is built with UCS2, it may raises this issue when running +super mode obufscated scripts. In this case, try to obfuscate script with +platform ``centos6.x86_64``, it's built with UCS2. For example:: + + pyarmor obfuscate --advanced 2 --platform centos6.x86_64 foo.py + + +NameError: name '__armor_wrap__' is not defined +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If :ref:`Restrict Mode` is set to 4 or 5, it may report this issue. In this case +try to set restrict mode to 2. + +If it's raised in the object method `__del__`, upgrade pyarmor to v6.7.3+, and +obfuscate the scripts again. + +Also refer to :ref:`Using restrict mode with threading and multiprocessing` and +next question. + +Object method `__del__` raise NameError exception +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If method `__del__` raises this exception:: + + NameError: name '__armor_enter__' is not defined + NameError: name '__armor_wrap__' is not defined + +Please upgrade pyarmor to v6.7.3+, and obfuscate the scripts again, and make +sure new runtime package is generated. + +If the scripts is obfuscated by non super mode and python is 3.7 and later, +please obfuscate the scrits by super mode. Or refine the scripts, do not +obfuscate the object method `__del__`. For example + +.. code:: python + + class MyData: + + ... + + def lambda_del(self): + # Real code for method __del__ + ... + + __del__ = lambda_del + +Any function name which starts with `lambda_` will not be obfuscated by pyarmor, +in above example, the method `lambda_del` is not obfuscated, so `__del__` is. + +The other solution is not obfuscating the script which includes ``__del__`` by +copying the plain script to overwrite the obfsucated one. Or obfuscate this +script by ``--obf-code 0``. + +SystemError: module filename missing +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When obfsucating the scripts by super mode, and with outer license, it may +complain of this error if the obfuscated scripts could not find the license file +`license.lic` in the current path. + +If `license.lic` is in the other path, set environment variable `PYARMOR_LICNSE` +to it with full path, for example:: + + export PYARMOR_LICNSE=/path/to/license.lic + +Android protection problem +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Most of Android system don't allow load dynamic library in the data path, but +there is one `_pytransform.so` in the runtime package of the obfuscated scripts, +so it may raise exception like this:: + + dlopen failed: couldn't map "/storage/emulated/0/dist/_pytransform.so" + segment 1: Operation not permitted + +Please consult Android development document, copy the whole folder `pytransform` +to right location where Android allow to load dynamic library, and set +`PYTHONPATH` or any other way only if Python could find and import it. + +libpython3.9.so.1.0: cannot open shared object file +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If missing any python core library such as `python39.dll`, `libpython3.9.so`, +etc, make sure this python interpreter is built with `--enable-shared`. By +default, the runtime extension `pytransform` is linked to python dynamic +library. + +In Linux platform, try to install `libpython3.9` by `apt` or any other +pacakge manage tool. + +Packing Obfuscated Scripts Problem +---------------------------------- + +error: unrecognized arguments +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For option `-e` and `-x`, it need an extra whitespace in option value, otherwise +it will complain of `error: unrecognized arguments`. For exmaple:: + + # Wrong, no heading whitespace before --advanced 2 + pyarmor pack -x "--advanced 2" ... + + # Right + pyarmor pack -x " --advanced 2" ... + +If no `-e` or `-x` is used, please check the man page of :ref:`pack` to +understand all support options. + +The final bundle does not work +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +First of all, please read the man page of :ref:`pack` completely. + +Next make sure the scripts could pack by PyInstaller directly and the final +bundle works. For example:: + + pyinstaller foo.py + dist/foo/foo + +If the final bundle complains of no module found, it need some extra PyInstaller +options, please refer to https://pyinstaller.readthedocs.io + +Then make sure the obfuscated scripts could work without packing. For example:: + + pyarmor obfuscate foo.py + python dist/foo.py + +If both of them OK, remove the output path `dist` and PyInstaller cached path +`build`, then pack the script with ``--debug``:: + + pyarmor pack --debug foo.py + +The build files will be kept, the patched `foo-patched.spec` could be used by +pyinstaller to pack the obfuscated scripts directly, for example:: + + pyinstaller -y --clean foo-patched.spec + +Check this patched `.spec` and change options in this `.spec` file, make sure +the final bundle could work. + +Also refer to :ref:`repack pyinstaller bundle with obfuscated scripts`, make +sure it works by this way. + +No module name pytransform +~~~~~~~~~~~~~~~~~~~~~~~~~~ +If report this error as running command `pyarmor pack`: + +* Make sure the script specified in the command line is not obfuscated +* Run `pack` with extra option ``--clean`` to remove cached `myscript.spec`:: + + pyarmor pack --clean foo.py + +NameError: name ‘__pyarmor__’ is not defined +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Check the traceback to find which script raises this exception, it's helpful to +find the problem: + +* If `pytransform.py` or `pytransform/__init__.py` raises this exception. Make + sure it is not obfuscated, it must be plain script. +* Also check system module `os`, `ctypes`, make sure they're not obfuscated. In + this case, try to exclude the Python system library path, for example:: + pyarmor pack -x " --exclude venv" foo.py + More information refer to :ref:`pack` +* Try to only copy your own scripts to an empty path, then pack it in this path. +* If it works in trial version, but fails after pyarmor is registered, try to + make a :ref:`clean uninstallation` + +PyArmor Registration Problem +---------------------------- + +Purchased pyarmor is not private +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Even obfuscated with purchased version, license from trial version works: + +* Make sure command `pyarmor register` shows correct registration information +* Make a :ref:`clean uninstallation`, and register again +* Make sure the current user is same as the one to register pyarmor +* Make sure environment variable `PYARMOR_HOME` is not set +* Try to reboot system. + +Could not query registration information +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +I tried to register in pyarmor with using the command and log:: + + ~ % pyarmor register pyarmor-regfile-1.zip + INFO PyArmor Version 6.5.2 + INFO Start to register keyfile: pyarmor-regfile-1.zip + INFO Save registration data to: /Users/Jondy/.pyarmor + INFO Extracting license.lic + INFO Extracting .pyarmor_capsule.zip + INFO This keyfile has been registered successfully. + +Watching whether I am registered, I got this output:: + + ~ % pyarmor register + INFO PyArmor Version 6.5.2 + PyArmor Version 6.5.2 + Registration Code: pyarmor-vax-000383 + Because of internet exception, could not query registration information. + +Ping domain `api.dashingsoft.com`, make sure ip address is resolved like this:: + + ~ % ping api.dashingsoft.com + + PING api.dashingsoft.com (119.23.58.77): 56 data bytes + Request timeout for icmp_seq 0 + Request timeout for icmp_seq 1 + +If not, add one line in the ``/etc/hosts``:: + + 119.23.58.77 pyarmor.dashingsoft.com + +Known Issues +------------ + +Obfuscate scripts in cross platform +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +From v5.6.0 to v5.7.0, there is a bug for cross platform. The scripts obfuscated +in linux64/windows64/darwin64 don't work after copied to one of this target +platform:: + + armv5, android.aarch64, ppc64le, ios.arm64, freebsd, alpine, alpine.arm, poky-i586 + +License Questions +----------------- +Refer to :ref:`License Questions` + +Is there anyway we could get an evaluation license +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Q: Is there anyway we could get an evaluation license that lifts the +restriction of large file sizes to test it on our systems? Perhaps restricted to +1-2 days with the promise that we will purchase if it fits our needs. + +A: There is no evaluation license for pyarmor at this time. + +For all of features changed by pyarmor, please check this section +https://pyarmor.readthedocs.io/en/latest/understand-obfuscated-scripts.html#the-differences-of-obfuscated-scripts + +Generally if your scripts don’t use any mentioned features, and any lower +features of Python like visiting frame (sys._getframe), inspecting code object +directly, it should work with pyarmor. + +One license of pyarmor cost only a small amount of money, even purchasing one +for evalution is not too hard to make a decision. + +Misc. Questions +--------------- + +How easy is to recover obfuscated code +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +If someone tries to break the obfuscation, he first must be an expert in the +field of reverse engineer, and be an expert of Python, who should understand the +structure of code object of python, how python interpreter each instruction. If +someone of them start to reverse, he/she must step by step thousands of machine +instruction, and research the algorithm by machine codes. So it's not an easy +thing to reverse pyarmor. + + +How to get receipt or invoice +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +MyCommerce handles all the sales of pyarmor + +Please get help from this page for order/recipt/invoice issue + +https://www.mycommerce.com/shopper-support/ + +Or contact "ClientSupport@MyCommerce.com" directly + +Would pyarmor be able to provide an evaluation license +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Sorry, pyarmor license could be work even offline, so there is no evaluation +license. + +Generally the obfuscated scripts could replace the original scripts +seamlessly. Excpet it uses the features changed by pyarmor, here list all +:ref:`The Differences of Obfuscated Scripts` + +Most of packages could work with pyarmor, for a few packages, pyarmor also works +after patching these packages simplify. Only those packages which visit byte +code or something like this could not work with pyarmor at all. + +.. include:: _common_definitions.txt diff --git a/docs.7/requirements.txt b/docs.7/requirements.txt new file mode 100644 index 00000000..93120e66 --- /dev/null +++ b/docs.7/requirements.txt @@ -0,0 +1 @@ +docutils<0.18 diff --git a/docs/security.rst b/docs.7/security.rst similarity index 98% rename from docs/security.rst rename to docs.7/security.rst index 1f1cad7d..6f809361 100644 --- a/docs/security.rst +++ b/docs.7/security.rst @@ -18,7 +18,7 @@ example, there is a file `foo.py`:: print('1 + 1 = %d' % sum(1, 1)) |PyArmor| first obfuscates the function `hello` and `sum`, then -obfuscates the whole moudle `foo`. In the runtime, only current called +obfuscates the whole module `foo`. In the runtime, only current called function is restored and it will be obfuscated as soon as code object completed execution. So even trace code in any ``c`` debugger, only a piece of code object could be got one time. diff --git a/docs/understand-obfuscated-scripts.rst b/docs.7/understand-obfuscated-scripts.rst similarity index 99% rename from docs/understand-obfuscated-scripts.rst rename to docs.7/understand-obfuscated-scripts.rst index b90b421e..d7fe6b9d 100644 --- a/docs/understand-obfuscated-scripts.rst +++ b/docs.7/understand-obfuscated-scripts.rst @@ -25,7 +25,7 @@ obfuscated scripts, and should not be distributed to the end users. .. important:: The capsule may help others to hack the obfuscated scripts, please do not - share your `private capsuel` to anyone else. + share your `private capsule` to anyone else. Obfuscated Scripts ------------------ diff --git a/docs/usage.rst b/docs.7/usage.rst similarity index 100% rename from docs/usage.rst rename to docs.7/usage.rst diff --git a/docs/ChangeLogs.8 b/docs/ChangeLogs.8 new file mode 100755 index 00000000..59351361 --- /dev/null +++ b/docs/ChangeLogs.8 @@ -0,0 +1,77 @@ +# Pyarmor 8.x Change Logs + +In order to improve security and support Python 3.11, there are significant +changes in Pyarmor 8.0 + +It has been rewritten and new features are implemented through the new commands: +`gen`, `reg`, `cfg`. These commands only work for Python 3.7 and above. + +## New features + +* Support Python 3.11 +* Introduce 3 new commands: gen, cfg, reg +* Introduce BCC Mode, an irreversible obfuscation method +* Introduce RFT mode, an irreversible obfuscation method +* Localize and internationalize runtime error messages +* Check expired date by NTP server + +Full changelogs for all 8.0+, refer to https://github.com/dashingsoft/pyarmor/releases + +## Import Notes for Pyarmor prior to 8.0 + +In future only bug fix for old commands, no new features for old commands. + +The old commands `obfuscate`, `licenses` etc. still could be used, but there +have some changes. + +There are 3 cases for old users after Pyarmor 8.0 is released: + +1. Never upgrade to 8.0+ + + - SPP mode doesn't work + + In order to use SPP mode, it's necessary to upgrade Pyarmor to 8.0+ + + - Command `pyarmor register` without any argument return `404` error + + It is used to query registration information in old Pyarmor, but now license + server doesn't serve this web api. + + Instead use `pyarmor -v` to make sure it's not trial version. + + - Registering Pyarmor by `pyarmor register pyarmor-regcode-xxxxxx.txt` can be + used no more than 10 times + + In order to use Pyarmor in new machine, CI server or docker, check the second + method described in the registration file "pyarmor-regcode-xxxxxx.txt": + + Downloading "pyarmor-regfile-xxxxxx.zip" once, use this `.zip` file to + register Pyarmor later. + +2. Upgrade to 8.0 but only use old features + + By default, command `pyarmor` only accepts new commands, there are 3 ways to + use old commands such as `obfuscate`, `licenses`: + + - Use `pyarmor-7` instead of `pyarmor` + - Export environment variable `PYARMOR_CLI=7`, then still use `pyarmor` + - Any way to call entry point `pyarmor.pyarmor:main_entry` + +3. Upgrade to 8.0 and use new features + + - Follow new [Pyarmor EULA](LICENSE). It's a big change for old personal + license, because new license only allows one proudct. + + - Not all old licenses could be upgraded to new license freely, refer to + [Pyarmor licenses][licenses] + + - The old commands need not internect connection, but new command need + + - The old commands support Python 2.7~3.10, but new command only support + Python 3.7+ + +Refer to +https://github.com/dashingsoft/pyarmor/tree/v8.0#import-notes-for-pyarmor-prior-to-80 +https://github.com/dashingsoft/pyarmor/releases/tag/v8.0 + +[licenses]: https://pyarmor.readthedocs.io/en/latest/licenses.html diff --git a/docs/ChangeLogs.9 b/docs/ChangeLogs.9 new file mode 100755 index 00000000..d0a7d557 --- /dev/null +++ b/docs/ChangeLogs.9 @@ -0,0 +1,79 @@ +# Pyarmor 9 Change Logs + +Because Pyarmor License is paid once in Pyarmor 8, but online verification for using Pyarmor License in CI/CD pipeline will continue consuming a lot of Pyarmor Server resources. + +So Pyarmor 9 has a big change about using Pyarmor in CI/CD pipeline: + +- Pyarmor Pro License couldn't be used in CI/CD pipeline since Pyarmor 9 +- Pyarmor CI License is right for CI/CD pipeline case +- Pyarmor Basic still could be used in CI/CD pipeline, but it need request one extra CI regfile + +In a short + +**If not use Pyarmor License in CI/CD pipeline, almost nothing changed** +**If not upgrade to Pyarmor 9, but still use Pyarmor 8, nothing changed** +**Pyarmor Pro License purchased after 2024-10-20 could not be used in CI/CD pipeline even use it with Pyarmor 8** +**Pyarmor CI License is paid each year, and couldn't be used in local machine** + +## Upgrade Basic or Pro License to Pyarmor 9 + +1. If Pyarmor License has been registered in this device + + - First upgrade to Pyarmor 9 + ```bash + $ pip install -U pyarmor + ``` + + - When first time to obfuscate scripts, it will show hints + ```bash + $ pyarmor gen foo.py + + ... + Pyarmor 9 has big change on CI/CD pipeline + If not using Pyarmor License in CI/CD pipeline + Press "c" to continue + Otherwise press "h" to check Pyarmor 9.0 Upgrade Notes + + Continue (c), Help (h), Quit (q): + ``` + - Just press `c` to continue, there is no prompt later + +2. If Pyarmor License isn't registered in this device + + - First use activation file to generate new registration file + ```bash + $ pip install -U pyarmor + + # Please replace XXX with real product name + $ pyarmor reg -p XXX pyarmor-regcode-xxxx.txt + ``` + - Save and backup new registration file `pyarmor-regfile-xxxx.zip` + + - Use this new regfile to register Pyarmor in other new device + ```bash + $ pyarmor reg pyarmor-regfile-xxxx.zip + $ pyarmor -v + ``` + + **If activation file is used too many times, please first install Pyarmor 8, then upgrade to Pyarmor 9** + +## Upgrade Group License to Pyarmor 9 + +It need generate device regfile again with Pyarmor 9.0+ + +- First upgrade to Pyarmor 9 + ```bash + $ pip install -U pyarmor + ``` + +- Then generate device regfile as before + + For example, generate device regfile `pyarmor-device-regfile-6000.1.zip` for device no. 1 + ```bash + $ pyarmor reg -g 1 /path/to/pyarmor-regfile-6000.zip + ``` + +- Finally, use new one to register Pyarmor in offline device + ```bash + $ pyarmor reg pyarmor-device-regfile-6000.1.zip + ``` diff --git a/docs/ReleasePlan.md b/docs/ReleasePlan.md new file mode 100755 index 00000000..aa8a9837 --- /dev/null +++ b/docs/ReleasePlan.md @@ -0,0 +1,113 @@ +# Release Plan + +The scheduled features for Pyarmor 8+ + +- [Pyarmor 8 (closed)](#plan-features-for-pyarmor-8) +- [Pyarmor 9 (working)](#plan-features-for-pyarmor-9) + +## Plan Features For Pyarmor 8 + +**8.0** + +Status: released + +- Support Python 3.11 +- Introduce 3 new commands: gen, cfg, reg +- Introduce BCC Mode, an irreversible obfuscation method +- Introduce RFT mode, an irreversible obfuscation method +- Localize and internationalize runtime error messages +- Check expired date by NTP server + +**8.1** + +Status: released + +- Support platform darwin.aarch64 (Apple M1), linux.aarch64 +- Support platform windows.x86, linux.x86, linux.armv7 without BCC mode + +**8.2** + +Status: released (2023-05-10) + +- Full documentation +- Group license available +- Support plugins and hooks +- BCC mode for windows.x86, linux.x86, linux.armv7 +- Variable runtime package name for non trial version by `pyarmor cfg package_name_format "xxxx"` +- Clear frame locals by `pyarmor cfg clear_frame_locals 1` +- Improve pack security by + - protect system package + - bind interp + - check debugger + +**8.3** + +Status: relealsed (2023-08-01) + +- Support `--enable-themida` +- pyarmor-webui for 8.0 +- Support multiple Python versions +- Support platforms: Android, Alpine Linux (musl-c/docker), FreeBSD +- Support new arch `loongarch64` for Linux +- Group license supports unlimited dockers which uses default bridge network and not highly customized. + +**8.4** + +Status: released (2023-10-18) + +- Support Python 3.12 + +**8.5** + +Status: released (2024-03-08) + +- Expert users could write their own python code in pyarmor_runtime initialization +- New plugin "post_script" called after each script has been obfuscated +- Runtime key could be obfuscated +- No patch `pyarmor_runtime.so` for Apple Silcon so Windows users could build obfuscated scripts for Apple Silcon + +## Plan Features For Pyarmor 9 + +**9.0** + +Status: released (2024-11-10) + +- Pyarmor.man package to find solutions quickly when something is wrong +- Add special Pyarmor CI License for CI/CD pipeline +- Support Python 3.13 (since v9.0.5) + +**9.1** + +Status: developing + +- Full testcases +- Refine RFT mode to improve performance for big scripts (same as MINI mode) +- MINI mode, a simple RFT mode, the goal is high performance after obfuscation + +**9.2** + +Status: not start + +- ECC mode, mini BCC mode, only transform part of function body to C code +- VMC mode, simple ECC mode, convert function body to VM, not real C code + +The following features are cancelled because ECC/VMC mode are introduced: + +- Convert unsupported node types as far as possible for BCC mode (cancel) +- Expert users could customize C sources generated by BCC mode (cancel) +- BCC mode support cross-platform (may not if too complex) (cancel) +- Obfuscate string constants in extension module "pyarmor_runtime" (cancel) + +**9.3** + +Status: not start + +- Learning system to help users learning and using Pyarmor + +**9.4** + +Status: plan, released on 2025-12-01, 2 months after Python 3.14 is released + +- Support Python 3.14 which will be released on 2025-10-01 as plan + +Pyarmor features will be stable in 2025 diff --git a/docs/_common_definitions.txt b/docs/_common_definitions.txt index a8eed720..54a97b04 100644 --- a/docs/_common_definitions.txt +++ b/docs/_common_definitions.txt @@ -1,18 +1,31 @@ -.. _PyArmor: https://pyarmor.dashingsoft.com/ +.. _Pyarmor: https://pypi.python.org/pypi/pyarmor/ +.. _issues: https://github.com/dashingsoft/pyarmor/issues/ +.. _discussions: https://github.com/dashingsoft/pyarmor/discussions/ +.. _Pyarmor Doc: https://pyarmor.readthedocs.io/en/stable/ +.. _Pyarmor 7.x Doc: https://pyarmor.readthedocs.io/en/v7.7/ +.. _Pyarmor EULA: https://github.com/dashingsoft/pyarmor/blob/master/LICENSE +.. _MyCommerce Shopping: https://order.mycommerce.com/product?vendorid=200089125&productid=301044051 +.. _Pyarmor Shopping Cart: https://jondy.github.io/paypal/index.html -.. |Homepage| replace:: https://pyarmor.dashingsoft.com/ +.. |pyarmor| replace:: :doc:`pyarmor ` +.. |Pyarmor| replace:: :term:`Pyarmor` +.. |Home| replace:: https://github.com/dashingsoft/pyarmor/ +.. |Website| replace:: https://pyarmor.dashingsoft.com/ .. |Manual| replace:: https://pyarmor.readthedocs.io/ -.. |PyArmor| replace:: `PyArmor` -.. |PyArmorVersion| replace:: PyArmor |version| +.. |Author| replace:: Jondy +.. |Contact| replace:: pyarmor@163.com -.. _issues: https://github.com/dashingsoft/pyarmor/issues/ -.. _pypi: https://pypi.python.org/pypi/pyarmor/ -.. _pyarmor-webui: https://pypi.python.org/pypi/pyarmor-webui/ +.. |FAQ| replace:: :doc:`FAQ ` +.. |Python| replace:: :term:`Python` + +.. _Python: https://www.python.org/ +.. _PyPI: https://pypi.python.org/pypi/ .. _PyInstaller: https://www.pyinstaller.org/ .. _Cython: https://cython.org/ -.. _PyUpdater: https://www.pyupdater.org/ -.. _pip: http://www.pip-installer.org/ -.. _ASProtect: http://www.aspack.com/ -.. _VMProtect: https://vmpsoft.com/ .. _NTP: http://www.ntp.org +.. _Themida: https://www.themida.com + +.. _bytecode: https://docs.python.org/3.11/glossary.html#term-bytecode +.. _extension module: https://docs.python.org/3.11/glossary.html#term-extension-module +.. _Packaging binary extensions: https://packaging.python.org/en/latest/guides/packaging-binary-extensions/ diff --git a/docs/conf.py b/docs/conf.py index c907506d..b2abf94b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -48,8 +48,8 @@ master_doc = 'index' # General information about the project. -project = 'PyArmor' -copyright = '2018 - 2020 Dashingsoft Corp.' +project = 'Pyarmor' +copyright = '2018 - 2024 Dashingsoft Corp.' author = 'Jondy Zhao' # The version info for the project you're documenting, acts as replacement for @@ -57,9 +57,9 @@ # built documents. # # The short X.Y version. -version = '7.2' +version = '9.0' # The full version, including alpha/beta/rc tags. -release = '7.2.0' +release = '9.0.8' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -202,7 +202,7 @@ #html_search_scorer = 'scorer.js' # Output file base name for HTML help builder. -htmlhelp_basename = 'PyArmordoc' +htmlhelp_basename = 'Pyarmordoc' # -- Options for LaTeX output --------------------------------------------- @@ -224,7 +224,7 @@ # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - (master_doc, 'PyArmor.tex', 'PyArmor Documentation', + (master_doc, 'Pyarmor.tex', 'Pyarmor Documentation', 'Jondy Zhao', 'manual'), ] @@ -254,7 +254,7 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - (master_doc, 'pyarmor', 'PyArmor Documentation', + (master_doc, 'pyarmor', 'Pyarmor Documentation', [author], 1) ] @@ -268,8 +268,8 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - (master_doc, 'PyArmor', 'PyArmor Documentation', - author, 'PyArmor', 'One line description of project.', + (master_doc, 'Pyarmor', 'Pyarmor Documentation', + author, 'Pyarmor', 'Power tool to obfuscate Python scripts.', 'Miscellaneous'), ] diff --git a/docs/how-to/ci.rst b/docs/how-to/ci.rst new file mode 100644 index 00000000..059f4ee9 --- /dev/null +++ b/docs/how-to/ci.rst @@ -0,0 +1,108 @@ +.. highlight:: console + +============================== + Using Pyarmor in CI Pipeline +============================== + +**Trial Version** could be used in CI/CD pipeline by one step:: + + pip install pyarmor + +For :term:`Pyarmor Basic` and :term:`Pyarmor CI` License + +- Refer to :ref:`initial registration`, first got :term:`registration file` like ``pyarmor-regfile-xxxx.zip`` +- In local device run the following command to request one CI regfile ``pyarmor-ci-xxxx.zip``:: + + $ pyarmor reg -C pyarmor-regfile-xxxx.zip + + Check CI license info in local machine:: + + $ pyarmor --home temp reg pyarmor-ci-xxxx.zip + +- In CI/CD pipeline, add 2 steps to register Pyarmor by CI regfile:: + + # Please replace "9.X.Y" with current Pyarmor version + pip install pyarmor=9.X.Y + pyarmor reg pyarmor-ci-xxxx.zip + + Check registration information in CI/CD pipeline:: + + pyarmor -v + +Notes + +* Do not request CI regfile in CI/CD pipeline +* CI regfile ``pyarmor-ci-xxxx.zip`` will be expired about in 360 days +* CI regfile may not work in future Pyarmor version +* Once CI regfile doesn't work, require new one +* One license can request <= 100 CI regfiles + +.. note:: + + In GitHub Action, it need one extra step, otherwise `CI license only works in CI/CD pipeline` + + 1. For Ubuntu, add this step:: + + - run: sudo mv /dev/disk /dev/disk-none + + 2. For Darwin, add this step:: + + - run: sudo mv /dev/rdisk0 /dev/rdisk0-none + + Refer to this thread `Error when using CI license in CI pipeline `_ + +.. important:: + + In CI/CD pipeline, each run will send license and docker information to Pyarmor License Server, excessive requests or requests beyond normal usage may be rejected by Pyarmor License Server + + It's not allowed to install Pyarmor in your customer's docker image + +:term:`Pyarmor Pro` and :term:`Pyarmor Group` License can't be used in CI/CD pipeline directly, but there is one workaround + +- First obfuscate the scripts in local device and store them to another branch like `master-obf` +- Then in CI/CD pipeline to check this new branch + +Here is an example, suppose test-project locates at `https://github.com/dashingsoft/test-project`, the directory tree as follows:: + + $ tree test-project + + test-project + └── src + ├── main.py + ├── utils.py + └── parent + ├── child + │ └── __init__.py + └── __init__.py + +In local device the scripts are obfuscated and are stored into another branch: + +.. code-block:: bash + + $ git clone https://github.com/dashingsoft/test-project + + $ pip install pyarmor + $ pyarmor reg /path/to/pyarmor-regfile-5068.zip + + # Create new branch + $ git checkout -B master-obf + + # Create output path "dist" link to project path + $ ln -s test-project dist + + # Obfuscate the script to "dist", which is same as "test-project" + # So "dist/src/main.py" is same as "test-project/src/main.py" + $ pyarmor gen -O dist -r --platform windows.x86_64,linux.x86_64,darwin.x86_64 test_project/src + + # Add runtime package to this branch + $ git add -f test_project/pyarmor_runtime_5068/* + + # Commit the results + $ git commit -m'Build obfuscated scripts' . + + # Push new branch to remote server + $ git push -u origin master-obf + +In CI/CD pipeline, it need not install Pyarmor, just checkout branch `master-obf`, and work as before. + +.. include:: ../_common_definitions.txt diff --git a/docs/how-to/obfuscation.rst b/docs/how-to/obfuscation.rst new file mode 100644 index 00000000..ce311c87 --- /dev/null +++ b/docs/how-to/obfuscation.rst @@ -0,0 +1,78 @@ +.. highlight:: console +.. program:: pyarmor gen + +============================ + Protecting system packages +============================ + +.. versionadded:: 8.2 +.. versionchanged:: 8.2.2 + Do not use :option:`--restrict` with :option:`--pack`, it doesn't work. + +When packing the scripts, Pyarmor could also protect system packages in the bundle. The idea is to list all the dependent modules and packages and obfuscate them too. + +Here it's an example to protect system packages for script ``foo.py``. + +We need generate a file ``file.list`` list all the dependent modules and packages of ``foo.py`` by using PyInstaller features. + +First generate ``foo.spec``:: + + $ pyi-makespec foo.py + +Then patch ``foo.spec``: + +.. code-block:: python + + a = Analysis( + ... + ) + + # Patched by Pyarmor to generate file.list + _filelist = [] + _package = None + for _src in sort([_src for _name, _src, _type in a.pure]): + if _src.endswith('__init__.py'): + _package = _src.replace('__init__.py', '') + _filelist.append(_package) + elif _package is None: + _filelist.append(_src) + elif not _src.startswith(_package): + _package = None + _filelist.append(_src) + with open('file.list', 'w') as _file: + _file.write('\n'.join(_filelist)) + # End of patch + +Next pack ``foo.py`` by PyInstaller and generate :file:`file.list` at the same time:: + + $ pyinstaller foo.py + +Finally repack the script with the following options:: + + $ pyarmor gen --assert-call --assert-import --pack dist/foo/foo foo.py @file.list + +This example only guides how to do, please write your own patch script and use other necessary options to obfuscate scripts. For example, you could manually edit :file:`file.list` to meet needs. + +==================== + Fix encoding error +==================== + +The default encoding is ``utf-8``, if encoding error occurs when obfuscating the scripts, set encoding to right one. For example, change default encoding to ``gbk``:: + + $ pyarmor cfg encoding=gbk + +When customizing runtime error message, it also could specify encoding for ``messages.cfg``. For example, set encoding to ``gbk`` by this command:: + + $ pyarmor cfg messages=messages.cfg:gbk + +==================== + Removing docstring +==================== + +It's easy to remove docstring from obfuscated scripts:: + + $ pyarmor cfg optimize 2 + +The argument optimize specifies the optimization level of the compiler; the default value of -1 selects the optimization level of the interpreter as given by -O options. Explicit levels are 0 (no optimization; __debug__ is true), 1 (asserts are removed, __debug__ is false) or 2 (docstrings are removed too). + +.. include:: ../_common_definitions.txt diff --git a/docs/how-to/packing.rst b/docs/how-to/packing.rst new file mode 100644 index 00000000..e290ff66 --- /dev/null +++ b/docs/how-to/packing.rst @@ -0,0 +1,47 @@ +======================== + Packing with outer key +======================== + +.. highlight:: console + +This example shows how to pack ``src/myapp.py`` with :term:`outer key` + +First pack it by PyInstaller:: + + $ pyinstaller myapp.py + +Next obfuscate the script with outer key:: + + $ pyarmor gen --outer --pack dist/myapp/myapp myapp.py + +Then generate an outer key:: + + $ pyarmor gen key -O keylist -e 30 + +For one-folder mode, generally save outer key in the runtime package. For example:: + + $ cp keylist/pyarmor.rkey dist/myapp/pyarmor_runtime_000000/ + +Thus it could run ``dist/myapp/myapp`` in any path. For example:: + + $ dist/myapp/myapp + +For one-file mode, generally store outer key to the same path of executable, and rename it to ``EXECUTABLE.KEYNAME``. For example:: + + $ pyinstaller --onefile myapp.py + $ pyarmor gen --outer --pack dist/myapp myapp.py + $ pyarmor gen key -O keylist -e 30 + $ cp keylist/pyarmor.rkey dist/myapp.pyarmor.rkey + +Thus it could run ``dist/myapp`` in any path. For example:: + + $ dist/myapp + +The outer key also could be stored in a fixed path specified by :envvar:`PYARMOR_RKEY`. For example:: + + $ export PYARMOR_RKEY=/opt/pyarmor/runtime_data + $ mkdir -p /opt/pyarmor/runtime_data + $ cp keylist/pyarmor.rkey /opt/pyarmor/runtime_data/ + $ dist/foo + +.. include:: ../_common_definitions.txt diff --git a/docs/how-to/protection.rst b/docs/how-to/protection.rst new file mode 100644 index 00000000..dcc40ad6 --- /dev/null +++ b/docs/how-to/protection.rst @@ -0,0 +1,131 @@ +================================ + Protecting Runtime Memory Data +================================ + +.. contents:: Contents + :depth: 2 + :local: + :backlinks: top + +.. highlight:: console + +.. program:: pyarmor gen + +Pyarmor focuses on protecting Python scripts, through several irreversible obfuscation methods, Pyarmor makes sure the obfuscated scripts can't be restored by any way. + +But it isn't good at memory protection and anti-debug. If you care about runtime memory data, or runtime key verification, generally it need extra methods to prevent debugger from hacking dynamic libraries. + +Pyarmor could prevent hacker from querying runtime data by valid Python C API and other Python ways, only if the Python interpreter and extension module ``pyarmor_runtime`` are not hacked. This is what extra tools need to protect, the common methods include + +- Signing the binary file to make sure they're not changed by others +- Using third-party binary protection tools to protect Python interpreter and extension module ``pyarmor_runtime`` +- Pyarmor provides some configuration options to check interps and debuggers. +- Pyarmor provides runtime patch feature to let expert users to write C functions or python scripts to improve security. + +.. + In Windows, using :option:`--enable-themida` could prevent from this attack, it could protect extension module ``pyarmor_runtime.pyd`` very well. But in the other platforms, it need extra tools to protect binary extension ``pyarmor_runtime.so``. + +**Basic steps** + +Above all, Python interpreter to run the obfuscated scripts can't be replaced, if the obfuscated scripts could be executed by patched Python interpreter, it's impossible to prevent others to read any Python runtime data. + +At this time Pyarmor need :option:`--pack` to implement this, and need move real code from main script to one module, because :option:`--private` doesn't work for main script. + +First configure necessary items [#]_:: + + $ pyarmor cfg check_debugger=1 check_interp=1 + +Next pack the script by the following options [#]_:: + + $ pyarmor gen --mix-str --assert-call --assert-import --private --pack onedir foo.py real_foo.py + +Then protect all the binary files in the output path :file:`dist/foo/` through external tools, make sure these binary files can not be replaced or modified in runtime. + +Available external tools: codesign, VMProtect + +.. rubric:: Note + +.. [#] Do not use ``check_interp`` in 32-bit x86 platforms, it doesn't work + +.. [#] If pack to one file by PyInstaller, it's not enough to protect this file alone. You must make sure all the binary files extracted from this file are protected too. + +**Hook Scripts** + +Expert users could write :term:`hook script` to check PyInstaller bootstrap modules to improve security. + +Here it's an example to show how to do, note that it may not work in different PyInstaller version, do not use it directly. + +.. code-block:: python + :linenos: + :emphasize-lines: 12-14 + + # Hook script ".pyarmor/hooks/foo.py" + + def protect_self(): + from sys import modules + + def check_module(name, checklist): + m = modules[name] + for attr, value in checklist.items(): + if value != sum(getattr(m, attr).__code__.co_code): + raise RuntimeError('unexpected %s' % m) + + checklist__frozen_importlib = {} + checklist__frozen_importlib_external = {} + checklist_pyimod03_importers = {} + + check_module('_frozen_importlib', checklist__frozen_importlib) + check_module('_frozen_importlib_external', checklist__frozen_importlib_external) + check_module('pyimod03_importers', checklist_pyimod03_importers) + + protect_self() + +The highlight lines need to be replaced with real check list. In order to get baseline, first replace function ``check_module`` with this fake function + +.. code-block:: python + + def check_module(name, checklist): + m = modules[name] + refs = {} + for attr in dir(m): + value = getattr(m, attr) + if hasattr(value, '__code__'): + refs[attr] = sum(value.__code__.co_code) + print(' checklist_%s = %s' % (name, refs)) + + +Run the following command to get baseline:: + + $ pyinstaller foo.py + $ pyarmor gen --pack dist/foo/foo foo.py + + ... + checklist__frozen_importlib = {'__import__': 9800, ...} + checklist__frozen_importlib_external = {'_calc_mode': 2511, ...} + checklist_pyimod03_importers = {'imp_lock': 183, 'imp_unlock': 183, ...} + +Edit hook script to restore ``check_module`` and replace empty check lists with real ones. + +Using this real hook script to generate the final bundle:: + + $ pyinstaller foo.py + $ pyarmor gen --pack dist/foo/foo foo.py + +**Runtime Patch** + +.. versionadded:: 8.3 + +Pyarmor provides runtime patch feature so that users could write one C or python script to do any anti-debug or other checks. It will be embedded into :term:`runtime files`, and called on extension module ``pyarmor_runtime`` initialization. + +First create script :file:`.pyarmor/hooks/pyarmor_runtime.py`, and do some checks in the function :func:`bootstrap`. For example: + +.. code-block:: python + + def bootstrap(user_data): + from ctypes import windll + if windll.kernel32.IsDebuggerPresent(): + print('found debugger') + return False + + +.. include:: ../_common_definitions.txt diff --git a/docs/how-to/register.rst b/docs/how-to/register.rst new file mode 100644 index 00000000..68451643 --- /dev/null +++ b/docs/how-to/register.rst @@ -0,0 +1,594 @@ +======================= + Using Pyarmor License +======================= + +.. contents:: Contents + :depth: 2 + :local: + :backlinks: top + +.. highlight:: console + +.. program:: pyarmor reg + +Prerequisite +============ + +First of all + +1. One :term:`activation file` of :term:`Pyarmor License`, refer to :doc:`../licenses` to purchase right one +2. One device has installed Pyarmor 9.0+ +3. Internet connection +4. Product name which bind to this license + +.. _initial registration: + +Initial registration +==================== + +Any license need this step to request :term:`registration file` from Pyarmor License Server by :term:`activation file` like :file:`pyarmor-regcode-xxxx.txt`:: + + $ pyarmor reg -p "XXX" pyarmor-regcode-xxxx.txt + +Using :option:`-p` to specify product name for this license, please replace "XXX" with real product name. For non-commercial use, replace it to ``non-profits``. + +If initial registration is successful, one :term:`registration file` like :file:`pyarmor-regfile-xxxx.zip` is generated in the current path at the same time. This file is used for subsequent registration in other machines. + +Once initial registration completed, activation file :file:`pyarmor-regcode-xxxx.txt` is invalid, do not use it again. + +Once initial registration completed, product name can't be changed. + +Please backup registration file :file:`pyarmor-regfile-xxxx.zip` carefully. If lost, Pyarmor is not responsible for keeping this license and no lost-found service. + +Product name is not decided +--------------------------- + +When a product is in development, and the product name is not decided. Set product name to ``TBD`` on initial registration. For example:: + + $ pyarmor reg -p "TBD" pyarmor-regcode-xxxx.txt + +In 6 months real product name must be set by this command:: + + $ pyarmor reg -p "XXX" pyarmor-regcode-xxxx.txt + +If it's not changed after 6 months, the product name will be set to ``non-profits`` automatically and can't be changed again. + +Using Pyarmor Basic or Pro +========================== + +1. Refer to :ref:`initial registration`, got :term:`registration file` like `pyarmor-regfile-xxxx.zip` +2. Using :term:`registration file` to register Pyarmor in other devices + +Copy :term:`registration file` to other machines, then run this command:: + + $ pyarmor reg pyarmor-regfile-xxxx.zip + +Check the registration information:: + + $ pyarmor -v + +After successful registration, all obfuscations will automatically apply this license, and each obfuscation requires online license verification. + +This license can register Pyarmor on at most 100 devices + +On each device it's enough to register Pyarmor once, do not register Pyarmor before each obfuscation + +Do not register Pyarmor in the CI/CD pipeline or docker container by this :term:`registration file`, each run will taken as one new device. + +.. seealso:: :doc:`ci` + +.. _using ci license: + +Using CI License +================ + +.. versionadded:: 9.0 + +Refer to :ref:`initial registration`, got :term:`registration file` like `pyarmor-regfile-xxxx.zip` + +Do not use ``pyarmor-regfile-xxxx.zip`` in CI/CD pipeline directly, it's only used to request CI regfile: + +- In local device run the following command to request one CI regfile ``pyarmor-ci-xxxx.zip``:: + + $ pyarmor reg -C pyarmor-regfile-xxxx.zip + + Check CI license info in local machine:: + + $ pyarmor --home temp reg pyarmor-ci-xxxx.zip + +- In CI/CD pipeline, add 2 steps to register Pyarmor by CI regfile:: + + # Please replace "9.X.Y" with current Pyarmor version + pip install pyarmor=9.X.Y + pyarmor reg pyarmor-ci-xxxx.zip + + Check registration information in CI/CD pipeline:: + + pyarmor -v + +Notes + +* Do not request CI regfile in CI/CD pipeline +* CI regfile ``pyarmor-ci-xxxx.zip`` will be expired about in 360 days +* CI regfile may not work in future Pyarmor version +* Once CI regfile doesn't work, require new one +* One license can request <= 100 CI regfiles + +.. important:: + + :term:`Pyarmor CI` License doesn't work in local device + + Even in the CI/CD pipeline, :term:`Pyarmor CI` License also doesn't work in the runner which has its own disk. If the runner is not docker container, use :term:`Pyarmor Pro` License instead. + +.. seealso:: :doc:`ci` + +.. _check device for group license: + +Check Device For Group License +============================== + +Check one device works for group license by this way: + +* First install Pyarmor 8.4.0+ trial version in this device +* Got machine id by the following command:: + + $ pyarmor reg -g 1 + ... + INFO current machine id is "mc92c9f22c732b482fb485aad31d789f1" + INFO device file has been generated successfully + +* Reboot this device, check machine id is same or not +* If machine id is same after each reboot, group license works in this device. Otherwise group license doesn't work in this device. + +For docker container, please check docker host as above. Only if docker host could work with group license, unlimited docker containers could be run in this docker host, refer to :doc:`how-to/register` section ``run unlimited dockers in offline device`` + +**If machine id of docker host is changed after reboot, group license doesn't work in any docker container** + +Most of physics machine, cloud server or VM like qemu, virtual box, vmware with same disk image work with Group license. Most of runners in CI/CD pipeline could not use Group License. + +.. _using group license: + +Using group license +=================== + +.. versionadded:: 8.2 + +Each :term:`Pyarmor Group` could have 100 offline devices, each device has its own number, from 1 to 100. + +Only the machine id of device is not changed after reboot, it could be used as group device. Most of physics machine, cloud server or VM like Qemu, Virtual box, Vmware with same disk image work with Group license. Refer to :ref:`Check Device For Group License` + +The allocated device No. is never free, if a device is reinstalled, it need allocate new one. + +Basic steps: + +1. Using activation file :file:`pyarmor-regcode-xxxx.txt` to initial registration, set product name bind to this license, and generate :term:`registration file` +2. Generating group device file separately on each offline device +3. Using :term:`registration file` and group device file to generate device registration file. +4. Using device registration file to register Pyarmor on offline device [#]_ + +.. [#] The device registration file is bind to specified device, each device has its own device regfile + +Initial registration +-------------------- + +After purchasing :term:`Pyarmor Group`, an activation file :file:`pyarmor-regcode-xxxx.txt` is sent to registration email. + +Initial registration need internet connection and Pyarmor 8.2+. Suppose product name is ``XXX``, then run this command:: + + $ pyarmor reg -p XXX pyarmor-regcode-xxxx.txt + +After initial registration completed, a :term:`registration file` ``pyarmor-regfile-xxxx.zip`` will be generated. + +Group device file +----------------- + +On each offline device, install Pyarmor 8.2+, and generate group device file. For example, on device no. 1, run this command:: + + $ pyarmor reg -g 1 + + INFO Python 3.12.0 + INFO Pyarmor 8.4.7 (trial), 000000, non-profits + INFO Platform darwin.x86_64 + INFO generating device file ".pyarmor/group/pyarmor-group-device.1" + INFO current machine id is "mc92c9f22c732b482fb485aad31d789f1" + INFO device file has been generated successfully + +It will generate group device file ``pyarmor-group-device.1``. + +In order to make sure group license works for this device, reboot this device, and run this command again:: + + $ pyarmor reg -g 1 + + ... + INFO current machine id is "mc92c9f22c732b482fb485aad31d789f1" + ... + +Make sure this machine id is same after reboot. + +Because group license is bind to device, so machine id should keep same after reboot. If it's changed after reboot, group license doesn't work in this device. + +For VM machine, WSL(Windows Subsystem Linux) or any other system, please check the documentation to configure the network and harddisk, make sure network mac address and serial number of harddisk are fixed. If they're volatile, group license could not work in this system. + +Generating offline device regfile +--------------------------------- + +Generating offline device regfile needs an internet connection, Pyarmor 8.2+, group device file ``pyarmor-group-device.1`` and group license :term:`registration file` ``pyarmor-regfile-xxxx.zip``. + +Copying group device file ``pyarmor-group-device.1`` to initial registration device or any computer which has internet connection and registration file, this file must be saved in the path ``.pyarmor/group/``, then run the following command to generate device regfile ``pyarmor-device-regfile-xxxx.1.zip``:: + + $ mkdir -p .pyarmor/group + $ cp pyarmor-group-device.1 .pyarmor/group/ + + $ pyarmor reg -g 1 /path/to/pyarmor-regfile-xxxx.zip + +The device regfile ``pyarmor-device-regfile-xxxx.1.zip`` is bind to machine id in the device file ``pyarmor-group-device.1``. + +.. note:: + + If there are new versions which fix any bug that machine id is changed after this device reboot, it need generate new device file ``pyarmor-group-device.2`` for this device by new Pyarmor version, and generate new device regfile ``pyarmor-device-regfile-xxxx.2.zip`` by new Pyarmor version too. + + Because device no. ``1`` has been used, so it need use next device no. ``2``, that is to say, one device may occupy more than one device no. Generally it should not be problem because there are 100 device no. available. + +Registering Pyarmor in offline device +------------------------------------- + +Once device regfile is generated, copy it to the corresponding device, run this command to register Pyarmor:: + + $ pyarmor reg /path/to/pyarmor-device-regfile-xxxx.1.zip + + INFO Python 3.12.0 + INFO Pyarmor 8.4.7 (trial), 000000, non-profits + INFO Platform darwin.x86_64 + INFO register "/path/to/pyarmor-device-regfile-xxxx.1.zip" + INFO machine id in group license: mc92c9f22c732b482fb485aad31d789f1 + INFO got machine id: mc92c9f22c732b482fb485aad31d789f1 + INFO this machine id matchs group license + INFO This license registration information: + + License Type : pyarmor-group + License No. : pyarmor-vax-006000 + License To : Tester + License Product : btarmor + + BCC Mode : Yes + RFT Mode : Yes + + Notes + * Offline obfuscation + +Note that this log says this device regfile is only for this machine id:: + + INFO machine id in group license: mc92c9f22c732b482fb485aad31d789f1 + +And this log show machine id of this device:: + + INFO got machine id: mc92c9f22c732b482fb485aad31d789f1 + +They must be matched, otherwise this device regfile doesn't work, it may need generate new device regfile for this device. + +Check registration information:: + + $ pyarmor -v + +After successful registration, all obfuscations will automatically apply this group license, and each obfuscation need not online license verification. + +Run unlimited dockers in offline device +--------------------------------------- + +.. versionadded:: 8.3 + +Group license supports unlimited dockers which uses default bridge network and not highly customized, the docker containers use same device regfile of host. + +**how it works** + +1. Each docker host is taken as an offlice device and must be registered as above. + +2. Then start an auth-server in docker host to listen auth-request from docker container. + +3. When run Pyarmor in docker container, it will send auth-request to auth-server in docker host, and verify the result returned from docker host. + +**Linux Docker Host** + +The practice for group license with unlimited docker containers: + +- Docker host, Ubuntu x86_64, Python 3.8 +- Docker container, Ubuntu x86_64, Python 3.11 + +The prerequisite in docker host: + +- offline device regfile ``pyarmor-device-regfile-xxxx.1.zip`` as above +- Pyarmor 8.4.1+ + +First copy the following files to docker host: + +- pyarmor-8.4.2.tar.gz +- pyarmor.cli.core-5.4.1-cp38-none-manylinux1_x86_64.whl +- pyarmor.cli.core-5.4.1-cp311-none-manylinux1_x86_64.whl +- pyarmor-device-regfile-6000.1.zip + +Then run the following commands in the docker host:: + + $ python3 --version + Python 3.8.10 + + $ pip install pyarmor.cli.core-5.4.1-cp38-none-manylinux1_x86_64.whl + $ pip install pyarmor-8.4.1.tar.bgz + +Next start ``pyarmor-auth`` to listen the request from docker containers:: + + $ pyarmor-auth pyarmor-device-regfile-6000.1.zip + + 2023-06-24 09:43:14,939: work path: /root/.pyarmor/docker + 2023-06-24 09:43:14,940: register "pyarmor-device-regfile-6000.1.zip" + 2023-06-24 09:43:15,016: listen container auth request on 0.0.0.0:29092 + +Do not close this console, open another console to run dockers. + +For Linux container run it with extra ``--add-host=host.docker.internal:host-gateway``:: + + $ docker run -it --add-host=host.docker.internal:host-gateway python bash + + root@86b180b28a50:/# python --version + Python 3.11.4 + root@86b180b28a50:/# + +In docker host open third console to copy files to container:: + + $ docker cp pyarmor-8.4.1.tar.gz 86b180b28a50:/ + $ docker cp pyarmor.cli.core-5.4.1-cp311-none-manylinux1_x86_64.whl 86b180b28a50:/ + $ docker cp pyarmor-device-regfile-6000.1.zip 86b180b28a50:/ + +In docker container, register Pyarmor with same device regfile. For example:: + + root@86b180b28a50:/# pip install pyarmor.cli.core-5.4.1-cp311-none-manylinux1_x86_64.whl + root@86b180b28a50:/# pip install pyarmor-8.4.1.tar.gz + root@86b180b28a50:/# pyarmor reg pyarmor-device-regfile-6000.1.zip + root@86b180b28a50:/# pyarmor -v + +If everything is fine, it should print group license information. And then test it with simple script:: + + root@86b180b28a50:/# echo "print('hello world')" > foo.py + root@86b180b28a50:/# pyarmor gen --enable-rft foo.py + +When need to verify license, the docker container will send request to docker host. The `pyarmor-auth` console should print auth request from docker container, if there is no any request, please check docker network configuration, make sure IPv4 addresses of docker host and container are in the same network. For example, in docker container:: + + root@86b180b28a50:/# ifconfig -a + + eth0: flags=4163 mtu 1500 + inet 172.17.0.2 netmask 255.255.0.0 broadcast 172.17.255.255 + ... + +In docker host:: + + $ ifconig -a + docker0: flags=4099 mtu 1500 + inet 172.17.0.1 netmask 255.255.0.0 broadcast 172.17.255.255 + ... + +**MacOS Docker Host** + +There is a little difference when docker host is MacOS, because docker container is running in Linux VM, not in MacOS directly. + +So one solution is running `pyarmor-auth` in Linux VM, in this case, it should take this Linux VM as offline device, and generate device regfile for this Linux VM, not for **MacOS**, and start docker container with extra options:: + + $ docker run --add-host=host.docker.internal:172.17.0.1 ... + +In this case, it may need some extra configuration for Linux VM to make sure the machine id could be fixed. + +Refer to `issue 1542`__ for more information. + +__ https://github.com/dashingsoft/pyarmor/issues/1542 + +**Windows Docker Host** + +For Windows docker host, first check Windows network configuration:: + + C:> ipconfig + + Ethernet adapter vEthernet (WSL): + + Connection-specific DNS Suffix . : + Link-local IPv6 Address . . . . . : fe80::8984:457:2335:588e%28 + IPv4 Address. . . . . . . . . . . : 172.22.32.1 + Subnet Mask . . . . . . . . . . . : 255.255.240.0 + Default Gateway . . . . . . . . . : + +If there is IPv4 Address, for example ``172.22.32.1``, which is in the same network as docker container, it's simple. Just take this Windows as offline device, and run `pyarmor-auth` on it, then start docker container with extra options:: + + $ docker run --add-host=host.docker.internal:172.22.32.1 ... + +Anyway, `pyarmor-auth` must listen on any IPv4 address which is in the same network as docker container. + +If there is no available IPv4 address in Windows, the other solution is running `pyarmor-auth` in WSL, in this case, WSL should be taken as offline device. + +**When something is wrong** + +1. Check docker container network: + +.. code-block:: bash + + root@86b180b28a50:/# ifconfig -a + + eth0: flags=4163 mtu 1500 + inet 172.17.0.2 netmask 255.255.0.0 broadcast 172.17.255.255 + ... + + root@86b180b28a50:/# ping host.docker.internal + PING host.docker.internal (172.17.0.1) 56(84) bytes of data. + 64 bytes from host.docker.internal (172.17.0.1): icmp_seq=1 ttl=64 time=0.048 ms + ... + +If `ping` doesn't works, please check docker host network. If docker host is MacOS, it also checks Linux VM network. If docker host is Windows, also check WSL network. + +And make sure IPv4 address of `host.docker.internal` is in same network as `eth0` which IPv4 address is `172.17.0.2`. In above example, it's `172.17.0.1`, so it's OK. + +If not, also check docker host network. If docker host is MacOS, it may need run `pyarmor-auth` in Linux VM, not MacOS. If docker host is Windows, it may need run `pyarmor-auth` in WSL, not Windows. + +Anyway, please configure the docker host/container network so that `pyarmor-auth` could listen in any IPv4 address which is in the same network as docker container. + +2. Check docker host to make sure group license works:: + + $ pyarmor -d reg pyarmor-device-regfile-6000.1.zip + $ pyarmor -v + +If run `pyarmor-auth` in Linux VM or WSL, please check group license could work in Linux VM or WSL. It may need generate new device regfile for Linux VM or WSL. + +Using multiple Pyarmor Licenses in same device +============================================== + +Generally the registration information is sotred in the Pyarmor :term:`Home Path`, the default value is :file:`~/.pyarmor`. It means + +- All Python virtual environments share same registration information +- It may not work to register other Pyarmor license in same device + +When need many Pyarmor Licenses in one machine, set each license to different path. For example:: + + $ pyarmor --home ~/.pyarmor1 reg pyarmor-regfile-2051.zip + $ pyarmor --home ~/.pyarmor1 gen project1/foo.py + + $ pyarmor --home ~/.pyarmor2 reg pyarmor-regfile-2052.zip + $ pyarmor --home ~/.pyarmor2 gen project2/foo.py + +.. _pyarmor: + +What need to do after upgrading Pyarmor +======================================= + +Generally it need do nothing after upgrading Pyarmor, the registration information still works. + +But in the following versions something is changed + +- **Pyarmor 8.0** + + Old license for Pyarmor 7 doesn't work + + - Some old licenses can be upgraded to Basic License freely, refer to :ref:`upgrade old license ` + - Old license can't be upgraded to Pro or Group License + +- **Pyarmor 9.0** + + A big change about using Pyarmor in CI/CD pipeline + + - :term:`Pyarmor Basic` + + - :ref:`upgrade to pyarmor 9` freely + - If using Pyarmor in CI/CD pipeline, refer to :doc:`ci` + + - :term:`Pyarmor Pro` + + - If not using Pyarmor in CI/CD pipeline, :ref:`upgrade to pyarmor 9` freely + - If using Pyarmor in CI/CD pipeline, 2 choices + + - Still use Pyarmor 8.x as before + - Upgrade to Pyarmor 9, and purchase new :term:`Pyarmor CI` + + - :term:`Pyarmor Group` + + It need generate device regfile for each offline device again by Pyarmor 9.0+, refer to :ref:`upgrade to pyarmor 9` + +.. _upgrading old license: + +Upgrading old license +--------------------- + +Not all the old license (Pyarmor 7) could be upgraded to latest version. + +The old license could be upgraded to Pyarmor Basic freely only if it matches these conditions: + +* Following new `Pyarmor EULA`_ +* The license no. starts with ``pyarmor-vax-`` +* The original activation file ``pyarmor-regcode-xxxx.txt`` exists and isn't used more than 100 times +* The old license is purchased before June 1, 2023. In principle, the old license purchased after Pyarmor 8 is available could not be upgraded to new license. + +If failed to upgrade the old license, please purchase new license to use Pyarmor latest version. + +The old license can't be upgraded to Pyarmor Pro and Group. + +**Upgrading old license to Pyarmor Basic** + +First find the activation file ``pyarmor-regcode-xxxx.txt``, which is sent to registration email when purchasing the license. + +Next install Pyarmor 8.2+, according to new `EULA of Pyarmor`_, each license is only for one product. + +Assume this license will be used to obfuscate product ``XXX``, run this command:: + + $ pyarmor reg -u -p "XXX" pyarmor-regcode-xxxx.txt + +Check the upgraded license information:: + + $ pyarmor -v + +After upgrade successfully, do not use activation file ``pyarmor-regcode-xxxx.txt`` again, it's invalid now. A new :term:`registration file` like :file:`pyarmor-regfile-xxxx.zip` will be generated at the same time. + +In other devices using this new :term:`registration file` to register Pyarmor by this command:: + + $ pyarmor reg pyarmor-regfile-xxxx.zip + +After successful registration, all obfuscations will automatically apply this license, and each obfuscation requires online license verification. + +If old license is used by many products (mainly old personal license), only one product could be used after upgrading. For the others, it need purchase new license. + +.. _upgrade to pyarmor 9: + +Upgrade to Pyarmor 9 +-------------------- + +1. :term:`Pyarmor Basic` and :term:`Pyarmor Pro` + + **If Pyarmor License has been registered in this device** + + - First upgrade to Pyarmor 9:: + + $ pip install -U pyarmor + + - When first time to obfuscate scripts, it will show hints:: + + $ pyarmor gen foo.py + + ... + Pyarmor 9 has big change on CI/CD pipeline + If not using Pyarmor License in CI/CD pipeline + Press "c" to continue + Otherwise press "h" to check Pyarmor 9.0 Upgrade Notes + + Continue (c), Help (h), Quit (q): + + - Just press :kbd:`c` to continue, there is no prompt later + + **If Pyarmor License isn't registered in this device** + + - First use :term:`activation file` to generate new :term:`registration file`:: + + $ pip install -U pyarmor + + # Please replace XXX with real product name + $ pyarmor reg -p XXX pyarmor-regcode-xxxx.txt + + - Save and backup new :term:`registration file` ``pyarmor-regfile-xxxx.zip`` + + - Use this new regfile to register Pyarmor in other new device:: + + $ pyarmor reg pyarmor-regfile-xxxx.zip + $ pyarmor -v + + If :term:`activation file` is used too many times, please first install Pyarmor 8, then upgrade to Pyarmor 9 + +2. :term:`Pyarmor Group` License + + It need generate device regfile again with Pyarmor 9.0+ + + - First upgrade to Pyarmor 9:: + + $ pip install -U pyarmor + + - Then generate device regfile as before + + For example, generate device regfile ``pyarmor-device-regfile-6000.1.zip`` for device no. 1:: + + $ pyarmor reg -g 1 /path/to/pyarmor-regfile-6000.zip + + - Finally, use new one to register Pyarmor in offline device:: + + $ pyarmor reg pyarmor-device-regfile-6000.1.zip + +.. include:: ../_common_definitions.txt diff --git a/docs/how-to/security.rst b/docs/how-to/security.rst new file mode 100644 index 00000000..ebbbf5b0 --- /dev/null +++ b/docs/how-to/security.rst @@ -0,0 +1,109 @@ +================================= + Highest security and performance +================================= + +.. contents:: Contents + :depth: 2 + :local: + :backlinks: top + +.. highlight:: console + +.. program:: pyarmor gen + +What's the most security pyarmor could do? +========================================== + +The following options could improve security + +* :option:`--enable-rft` almost doesn't impact performance +* :option:`--enable-bcc` may be a little faster than a plain script, but it consumes more memory to load binary code +* :option:`--enable-jit` prevents static decompilation +* :option:`--enable-themida` prevents most of debuggers, only available in Windows, and reduces performance remarkably +* :option:`--mix-str` protects string constants in the script +* ``pyarmor cfg mix_argnames=1`` may broken annotations +* :option:`--obf-code` ``2`` could make it more difficult to reverse byte code + +The following options hide module attributes + +* :option:`--private` +* :option:`--restrict` also not allow plain script import obfuscated module + +The following options prevent functions or modules from being replaced by hack code + +* :option:`--assert-call` +* :option:`--assert-import` + +What's the best performance pyarmor could do? +============================================= + +Using default options and the following settings + +* :option:`--obf-code` ``0`` +* :option:`--obf-module` ``0`` +* ``pyarmor cfg restrict_module=0`` + +With these options, the security is almost the same as `.pyc` + +In order to improve security, and doesn't reduce performance, also enable RFT mode + +* :option:`--enable-rft` + +If there are sensitive strings, enable mix-str with filter + +* ``pyarmor cfg mix.str:includes "/regular expression/"`` +* :option:`--mix-str` + +Without the filter, all of the string constants in the scripts are encrypted, which may reduce performance. Using filter only encrypt the sensitive string may balance security and performance. + +Recommended options for different applications +============================================== + +**For Django application or serving web request** + + If RFT mode is safe enough, you can check the transformed scripts to make a decision, using these options + + * :option:`--enable-rft` + * :option:`--obf-code` ``0`` + * :option:`--obf-module` ``0`` + * :option:`--mix-str` with filter + + If RFT mode is not safe enough, using these options + + * :option:`--enable-rft` + * :option:`--no-wrap` + * :option:`--mix-str` with filter + +**For most applications and packages** + + If RFT mode and BCC mode are available + + * :option:`--enable-rft` + * :option:`--enable-bcc` + * :option:`--mix-str` with filter + * :option:`--assert-import` + + If RFT mode and BCC mode are not available + + * :option:`--enable-jit` + * :option:`--private` or :option:`--restrict` + * :option:`--mix-str` with filter + * :option:`--assert-import` + * :option:`--obf-code` ``2`` + + If care about monkey trick, also + + * :option:`--assert-call` with inline marker to make sure all the key functions are protected + + If it's not performance sensitive, using :option:`--enable-themida` prevent from debuggers + +Reforming scripts to improve security +===================================== + +**Move main script module level code to other module** + +Pyarmor will clear the module level code after the module is imported, the injected code could not get any module level code because it's gone. + +But the main script module level code is never cleared, so moving unnecessary code here to another module could improve security. + +.. include:: ../_common_definitions.txt diff --git a/docs/how-to/third-party.rst b/docs/how-to/third-party.rst new file mode 100644 index 00000000..795ae989 --- /dev/null +++ b/docs/how-to/third-party.rst @@ -0,0 +1,225 @@ +================================= + Work with Third-Party Libraries +================================= + +.. contents:: Contents + :depth: 2 + :local: + :backlinks: top + +.. highlight:: console + +.. program:: pyarmor gen + +There are countless big packages in the Python world, many packages I never use and which I don't know at all. It's also not easy for me to research a complex package to find which line conflicts with pyarmor, and it's difficult for me to run all of these complex packages on my local machine. + +Pyarmor provides rich options to meet various needs, for complex applications, please spend some time checking :doc:`../reference/man` to understand all of these options, one of them may be just for your problem. **I won't learn your application and tell you should use which options** + +I'll improve pyarmor and make it work with other libraries as far as possible, but some issues can't be fixed from Pyarmor side. + +Generally most of problems for these third party libraries are + +* they try to use low level object `frame` to get local variable or other runtime information of obfuscated scripts +* they try to visit code object directly to get something which is just pyarmor protected. The common case is using :mod:`inspect` to get source code. +* they pickle the obfuscated code object and pass it to other processes or threads. + +Also check :ref:`the differences of obfuscated scripts`, if third party library uses any feature changed by obfuscated scripts, it will not work with pyarmor. Especially for :term:`BCC mode`, it changes more. + +The common solutions to fix third-party libraries issue + +- Use RFT mode with ``--obf-code=0`` + + RFT mode almost doesn't change internal structure of code object, it transforms the script in source level. :option:`--obf-code` is also required to disable code object obfuscation. The recommended options are like this:: + + $ pyarmor gen --enable-rft --obf-code 0 /path/to/myapp + + First make sure it works, then try other options. For example:: + + $ pyarmor gen --enable-rft --obf-code 0 --mix-str /path/to/myapp + $ pyarmor gen --enable-rft --obf-code 0 --mix-str --assert-call /path/to/myapp + +- Ignore problem scripts + + If only a few scripts are in trouble, try to obfuscate them with ``--obf-code 0``. For example, if only module ``config.py`` has problem, all the other are fine, then:: + + $ pyarmor cfg -p myapp.config obf_code=0 + $ pyarmor gen [other options] /path/to/myapp + + Another way is to copy plain script to overwrite the obfuscated one roughly:: + + $ pyarmor gen [other options] /path/to/myapp + $ cp /path/to/myapp/config.py dist/myapp/config.py + +- Patch third-party library + + Here is an example + + .. code-block:: python + + @cherrypy.expose(alias='myapi') + @cherrypy.tools.json_out() + # pylint: disable=no-member + @cherrypy.tools.authenticate() + @cherrypy.tools.validateOptOut() + @cherrypy.tools.validateHttpVerbs(allowedVerbs=['POST']) + # pylint: enable=no-member + def abc_xyz(self, arg1, arg2): + """ + This is the doc string + """ + + If call this API with alias name "myapi" it throws me 404 Error and the API's which do not have any alias name works perfectly. Because ``cherrypy.expose`` decorator uses + + .. code-block:: python + + parents = sys._getframe(1).f_locals + + And ``sys._getframe(1)`` return unexpected frame in obfuscated scripts. But it could be fixed by patching this decorator to + + .. code-block:: python + + parents = sys._getframe(2).f_locals + + .. note:: + + If :mod:`cheerypy` is also used by others, clone private one. + +Third party libraries +===================== + +Here are the list of problem libraries and possible solutions. You are welcome to create a pull request to append new libraries (sort alphabetically case insensitivity). + +.. list-table:: Table-1. Third party libraries + :header-rows: 1 + + * - Package + - Status + - Remark + * - cherrypy + - patch work [#patch]_ + - use sys._getframe + * - `pandas`_ + - patch work [#patch]_ + - use sys._getframe + * - playwright + - patch should work [#RFT]_ + - Not verify yet + * - `nuitka`_ + - Should work with restrict_module = 0 + - Not verify yet + * - `Cython`_ + - Should work with restrict_module = 0 + - + +.. rubric:: Footnotes + +.. [#patch] the patched package could work with Pyarmor +.. [#RFT] this package work with Pyarmor RFT mode +.. [#obfcode0] this package only work with ``--obf-code 0`` +.. [#not] this package not work with Pyarmor any mode + +pandas +------ + +Another similar example is :mod:`pandas` + +.. code-block:: python + + import pandas as pd + + class Sample: + def __init__(self): + self.df = pd.DataFrame( + data={'name': ['Alice', 'Bob', 'Dave'], + 'age': [11, 15, 8], + 'point': [0.9, 0.1, 0.4]} + ) + + def func(self, val: float = 0.5) -> None: + print(self.df.query('point > @val')) + + sampler = Sample() + sampler.func(0.3) + +After obfuscated, it raises:: + + pandas.core.computation.ops.UndefinedVariableError: local variable 'val' is not defined + +It could be fixed by changing ``sys._getframe(self.level)`` to ``sys._getframe(self.level+1)``, ``sys._getframe(self.level+2)`` or ``sys._getframe(self.level+3)`` in ``scope.py`` of pandas. + +nuitka +------ + +Because the obfuscated scripts could be taken as normal scripts with an extra runtime package, they also could be translated to C program by Nuitka. + +I haven't tested it, but it's easy to verify it. + +First disable restrict mode:: + + $ pyarmor cfg restrict_module=0 + +Next use default options to obfuscate the scripts:: + + $ pyarmor gen foo.py + +Finally nuitka the obfuscated script ``dist/foo.py``, check whether it works or not. + +Try more options, but I think restrict options such as :option:`--private`, :option:`--restrict`, :option:`--assert-call`, :option:`--assert-import` may not work. + +.. note:: + + It may requires v9.0.8+ and non-trial version. Because Nuitka will convert package `pyarmor_runtime_000000/__init__.py` to `pyarmor_runtime_000000_init_.py`, it also results in ``RuntimeError: unauthorized use of script``, this is fixed in v9.0.8 + +streamlit +--------- + +It need change default configurations. At least:: + + $ pyarmor cfg restrict_module=0 + $ pyarmor cfg clear_module_co=0 + +This first one solves issue `RuntimeError: unauthorized use of script (1:1102)` + +Then second one solves issue `RuntimeError: the format of obfuscated script is incorrect (1:1082)` + +Now obfuscate the scripts:: + + $ pyarmor gen foo.py + +**It may still not work because of Streamlit may patch code object by itself** + +Cython +------ + +Here it's an example show how to `cythonize` a python script `foo.py` obfuscated +by pyarmor:: + + print('Hello Cython') + +First obfuscate it with some extra options:: + + $ pyarmor cfg restrict_module=0 + $ pyarmor gen foo.py + $ ls dist/ + foo.py pyarmor_runtime_000000 + +The obfuscated script and runtime files will be saved in the path `dist` + +Next `cythonize` the obfuscated script `dist/foo.py` to `foo.c`:: + + $ cd dist + $ cythonize -3 foo.py + +Then compile `foo.c` to the extension modules(it may need extra cfalg ``-fPIC`` in some platforms):: + + $ gcc -shared $(python-config --cflags) $(python-config --ldflags) \ + -o foo$(python-config --extension-suffix) foo.c + +Finally test it, remove `dist/foo.py` and import the extension module:: + + $ rm foo.py + $ python -c 'import foo' + +It will print `Hello Cython` as expected. + +.. include:: ../_common_definitions.txt diff --git a/docs/how-to/wheel.rst b/docs/how-to/wheel.rst new file mode 100644 index 00000000..962c4b41 --- /dev/null +++ b/docs/how-to/wheel.rst @@ -0,0 +1,173 @@ +.. highlight:: console + +=========================== + Building obfuscated wheel +=========================== + +The test-project hierarchy is as follows:: + + $ tree test-project + + test-project + ├── MANIFEST.in + ├── pyproject.toml + ├── setup.cfg + └── src + └── parent + ├── child + │ └── __init__.py + └── __init__.py + +4 directories, 5 files + +The content of ``MANIFEST.in`` is:: + + recursive-include dist/parent/pyarmor_runtime_00xxxx *.so + +The content of ``pyproject.toml`` is: + +.. code-block:: ini + + [build-system] + requires = [ + "setuptools>=66.1.1", + "wheel" + ] + build-backend = "setuptools.build_meta" + +The content of ``setup.cfg`` is: + +.. code-block:: ini + + [metadata] + name = parent.child + version = attr: parent.child.VERSION + + [options] + package_dir = + =dist/ + + packages = + parent + parent.child + parent.pyarmor_runtime_00xxxx + + include_package_data = True + +:file:`src/parent/__init__.py` and :file:`src/parent/child/__init__.py` are the same: + +.. code-block:: python + + VERSION = '0.0.1' + +First obfuscate the package:: + + $ cd test-project + $ pyarmor gen --recursive -i src/parent + +After successful execution the output is the following directory:: + + $ tree dist + + dist + └── parent + ├── child + │ ├── __init__.py + │ └── __pycache__ + │ └── __init__.cpython-311.pyc + ├── __init__.py + └── pyarmor_runtime_00xxxx + ├── __init__.py + └── pyarmor_runtime.so + +Next, build the wheel package:: + + $ python -m build --skip-dependency-check --no-isolation + +Unfortunately it raises exception: + +.. code-block:: python + + * Building sdist... + Traceback (most recent call last): + File "/usr/lib/python3/dist-packages/setuptools/config/expand.py", line 81, in __getattr__ + return next( + ^^^^^ + StopIteration + + The above exception was the direct cause of the following exception: + + Traceback (most recent call last): + File "/usr/lib/python3/dist-packages/setuptools/config/expand.py", line 191, in read_attr + return getattr(StaticModule(module_name, spec), attr_name) + +From traceback we found it uses ``StaticModule``, then check the source ``/usr/lib/python3/dist-packages/setuptools/config/expand.py`` at line 191 to find class ``StaticModule`` definition. By the source code we know it uses ``ast.parse`` to parse source code directly to get locals. It's impossible for obfuscated scripts, in order to fix this problem, we need insert a line in the ``dist/parent/child/__init__.py`` like this: + +.. code-block:: python + + from pyarmor_runtime_00xxxx import __pyarmor__ + VERSION = '0.0.1' + ... + +But pyarmor doesn't allow to change obfuscated scripts by default, it need disable this restriction by this command:: + + $ pyarmor cfg -p parent.child.__init__ restrict_module = 0 + $ pyarmor gen --recursive -i src/parent + +The option :option:`pyarmor cfg -p` ``parent.child.__init__`` lets pyarmor disable this restriction only for ``parent/child/__init__.py``. + +Now patch ``dist/parent/child/__init__.py`` and rebuild wheel:: + + $ python -m build --skip-dependency-check --no-isolation + +**Rename runtime package and store it in sub-package** + +If you would rather to rename runtime package to ``libruntime`` and store it in the sub-package ``parent.child``, you need change the content of ``MANIFEST.in`` to:: + + recursive-include dist/parent/child/libruntime *.so + +and change the content of ``setup.cfg`` to:: + + [options] + ... + packages = + parent + parent.child + parent.child.libruntime + ... + +And obfuscate the scripts by these configurations:: + + $ pyarmor cfg package_name_format "libruntime" + $ pyarmor gen --recursive --prefix parent.child src/parent + +Don't forget to patch ``dist/parent/child/__init__.py``, then build wheel:: + + $ python -m build --skip-dependency-check --no-isolation + +**Further more** + +In order to patch ``dist/parent/child/__init__.py`` automatically, you can write a plugin script ``.pyarmor/myplugin.py``: + +.. code-block:: python + + __all__ = ['VersionPlugin'] + + class VersionPlugin: + + @staticmethod + def post_build(ctx, inputs, outputs, pack): + script = os.path.join(outputs[0], 'parent', 'child', '__init__.py') + with open(script, 'a') as f: + f.write("\nVERSION = '0.0.1'") + +And enable this plugin:: + + $ pyarmor cfg plugins + "myplugin" + +After that, each build only run the following commands:: + + $ pyarmor gen --recursive --prefix parent.child src/parent + $ python -m build --skip-dependency-check --no-isolation + +.. include:: ../_common_definitions.txt diff --git a/docs/index.rst b/docs/index.rst index 36d0a252..76469f12 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,57 +1,65 @@ -.. pyarmor documentation master file, created by - sphinx-quickstart on Sat Dec 1 11:22:25 2018. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - -PyArmor's Documentation -======================= - -:Version: |PyArmorVersion| -:Homepage: |Homepage| -:Contact: jondy.zhao@gmail.com -:Authors: Jondy Zhao +================================= + Pyarmor |version| Documentation +================================= + +:Version: |release| +:Homepage: |Website| +:Contact: |Contact| +:Authors: |Author| :Copyright: This document has been placed in the public domain. +.. important:: + + New features introduced by Pyarmor 9.1.0 are moved to Pyarmor Learning System + + https://eke.dashingsoft.com/pyarmor/docs/en/index.html + + It includes 3 commands: `pyarmor init`, `pyarmor env`, `pyarmor build` + + And new obfuscated script types: `rft`, `mini`, `vmc`, `ecc` + +How the documentation is organized +================================== + +|Pyarmor| has a lot of documentation. A high-level overview of how it's organized will help you know where to look for certain things: -|PyArmor| is a command line tool used to obfuscate python scripts, -bind obfuscated scripts to fixed machine or expire obfuscated -scripts. It protects Python scripts by the following ways: +* :doc:`Part 1: Tutorials ` takes you by the hand through a series of steps to obfuscate |Python| scripts and packages. Start here if you're new to |Pyarmor|. Also look at the :doc:`tutorial/getting-started` -* Obfuscate code object to protect constants and literal strings. -* Obfuscate co_code of each function (code object) in runtime. -* Clear f_locals of frame as soon as code object completed execution. -* Verify the license file of obfuscated scripts while running it. +* :doc:`Part 2: How To ` guides are recipes. They guide you through the steps involved in addressing key problems and use-cases. They are more advanced than tutorials and assume some knowledge of how |Python| works. -|PyArmor| supports Python 2.6, 2.7 and Python 3. +* :doc:`Part 3: References ` guides contain key concepts, man page, configurations and other aspects of |Pyarmor| machinery. -|PyArmor| is tested against ``Windows``, ``Mac OS X``, and ``Linux``. +* :doc:`Part 4: Topics ` guides insight into key topics and provide useful background information and explanation. They describe how it works and how to use it but assume that you have a basic understanding of key concepts. -|PyArmor| has been used successfully with ``FreeBSD`` and embedded -platform such as ``Raspberry Pi``, ``Banana Pi``, ``Orange Pi``, ``TS-4600 / TS-7600`` etc. -but is not fullly tested against them. +* :doc:`Part 5: Licenses ` describes EULA of |Pyarmor|, the different |Pyarmor| licenses and how to purchase |Pyarmor| license. -Contents: +Getting help +============ + +Having trouble? + +Try the :doc:`FAQ ` – it's got answers to many common questions. + +Looking for specific information? Try the :ref:`genindex`, or :ref:`the detailed table of contents `. + +Not found anything? See :ref:`asking questions in github `. + +Report bugs with Pyarmor_ in issues_ + +Table of Contents +================= .. toctree:: - :maxdepth: 2 - - installation - usage - advanced - build-wheel - examples - project - man - understand-obfuscated-scripts - how-to-do - pytransform - platforms - mode - performance - security + :numbered: + :maxdepth: 3 + :name: mastertoc + + part-1 + part-2 + part-3 + part-4 + licenses questions - license - change-logs Indices and tables ================== diff --git a/docs/licenses.rst b/docs/licenses.rst new file mode 100644 index 00000000..30a488bb --- /dev/null +++ b/docs/licenses.rst @@ -0,0 +1,382 @@ +=============== + License Types +=============== + +.. contents:: Contents + :depth: 2 + :local: + :backlinks: top + +.. highlight:: console + +This documentation is only apply to Pyarmor_ 8.0 plus. + +Pyarmor is published in `PyPI`_, free trial version never expires. Try it by the following commands:: + + $ pip install pyarmor + $ pyarmor gen foo.py + $ python dist/foo.py + +There are some limitations in free version, for example, can't obfuscate big scripts etc. These limitations can be unlocked by different license types. Pyarmor has 4 kind of licenses: + +- Basic +- Pro: Irreversible Obfuscation +- Group: Offline build +- CI: for CI/CD pipeline, new in v9.0 + +.. list-table:: Table-1. Compare Different Licenses + :widths: 40 12 12 12 12 12 + :header-rows: 1 + :stub-columns: 1 + + * - Feature + - Free + - Basic + - Pro + - Group + - CI + * - Big Script / Mix String [#]_ + - + - Y + - Y + - Y + - Y + * - BCC / RFT / FLY mode [#]_ + - + - + - Y + - Y + - Y + * - Offline build [#]_ + - + - + - + - Y + - + * - Maximum build devices [#]_ + - + - 100 + - 100 + - 200 + - 0 + * - Unlimited local dockers [#]_ + - + - + - + - Y + - + * - Work in CI/CD pipeline [#]_ + - Y + - Y + - + - + - Y + +.. rubric:: Notes + +.. [#] + - Big Script: file size exceeds a certain value. + - Mix Str: obfuscating string constant in script +.. [#] + - RFT Mode: renaming function/class/method/variable in Python scripts + - BCC Mode: Transforming some Python functions in scripts to c functions, compile them to machine instructions directly +.. [#] Offline build: the build device need not be online to verify Pyarmor License +.. [#] Maximum devices could install Pyarmor, each docker run will be taken as one new build device, CI license only works in CI/CD pipeline +.. [#] Unlimited local dockers: run docker container in local machine, may be offline or in private network +.. [#] Work in CI/CD pipeline: it need special option to register Pyarmor in CI/CD pipeline. + +.. important:: + + CI license doesn't work in the runner which has its own disk. + + If the runner is not docker container, use Pro license instead. + +.. important:: + + All Pyarmor Licenses are only used to generate the obfuscated scripts in build device + + It need neither install Pyarmor nor verify Pyarmor License to execute obfuscated scripts + + The obfuscated scripts are generated by Pyarmor, but they are completely independent of Pyarmor + + You can take them as normal Python scripts, so what the obfuscated scripts do is controlled by you, not by Pyarmor + +Terms of Use +============ + +1. Only use Pyarmor on your own scripts + + In any case, even you have purchased Pyarmor License, it's not allowed to obfuscate other scripts of which you haven't property. For example, call pyarmor in your app to obfuscate your customer's scripts, provide obfuscation services based on Pyarmor by website etc. + +2. No profit no license required + + Free version can be used to obfuscate your scripts which COULD NOT make lot of money for you. Otherwise it need purchase Pyarmor License. + +3. One product one license + + One product means one kind of product, not one copy of product. For example, Micorsoft Excel is one product, even it's installed on countless devices + + Each license has an unique number, the format is ``pyarmor-vax-xxxxxx``, which x stands for a digital. + +4. Pay once + + Except CI License, all the other licenses work forever with Pyarmor version when this license is purchased, but **may not work in future Pyarmor versions** + +5. Fair use + + If you have purchased Pyarmor License for one product, but you have another product, and the total revenue of the other project is less than 100 x Pyarmor License fee, you could rent this license in your aother product + + Pyarmor CI License has rate limit in CI/CD pipeline + +In details check `Pyarmor EULA`_ + +.. seealso:: :doc:`Using Pyarmor Licenses ` + +Privacy +======= + +License No. and product name will be embedded into obfuscated scripts, all the other user's information, for example, regname, email are not + +For Pyarmor Basic and Pro License, only Pyarmor License file, serial number of hard disk, Ethernet address, IPv4/IPv6 address, and hostname will be sent to Pyarmor License Server for verification + +When using Basic or CI License in CI/CD pipeline, some information about docker like docker name, ethernet address, IPv4/IPv6 address, and license information will be sent to Pyarmor License Server for verification + +No any user script will be uploaded to Pyarmor License Server + +Technical Support +================= + +License fees only for unlock features, not include technical supports + +Users need to learn Pyarmor features and how to use it by themself. Generally Pyarmor Team won't help to debug users' case and teach them how to use Pyarmor + +Pyarmor provides comprehensive learning systems, including but not limited to the following ways: + +- :doc:`Online documentation ` +- :doc:`Checklist ` and :doc:`FAQs ` could fix 90% issues reported to Pyarmor Team +- `Discussions`_ in Pyarmor project home +- Full examples to show each option usage and common cases by command ``pyarmor man`` +- Learn Pyarmor concepts by figure and animations in EKE Learning Platform (coming soon) + +Rome was not bulit in a day. Pyarmor Team keeps improving documentation and learning systems according to users feedback to make it easy and effects + +Report bugs and request new features in Pyarmor project home, Email to pyarmor@163.com is only for security and private issues, there may no reply for common technical issues. + +Pyarmor team generally will handle submitted issues within 24 hours (working time), but may be extended during holidays or special circumstances + +Pyarmor Team doesn't provide any instant technical support by telphone or other similar tools. + +Purchasing license +================== + +If you have Pyarmor 8.6+ installed, this command also could open shopping cart:: + + $ pyarmor reg --buy + +Open `Pyarmor Shopping Cart`_ in any web browser: + + https://jondy.github.io/paypal/index.html + +All of these license are only for Pyarmor 8.0+ with Python 3.7+, if need work with Pyarmor 7.x which supports Python < 3.7, please purchase `Pyarmor Old License`: + + https://jondy.github.io/paypal/obsolete.html + +Refund policy +============= + +If activation file isn't used, and purchasing date is in 30 days, refund is acceptable. + +- If purchasing order from MyCommerce: + + 1. Email to Ordersupport@mycommerce.com with order information and ask for refund. + 2. Or click `FindMyOrder page`_ to submit refund request + +- If purchasing order from reseller, contact your reseller + +- For other cases, email to pyarmor@163.com + +Out of 30 days, or activation file has been used, refund request will be rejected. + +.. _FindMyOrder page: https://www.findmyorder.com/store?Action=DisplayEmailCustomerServicePage&Env=BASE&Locale=en_US&SiteID=findmyor + +Appendix +======== + +What is one product +------------------- + +First of all, if not for sale, all the Python scripts are belong to one product "non-profits". + +One product in Pyarmor world means a product name and everything that makes up this name. + +It includes all the devices to develop, build, debug, test product. + +It also includes product current version, history versions and all the future versions. + +One product may has several variants, each variant name is composed of product name plus feature name. As long as the proportion of the variable part is far less than that of the common part, they're considered as "one product". + +Pyarmor is one product, it includes: + +* Pyarmor basic, Pyarmor pro, and Pyarmor group +* pyarmor-webui which provides graphics interface for pyarmor. +* the order system of Pyarmor is a Django's app running in cloud-server. This Django's app also belongs to one product Pyarmor. +* the laptop used to develop Pyarmor, the PCs used to test Pyarmor, the cloud-server to serve order system of Pyarmor, all of them belong to one product Pyarmor. +* Pyarmor 7.x, Pyarmor 8.x and Pyarmor 9.x + +Microsoft Office is not one product, because each product in Microsoft Office is functional independence. For example, Microsoft Word and Microsoft Excel belong to Microsoft Office, but they're totally different. + +Microsoft Word is one product, and Microsoft Word 2003,Word 2007 etc. are belong to one product Microsoft word. + +.. _select-license-type: + +Which license type is right for me +---------------------------------- + +All of the following licenses are only for Python 3.7+ + +.. list-table:: Table-2. Select Different Licenses + :widths: 40 12 12 12 12 12 + :header-rows: 1 + :stub-columns: 1 + + * - Condition + - Free + - Basic + - Pro + - Group + - CI + * - Less than 100 runs per month in CI/CD pipeline + - Y + - Y + - Y + - + - Y + * - More than 100 runs per month in CI/CD pipeline + - Y + - Y + - + - + - Y + * - Need offline obfuscation + - Y + - + - + - Y + - + * - Need irreversible obfuscation + - + - + - Y + - Y + - Y + * - Less than 100 runs per month in local dockers + - Y + - Y + - Y + - Y + - Y + * - More than 100 runs per month in local dockers + - Y + - Y + - + - Y + - Y + +.. _how-many-licenses-required: + +How many licenses are required +------------------------------ + +1. List all the products which are sold separately + + - If the sales revenue of the product is less than 100 x Pyarmor license fee, there is no need to list the product + - If no more than 2 products left, one license is OK + +2. How to tell diffenent product could be taken as one proudct in Pyarmor view + + Suppose there are 2 products in step 1: X and Y + + a. case 1, X and Y could use one license + + - Y includes whole X features + - The extra features of Y is related to X features + + For example, Pyarmor Basic (X), Pyarmor Pro (Y) could use one license because + + - Pyarmor Pro includes all the features of Pyarmor Basic + - The extra features of Pyarmor Pro is irreversible obfuscation which is an enhancement of Pyarmor Basic + + b. case 2, X and Y need 2 licenses + + - Y includes whole X features + - But X features is very small in the Y features + + For example, X is a facial recognition product, B is an attendance management system that uses facial recognition functionality + + c. case 3, X and Y could use one license + + - Y is a functional supplement for product X + + For example, X is CAD Editor, Y is CAD Tool which is used to convert CAD file to PDF file + + d. case 4, X and Y need 2 licenses + + - The functions of X and Y are almost independent + + For example, Microsoft Word (X), Micorsoft Excel (Y) are 2 products, even they're belong to Micorsoft Office Suite + +3. Regard to shared backend system + + There are 2 product A and B, share one backend engineer C + + a. case 1: need obfuscate backend C, but frontend of A and B need not + + - one license for backend C is OK + + b. case 2: need obfuscate backend C, and the frontend of A and B + + - 2 licenses, one for A, another for B + - C need not new license, use any license for A or B + +4. Many products need use one license in technical + + If there are many products (in Pyarmor view) need use one Pyarmor license in development view, it should + + - Purchase many licenses + - Register each product with different license once + - Use one of registeration file to generate the obfuscated scripts for all the products + +.. _how-to-upgrade-license: + +How to upgrade Pyarmor Licenses +------------------------------- + +Now it doesn't support to upgrade one Pyarmor License to any other Pyarmor License. For example, upgrade Pyarmor Basic to Pyarmor Pro by paying price difference + +There are only the following special cases + +- Pyarmor Old License used by Pyarmor <= 7.x could be upgrade to Pyarmor Basic License in some conditions +- If upgrading Pyarmor v8 to v9, Pyarmor License need to be re-activated + +Please refer to `What need to do after upgrading Pyarmor `_ + +In short, if it could be upgraded successfully by the guide in the documentation, it's OK. If something is wrong, this license can't be upgraded. Pyarmor Team doesn't handle this kind of request, there may be no any reply. + +Q & A +----- + +**I have just started selling my product, do I can use Pyarmor trial version to product it** + +Before the value of sales exceed 100 x Pyarmor license fee, Pyarmor trial version can be used to obfuscate the product. After the value of sales exceed, it need purchase Pyarmor license. + +**I noticed that the pricing table lists "0 Maximum build devices," for the CI/CD plan which has left me a bit confused. Could you please clarify what this means?** + + It means CI License only works in CI/CD pipeline, can’t be used in local device. + +**If we are using the product in our CI/CD environment we can deploy it to unlimited number of kubernetes pods** + + If the product means the obfuscated scripts, Pyarmor has no any limitation on it. + + Pyarmor License is only apply to build machine in which to generate the obfuscated scripts. + +.. include:: _common_definitions.txt diff --git a/docs/part-1.rst b/docs/part-1.rst new file mode 100644 index 00000000..c00a8199 --- /dev/null +++ b/docs/part-1.rst @@ -0,0 +1,14 @@ +=========== + Tutorials +=========== + +.. toctree:: + :maxdepth: 3 + + tutorial/getting-started + tutorial/installation + tutorial/obfuscation + tutorial/advanced + tutorial/customization + +.. include:: _common_definitions.txt diff --git a/docs/part-2.rst b/docs/part-2.rst new file mode 100644 index 00000000..e4eee10d --- /dev/null +++ b/docs/part-2.rst @@ -0,0 +1,17 @@ +======== + How To +======== + +.. toctree:: + :maxdepth: 3 + + how-to/security + how-to/protection + how-to/packing + how-to/wheel + how-to/obfuscation + how-to/register + how-to/ci + how-to/third-party + +.. include:: _common_definitions.txt diff --git a/docs/part-3.rst b/docs/part-3.rst new file mode 100644 index 00000000..3271d488 --- /dev/null +++ b/docs/part-3.rst @@ -0,0 +1,14 @@ +========== +References +========== + +.. toctree:: + :maxdepth: 3 + + reference/concepts + reference/man + reference/environments + reference/errors + reference/solutions + +.. include:: _common_definitions.txt diff --git a/docs/part-4.rst b/docs/part-4.rst new file mode 100644 index 00000000..101e9845 --- /dev/null +++ b/docs/part-4.rst @@ -0,0 +1,16 @@ +======== + Topics +======== + +.. toctree:: + :maxdepth: 3 + + topic/obfuscation + topic/obfuscated-script + topic/repack + topic/rftmode + topic/bccmode + topic/performance + topic/localization + +.. include:: _common_definitions.txt diff --git a/docs/questions.rst b/docs/questions.rst index 55decc22..1b9821d8 100644 --- a/docs/questions.rst +++ b/docs/questions.rst @@ -1,963 +1,422 @@ -.. _questions: +===== + FAQ +===== -When Things Go Wrong -==================== +.. highlight:: none -Some necessary knowledges and technicals are required to used pyarmor. Check -this list, make sure you know them, and your question is not related to them. +Pyarmor provides rich options for different cases, the default option only works for common case. When something is wrong, it may be not bug, but need the right options. Users need spend time learning Pyarmor by documentation or `pyarmor man`, and find the right options for their project. Generally pyarmor team won't learn user's project and tell user which options should be used. -.. _Necessary Knowledges: +Pyarmor is well document, you needn't read all of them at first, but it's necessary to read :doc:`tutorial/getting-started` which includes essentials concepts. If you spend 10 minutes reading full of it, it may save your several hours to solve the wrong usage problems. -Necessary Knowledges --------------------- - -Shell -~~~~~ - -pyarmor is a command line tool, it must be run in the shell or terminal. If you -know nothing about shell command, use `pyarmor-webui`_ instead. - -When command `pyarmor` complains of argument error, unknown option etc. Please -use option ``-h`` to list all the available options, and fix command syntax -error by these hints. For example:: - - pyarmor obfuscate -h - -Python -~~~~~~ - -How to run Python -https://docs.python.org/3.8/tutorial/interpreter.html#using-the-python-interpreter - -Source Code Encoding -~~~~~~~~~~~~~~~~~~~~ - -If the obfuscated scripts print unexpected output, you need learn this - -https://docs.python.org/3.8/tutorial/interpreter.html#source-code-encoding - -Then set the right source code encoding in the scripts, first run the plain -script to make sure everything is fine, then obfuscate the scripts again. - - -Python Import System -~~~~~~~~~~~~~~~~~~~~ - -The obfuscated scripts need an extra :ref:`Runtime Package` to run, it's a -common Python package, which could be imported as normal Python module or -package. If it can't be imported correctly, for example, not distributed with -the obfuscated scripts or stored in the wrong place, the obfuscated scripts may -raise exceptions like this:: - - ModuleNotFoundError: No module named 'app.pytransform' - -This is not PyArmor's error, just Python can not find it. In this case, you need -know Python how to import module, package, what's absolute import and relative -import, you must know what's ``sys.path`` - -https://docs.python.org/3.8/library/sys.html#sys.path - -The obfuscated script is a very simple Python script, the first line is an -import statement, the second line is a function call. For any import or no -module found error, for example:: - - ImportError: No module named model.NukepediaDB - -Just think it as a common python script, check whether the module, package or -extension file locates in the right place according to Python Import System. If -not, move the module, package or extension file to right path. - -Refer to the following official document or by search engineer to understand -Python Import System - -https://docs.python.org/3.8/reference/simple_stmts.html#the-import-statement - - -PyInstaller -~~~~~~~~~~~ - -If you'd like to pack the obfuscated scripts to one executable, and your project -structure is complex, you must know `PyInstaller`_ and could pack your project -by `PyInstaller`_ directly. - -https://pyinstaller.readthedocs.io/en/stable/usage.html - - -Common Solutions ----------------- - -I have receive a lot of issues, most of them aren't pyarmor's defect, but use -pyarmor in wrong way. So when you're in trouble with pyarmor, spending a few -hours to understand pyarmor may solve the problem quickly. Self-help is better -than help from others, it also could save time for both of us. - -First make sure you have read the basic guide :ref:`Using PyArmor`. - -Look through :ref:`Understanding Obfuscated Scripts`, especially the section -:ref:`The Differences of Obfuscated Scripts` - -If you don't know how to use pyarmor in a special case, have a glance at the toc -of :ref:`Advanced Topics`. - -Here are several common solutions - -* Upgrade pyarmor to latest stable version, please check :ref:`change logs` - before upgrading. If pyarmor works fine before, but now doesn't work, also - make a :ref:`clean uninstallation`, re-install pyarmor, and start everything - from refresh state. - -* As obfuscating the script by ``pyarmor``, check not only the last error - message, but also each log carefully to understand what pyarmor is doing, it's - very helpful to find the problem. And try to get more information by common - option ``-d``. For example:: - - pyarmor -d obfuscate --recursive foo.py - -* As running the obfuscated scripts, turn on Python debug option by ``-d`` to - print more information. If there is line number and script name in the - traceback, check the source script around this line. Make sure it doesn't use - any feature changed by obfuscated scripts. For example:: - - python -d obf_foo.py - -* If you distribute the obfuscated scripts in different platform or docker, make - sure the related cross platform options are set. Because the obfuscated - scripts include binary library, it's platform dependent, and Python version in - target must be same as the version to obfuscate the scripts. - -* If you are using command :ref:`pack`, make sure PyInstaller could pack the - plain scripts directly and the final bundle works. - -* If you are using the scripts obfuscated by :ref:`Restrict mode` 3 or more, try - to use the default restrict mode. If low restrict mode works, check the - scripts make sure they don't violate the restrict mode. - -* If you are using complex scripts or packages, try a simple script or package - to check it works or not. - -* Understanding pyarmor by doing a test in a few minutes if something you're not - sure. - -The default option of pyarmor works for common cases, but for complex cases, you -need understand the different options for each command. First list all available -options of ``obfuscate`` by option ``-h``:: - - pyarmor obfuscate -h - -You may find the desired option by its short description. If you're not sure, go -to :ref:`Man Page` to read the details of each option. - -Maybe the simplest way to understand an option is, do a test in one minute. For -example, the option ``--bootstrap`` is used to control how to generate the -bootstrap code for obfuscated scripts, do tests in a fresh path like this:: - - cd /path/to/test - mkdir case-1 - cd case-1 - echo "print('Hello')" > foo.py - pyarmor obfuscate --bootstrap 2 foo.py - ls dist/ - cat dist/foo.py - - cd /path/to/test - mkdir case-2 - cd case-2 - echo "print('Hello')" > foo.py - pyarmor obfuscate --bootstrap 3 foo.py - ls dist/ - cat dist/foo.py - -You can combine different options to do similar tests, it could help you -understand pyarmor quickly. - -.. note:: - - There are a lot of reporeted `issues`_, search here first try to find same - issue. - -.. _reporting an issue: - -Reporting an issue ------------------- - -When there is no solution in the document, about security issue, send email to -pyarmor@163.com, all the others please click `issues`_ to report, and -provide the necessary information - -1. The full pyarmor command and full output log (required) -2. If distributing the obfuscated script to other machine, which files are copied (optional) -3. The command to run the obfuscated scripts and full traceback when something is wrong - -The output log could be redirected to a file by this way:: - - pyarmor obfuscate foo.py >log.txt 2>&1 - -Here it's an example, the title of issue:: - - cannot import name 'pyarmor' from 'pytransform' - -The content of issue (copy all of these to github and modify it):: - - 1. On MacOS 10.14 run pyarmor to obfuscate the script - ``` - $ pyarmor obfuscate --exact main.py - INFO Create pyarmor home path: /Users/jondy/.pyarmor - INFO Create trial license file: /Users/jondy/.pyarmor/license.lic - INFO Generating public capsule ... - INFO PyArmor Trial Version 7.0.1 - INFO Python 3.7.10 - INFO Target platforms: Native - INFO Source path is "/Users/jondy/workspace/pyarmor-webui/test/__runner__/__src__" - INFO Entry scripts are ['main.py'] - INFO Use cached capsule /Users/jondy/.pyarmor/.pyarmor_capsule.zip - INFO Search scripts mode: Exact - INFO Save obfuscated scripts to "dist" - INFO Read product key from capsule - INFO Obfuscate module mode is 2 - INFO Obfuscate code mode is 1 - INFO Wrap mode is 1 - INFO Restrict mode is 1 - INFO Advanced value is 0 - INFO Super mode is False - INFO Super plus mode is not enabled - INFO Generating runtime files to dist/pytransform - INFO Extract pytransform.key - INFO Generate default license file - INFO Update capsule to add default license file - INFO Copying /Users/jondy/workspace/pyarmor-webui/venv/lib/python3.7/site-packages/pyarmor/platforms/darwin/x86_64/_pytransform.dylib - INFO Patch library dist/pytransform/_pytransform.dylib - INFO Patch library file OK - INFO Copying /Users/jondy/workspace/pyarmor-webui/venv/lib/python3.7/site-packages/pyarmor/pytransform.py - INFO Rename it to pytransform/__init__.py - INFO Generate runtime files OK - INFO Start obfuscating the scripts... - INFO /Users/jondy/workspace/pyarmor-webui/test/__runner__/__src__/main.py -> dist/main.py - INFO Insert bootstrap code to entry script dist/foo.py - INFO Obfuscate 1 scripts OK. - ``` - 2. Copy the whole folder `dist/` to target machine Ubuntu - 3. Failed to run the obfuscated script by Python 3.7 in Unbutu - ``` - $ cd dist/ - $ python3 main.py - Traceback (most recent call last): - File "main.py", line 1, in - from pytransform import pyarmor - ImportError: cannot import name 'pyarmor' from 'pytransform' (/home/jondy/dist/pytransform/__init__.py) - ``` +If using :command:`pyarmor-7` or Pyarmor < 8.0, please check `Pyarmor 7.x Doc`_ .. important:: - The issue may be marked as ``invalid`` and be closed directly in any of: - - * Not reported as template or missing necessary information - * There is the exact solution in the documentation - - -Segment fault -------------- - -In the following cases, obfuscated scripts may crash - -* Running obfuscated script by debug version Python -* Obfuscating scripts by Python X.Y but running the obfuscated scripts by - different Python version M.N -* Running the scripts in different platform but obfuscate them without option - ``--platform`` - - - Docker, it's Alpine Linux, in PyArmor, the platform name is `musl.x86_64`, - not `linux.x86_64` - - In Windows, 32-bit Windows is different from 64-bit Windows - - In 64-bit Windows, 32-bit Python is different from 64-bit Python - -* Read ``co_code`` or other attributes of the obfuscated code object by any way, - some third packages may analysis the byte code to do something. -* Importing the scripts obfuscated by restrict mode 3 and more in non-obfuscated - script may crash. It also may crash if it's obfuscated by ``obf-code=0`` -* Mixing the scripts obfuscated by different option ``--advanced`` -* In MacOS, the core library of pyarmor is linked to standard system Python, for - others, use ``install_name_tool`` to change ``rpath`` to adapt this machine. - -For PyArmor 5.5.0 ~ 6.6.0, some machines may be crashed because of advanced -mode. A quick workaround is to disable advanced mode by editing the file -:file:`pytransform.py` which locates in the installed path of ``pyarmor`` , in -the function ``_load_library``, uncomment about line 202. The final code looks -like this:: - - # Disable advanced mode if required - m.set_option(5, c_char_p(1)) - + Pyarmor team handles too many wrong usage issues, so one document :doc:`reference/solutions` has been arranged to solve this kind of issue quickly. If you aren't sure this issue is wrong usage or not, please check this doc or https://eke.dashingsoft.com/pyarmor/ask/ at first. -Bootstrap Problem ------------------ + Pyarmor team will mark this kind of issue as `invalid` or `documented` and close it immediately. -Could not find `_pytransform` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Generally, the dynamic library `_pytransform` is in the :ref:`runtime package`, -before v5.7.0, it's in the same path of obfuscated scripts. It may be: +.. _asking questions: -* `_pytransform.so` in Linux -* `_pytransform.dll` in Windows -* `_pytransform.dylib` in MacOS +Asking questions in GitHub +========================== -First check whether the file exists. If it exists: +Before ask question, please try the following common solutions in order to avoid duplicated issues: -* Check the permissions of dynamic library +- If need some feature, first check :ref:`the detailed table of contents ` - If there is no execute permissions in Windows, it will complain: - `[Error 5] Access is denied` +- If there is error message, check :doc:`reference/errors` -* Check whether `ctypes` could load `_pytransform`:: +- If obfuscated scripts don't work as expected, make sure you have read :doc:`topic/obfuscated-script` - from pytransform import _load_library - m = _load_library(path='/path/to/dist') +- If you have trouble in pack, check :doc:`topic/repack` -* Try to set the runtime path in the :ref:`Bootstrap Code` of entry - script:: +- If you have trouble in :term:`RFT Mode`, check :ref:`using rftmode` - from pytransform import pyarmor_runtime - pyarmor_runtime('/path/to/dist') +- If you have trouble in :term:`BCC Mode`, check :ref:`using bccmode` -Still doesn't work, report an issues_ +- If you have trouble with third-party libraries, check :doc:`how-to/third-party` +- If it's related to security and performance, check :doc:`topic/performance` -ERROR: Unsupport platform linux.xxx -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Please refer to :ref:`Support Platforms` +- Look through this page +- Enable debug mode and trace log, check console log and trace log to find more information -/lib64/libc.so.6: version 'GLIBC_2.14' not found -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -In some machines there is no `GLIBC_2.14`, it will raise this exception. +- Make sure the scripts work without obfuscation -One solution is patching `_pytransform.so` by the following way. +- Do a simple test, obfuscate a hello world script, and run it with python -First check version information:: +- If not using latest Pyarmor version, try to upgrade Pyarmor to latest version. - readelf -V /path/to/_pytransform.so - ... +- Search in the Pyarmor `issues`_ - Version needs section '.gnu.version_r' contains 2 entries: - Addr: 0x00000000000056e8 Offset: 0x0056e8 Link: 4 (.dynstr) - 000000: Version: 1 File: libdl.so.2 Cnt: 1 - 0x0010: Name: GLIBC_2.2.5 Flags: none Version: 7 - 0x0020: Version: 1 File: libc.so.6 Cnt: 6 - 0x0030: Name: GLIBC_2.7 Flags: none Version: 8 - 0x0040: Name: GLIBC_2.14 Flags: none Version: 6 - 0x0050: Name: GLIBC_2.4 Flags: none Version: 5 - 0x0060: Name: GLIBC_2.3.4 Flags: none Version: 4 - 0x0070: Name: GLIBC_2.2.5 Flags: none Version: 3 - 0x0080: Name: GLIBC_2.3 Flags: none Version: 2 +- Search in the Pyarmor `discussions`_ -Then replace the entry of `GLIBC_2.14` with `GLIBC_2.2.5`: +Please report bug in `issues`_ and ask questions in `discussions`_ -* Copy 4 bytes at 0x56e8+0x10=0x56f8 to 0x56e8+0x40=0x5728 -* Copy 4 bytes at 0x56e8+0x18=0x5700 to 0x56e8+0x48=0x5730 +.. _reporting bugs: -Here are sample commands:: +Reporting bug +============= - xxd -s 0x56f8 -l 4 _pytransform.so | sed "s/56f8/5728/" | xxd -r - _pytransform.so - xxd -s 0x5700 -l 4 _pytransform.so | sed "s/5700/5730/" | xxd -r - _pytransform.so +A good report should have -.. note:: +- A clear title +- Reproduced steps +- Actual results +- Expected results - From v5.7.9, this patch is not required. In cross-platform all you need to do - is specify the platform to `centos6.x86_64` to fix this issue. For example:: +It's recommand to report issue by command `pyarmor man`. - pyarmor obfuscate --platform centos6.x86_64 foo.py - -'pyarmor' is not recognized issue -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -If `pyarmor` is installed by pip, please search "pyarmor" in the computer, then -run full path pyarmor, or add path of pyarmor to environment variable PATH. - -If not by pip, the equivalent of the pyarmor command is running Python script -"pyarmor.py" found in the distribution folder. - -__snprintf_chk: symbol not found -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -When run pyarmor in some dockers, it may raise this exception. Because these -dockers are built with musl-libc, but the default ``_pytransform.so`` is built -with glibc, ``__snprintf_chk`` is missed in the musl-libc. - -In this case, try to download the corresponding dynamic library - -For x86/64 -http://pyarmor.dashingsoft.com/downloads/latest/alpine/_pytransform.so +.. important:: -For ARM -http://pyarmor.dashingsoft.com/downloads/latest/alpine.arm/_pytransform.so + If a bug report misses necessary information and not clear, it may be marked as invalid and closed immediately. -And overwrite the old one which filename could be found in the traceback. +Build issues +------------ +If there is error message when run pyarmor, please first check :doc:`reference/errors` to find solutions -Obfuscating Scripts Problem ---------------------------- +If still no solution, please report issue based on :file:`pyarmor.bug.log` generated by Pyarmor automically. For example:: -Warning: code object xxxx isn't wrapped -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -It means this function isn't been obfuscated, because it includes some -special instructions. + [BUG]: no found input "fooxxx.py" -For example, there is 2-bytes instruction `JMP 255`, after the code -object is obfuscated, the operand is increased to `267`, and the -instructions will be changed to:: + ## Command Line + pyarmor gen fooxxx.py - EXTEND 1 - JMP 11 + ## Environments + Home /Users/jondy/.pyarmor + Platform darwin.x86_64 (darwin.x86_64) + Python 3.12.0 + Pyarmor 9.0.4 (group), 006000, btarmor -In this case, it's complex to obfuscate the code object with wrap -mode. So the code object is obfuscated with non wrap mode, but all the -other code objects still are obfuscated with wrap mode. +Pack issues +----------- -In current version add some unused code in this function so that the -operand isn't the critical value may avoid this warning. +Check list for pack issues: -.. note:: +- Using PyInstaller to pack the script without obfuscation, make sure the final bundle works +- Without packing, only obfuscate scripts, make sure the obfuscated scripts works - Before v5.5.0, in this case the code object is left as it is. +If check list pass, then report bug as the next section guide -Code object could not be obufscated with advanced mode 2 -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Runtime issues +-------------- -Because this function includes some jump instructions that couldn't be -handled. In this case, just refine this function, make sure the first statement -will not generate jump instruction. For example, assignment, function call or -any simple statement. However, the compound statements, for examples, `try`, -`for`, `if`, `with`, `while` etc. will generate the jump instructions. If there -is no anyway to refactor the function, insert the following statement at the -beginning of this function:: +If the obfuscated scripts doesn't work or not as expected, first check :doc:`topic/obfuscated-script` - [None, None] +If there is error message, also check :doc:`reference/errors` to find solutions -It will generate some instructions but doesn't change anything. +If using :option:`--enable-bcc`, try to obfuscate script without it. If make sure this option results in problem, check :ref:`using bccmode` to find solutions -Error: Try to run unauthorized function -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -If there is any file `license.lic` or `pytransform.key` in the current -path, pyarmor maybe reports this error. One solution is to remove all -of that files, the other solution to upgrade PyArmor to v5.4.5 later. +If using :option:`--enable-rft`, , try to obfuscate script without it. If make sure this option results in problem, check :ref:`using rftmode` to find solutions +Try to use less options to obfuscate script, find the minimum options to reproduce this issue -'XXX' codec can't decode byte 0xXX -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Add the exact source encode at the begin of the script. For example:: +Report runtime issue with: - # -*- coding: utf-8 -*- +- A clear title +- Full command options to obfuscate scripts +- Full command to run the obfuscated scripts and traceback (if any) +- Necessary supplements and explanations -Refer to https://docs.python.org/2.7/tutorial/interpreter.html#source-code-encoding +Hot Questions +============= -If the source encode has been added into main script, it still raises this -issue. Please check the output log to find the exact script name, it may not the -main script. +**Is there any tool could broken Pyarmor?** + Pyarmor team doesn't care about these kind of tools, but focus on researching CPython source to design obfuscation algorithm. Through several irreversible obfuscation methods, Pyarmor makes sure the obfuscated scripts can't be restored by any way. -Why plugin doesn't work -~~~~~~~~~~~~~~~~~~~~~~~ + Refer to :doc:`how-to/security`, use the highest security options available for you to obfuscate this script -If the plugin script doesn't work as expected, first check the plugin script -could be injected into the entry script by set Python debug flag:: + .. code-block:: python - # In linux - export PYTHONDEBUG=y - # In Windows - set PYTHONDEBUG=y + import sys - pyarmor obfuscate --exact --plugin check_ntp_time foo.py + def fib(n): + a, b = 0, 1 + while a < n: + print(a, end=' ') + a, b = b, a+b + print() -It will generate patched file ``foo.py.pyarmor-patched``, make sure the content -of plugin script has been inserted into the right place, and the verify function -will be executed. + print('python version:', sys.version_info[:2]) + print('this is fib(10)', fib(10)) + And then try any tool to broken it. -Running Obfuscated Scripts Problem ----------------------------------- + If it's broken, please send Python version, Pyarmor version, Platform, Obfuscation options, sample script and broken steps to |Contact| -The `license.lic` generated doesn't work -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The key is that the capsule used to obfuscate scripts must be same as -the capsule used to generate licenses. + **Do not publish any pyarmor hack link in Pyarmor project** -The :ref:`Global Capsule` will be changed if the trial license file of -|PyArmor| is replaced with normal one, or it's deleted occasionally -(which will be generated implicitly as running command `pyarmor -obfuscate` next time). + Pyarmor is good at protecting Python scripts, but not good at memory protection and anti-debug. If you care about runtime memory data, or runtime key verification, generally it need extra methods to prevent debugger from hacking dynamic libraries. More information check :doc:`how-to/protection` -In any cases, generating new license file with the different capsule -will not work for the obfuscated scripts before. If the old capsule is -gone, one solution is to obfuscate these scripts by the new capsule -again. +.. + Segment fault in Apple + ====================== + First upgrade Pyarmor to 8.3.0+ which has fixed non-system Python crash issues. -NameError: name '__pyarmor__' is not defined -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -No :ref:`Bootstrap Code` are executed before importing obfuscated scripts. + If it has been the latest version, then check both of prebuilt extensions `pytransform3.so` and `pyarmor_runtime.so` -* When creating new process by `Popen` or `Process` in mod `subprocess` or - `multiprocessing`, to be sure that :ref:`Bootstrap Code` will be called before - importing any obfuscated code in sub-process. Otherwise it will raise this - exception. -* If `pytransform.py` or `pytransform/__init__.py` raises this exception. Make - sure it is not obfuscated, it must be plain script. -* Also check system module `os`, `ctypes`, make sure they're not obfuscated, try - to use option ``--exclude`` to exclude the whole Python system library path. + * Make sure code sign is OK by `codesign -v /path/to/xxx.so` + * Check used shared libraries `otool -L /path/to/xxx.so`, make sure all of them exist. -How to check :ref:`Bootstrap Code` executed or not? One simple way is to insert -one print statement before them. For example + For Pyarmor prior to 8.3.0, check the following issues -.. code:: python + **Generally it's code sign issue** - print('Start to run bootstrap code') - from pytransfrom import pyarmor_runtime - pyarmor_runtime() + If segment fault when obfuscating scripts or registering Pyarmor, try to re-sign extension ``pytransform3.so``:: -If the message is printed, then it's OK. Removing the print statement and check -other causes. + $ codesign -s - -f /path/to/lib/pythonX.Y/site-packages/pyarmor/cli/core/pytransform3.so -The other solution for this issue is :ref:`using super mode` to obfuscate the -scripts. + If segment fault when launching obfuscated scripts, try to re-sign extension ``pyarmor_runtime.so``:: -Marshal loads failed when running xxx.py -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -1. Check whether the version of Python to run obfuscated scripts is same as the - version of Python to obfuscate script + $ codesign -s - -f dist/pyarmor_runtime_000000/pyarmor_runtime.so -2. Run obfuscated script by `python -d` to show more error message. + If your app doesn’t have the new signature format, or is missing the DER entitlements in the signature, you’ll need to re-sign the app on a Mac running macOS 11 or later, which includes the DER encoding by default. -3. Be sure the capsule used to generated the license file is same as the capsule - used to obfuscate the scripts. The filename of the capsule will be shown in - the console when the command is running. + If you’re unable to use macOS 11 or later to re-sign your app, you can re-sign it from the command-line in macOS 10.14 and later. To do so, use the following command to re-sign the MyApp.app app bundle with DER entitlements by using a signing identity named "Your Codesign Identity" stored in the keychain:: -4. For cross platform obfuscation, make sure the dynamic library feature is set - correctly, refer to :ref:`Obfuscating scripts with different features` + $ codesign -s "Your Codesign Identity" -f --preserve-metadata --generate-entitlement-der /path/to/MyApp.app -_pytransform can not be loaded twice -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -When the function `pyarmor_runtime` is called twice, it will complaint -`_pytransform can not be loaded twice` + Refer to Apple official documentation `Using the latest code signature format`__ -For example, if an obfuscated module includes the following lines:: + **Not system Python** - from pytransform import pyarmor_runtime - pyarmor_runtime() - __pyarmor__(....) + The prebuilt ``pytrnasform.so`` and ``pyarmor_runtime.so`` need Python shared library, if there is no found Python shared library, it may crash. -When importing this module from entry script, it will report this -error. The first 2 lines should be in the entry script only, not in -the other module. + Using command line tool ``otool`` and ``install_name_tool`` to fix Python shared library issue. -This limitation is introduced from v5.1, to disable this check, just -edit `pytransform.py` and comment these lines in function -`pyarmor_runtime`:: + To display the names and version numbers of the shared libraries that the object file uses:: - if _pytransform is not None: - raise PytransformError('_pytransform can not be loaded twice') + $ otool -L /path/to/lib/python3.9/site-packages/pyarmor/cli/core/pytransform3.so -.. note:: + /path/to/lib/python3.9/site-packages/pyarmor/cli/core/pytransform3.so: + pytransform3.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) + ... - This limitation has been removed from v5.3.5. + And ``rpath`` is configured by:: + $ install_name_tool -id pytrnsform3.so \ + -change $deplib @rpath/lib/libpython$ver.dylib \ + -add_rpath @executable_path/.. \ + -add_rpath @loader_path/.. \ + -add_rpath /System/Library/Frameworks/Python.framework/Versions/$ver \ + -add_rpath /Library/Frameworks/Python.framework/Versions/$ver \ + build/$host/libs/cp$ver/$name.so -Check restrict mode failed -~~~~~~~~~~~~~~~~~~~~~~~~~~ -Use obfuscated scripts in wrong way, by default all the obfuscated -scripts can't be changed any more. + So check there is ``@rpath/lib/libpython3.9.dylib``. If it doesn't exists, please adapt to current Python by using ``install_name_tool``. Suppose current Python shared library is ``/usr/local/Python.framework/Versions/3.9/Python``:: -Besides packing the obfuscated scripts will report this error -either. Do not pack the obfuscated scripts, but pack the plain scripts -directly. + $ install_name_tool -change @rpath/lib/libpython3.9.dylib /usr/local/Python.framework/Versions/3.9/Python \ + /path/to/lib/pythonX.Y/site-packages/pyarmor/cli/core/pytransform3.so -For more information, refer to :ref:`Restrict Mode` + How to find current Python shared library, please search network to find answer. Note that some Python may not built with shared library, it can't work with Pyarmor, please rebuild Python with shared library to fix this kind of issue. + It's same for ``dist/pyarmor_runtime_000000/pyarmor_runtime.so``. -Protection Fault: unexpected xxx -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Use obfuscated scripts in wrong way, by default, all the runtime files -can't be changed any more. Do not touch the following files + Refer to Apple official documentation `Run-Path Dependent Libraries`__ -* pytransform.py -* _pytransform.so/.dll/.dylib + **If there are many same version Python installed, make sure pytransform3.so or pyarmor_runtime.so links to the right one** -If the entry script is obfuscated by new version, but the runtime files are -still old, it may raise this exception. Using option ``--no-cross-protection`` -to disable this protection, or using option ``--runtime`` to specify the same -runtime files when obfuscating the scrpits, could fix this issue. + For example, there is default Python3.9 in ``/Library/Frameworks/Python.framework/Versions/3.9/`` and anaconda3 Python 3.9 in ``/Users/my_username/anaconda3/bin/python`` -For more information, refer to :ref:`Special Handling of Entry Script` + When using ``/Users/my_username/anaconda3/bin/python`` to run the obfuscated script, it will load ``dist/pyarmor_runtime_000000/pyarmor_runtime.so``, and this library need Python dynamic library. According to RPATH settings, first search ``/Users/my_username/anaconda3/bin/python/../lib/libpython3.9.dylib``, if it exists, everything is fine. If it doesn't exists, then search ``/Library/Frameworks/Python.framework/Versions/3.9/lib/libpython3.9.dylib``, load this unexpected Python dynamic library, and results in crash issue. + In this canse using `install_name_tool` to modify ``dist/pyarmor_runtime_000000/pyarmor_runtime.so`` so that it could load Python dynamic library in anaconda3. -Run obfuscated scripts reports: Invalid input packet -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Mixing trial version and purchased version to obfuscate scripts and generate -`license.lic` also may raise this exception. Make sure all the files generated -by trial version, for example, obfusbcated script, license file and runtime -files, are removed. + Note that the obfuscated scripts work with system Python by default, and as possible as work with Python installed in the other locations. -Make sure the runtime module or package `pytransform` imported by the obfuscated -scripts is the one distributed with the obfuscated scripts. For example, running -the obfuscated scripts `python dist/foo.py` in the source path of pyarmor -package may rasie this exception, because `pytransform.py` of pyarmor will be -imported by the `dist/foo.py` unexpectedly. + **Application settings** -If the scripts are obfuscated in different platform, check the notes in -:ref:`Distributing Obfuscated Scripts To Other Platform` + Pyarmor uses JIT to improve security, In Apple M1, it need extra entitlements. Check Python entitlements:: -Before v5.7.0, check if there is any of `license.lic` or `pytransform.key` in -the current path. Make sure they're generated for the obfuscated scripts. If -not, rename them or move them to other path. + $ codesign -d --entitlements - $(which python) -Because the obfuscated scripts will first search the current path, then search -the path of runtime module `pytransform.py` to find the file `license.lic` and -`pytransform.key`. If they're not generated for the obfuscated script, this -error will be reported. + Refer to Apple official documentation `Allow Execution of JIT-compiled Code Entitlement`__ + **Check system segment fault log, and search solution by error message** -OpenCV fails because of `NEON - NOT AVAILABLE` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -In some Raspberry Pi platform, run the obfuscated scripts to import -OpenCV fails:: + __ https://developer.apple.com/documentation/xcode/using-the-latest-code-signature-format/ + __ https://developer.apple.com/library/archive/documentation/DeveloperTools/Conceptual/DynamicLibraries/100-Articles/RunpathDependentLibraries.html + __ https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_cs_allow-jit - ************************************************** **************** - * FATAL ERROR: * - * This OpenCV build doesn't support current CPU / HW configuration * - * * - * Use OPENCV_DUMP_CONFIG = 1 environment variable for details * - ************************************************** **************** + Registering + =========== - Required baseline features: - NEON - NOT AVAILABLE - terminate called after throwing an instance of 'cv :: Exception' - what (): OpenCV (3.4.6) /home/pi/opencv-python/opencv/modules/core/src/system.cpp:538: error: - (-215: Assertion failed) Missing support for required CPU baseline features. Check OpenCV build - configuration and required CPU / HW setup. in function 'initialize' + **ERROR request license token failed (104)** -One solution is to specify optioin ``--platform`` to `linux.armv7.0`:: + Please make sure firewall doesn't block the response of license server. If possible, turn off the firewall to verify it. - pyarmor obfuscate --platform linux.armv7.0 foo.py - pyarmor build --platform linux.armv7.0 - pyarmor runtime --platform linux.armv7.0 + In Windows ``pytransform.pyd`` will connect to ``pyarmor.dashingsoft.com`` port ``80`` to request token for online obfuscation, in other platforms it is ``pytransform3.so``. Refer to firewall documentation to allow it to connect ``pyarmor.dashingsoft.com:80``. -The other solution is to set environment variable `PYARMOR_PLATFORM` -to `linux.armv7.0`. For examples:: + **Group license raises "ERROR request license token failed"** - PYARMOR_PLATFORM=linux.armv7.0 pyarmor obfuscate foo.py - PYARMOR_PLATFORM=linux.armv7.0 pyarmor build + First upgrade Pyarmor to 8.4.0+ - Or, + Then register group license with debug option ``-d`` in offline device. For example:: - export PYARMOR_PLATFORM=linux.armv7.0 - pyarmor obfuscate foo.py - pyarmor build + $ pyarmor -d reg pyarmor-device-regfile-6000.4.zip -How to customize error message -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Check log, make sure current machine id is inclueded by offline regfile. For example:: -I have started to play around with pyarmor. When using a license file that -expires you get the message “License is expired”. Is there a way to change this -message? + DEBUG group license for machines: ['tokens/mb04eb35da4f5378185c8663522e0a5e3'] + DEBUG got machine id: mb04eb35da4f5378185c8663522e0a5e3 -At this time, you need patch the source script `pytransform.py` in the pyarmor -package. There is a function `pyarmor_runtime` + If machine id is mismatched, please generate new device file for this device by Pyarmor 8.4.0+:: -.. code:: python + $ pyarmor -v - def pyarmor_runtime(path=None, suffix='', advanced=0): + Pyarmor 8.4.0 ... - try: - pyarmor_init(path, is_runtime=1, suffix=suffix, advanced=advanced) - init_runtime() - except Exception as e: - if sys.flags.debug or hasattr(sys, '_catch_pyarmor'): - raise - sys.stderr.write("%s\n" % str(e)) - sys.exit(1) - -Change the hanler of the exception as you desired. -If the scripts are obfuscated by super mode, this solution doesn't work. You may -create a script to catch exceptions raised by obfuscated script `foo.py`. For -example + $ pyarmor reg -g 5 -.. code:: python + For virtual machine, make sure machine id is same after reboot. - try: - import foo - except Exception as e: - print('something is wrong') +License +======= -By this way not only the exceptions of pyarmor but also of normal scripts are -catched. In order to handle the exceptions of pyarmor only, first create runtime -package by :ref:`runtime`, and obfuscate the scripts with it:: +**Will Pyarmor Pro license upload my scripts to remote server to verify license?** - pyarmor runtime --advanced 2 -O dist - pyarmor obfuscate --advanced 2 --runtime @dist foo.py + No. For Pyarmor Basic and Pro License, only Pyarmor License file, serial number of hard disk, Ethernet address, IPv4/IPv6 address, and hostname will be sent to remote server for verification. -Then create a boot script ``dist/foo_boot.py`` like this +**I am interested to know if the users are entitled to updates to ensure compatibility with future versions of Python.** -.. code:: python + No. Pyarmor license works with current Pyarmor version forever, but may not work with future Pyarmor version. I can't make sure current Pyarmor version could support all the future versions of Python, so the answer is no. - try: - import pytransform_bootstrap - except Exception as e: - print('something is wrong') - else: - import foo +**we use Docker to build/obfuscate the code locally then publish the Docker file to the client. After the build stage, the whole environment (and the license) is gone. I wonder how the workflow would be? Can I add the license file to the pipeline and register every time and build?** -The script ``dist/pytransform_bootstrap.py`` is created by :ref:`runtime`, it's -obfuscated from an empty script, so only pyarmor bootstrap exceptions are raised -by it. + Please refer to :doc:`how-to/ci` +**We are currently using a trial license for testing, but unfortunately our scripts are big and we are not able to statistically test the operation of Pyarmor. Do you have a commercial trial license for a certain trial period so that we can test the operation of Pyarmor for our scripts?** -undefined symbol: PyUnicodeUCS4_AsUTF8String -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Sorry, Pyarmor is a small tool and only cost small money, there is no demo license plan. -If Python interpreter is built with UCS2, it may raises this issue when running -super mode obufscated scripts. In this case, try to obfuscate script with -platform ``centos6.x86_64``, it's built with UCS2. For example:: + Most of features could be verified in trial version, other advanced features, for example, mix-str, bcc mode and rft mode, could be configured to ignore one function or one script so that all the others could work with these advanced features. - pyarmor obfuscate --advanced 2 --platform centos6.x86_64 foo.py +**Is the Internet connection only required to generate the obfuscated script? No internet connection is required on the target device that uses such script?** + No internet connection is required on target device. -NameError: name '__armor_wrap__' is not defined -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Pyarmor has no any control or limitation to obfuscated scripts, the behaviors of obfuscated scripts are totally defined by user. -If :ref:`Restrict Mode` is set to 4 or 5, it may report this issue. In this case -try to set restrict mode to 2. + Please check Pyarmor EULA 3.4.1 -If it's raised in the object method `__del__`, upgrade pyarmor to v6.7.3+, and -obfuscate the scripts again. +**Our company has a suite of products that we offer together or separately to our clients. Do we need a different license for each of them?** -Also refer to :ref:`Using restrict mode with threading and multiprocessing` and -next question. + For a suite of products, if each product is different totally, for example, a suite "Microsoft Office” includes “Microsoft Excel”, “Microsoft Word”, each product need one license. -Object method `__del__` raise NameError exception -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + If a suite of products share most of Python scripts, as long as the proportion of the variable part of each product is far less than that of the common part, they’re considered as "one product". -If method `__del__` raises this exception:: + If each product in a suite of products is functionally complementary, for example, product "Editor" for editing the file, product "Viewer" for view the file, they’re considered as "one product" - NameError: name '__armor_enter__' is not defined - NameError: name '__armor_wrap__' is not defined +**Which PyArmor 8.0 license for CI, more than 100 runs / day** -Please upgrade pyarmor to v6.7.3+, and obfuscate the scripts again, and make -sure new runtime package is generated. + It's recommend to upgrade to Pyarmor 9, and use :term:`Pyarmor CI` License or :term:`Pyarmor Basic` License. See also :doc:`how-to/ci` -If the scripts is obfuscated by non super mode and python is 3.7 and later, -please obfuscate the scrits by super mode. Or refine the scripts, do not -obfuscate the object method `__del__`. For example +**We should be able to assume that the CI regfile will keep working as long as it is within the license limits. Otherwise, builds might break at a moment notice. Could you confirm whether it is safe to assume that the CI license will keep working?** -.. code:: python + Fix Pyarmor version in the CI/CD pipeline, CI regfile works within the validity period. - class MyData: - - ... +**About CI License, is there an option to allow offline usage in the CI/CD pipeline?** - def lambda_del(self): - # Real code for method __del__ - ... + No. There is no offline option for CI License. - __del__ = lambda_del +.. + **We intend to use PyArmor in CI to build obfuscated Docker images. According to the docs we can't use PyArmor in CI because the machine IDs will be different across each CI run (we verified this is the case), but according to this section in the docs, we can use PyArmor on "all the devices to develop, build, test the product", so we're a bit confused on whether this would work in CI. Could we buy a group license and then activate PyArmor as part of the CI pipeline? All of these different CI machines and developer laptops are building "one product"** -Any function name which starts with `lambda_` will not be obfuscated by pyarmor, -in above example, the method `lambda_del` is not obfuscated, so `__del__` is. + Unfortunately the answer is **NO**. One license could be used in any device for one product, at the same time, they must be under license limitation. -The other solution is not obfuscating the script which includes ``__del__`` by -copying the plain script to overwrite the obfsucated one. Or obfuscate this -script by ``--obf-code 0``. +Upgrading +--------- -SystemError: module filename missing -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +**If we buy version 8 license, is it compatible with earlier versions like 6.7.2?** -When obfsucating the scripts by super mode, and with outer license, it may -complain of this error if the obfuscated scripts could not find the license file -`license.lic` in the current path. + No. Pyarmor 8 license can't be used with earlier versions, it may report HTTP 401 error or some unknown errors. -If `license.lic` is in the other path, set environment variable `PYARMOR_LICNSE` -to it with full path, for example:: +**Can we obfuscate our code base with the same level as current? (we are obfuscating our code using super plus mode ("--advanced 5"). Is that available on Pyarmor Basic?** - export PYARMOR_LICNSE=/path/to/license.lic + The old license is valid for ever. In this case need not upgrade old license to Pyarmor Basic license, just install Pyarmor 8.x, and using :command:`pyarmor-7` with old license. -Android protection problem -~~~~~~~~~~~~~~~~~~~~~~~~~~ + Check :doc:`how-to/register` for more information about upgrading -Most of Android system don't allow load dynamic library in the data path, but -there is one `_pytransform.so` in the runtime package of the obfuscated scripts, -so it may raise exception like this:: +**If we upgrade the old license, will the current license expire? (no more available in terms of Pyarmor v7?** - dlopen failed: couldn't map "/storage/emulated/0/dist/_pytransform.so" - segment 1: Operation not permitted + If upgrade old license to any Pyarmor 8 license, the current license is no more available in the terms of Pyarmor 7. -Please consult Android development document, copy the whole folder `pytransform` -to right location where Android allow to load dynamic library, and set -`PYTHONPATH` or any other way only if Python could find and import it. +**How long is the current license valid? Is there a published end-of-support schedule?** -libpython3.9.so.1.0: cannot open shared object file -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + The license is valid for ever with Pyarmor version when purchasing this license, but may not work for future Pyarmor, there is no schedule about in which version current license doesn't work. -If missing any python core library such as `python39.dll`, `libpython3.9.so`, -etc, make sure this python interpreter is built with `--enable-shared`. By -default, the runtime extension `pytransform` is linked to python dynamic -library. + Since the first release Pyarmor changed its license 2 times because the core libraries are rewritten: -In Linux platform, try to install `libpython3.9` by `apt` or any other -pacakge manage tool. + - the initial license issued around year 2010 (I forget the exact date) + - the second license issued on 2019-10-10 + - this is the third license, issued on 2023-03-10 -Packing Obfuscated Scripts Problem ----------------------------------- +**Does the license include access to support and software updates? If so, what is the duration of support and how are updates delivered?** -The final bundle does not work -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Generally the license could be used in the next versions, until there are big changes in one major version, but I have no plan in details. Just run `pip install` to upgrade Pyarmor to latest version, the license will keep work. -First of all, please read the man page of :ref:`pack` completely. + But there is no more technical supports about how to use Pyarmor, Pyarmor is a command line tool, and all the options are full documentations. Pyarmor users need spend some time to learn Pyarmor by himself. -Next make sure the scripts could pack by PyInstaller directly and the final -bundle works. For example:: +**Is there an option for custom licensing arrangements to accommodate specific project or organizational needs?** - pyinstaller foo.py - dist/foo/foo + At this time, the answer is no. -If the final bundle complains of no module found, it need some extra PyInstaller -options, please refer to https://pyinstaller.readthedocs.io +**In the old pyarmor 7, I'm using "pyarmor pack ...", I could not find any relate information for this in the pyarmor 8.2. How to solve this?** -Then make sure the obfuscated scripts could work without packing. For example:: + There is no identical pack in Pyarmor 8, Pyarmor 8+ only provide repack function to handle bundle of PyInstaller. Refer to basic tutorial, topic `insight into pack`__ and this solved issue `Pyarmor pack missing in pyarmor 8.0`__ - pyarmor obfuscate foo.py - python dist/foo.py +**Using PyArmor 9.x newer version, we cannot generated licenses compatible with "Software" created using older PyArmor (7.x versions), mainly using license.lic. Correct?** -If both of them OK, remove the output path `dist` and PyInstaller cached path -`build`, then pack the script with ``--debug``:: + You're right. At this time one possible solution is still using Pyarmor 7 obfuscated script to verify old runtime key, in Pyarmor 9 obfuscated script check old runtime key by calling Pyarmor 7 obfuscated script indirectly (IPC) - pyarmor pack --debug foo.py +__ https://pyarmor.readthedocs.io/en/stable/topic/repack.html +__ https://github.com/dashingsoft/pyarmor/discussions/1107 -The build files will be kept, the patched `foo-patched.spec` could be used by -pyinstaller to pack the obfuscated scripts directly, for example:: +Purchasing +========== - pyinstaller -y --clean foo-patched.spec +**How to refund my order?** -Check this patched `.spec` and change options in this `.spec` file, make sure -the final bundle could work. + If this order isn't activated and in 30 days since purchasing, you can refund the order by one of ways -Also refer to :ref:`repack pyinstaller bundle with obfuscated scripts`, make -sure it works by this way. - -No module name pytransform -~~~~~~~~~~~~~~~~~~~~~~~~~~ -If report this error as running command `pyarmor pack`: - -* Make sure the script specified in the command line is not obfuscated -* Run `pack` with extra option ``--clean`` to remove cached `myscript.spec`:: - - pyarmor pack --clean foo.py - -NameError: name ‘__pyarmor__’ is not defined -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Check the traceback to find which script raises this exception, it's helpful to -find the problem: - -* If `pytransform.py` or `pytransform/__init__.py` raises this exception. Make - sure it is not obfuscated, it must be plain script. -* Also check system module `os`, `ctypes`, make sure they're not obfuscated. In - this case, try to exclude the Python system library path, refer to :ref:`pack` -* Try to only copy your own scripts to an empty path, then pack it in this path. -* If it works in trial version, but fails after pyarmor is registered, try to - make a :ref:`clean uninstallation` - -PyArmor Registration Problem ----------------------------- - -Purchased pyarmor is not private -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Even obfuscated with purchased version, license from trial version works: - -* Make sure command `pyarmor register` shows correct registration information -* Make a :ref:`clean uninstallation`, and register again -* Make sure the current user is same as the one to register pyarmor -* Make sure environment variable `PYARMOR_HOME` is not set -* Try to reboot system. - -Could not query registration information -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -I tried to register in pyarmor with using the command and log:: - - ~ % pyarmor register pyarmor-regfile-1.zip - INFO PyArmor Version 6.5.2 - INFO Start to register keyfile: pyarmor-regfile-1.zip - INFO Save registration data to: /Users/Jondy/.pyarmor - INFO Extracting license.lic - INFO Extracting .pyarmor_capsule.zip - INFO This keyfile has been registered successfully. - -Watching whether I am registered, I got this output:: - - ~ % pyarmor register - INFO PyArmor Version 6.5.2 - PyArmor Version 6.5.2 - Registration Code: pyarmor-vax-000383 - Because of internet exception, could not query registration information. - -Ping domain `api.dashingsoft.com`, make sure ip address is resolved like this:: - - ~ % ping api.dashingsoft.com - - PING api.dashingsoft.com (119.23.58.77): 56 data bytes - Request timeout for icmp_seq 0 - Request timeout for icmp_seq 1 - -If not, add one line in the ``/etc/hosts``:: - - 119.23.58.77 pyarmor.dashingsoft.com - -Known Issues ------------- + * If purchasing order from MyCommerce: -Obfuscate scripts in cross platform -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -From v5.6.0 to v5.7.0, there is a bug for cross platform. The scripts obfuscated -in linux64/windows64/darwin64 don't work after copied to one of this target -platform:: + 1. Email to Ordersupport@mycommerce.com with order information and ask for refund. + 2. Or click `FindMyOrder page`_ to submit refund request - armv5, android.aarch64, ppc64le, ios.arm64, freebsd, alpine, alpine.arm, poky-i586 + * If purchasing order from reseller, contact your reseller -License Questions ------------------ -Refer to :ref:`License Questions` + * For other cases, email to pyarmor@163.com -Misc. Questions ---------------- +.. _FindMyOrder page: https://www.findmyorder.com/store?Action=DisplayEmailCustomerServicePage&Env=BASE&Locale=en_US&SiteID=findmyor -How easy is to recover obfuscated code -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -If someone tries to break the obfuscation, he first must be an expert in the -field of reverse engineer, and be an expert of Python, who should understand the -structure of code object of python, how python interpreter each instruction. If -someone of them start to reverse, he/she must step by step thousands of machine -instruction, and research the algorithm by machine codes. So it's not an easy -thing to reverse pyarmor. +Misc. +===== +**What is the ECCN or rating of Pyarmor (EAR99,5D99S,5D002 or other type ECCN)?** -How to get receipt or invoice -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + EAR99 -MyCommerce handles all the sales of pyarmor +**Does Pyarmor contain any encryption capabilities?** -Please get help from this page for order/recipt/invoice issue + Pyarmor uses AES/RSA etc., but it hasn’t its own encryption algorithms. -https://www.mycommerce.com/shopper-support/ +**What is the country of origin of this package?** -Or contact "ClientSupport@MyCommerce.com" directly + China -Would pyarmor be able to provide an evaluation license -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +**Where is the final built for Pyarmor?** -Sorry, pyarmor license could be work even offline, so there is no evaluation -license. + All of Pyarmor packages are published in the PyPI_, refer to :term:`Pyarmor Package` and section `Installation in offline device` in the chapter :doc:`tutorial/installation` -Generally the obfuscated scripts could replace the original scripts -seamlessly. Excpet it uses the features changed by pyarmor, here list all -:ref:`The Differences of Obfuscated Scripts` +**Pyarmor Pro checks the internet, what is the output IP or DNS? I need to release it on my client's firewall, placing an outbound rule for yours IP.** -Most of packages could work with pyarmor, for a few packages, pyamor also works -after patching these packages simplify. Only those packages which visit byte -code or something like this could not work with pyarmor at all. + Now it's `119.23.58.77` (March 20, 2024) .. include:: _common_definitions.txt diff --git a/docs/reference/concepts.rst b/docs/reference/concepts.rst new file mode 100644 index 00000000..2c7611a7 --- /dev/null +++ b/docs/reference/concepts.rst @@ -0,0 +1,236 @@ +========== + Concepts +========== + +.. glossary:: + + Activation File + + A text file used for initial registration :term:`Pyarmor License` + + When purchasing any :term:`Pyarmor License`, an activation file is be sent to registration email after payment is completed. + + BCC Mode + + An obfuscation method of Pyarmor by converting Python functions to C functions + + extension module + + A module written in C or C++, using Python’s C API to interact with the core and with user code. + + Build Machine + + The device in which to install pyarmor, and to run pyarmor to generate obfuscated scripts. + + Global Path + + Store Pyarmor global configuration file, default is :file:`~/.pyarmor/config/` + + It's always relative to :term:`Home Path` + + Home Path + + Store Pyarmor registration file, global configuration, other data file generated by :command:`pyarmor`, the default path is in user home path :file:`~/.pyarmor/` + + Local Path + + Store Pyarmor local configuration file, default is in the current path :file:`./.pyarmor/` + + Hook script + + Hook script is a python script which locates in sub-path ``hooks`` of :term:`local path` or :term:`global path`. + + When obfuscating the scripts, if there is any same name script exists, it's called module hook script, and will be inserted into the obfuscated scripts. + + The hook script will be executed first when running the obfuscated scripts. + + JIT + + Abbr. JUST-IN-TIME, just generating machine instructions in run time. + + Outer Key + + A file generally named ``pyarmor.rkey`` to store :term:`Runtime Key` + + The outer key file must be located in one of path + + - :term:`Runtime package` + - :envvar:`PYARMOR_RKEY`, no trailing slash or backslash, and no ``..`` in the path. Generally it's an absolute path, for example, ``/var/data`` + - Current path + + Or a file ``sys.executable`` + ``.pyarmor.rkey``. For example, ``dist/myapp.exe.pyarmor.rkey`` + + Platform + + The standard platform name defined by Pyarmor. It's composed of os.arch. + + Supported platforms list: + + * Windows + - windows.x86_64 + - windows.x86 + * Many Linuxes + - linux.x86_64 + - linux.x86 + - linux.aarch64 + - linux.armv7 + * Apple Intel and Silicon + - darwin.x86_64 + - darwin.aarch64 or darwin.arm64 + * FreeBSD + - freebsd.x86_64 + * Alpine Linux (musl-c) + - alpine.x86_64 + - alpine.aarch64 + * Android + - android.x86_64 + - android.x86 + - android.aarch64 + - android.armv7 + + Plugin script + + A python script will be called in building stage to do some customization work. + + Pyarmor + + Pyarmor is product domain, the goal is to provide functions and services to obfuscate Python scripts in high security and high performance. The mission of Pyarmor is let Python use easily in commercial product. + + Pyarmor is composed of + + - :term:`Pyarmor Home` + - :term:`pyarmor package` + + Pyarmor Basic + + A :term:`Pyarmor License` type + + Pyarmor CI + + A :term:`Pyarmor License` type + + Pyarmor Group + + A :term:`Pyarmor License` type + + Pyarmor Home + + Host in GitHub: |Home| + + It serves open source part of Pyarmor, `issues`_ and documentations. + + Pyarmor License + + Issued by Pyarmor Team to unlock some limitations in Pyarmor trial version. + + Refer to :doc:`Pyarmor License Types <../licenses>` + + Pyarmor Package + + A :term:`Python Package`, it includes + + - :mod:`pyarmor` + - :mod:`pyarmor.cli` + - :mod:`pyarmor.cli.core` + - :mod:`pyarmor.cli.runtime` + + Since Pyarmor 8.3, :mod:`pyarmor.cli.runtime` is split into serval packages: + + - :mod:`pyarmor.cli.core.freebsd` + - :mod:`pyarmor.cli.core.android` + - :mod:`pyarmor.cli.core.windows` + - :mod:`pyarmor.cli.core.themida` + - :mod:`pyarmor.cli.core.linux` + - :mod:`pyarmor.cli.core.alpine` + - :mod:`pyarmor.cli.core.darwin` + + All of them are published in the PyPI_ + + Pyarmor Pro + + A :term:`Pyarmor License` type + + Pyarmor Users + + Developers or organizations who use Pyarmor to obfuscate their Python scripts + + Python + + A program language. + + Python Script + + A file that serves as an organizational unit of Python code. + + Refer to https://docs.python.org/3.11/glossary.html#term-module + + Python Package + + Refer to https://docs.python.org/3.11/glossary.html#term-package + + Registration File + + A zip file generated after initial registration is successful. It's used to register :term:`Pyarmor License` except initial registration. + + RFT Mode + + An obfuscation method of Pyarmor by renaming function/class in the scripts + + Runtime Files + + All the files required to run the obfuscated scripts. + + Generally it equals :term:`Runtime Package`. If :term:`outer key` is used, plus this outer key file. + + Runtime Key + + The settings of obfuscated scripts. It may include the expired date, device information of bind to obfuscated scripts. Also include all the flags to control the behaviors of obfuscated scripts. + + Generally it's embedded into :term:`Runtime Package`, but it also could be stored to an independent file :term:`outer key` + + Runtime Package + + A :term:`Python Package` generally named ``pyarmor_runtime_000000``. + + When obfuscating the scripts, it's be generated at the same time. + + It's required to run the obfuscated scripts. + + Target Device + + In which run the obfuscated scripts distributed by :term:`Pyarmor Users`, generally it's in customer side + +.. module:: pyarmor + :synopsis: A command line tool used to obfuscate Python scripts. This package provides cli command for both Pyarmor 7 and Pyarmor 8 + +.. module:: pyarmor.cli + :synopsis: A command line tool used to obfuscate Python scripts. This package provides only Pyarmor 8 cli + +.. module:: pyarmor.cli.core + :synopsis: A binary wheel to provide prebulit extension modules `pytransform3` which is required to run Pyarmor + +.. module:: pyarmor.cli.runtime + :synopsis: A universal wheel is used for cross-platform obfuscation, it provides prebuilt extension modules `pyarmor_runtime` which is required to run the obfuscated scripts in all supported platforms + +.. module:: pyarmor.cli.core.android + :synopsis: It's required in Android for Pyarmor, it provides prebuilt extension modules both `pytransform3` and `pyarmor_runtime` for Android + +.. module:: pyarmor.cli.core.freebsd + :synopsis: It's required in FreeBSD for Pyarmor, it provides prebuilt extension modules both `pytransform3` and `pyarmor_runtime` for FreeBSD + +.. module:: pyarmor.cli.core.windows + :synopsis: It's used for cross-platform obfuscation, only provides all prebuilt extensions `pyarmor_runtime` in Windows + +.. module:: pyarmor.cli.core.themida + :synopsis: It's used for cross-platform obfuscation, only provides all prebuilt extensions `pyarmor_runtime` in Windows and protected by Themida + +.. module:: pyarmor.cli.core.linux + :synopsis: It's used for cross-platform obfuscation, only provides all prebuilt extensions `pyarmor_runtime` in Linux with glibc + +.. module:: pyarmor.cli.core.alpine + :synopsis: It's used for cross-platform obfuscation, only provides all prebuilt extensions `pyarmor_runtime` in Alpine Linux (musl-c) + +.. module:: pyarmor.cli.core.darwin + :synopsis: It's used for cross-platform obfuscation, only provides all prebuilt extensions `pyarmor_runtime` in Darwin + +.. include:: ../_common_definitions.txt diff --git a/docs/reference/environments.rst b/docs/reference/environments.rst new file mode 100644 index 00000000..7e67cac6 --- /dev/null +++ b/docs/reference/environments.rst @@ -0,0 +1,452 @@ +.. highlight:: none + +======================= + Building Environments +======================= + +Command :command:`pyarmor` runs in :term:`build machine` to generate obfuscated scripts and all the other required files. + +Here list everything related to :command:`pyarmor`. + +Above all it only runs in the `supported platforms`_ by `supported Python versions`_. + +Command line options, `configuration options`_, `plugins`_, `hooks`_ and a few environment variables control how to generate obfuscated scripts and runtime files. + +All the command line options and environment variables are described in :doc:`man` + +Supported Python versions +========================= + +.. table:: Table-1. Supported Python Versions + :widths: auto + + =================== ===== ========= ========== ====== ====== ======= ============== + Python Version 2.7 3.0~3.6 3.7~3.10 3.11 3.12 3.13 Remark + =================== ===== ========= ========== ====== ====== ======= ============== + pyarmor 8 RFT Mode No No Y Y Y Y [#]_ + pyarmor 8 BCC Mode No No Y Y Y Y + pyarmor 8 others No No Y Y Y Y + pyarmor-7 Y Y Y No No No + =================== ===== ========= ========== ====== ====== ======= ============== + +Supported platforms +=================== + +.. table:: Table-2. Supported Platforms (1) + :widths: auto + + =================== ============ ======== ======= ============ ========= ======= ======= + OS Windows Apple [#]_ Linux [#]_ + ------------------- ------------ ----------------- ----------------------------------------- + Arch x86/x86_64 x86_64 arm64 x86/x86_64 aarch64 armv7 armv6 + =================== ============ ======== ======= ============ ========= ======= ======= + Themida Protection Y No No No No No No + pyarmor 8 RFT Mode Y Y Y Y Y Y No + pyarmor 8 BCC Mode Y Y Y Y Y N/y No + pyarmor 8 others Y Y Y Y Y Y No + pyarmor-7 [#]_ Y Y Y Y Y Y Y + =================== ============ ======== ======= ============ ========= ======= ======= + +.. table:: Table-3. Supported Platforms (2) [#]_ + :widths: auto + + =================== ============ ========= ========= ============ ========= ======= ======= + OS FreeBSD Alpine Linux Android + ------------------- ------------ -------------------- ----------------------------------------- + Arch x86_64 x86_64 aarch64 x86/x86_64 aarch64 armv7 armv6 + =================== ============ ========= ========= ============ ========= ======= ======= + pyarmor 8 RFT Mode Y Y Y Y Y Y No + pyarmor 8 BCC Mode Y Y Y Y Y Y No + pyarmor 8 others Y Y Y Y Y Y No + pyarmor-7 Y Y Y Y Y Y Y + =================== ============ ========= ========= ============ ========= ======= ======= + +.. table:: Table-4. Supported Platforms (3) [#]_ + :widths: auto + + =================== ============= ================================================== + OS Linux Linux, Alpine Linux + ------------------- ------------- -------------------------------------------------- + Arch loongarch64 ppc64le, mips32el/64el, riscv64 [#]_ + =================== ============= ================================================== + pyarmor 8 RFT Mode Y Y + pyarmor 8 BCC Mode N N + JIT Feature N Y + pyarmor 8 others Y Y + =================== ============= ================================================== + +.. rubric:: notes + +.. [#] ``N/y`` means not yet now, but will be supported in future. +.. [#] Apple Silcon only supports Python 3.9+ +.. [#] This Linux is built with glibc +.. [#] pyarmor-7 also supports more linux arches, refer to `Pyarmor 7.x platforms`__. +.. [#] These platforms are introduced in Pyarmor 8.3 +.. [#] The prebuilt extensions for these arches are published in the package `pyarmor.cli.core.linux`__ and `pyarmor.cli.core.alpine`__ respectively since Pyarmor 8.5.9 (not tested) +.. [#] For riscv64, only support Python 3.10+ + +.. important:: + + pyarmor-7 is bug fixed Pyarmor 7.x version, it's same as Pyarmor 7.x, and only works with old license. Do not use it with new license, it may report ``HTTP 401 error``. + +__ https://pyarmor.readthedocs.io/en/v7.7/platforms.html +__ https://pypi.org/project/pyarmor.cli.core.linux/#files +__ https://pypi.org/project/pyarmor.cli.core.alpine/#files + +Configuration options +===================== + +There are 3 kinds of configuration files + +* global: an ini file :file:`~/.pyarmor/config/global` +* local: an ini file :file:`./.pyarmor/config` +* private: each module may has one ini file in :term:`Local Path`. For example, :file:`./.pyarmor/foo.rules` is private configuration of module ``foo`` + +Use command :ref:`pyarmor cfg` to change options in configuration files. + +.. _plugins: + +Plugins +======= + +.. versionadded:: 8.2 + +.. program:: pyarmor gen + +Plugin is a Python script used to do some post-build work when generating obfuscated scripts. + +Plugin use cases: + +- Additional processing in the output path +- Fix import statement in the obfuscated script for special cases +- Add comment to :term:`outer key` file +- Rename binary extension :mod:`pyarmor_runtime` suffix to avoid name conflicts +- In Darwin use `install_name_tool` to fix :term:`extension module` :mod:`pyarmor_runtime` couldn't be loaded if Python is not installed in the standard path +- In Darwin codesign pyarmor runtime extensions + +Plugin script must define attribute ``__all__`` to export plugin name. + +Plugin script could be any name. + +Plugin script could define one or more plugin classes: + +.. py:class:: PluginName + + .. py:staticmethod:: post_script(ctx, res, source) + + This method is optional. + + This method is called after each script has been obfuscated + + Generally it's used to patch the obfuscated `source`, and return patched `source` + + :param Context ctx: building context + :param FileResource res: instance of `pyarmor.cli.resource.FileResource` + :param str source: the source of obfuscated script + + .. py:staticmethod:: post_build(ctx, inputs, outputs, pack=None) + + This method is optional. + + This method is called when all the obfuscated scripts and runtime files have been generated by :ref:`pyarmor gen` + + :param Context ctx: building context + :param list inputs: all the input paths + :param list outputs: all the output paths + :param str pack: if not None, it's an executable file specified by :option:`--pack` + + .. py:staticmethod:: post_key(ctx, keyfile, **keyinfo) + + This method is optional. + + This method is called when :term:`outer key` has been generated by :ref:`pyarmor gen key` + + :param Context ctx: building context + :param str keyfile: path of generated key file + :param dict keyinfo: runtime key information + + The possible items in the ``keyinfo``: + + :key expired: expired epoch or None + :key devices: a list for binding device hardware information or None + :key data: binding data (bytes) or None + :key period: period in seconds or None + + .. py:staticmethod:: post_runtime(ctx, source, dest, platform) + + This method is optional. + + This method is called when the runtime extension module ``pyarmor_runtime.so`` in the :term:`runtime package` has been generated by :ref:`pyarmor gen`. + + It may be called many times if many platforms are specified in the command line. + + :param Context ctx: building context + :param str source: source path of pyarmor extension + :param str dest: output path of pyarmor extension + :param str platform: standard :term:`platform` name + +To make plugin script work, configure it with script name without extension ``.py`` by this way:: + + $ pyarmor cfg plugins + "script name" + +Pyarmor search plugin script in these paths in turn: + +- Current path +- :term:`local path`, generally ``./.pyarmor/`` +- :term:`global path`, generally ``~/.pyarmor/`` + +Here it's an example plugin script ``fooplugin.py`` + +.. code-block:: python + + __all__ = ['EchoPlugin'] + + class EchoPlugin: + + @staticmethod + def post_runtime(ctx, source, dest, platform): + print('-------- test fooplugin ----------') + print('ctx is', ctx) + print('source is', source) + print('dest is', dest) + print('platform is', platform) + +Store it to local path ``.pyarmor/fooplugin.py``, and enable it:: + + $ pyarmor cfg plugins + "fooplugin" + +Check it, this plugin information should be printed in the console:: + + $ pyarmor gen foo.py + +Disable this plugin:: + + $ pyarmor cfg plugins - "fooplugin" + +.. _hooks: + +Hooks +===== + +.. versionadded:: 8.2 + +Hook is a Python script which is embedded into the obfuscated script, and executed when the obfuscated script is running. + +When obfuscating the scripts, Pyarmor searches path ``hooks`` in the :term:`local path` and :term:`global path` in turn. If there is any same name script exists, it's called module hook script. + +For example, ``.pyarmor/hooks/foo.py`` is hook script of ``foo.py``, ``.pyarmor/hooks/joker.card.py`` is hook script of ``joker/card.py``. + +When generating obfuscate script by this command:: + + $ pyarmor gen foo.py + +``.pyarmor/hooks/foo.py`` will be inserted into the beginning of ``foo.py``. + +A hook script is a normal Python script, it could do everything Python could do. And it could use 2 special function :func:`__pyarmor__` and :func:`__assert_armored__` to do some interesting work. + +Note that all the source lines in the hook script are inserted into module level of original script, be careful to avoid name conflicts. + +.. seealso:: :func:`__pyarmor__` :func:`__assert_armored__` + +Special hook script +------------------- + +.. versionadded:: 8.3 + +If want to do something before obfuscated scripts are executed, it need use a special hook script ``.pyarmor/hooks/pyarmor_runtime.py``, it will be called when initializing Python extension `pyarmor_runtime`. + +First create script ``.pyarmor/hooks/pyarmor_runtime.py`` and define all in the hook function :func:`bootstrap`, only this function will be called. + +.. function:: bootstrap(user_data) + + :param bytes user_data: user data in runtime key + :return: False, quit and raise protection exception + Any others, continue to execute the obfuscated scripts. + :raises SystemExit: quit without traceback + :raises ohter Exception: quit with traceback + +An example script: + +.. code-block:: python + + def bootstrap(user_data): + # Import everything in the function, not in the module level + import sys + import time + from struct import calcsize + + print('user data is', user_data) + + # Check platform + if sys.platform == 'win32' and calcsize('P'.encode()) * 8 == 32: + raise SystemExit('no support for 32-bit windows') + + # Check debugger in Windows + if sys.platform == 'win32': + from ctypes import windll + if windll.kernel32.IsDebuggerPresent(): + print('found debugger') + return False + + # In this example, user_data is timestamp + if time.time() > int(user_data.decode()): + return False + +Check it, first copy this script to ``.pyarmor/hooks/pyarmor_runtime.py``:: + + $ pyarmor gen --bind-data 12345 foo.py + $ python dist/foo.py + + user data is b'12345' + Traceback (most recent call last): + File "dist/foo.py", line 2, in + ... + RuntimeError: unauthorized use of script (1:10325) + +If need query hardware information, the simple way is to import Pyarmor extension `pytransform3`. For example, in Windows, copy the corresponding `pytransform3.pyd` to target device, then get machine id by this way + +.. code-block:: python + + def bootstrap(user_data): + from pytransform3 import get_hd_info + # Refer to pyarmor/cli/get_hd_info.py + print('Machine ID: %s' % get_hd_info(22).decode()) + +.. _target environments: + +===================== + Target Environments +===================== + +Obfuscated scripts run in :term:`target device`. + +Supported Python versions and platforms +======================================= + +Supported platforms, arches and Python versions are same as `Building Environments`_ + +Environment variables +===================== + +A few environment variables are used by obfuscated scripts. + +.. envvar:: LANG + + OS environment variable, used to select runtime error language. + +.. envvar:: PYARMOR_LANG + + It's used to set language runtime error language. + + If it's set, :envvar:`LANG` is ignored. + +.. envvar:: PYARMOR_RKEY + + Set search path for :term:`outer key` + +Supported Third-Party Interpreter +================================= + +About third-party interpreter, for example Jython, and any embedded Python C/C++ code, only they could work with CPython :term:`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. + +Specialized builtin functions +============================= + +.. versionadded:: 8.2 + +There are 2 specialized builtin functions, both of them could be used without import in the obfuscated scripts. + +Generally they're used with inline marker or in the hook scripts. + +.. function:: __pyarmor__(arg, kwarg, name, flag) + + :param bytes name: must be ``b'hdinfo'`` or ``b'keyinfo'`` + :param int flag: must be ``1`` + + **get hdinfo** + + When ``name`` is ``b'hdinfo'``, call it to get hardware information. + + :param int arg: query which kind of device + :param str kwarg: None or device name + :return: arg == 0 return the serial number of first harddisk + :return: arg == 1 return mac address of first network card + :return: arg == 2 return ipv4 address of first network card + :return: arg == 3 unused + :return: arg == 4 return domain name + :rtype: str + :raises RuntimeError: when something is wrong + + For example, + + .. code-block:: python + + __pyarmor__(0, None, b'hdinfo', 1) + __pyarmor__(1, None, b'hdinfo', 1) + + In Linux, ``kwarg`` is used to get named network card or named hard disk. For example: + + .. code-block:: python + + __pyarmor__(0, "/dev/vda2", b'hdinfo', 1) + __pyarmor__(1, "eth2", b'hdinfo', 1) + + In Windows, ``kwarg`` is used to get all network cards and hard disks. For example: + + .. code-block:: python + + __pyarmor__(0, "/0", b'hdinfo', 1) # First disk + __pyarmor__(0, "/1", b'hdinfo', 1) # Second disk + + __pyarmor__(1, "*", b'hdinfo', 1) + __pyarmor__(1, "*", b'hdinfo', 1) + + + **get keyinfo** + + When ``name`` is ``b'keyinfo'``, call it to query user data in the runtime key. + + :param int arg: what information to get from runtime key + :param kwarg: always None + :return: arg == 0 return bind data, no bind data return empty bytes + :rtype: Bytes + :return: arg == 1 return expired epoch, -1 if there is no expired date + :rtype: Long + :return: None if something is wrong + + For example: + + .. code-block:: python + + print('bind data is', __pyarmor__(0, None, b'keyinfo', 1)) + print('expired epoch is' __pyarmor__(1, None, b'keyinfo', 1)) + +.. function:: __assert_armored__(arg) + + :param object arg: arg is a module, function or method object + :returns: return ``arg`` if ``arg`` is obfuscated, otherwise, raise protection error. + + For example + + .. code-block:: python + + m = __import__('abc') + __assert_armored__(m) + + def hello(msg): + print(msg) + + __assert_armored__(hello) + hello('abc') + +.. include:: ../_common_definitions.txt diff --git a/docs/reference/errors.rst b/docs/reference/errors.rst new file mode 100644 index 00000000..77118152 --- /dev/null +++ b/docs/reference/errors.rst @@ -0,0 +1,233 @@ +================ + Error Messages +================ + +.. highlight:: none + +.. program:: pyarmor gen + +Here are all the list of errors when running :command:`pyarmor` or obfuscated scripts. + +If something is wrong, search error message here to find the reason. + +If no exact error message found, most likely it's not caused by Pyarmor, search it in google or any other search engine to find the solution. + +Building Errors +=============== + +**Obfuscating Errors** + +.. list-table:: Table-1. Build Errors + :name: building errors + :widths: 10 20 + :header-rows: 1 + + * - Error + - Reasons + * - out of license + - Using not available features, for example, big script + + Purchasing license to unlock the limitations, refer to :doc:`../licenses` + * - not machine id + - This machine is not registered, or the hardware information is changed. + + Try to register Pyarmor again to fix it. + * - query machine id failed + - Could not get hardware information in this machine + + Pyarmor need query hard disk serial number, mac address etc. + + If it could not get hardware information, it complains of this. + + * - relative import "%s" overflow + - Obfuscating `.py` script which uses relative import + + Solution: obfuscating the whole package (path), instead of one module (file) separately + +**Registering Errors** + +.. list-table:: Table-1.1 Register Errors + :name: register errors + :widths: 10 20 + :header-rows: 1 + + * - Error + - Reasons + * - HTTP Error 400: Bad Request + - Please upgrade Pyarmor to 8.2+ to get the exact error message + * - HTTP Error 401: Unauthorized + - Using old pyarmor commands with new license + + Please using Pyarmor 8 commands to obfuscate the scripts + * - HTTP Error 503: Service Temporarily Unavailable + - Invoking too many register command in 1 minute + + For security reason, the license server only allows 3 register requests in 1 minute + * - unknown license type OLD + - Using old license in Pyarmor 8, the old license only works for Pyarmor 7.x + + Here are :doc:`the latest licenses <../licenses>` + + Please use ``pyarmor-7`` or downgrade pyarmor to 7.7.4 + * - This code has been used too many times + - If this code is used in CI/Docker pipeline, please send **order information** by registration email of this code to pyarmor@163.com to unlock it. Do not send this code only, it doesn't make sense. + * - no registration code found in pyarmor-regcode-xxxx.txt + - Download `pyarmor-regcode-xxxx.txt` again, check its content, make sure it's same as email body + * - update license token failed + - If run register command more than 3 times in 1 minute, wait for 5 minutes, and try again. + + If more than 100 runs in different devices or docker containers in 24 hours, please wait until any token is released + + If the date time of this device is not correct, restore it to current date + + If not, try to open `http://pyarmor.dashingsoft.com//api/auth2/` in web browser + + If the page says `NO:missing parameters`, it means network is fine, and license server is fine. + + If Pyarmor is prior to v8.5.3, upgrade Pyarmor to v8.5.3+, then check Python interpreter by the following commands:: + + $ python + >>> from urllib.request import urlopen + >>> res = urlopen('http://pyarmor.dashingsoft.com//api/auth2/') + >>> print(res.read()) + b'NO:missing parameter' + + If not return this, but raises exception, it's firewall problem, please configure it to allow Python interpreter to visit `pyarmor.dashingsoft.com:80` + +Runtime Errors +============== + +**Error messages reported by pyarmor** + +If it has an error code, it could be customized. + +.. list-table:: Table-2. Runtime Errors of Obfuscated Scripts + :name: runtime errors + :widths: 10 10 20 + :header-rows: 1 + + * - Error Code + - Error Message + - Reasons + * - + - error code out of range + - Internal error + * - error_1 + - this license key is expired + - + * - error_2 + - this license key is not for this machine + - + * - error_3 + - missing license key to run the script + - + * - error_4 + - unauthorized use of script + - + * - error_5 + - this Python version is not supported + - + * - error_6 + - the script doesn't work in this system + - + * - error_7 + - the format of obfuscated script is incorrect + - 1. the obfuscated script is made by other Pyarmor version + 2. can not get runtime package path + * - error_8 + - the format of obfuscated function is incorrect + - + * - + - RuntimeError: Resource temporarily unavailable + - When using option ``-e`` to obfuscate the script, the obfuscated script need connect to `NTP`_ server to check expire date. If network is not available, or something is wrong with network, it raises this error. + + Solutions: + + 1. use local time if device is not connected to internet. + + 2. try it again it may works. + + 3. Upgrade to Pyarmor 8.4.4+, then check network time by http server. For example, set time server by this command `pyarmor cfg nts=http://your.http-server.com/api/v2/` + + * - + - Protection Exception + - If using :option:`--assert-call` or :option:`assert-import`, check section `Filter assert function and import` in the :doc:`../tutorial/advanced`, ignore those problem functions and modules by the traceback. + +**Error messages reported by Python interpreter** + +Generally they are not pyarmor issues. Please consult Python documentation or google error message to fix them. + +.. list-table:: Table-2.1 Other Errors of Obfuscated Scripts + :name: other runtime errors + :widths: 10 20 + :header-rows: 1 + + * - Error Message + - Reasons + * - ImportError: attempted relative import with no known parent package + - 1. ``from .pyarmor_runtime_000000 import __pyarmor__`` + + Do not use :option:`-i` or :option:`--prefix` if you don't know what they're doing. + + For all the other relative import issue, please check Python documentation to learn about relative import knowledge, then check Pyarmor :doc:`man` to understand how to generate runtime packages in different locations. + + +Outer Errors +============ + +Here is a list of some outer errors. Most of them are caused by missing some system libraries, or unexpected configuration. It has nothing to do with Pyarmor, just install necessary libraries or change system configurations to fix the problem. + +By searching error message in google or any other search engine to find the solution. + +**Operation did not complete successfully because the file contains a virus or is potentially unwanted software question** + + It's caused by Windows Defender, not Pyarmor. I'm sure Pyarmor is safe, but it uses some techniques which let anti-virus tools make wrong decision. The solutions what I thought of + + 1. Check documentation of Windows Defender + 2. Ask question in MSDN + 3. Google this error message + +**Library not loaded: '@rpath/Frameworks/Python.framework/Versions/3.9/Python'** + + When Python is not installed in the standard path, or this Python is not Framework, pyarmor reports this error. The solution is using ``install_name_tool`` to change ``pytransform3.so``. For example, in `anaconda3` with Python 3.9, first search which CPython library is installed:: + + $ otool -L /Users/my_username/anaconda3/bin/python + + Find any line includes ``Python.framework``, ``libpython3.9.dylib``, or ``libpython3.9.so``, the filename in this line is CPython library. Or find it in the path:: + + $ find /Users/my_username/anaconda3 -name "Python.framework/Versions/3.9/Python" + $ find /Users/my_username/anaconda3 -name "libpython3.9.dylib" + $ find /Users/my_username/anaconda3 -name "libpython3.9.so" + + Once find CPython library, using ``install_name_tool`` to change and codesign it again:: + + $ install_name_tool -change @rpath/Frameworks/Python.framework/Versions/3.9/Python /Users/my_username/anaconda3/lib/libpython3.9.dylib /Users/my_username/anaconda3/lib/python3.9/site-packages/pyarmor/cli/core/pytransform3.so + $ codesign -f -s - /Users/my_username/anaconda3/lib/python3.9/site-packages/pyarmor/cli/core/pytransform3.so + +**ImportError: libdl.so: cannot open shared object file: No such file or directory** + + When running obfuscated scripts in unmatched platform, it may raise this error. + + In this case checking dependencies by `ldd /path/to/pyarmor_runtime.so` to make sure it works. If not, please select right `--platform` to obfuscate the scripts. + + For example, when obfuscating the scripts in Linux with target platform Termux, somethimes it need specify `--platform linux.aarch64`, not `--platform android.aarch64`, more information refer to `issue 1674`__ + +__ https://github.com/dashingsoft/pyarmor/discussions/1674 + +**No such file or directory: 'nul'** + +Generally something is wrong with Windows System. + +Try to create NUL device again by this command:: + + sc create null binpath=C:\Windows\System32\drivers\null.sys type=kernel start=auto error=normal + +Then start null:: + + sc start null + +If it works, this problem should be fixed. + +If not, please google the solution. It's Windows System issue. + +.. include:: ../_common_definitions.txt diff --git a/docs/reference/man.rst b/docs/reference/man.rst new file mode 100644 index 00000000..7b023cfe --- /dev/null +++ b/docs/reference/man.rst @@ -0,0 +1,1095 @@ +========== + Man Page +========== + +.. contents:: Contents + :depth: 2 + :local: + :backlinks: top + +.. highlight:: console + +Pyarmor is a powerful tool to obfuscate Python scripts with rich option set that provides both high-level operations and full access to internals. + +pyarmor +======= + +.. program:: pyarmor + +.. describe:: Syntax + + pyarmor [options] ... + +.. describe:: Options + +-h, --help show available command set then quit +-v, --version show version information then quit +-q, --silent suppress all normal output :option:`... <-q>` +-d, --debug generate debug log file :option:`... <-d>` +--home PATH set Pyarmor HOME path :option:`... <--home>` + +These options can be used after :program:`pyarmor` but before command, here are available commands: + +================================ ==================================== +:ref:`gen ` Obfuscate scripts +:ref:`gen key ` Generate outer runtime key +:ref:`cfg ` Show and configure environments +:ref:`reg ` Register Pyarmor +================================ ==================================== + +See :command:`pyarmor -h` for more information on a specific command. + +.. describe:: Description + +.. option:: -q, --silent + + Suppress all normal output. + +For example:: + + pyarmor -q gen foo.py + +.. option:: -d, --debug + + Generate debug log :file:`pyarmor.debug.log` + +When something is wrong, use this option to generate :file:`pyarmor.debug.log` to get more information. For example:: + + $ pyarmor -d gen foo.py + $ cat pyarmor.debug.log + +.. option:: --home PATH[,GLOBAL[,LOCAL[,REG]]] + + Set Pyarmor :term:`Home Path`, :term:`Global Path`, :term:`Local Path` and registration file path + +The default paths + +* :term:`Home Path` is :file:`~/.pyarmor/` + +* :term:`Global Path` is :file:`~/.pyarmor/config/` + +* :term:`Local Path` is :file:`./.pyarmor/` + +* registration file path is same as :term:`Home Path` + +All of them could be changed by this option. For example, change home path to :file:`~/.pyarmor2/`:: + + $ pyarmor --home ~/.pyarmor2 ... + +Then + +* :term:`Global Path` is :file:`~/.pyarmor2/config/` +* Registration files are stored in the :file:`~/.pyarmor2/` +* :term:`Local Path` still is :file:`./.pyarmor/` + +Another example, keep all others but only change global path to :file:`~/.pyarmor/config2/`:: + + $ pyarmor --home ,config2 ... + +Another, keep all others but only change local path to :file:`/var/myproject`:: + + $ pyarmor --home ,,/var/myproject/ ... + +Another, set registration file path to :file:`/opt/pyarmor/`:: + + $ pyarmor --home ,,,/opt/pyarmor ... + +It's useful when using :command:`sudo` to run :command:`pyarmor` occasionally. This makes sure the registration file could be found even switch to another user. + +When there are many Pyarmor Licenses registered in one machine, set each license to different path. For example:: + + $ pyarmor --home ~/.pyarmor1 reg pyarmor-regfile-2051.zip + $ pyarmor --home ~/.pyarmor1 gen project1/foo.py + + $ pyarmor --home ~/.pyarmor2 reg pyarmor-regfile-2052.zip + $ pyarmor --home ~/.pyarmor2 gen project2/foo.py + +Start pyarmor with clean configuration by setting :term:`Global Path` and :term:`Local Path` to any non-exists path ``x``:: + + $ pyarmor --home ,x,x, gen foo.py + +.. seealso:: :envvar:`PYARMOR_HOME` + +.. _pyarmor gen: + +pyarmor gen +=========== + +Generate obfuscated scripts and all the required runtime files. + +.. program:: pyarmor gen + +.. describe:: Syntax + + pyarmor gen