From ba382194463c4b9130ae22bac18b4c3979457fef Mon Sep 17 00:00:00 2001 From: ErdemOzgen Date: Mon, 11 Dec 2023 08:58:49 +0300 Subject: [PATCH] Add instructions for reverse engineering Python executable --- ...on Exe to Source and Protection Methods.md | 177 ++++++++++++++++++ 1 file changed, 177 insertions(+) diff --git a/content/CyberSecurity/Reverse Engineering Python Exe to Source and Protection Methods.md b/content/CyberSecurity/Reverse Engineering Python Exe to Source and Protection Methods.md index 8b1378917..69c737b6e 100644 --- a/content/CyberSecurity/Reverse Engineering Python Exe to Source and Protection Methods.md +++ b/content/CyberSecurity/Reverse Engineering Python Exe to Source and Protection Methods.md @@ -1 +1,178 @@ +```bash +conda create -n uncompile python=3.6 +conda activate uncompile +# We will use pyinstxtractor-ng +# git clone https://github.com/countercept/python-exe-unpacker.git +#cd python-exe-unpacker +#python pyinstxtractor.py ~/Downloads/Aimful/aimful-kucoin.exe +git clone https://github.com/pyinstxtractor/pyinstxtractor-ng.git +pyinstxtractor-ng --one-dir main.exe +cd main.exe_extracted +pip install uncompyle6 +uncompyle6 -o output *.pyc +cd output && ls -lah +``` + +## Reverse engineering + + +Basic information about binaries. There are two main versions of the program in question: +`aimful-kucoin.exe` and `aimful-binance.exe`. They are both Windows executables. From the FAQ section of the discord server, the following information is available: + +> In what language was this bot written? +> - Python. + +1. Extract the contents of the binary + +```bash +git clone https://github.com/countercept/python-exe-unpacker.git +cd python-exe-unpacker +python pyinstxtractor.py ~/Downloads/Aimful/aimful-kucoin.exe +``` + +Install python decompiler called `decompyle3` based on `uncompyle6`: + +```bash +git clone https://github.com/rocky/python-decompile3.git +cd python-decompile3 +pip install -e . +``` + +2. Attempt to decompile the contents of the binary + +```bash +cd ./aimful-kucoin.exe_extracted/PYZ-00.pyz_extracted +decompyle3 ./kucoin.client.pyc + +> ... +> ImportError: Ill-formed bytecode file ./kucoin.client.pyc +> ; bad marshal data (unknown type code) +``` + +By searching for the file header "e3000000" on google, we can find this article: +https://timonpeng.com/tips-of-pyinstaller-executable-file-decompilation/ + +3. Inspect the struct file header bytes +```bash +xxd < struct | head -5 +``` + +Output: +``` +00000000: 420d 0d0a 0000 0000 7079 6930 1001 0000 B.......pyi0.... +00000010: e300 0000 0000 0000 0000 0000 0008 0000 ................ +00000020: 0040 0000 0073 3800 0000 6400 6401 6402 .@...s8...d.d.d. +00000030: 6403 6404 6405 6406 6407 6708 5a00 6408 d.d.d.d.d.g.Z.d. +00000040: 6409 6c01 5400 6408 640a 6c01 6d02 5a02 d.l.T.d.d.l.m.Z. +``` + +Mainly we are interested in the first 16 bytes: +``` +420d 0d0a 0000 0000 7079 6930 1001 0000 +``` + +> You can find that the first byte of the main program is `e3`, therefore, the contents before `e3` in the struct file are filled to the front of the main program file. + +```bash +cp ./aiumful-kucoin ./aimful-kucoin.pyc + +set pyc_file ./aimful-kucoin.pyc + +# pad file with extra 4 bytes at the beginning +printf '\x00\x00\x00\x00' > $pyc_file.new +cat $pyc_file >> $pyc_file.new +mv $pyc_file.new $pyc_file +# replace the binary header with the 16 bytes above +printf '\x42\x0d\x0d\x0a\x00\x00\x00\x00\x70\x79\x69\x30\x10\x01\x00\x00' | dd of=$pyc_file bs=1 seek=0 count=16 conv=notrunc +``` + +4. Attempt to decompile the binary +```bash +decompyle3 ./aimful-kucoin.pyc +``` + +Output: +``` +# decompyle3 version 3.7.6 +# Python bytecode 3.7 (3394) +# Decompiled from: Python 3.7.12 (default, Oct 9 2021, 17:28:41) +# [Clang 12.0.0 (clang-1200.0.32.29)] +# Embedded file name: dist\obf\aimful-kucoin.py +# Compiled at: 1995-09-27 10:18:56 +# Size of source mod 2**32: 272 bytes +from pytransform import pyarmor_runtime +pyarmor_runtime() +__pyarmor__(__name__, __file__, b'PYARMOR\x00\x00\x03\x07\x00B\r\r\n\x06*\xa0\x01\x00\x00\x00\x00\x01\x00\x00\x00@\x00\x00\x00\x94\xa5\x01\x00\x00\x00\x00\x18>\x11 +... binary obfuscated ... +``` + +From here we can deduce that the binary is obfuscated with the `pytransform` library using `pyarmor_runtime()`. +Quick search reveals there are some tools that can de-obfuscate the binary data. + +```bash +git clone https://github.com/u0pattern/PyArmor-Deobfuscator.git +# copy PyArmorDeobfuscator.py to the working directory +# ... + +decompyle3 ./aimful-kucoin.pyc > ./aimful-kucoin-obf.py +pip install uncompyle6 +python ./PyArmorDeobfuscator.py -f ./pyimful-kucoin-obf.py -o ./aimful-kucoin.py +``` + +Output is: +``` +[-] _pytransform.dll file not found [-] +``` +At this point we need to start looking at the source code of the decompiler script. +From the comment in the source code we can see that we need the following: +```py +# please make sure you have _pytransform.dll and __init__.py in /dist/pytransform/ directory !!! +``` + +https://forum.tuts4you.com/topic/41945-python-pyarmor-my-protector/?tab=comments#comment-203008 +From another article online, I found that using the extracted files, we can create the following directory structure: +``` +. +|-- some-python-bytecode.pyc +`-- pytransform + |-- __init__.py + |-- _pytransform.dll + |-- _pytransform.dylib + +1 directory, 5 files +``` +The DLL was already included in the directory. For running on OSX I downloaded the pytransform library and placed it in the same directory as the script. +https://pyarmor.dashingsoft.com/platforms.html + +The contents of `__init__.py` are: +```py +from pytransform import pyarmor_runtime +pyarmor_runtime('/path/to/runtime') +``` +as per docs at https://pyarmor.readthedocs.io/en/latest/understand-obfuscated-scripts.html + +5. Another attempt +```bash +python ./PyArmorDeobfuscator.py -f ./pyimful-kucoin-obf.py -o ./aimful-kucoin.py +``` +Output is: +``` +ImportError: File name: './aimful-kucoin-obf.pyc' doesn't exist +``` + +Rename `aimful-kucoin.pyc` to `aimful-kucoin-obf.pyc` and re-run the script. +The output is the same as the previous one, with a slight difference because this time we are using uncompyle6 to decompile the file. +``` +# uncompyle6 version 3.7.4 +# Python bytecode 3.7 (3394) +# Decompiled from: Python 3.7.12 (default, Oct 9 2021, 17:28:41) +# [Clang 12.0.0 (clang-1200.0.32.29)] +# Embedded file name: dist\obf\aimful-kucoin.py +# Compiled at: 1995-09-27 10:18:56 +# Size of source mod 2**32: 272 bytes +from pytransform import pyarmor_runtime +pyarmor_runtime() +__pyarmor__(__name__, __file__, b'PYARMOR\x00\x00\x03\x07\x00B\r\r\n\x06*\xa0\x01\x00\x00\x00\x00\x01\x00\x00\x00@\x00\x00\x00\x94\xa5\x01\x00\x00\x00\x00\x18>\x11\xb8\n\x00\x0 +... binary obfuscated ... +``` \ No newline at end of file