diff --git a/README.md b/README.md index 0fbf1a8..e63d40d 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ 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). -Currently we are trying to support Pyarmor 8.0 - latest (9.0.8), Python 3.6 - 3.13, platforms covering Windows, Linux, macOS, and Android, with obfuscating options as many as possible. (However, we only have limited tests.) +Currently we are trying to support Pyarmor 8.0 - latest (9.1.0), 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.) 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/). diff --git a/helpers/shot.py b/helpers/shot.py index ca337ae..8724cf2 100644 --- a/helpers/shot.py +++ b/helpers/shot.py @@ -101,7 +101,7 @@ def main(): ) logger = logging.getLogger('shot') - print(''' + print(r''' ____ ____ ( __ ) ( __ ) | |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~| | @@ -157,8 +157,12 @@ def main(): except: pass - with open(file_path, 'rb') as f: - beacon = f.read(16 * 1024 * 1024) + try: + with open(file_path, 'rb') as f: + beacon = f.read(16 * 1024 * 1024) + except: + logger.error(f'Failed to read file: {relative_path}') + continue # is UTF-8 source? # TODO: only support natural one line now diff --git a/pyc_code.cpp b/pyc_code.cpp index ba63eed..07c9693 100644 --- a/pyc_code.cpp +++ b/pyc_code.cpp @@ -64,11 +64,14 @@ void PycCode::load(PycData* stream, PycModule* mod) else m_flags = 0; + bool pyarmor_co_obfuscated_flag = m_flags & 0x20000000; + if (mod->verCompare(3, 8) < 0) { // Remap flags to new values introduced in 3.8 - if (m_flags & 0xF0000000) - throw std::runtime_error("Cannot remap unexpected flags"); - m_flags = (m_flags & 0xFFFF) | ((m_flags & 0xFFF0000) << 4); + // Pyarmor CO_OBFUSCATED flag always locates at 0x20000000 + if (m_flags & 0xD0000000) + fprintf(stderr, "Remapping flags (%08X) may not be correct\n", m_flags); + m_flags = (m_flags & 0x1FFF) | ((m_flags & 0xFFFE000) << 4) | (m_flags & 0x20000000); } m_code = LoadObject(stream, mod).cast(); diff --git a/pyc_code.h b/pyc_code.h index d70fe98..845c2aa 100644 --- a/pyc_code.h +++ b/pyc_code.h @@ -36,8 +36,7 @@ public: CO_FUTURE_ANNOTATIONS = 0x1000000, // 3.7 -> CO_NO_MONITORING_EVENTS = 0x2000000, // 3.13 -> - // TODO: Shift things - CO_PYARMOR_OBFUSCATED = 0x20000000, + CO_PYARMOR_OBFUSCATED = 0x20000000, // Pyarmor all }; PycCode(int type = TYPE_CODE) diff --git a/pycdas.cpp b/pycdas.cpp index 2b4a6fe..681e21b 100644 --- a/pycdas.cpp +++ b/pycdas.cpp @@ -104,7 +104,7 @@ void output_object(PycRef obj, PycModule* mod, int indent, unsigned int orig_flags = codeObj->flags(); if (mod->verCompare(3, 8) < 0) { // Remap flags back to the value stored in the PyCode object - orig_flags = (orig_flags & 0xFFFF) | ((orig_flags & 0xFFF00000) >> 4); + orig_flags = (orig_flags & 0x1FFF) | ((orig_flags & 0xDFFE0000) >> 4) | (orig_flags & 0x20000000); } iprintf(pyc_output, indent + 1, "Flags: 0x%08X", orig_flags); print_coflags(codeObj->flags(), pyc_output);