Skip to content

Commit 6aa3380

Browse files
authored
Merge pull request #1728 from HackTricks-wiki/update_VVS_Discord_Stealer_Using_Pyarmor_for_Obfuscation__20260102_124039
VVS Discord Stealer Using Pyarmor for Obfuscation and Detect...
2 parents 6f74a08 + 39a4a95 commit 6aa3380

File tree

1 file changed

+18
-2
lines changed
  • src/generic-methodologies-and-resources/basic-forensic-methodology/specific-software-file-type-tricks

1 file changed

+18
-2
lines changed

src/generic-methodologies-and-resources/basic-forensic-methodology/specific-software-file-type-tricks/.pyc.md

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,10 @@ test@test:python python_exe_unpack.py -p unpacked/malware_3.exe/archive
115115
116116
If you weren't able to extract the python "original" code following the previous steps, then you can try to **extract** the **assembly** (but i**t isn't very descriptive**, so **try** to extract **again** the original code).In [here](https://bits.theorem.co/protecting-a-python-codebase/) I found a very simple code to **disassemble** the _.pyc_ binary (good luck understanding the code flow). If the _.pyc_ is from python2, use python2:
117117
118-
```bash
118+
<details>
119+
<summary>Disassemble a .pyc</summary>
120+
121+
```python
119122
>>> import dis
120123
>>> import marshal
121124
>>> import struct
@@ -158,6 +161,19 @@ True
158161
17 RETURN_VALUE
159162
```
160163
164+
</details>
165+
166+
## PyInstaller raw marshal & Pyarmor v9 static unpack workflow
167+
168+
- **Extract embedded marshal blobs**: `pyi-archive_viewer sample.exe` and export raw objects (e.g., a file named `vvs`). PyInstaller stores bare marshal streams that start with `0xe3` (TYPE_CODE with FLAG_REF) instead of full `.pyc` files. Prepend the correct **16-byte `.pyc` header** (magic for the embedded interpreter version + zeroed timestamp/size) so decompilers accept it. For Python 3.11.5 you can grab the magic via `imp.get_magic().hex()` and patch it with `dd`/`printf` before the marshal payload.
169+
- **Decompile with version-aware tools**: `pycdc -c -v 3.11.5 vvs.pyc > vvs.py` or PyLingual. If only partial code is needed, you can walk the AST (e.g., `ast.NodeVisitor`) to pull specific arguments/constants.
170+
- **Parse the Pyarmor v9 header** to recover crypto parameters: signature `PY<license>` at `0x00`, Python major/minor at `0x09/0x0a`, protection type `0x09` when **BCC** is enabled (`0x08` otherwise), ELF start/end offsets at `0x1c/0x38`, and the 12-byte AES-CTR nonce split across `0x24..0x27` and `0x2c..0x33`. The same pattern repeats after the embedded ELF.
171+
- **Account for Pyarmor-modified code objects**: `co_flags` has bit `0x20000000` set and an extra length-prefixed field. Disable CPython `deopt_code()` during parsing to avoid decryption failures.
172+
- **Identify encrypted code regions**: bytecode is wrapped by `LOAD_CONST __pyarmor_enter_*__` … `LOAD_CONST __pyarmor_exit_*__`. Decrypt the enclosed blob with AES-128-CTR using the runtime key (e.g., `273b1b1373cf25e054a61e2cb8a947b8`). Derive the per-region nonce by XORing the payload-specific 12-byte XOR key (from the Pyarmor runtime) with the 12 bytes in the `__pyarmor_exit_*__` marker. After decryption, you may also see `__pyarmor_assert_*__` (encrypted strings) and `__pyarmor_bcc_*__` (compiled dispatch targets).
173+
- **Decrypt Pyarmor “mixed” strings**: constants prefixed with `0x81` are AES-128-CTR encrypted (plaintext uses `0x01`). Use the same key and the runtime-derived string nonce (e.g., `692e767673e95c45a1e6876d`) to recover long string constants.
174+
- **Handle BCC mode**: Pyarmor `--enable-bcc` compiles many functions to a companion ELF and leaves Python stubs that call `__pyarmor_bcc_*__`. Map those constants to ELF symbols with tooling such as `bcc_info.py`, then decompile/analyze the ELF at the reported offsets (e.g., `__pyarmor_bcc_58580__` → `bcc_180` at offset `0x4e70`).
175+
176+
161177
## Python to Executable
162178
163179
To start, we’re going to show you how payloads can be compiled in py2exe and PyInstaller.
@@ -217,7 +233,7 @@ C:\Users\test\Desktop\test>pyinstaller --onefile hello.py
217233
## References
218234
219235
- [https://blog.f-secure.com/how-to-decompile-any-python-binary/](https://blog.f-secure.com/how-to-decompile-any-python-binary/)
220-
236+
- [VVS Discord Stealer Using Pyarmor for Obfuscation and Detection Evasion](https://unit42.paloaltonetworks.com/vvs-stealer/)
221237
222238
{{#include ../../../banners/hacktricks-training.md}}
223239

0 commit comments

Comments
 (0)