diff --git a/README.md b/README.md index 59416b0..3f5c0f3 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,38 @@ -# Pyarmor-Static-Unpack-1shot +# Pyarmor Static Unpack 1-shot -Generally this project aims to statically convert (without executing) armored data - which can be regarded as an encrypted variant of pyc files - back to disassembly and (experimentally) source code. Therefore we forked the awesome [Decompyle++](https://github.com/zrax/pycdc) (aka pycdc). +[Pyarmor](https://github.com/dashingsoft/pyarmor) is a popular tool to protect Python source code. It turns Python scripts into binary data, which can be regarded as an encrypted variant of pyc files. They can be decrypted by a shared library (pyarmor_runtime) and then executed by Python interpreter. -Currently we are trying to support Pyarmor 8.0 - latest (9.1.1), Python 3.7 - 3.13, platforms covering Windows, Linux, macOS, and Android, with obfuscating options as many as possible. (However, we only have limited tests.) +This project aims to convert armored data back to bytecode assembly and (experimentally) source code. We forked the awesome [Decompyle++](https://github.com/zrax/pycdc) (aka pycdc), and added some processes on it like modifying abstract syntax tree. -If the data starts with `PY` followed by six digits, it is supported. Otherwise, if it starts with `PYARMOR`, it is generated by Pyarmor 7 or before, and is not supported. +
+Write-up will be available soon. -We cannot wait to make it public. Detailed write-up will be available soon. For those who are curious, temporarily you can check out [the similar work of G DATA Advanced Analytics](https://cyber.wtf/2025/02/12/unpacking-pyarmor-v8-scripts/). +We cannot wait to make the tool public. For those who are curious, temporarily you can check out [the similar work of G DATA Advanced Analytics](https://cyber.wtf/2025/02/12/unpacking-pyarmor-v8-scripts/). +
+ +> [!WARNING] +> +> **Disassembly results are accurate, but decompiled code can be incomplete and incorrect.** Bytecode has changed a lot in recent Python versions, especially in the area of exception handling that Pyarmor strongly relies on, and pycdc has limited support for bytecode in newer versions. + +## Features + +### Statical + +You don't need to execute the encrypted script. We decrypt them by the same algorithm as pyarmor_runtime does. This is useful when the scripts are not trusted. + +### Universal + +Currently we are trying to support Pyarmor 8.0 - latest (9.1.1), Python 3.7 - 3.13, on all operating systems, with obfuscating options as many as possible. (However, we only have limited tests.) + +You can run this tool in any environment, no need to be the same with obfuscated scripts or runtime. + +> [!NOTE] +> +> If the data starts with `PY` followed by six digits, it is supported. Otherwise, if it starts with `PYARMOR`, it is generated by Pyarmor 7 or earlier, and is not supported. + +### Easy to use + +The only thing you need to do is specifying where your obfuscated scripts are. The tool does everything like detecting armored data, parsing, disassembling, and decompiling. See "Usage" section below. ## Build @@ -18,23 +44,25 @@ cmake --build . cmake --install . ``` +You can also download prebuilt binary files on [releases page](https://github.com/Lil-House/Pyarmor-Static-Unpack-1shot/releases). + ## Usage -Make sure the executable `pyarmor-1shot` (`pyarmor-1shot.exe` on Windows) exists in `helpers` directory, and run `helpers/shot.py` in Python 3 (no need to use the same version with obfuscated scripts) with the "root" directory of obfuscated scripts. It will recursively find and handle `pyarmor_runtime` and as much armored data as possible. For example: - ``` bash -$ ls /path/to/scripts -__pycache__ pyarmor_runtime_000000 obf_main.py plain_src.py util.pyc packed.so folder_with_other_scripts readme.unrelated -$ python /path/to/helpers/shot.py /path/to/scripts +python /path/to/helpers/shot.py /path/to/scripts ``` +Before running `shot.py`, make sure the executable `pyarmor-1shot` (`pyarmor-1shot.exe` on Windows) exists in `helpers` directory. + +You only need to specify the directory that contains all armored data and `pyarmor_runtime`. The tool finds and handles them recursively as much as possible. + When necessary, specify a `pyarmor_runtime` executable with `-r path/to/pyarmor_runtime[.pyd|.so|.dylib]`. All files generated from this tool have a `.1shot.` in file names. If you want to save them in another directory instead of in-place, use `-o another/path/`. Folder structure will remain unchanged. Note: -- Subdirectories called `__pycache__` or `site-packages` will not be touched, and symbolic links will not be followed, to avoid repeat or forever loop and save time. If you really need them, run the script later in these directories (as "root" directory) and specify the runtime. +- Subdirectories called `__pycache__` or `site-packages` will not be touched, and symbolic links will not be followed, to avoid repeat or forever loop and save time. If you really need them, run the script later in these directories and specify the runtime. - Archives, executables generated by PyInstaller and so on, must be unpacked by other tools before decrypting, or you will encounter undefined behavior. ## Feedback @@ -46,7 +74,6 @@ Feel free to open an issue if you have any questions, suggestions, or problems. - [ ] Write-up - [ ] Multi-platform pyarmor_runtime executable - [ ] Accept more input forms -- [ ] Tests for different Pyarmor and Python versions - [ ] Support more obfuscating options - [ ] Use asyncio for concurrency - [ ] Pyarmor 7 and before (Later or never.)