
Um descompilador de bytecode Python multi-versão
|buildstatus| |Pypi Installs| |Latest Version| |Supported Python Versions|
|packagestatus|
.. contents::
Um descompilador nativo Python de versão cruzada e descompilador de fragmentos. O sucessor do decompyle, uncompyle e uncompyle2.
Eu dei uma palestra sobre isso no BlackHat Asia 2024 <https://youtu.be/H-7ZNrpsV50?si=nOaixgYHr7RbILVS>_.
uncompyle6 traduz bytecodes Python de volta para código-fonte Python equivalente. Ele aceita bytecodes da versão 1.0 à versão 3.8 do Python, abrangendo mais de 24 anos de lançamentos do Python. Incluímos o bytecode Python 2.5 do Dropbox e alguns bytecodes do PyPy.
Ok, eu vou dizer: este software é incrível. É mais do que um descompilador hackeado normal. Usando a tecnologia compiler_, o programa cria uma árvore de análise do programa a partir das instruções; nós nos níveis superiores que se parecem um pouco com o que poderia vir de um AST Python. Então podemos realmente classificar e entender o que está acontecendo em seções de bytecode Python.
Com base nisso, outra coisa que torna isso diferente de outros descompiladores de bytecode CPython é a capacidade de deparsar apenas fragmentos de código-fonte e fornecer informações do código-fonte em torno de um determinado deslocamento de bytecode.
Eu uso os fragmentos da árvore para deparsar fragmentos de código em tempo de execução dentro dos meus trepan_ debuggers_. Para isso, deslocamentos de bytecode são registrados e associados a fragmentos do código-fonte. Este propósito, embora compatível com a intenção original, é um pouco diferente. Veja this_ para mais informações.
O deparsing de fragmentos Python, dado um deslocamento de instrução, é útil para mostrar rastreamentos de pilha e pode ser incorporado em qualquer programa que queira mostrar uma localização com mais detalhes do que apenas um número de linha em tempo de execução. Este código também pode ser usado quando a informação do código-fonte não existe e há apenas bytecode. Novamente, meus depuradores fazem uso disso.
Houve (e ainda existem) vários forks de decompyle, uncompyle, uncompyle2, uncompyle3 por aí. Muitos deles vêm basicamente da mesma base de código, e (quase?) todos não são mais mantidos ativamente. Um era muito bom em descompilar Python 1.5-2.3, outro é muito bom em Python 2.7, mas só isso. Outro lida apenas com Python 3.2; outro corrigiu esse e lidava apenas com 3.3. Você entendeu. Este código reúne todos esses forks e avança. Há uma séria refatoração e limpeza nesta base de código em relação aos forks antigos. Uma refatoração ainda mais experimental está acontecendo no decompyle3_.
Isso demonstravelmente faz o melhor na descompilação Python em todas as versões do Python. E mesmo quando há outro projeto que fornece apenas descompilação para um subconjunto de versões Python, geralmente nos saímos demonstravelmente melhor também para essas.
Como podemos saber? Pegando o bytecode Python que vem distribuído com essa versão do Python e descompilando-o. Entre aqueles que descompilam com sucesso, podemos então garantir que os programas resultantes sejam sintaticamente corretos executando o interpretador Python para essa versão do bytecode. Finalmente, nos casos em que o programa tem um teste para si mesmo, podemos executar a verificação no código descompilado.
Usamos processos automatizados para encontrar bugs. Nos rastreadores de problemas de outros descompiladores, você encontrará vários bugs que encontramos ao longo do caminho. Muito poucos deles são corrigidos nos outros descompiladores.
O código no repositório git pode ser executado do Python 2.4 até a versão mais recente do Python, exceto Python 3.0 a 3.2. Voluntários são bem-vindos para resolver essas deficiências se houver desejo de fazê-lo.
A maneira como faz isso, no entanto, é segregando versões consecutivas do Python em branches git:
master Python 3.11 e superiores (usa instalação poetry e expressões idiomáticas Python mais recentes) python-3.6-to-3.10 Python 3.6 a 3.10 (usa f-strings mais novas, expressões idiomáticas mais modernas e anotações de tipo mais modernas) python-3.3-to-3.5 Python 3.3 a 3.5 (Python 3 Genérico) python-3.0-to-3.2 Python 3.0 a 3.2 (Python 3 inicial; 3.0 era em algumas áreas mais próximo do Python 2.6 do que do Python 2.7) python-2.4-to-2.7 Python 2.4 a 2.7 (Python 2 Genérico)
PyPy da Versão 2.4 em diante também funciona.
Os arquivos de bytecode que ele pode ler foram testados em bytecodes Python das versões 1.4, 2.1-2.7 e 3.0-3.8 e versões posteriores do PyPy.
Para lançamentos recentes do Python (Python 3.11+), você pode instalar do PyPI usando o nome uncompyle6::
pip install uncompyle6
Para lançamentos do Python anteriores ao 3.11, não instale usando PyPI, mas sim instale usando um arquivo na seção de Lançamentos do GitHub. Python antigo costumava usar easy_install <https://python101.pythonlibrary.org/chapter29_pip.html#using-easy-install>_. Mas isso não é mais suportado no PyPi ou em versões mais novas do Python. E vice-versa, poetry nem pip (as maneiras mais novas) não são suportados em Pythons antigos.
Se a versão do Python em que você está executando o uncompyle6 estiver entre Python 2.4 e 2.7, use um tarball chamado uncompyle6_24-x.y.z.tar.gz.
Se a versão do Python em que você está executando o uncompyle6 estiver entre Python 3.0 e 3.2, use um tarball chamado uncompyle6_30-x.y.z.tar.gz.
Se a versão do Python em que você está executando o uncompyle6 estiver entre Python 3.3 e 3.5, use um tarball chamado uncompyle6_33-x.y.z.tar.gz.
Se a versão do Python em que você está executando o uncompyle6 estiver entre Python 3.6 e 3.11, use um tarball chamado uncompyle6_36-x.y.z.tar.gz.
Se a versão do Python em que você está executando o uncompyle6 for 3.11 ou posterior, use um tarball chamado uncompyle6-x.y.z.tar.gz.
Você também pode tentar eggs ou wheels que tenham a mesma designação de versão, por exemplo, uncompyle6-x.y.z-py39-none-any.whl para uma instalação Python 3.9. No entanto, observe que a versão sem a designação significa Python 3.11 ou superior.
Da mesma forma, um tarball sem _xx funciona apenas para Python 3.11 ou superior.
Justificativa para usar branches Git ++++++++++++++++++++++++++++++++
Atualmente é impossível (senão impraticável) ter um único código-fonte Python dessa complexidade e com tantos recursos que possa executar tanto Python 2.7 quanto Python 3.13+. As linguagens divergiram muito, e o empacotamento é vastamente diferente. Na verdade, a prática de empacotamento para Python 3.11+ é incompatível com Python 2.7 (e anteriores até Python 2.4), que favorecia "easy_install".
Instalação a partir do código-fonte ++++++++++++++++++++++++++++++
Para instalar a partir do código-fonte, certifique-se de ter o branch Git correto. Consulte a seção Requisitos para os nomes dos branches Git.
Após definir o branch correto::
$ pip install -e . # set up to run from source tree
Um GNU Makefile também é fornecido, então :code:make install (possivelmente como root ou sudo) fará os passos acima.
::
make check
Um GNU makefile foi adicionado para suavizar a configuração e execução do comando correto, e executar testes do mais rápido ao mais lento.
Se você tiver o remake_ instalado, pode ver a lista de todas as tarefas incluindo testes via :code:remake --tasks
Execute
::
$ uncompyle6 compiled-python-file-pyc-or-pyo
Para ajuda de uso:
::
$ uncompyle6 -h