
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
Em versões antigas do Python, era possível verificar bytecode descompilando-o e depois compilando usando o interpretador Python para essa versão de bytecode. Feito isso, o bytecode produzido podia ser comparado com o bytecode original. No entanto, à medida que a geração de código do Python melhorou, isso não era mais viável.
Se você deseja verificação de sintaxe Python da correção do processo de descompilação, adicione a opção :code:--syntax-verify. No entanto, como a sintaxe do Python muda. Você deve usar esta opção se o bytecode for o bytecode correto para o interpretador Python que verificará a sintaxe.
Você também pode comparar cruzadamente os resultados com outra versão do uncompyle6 já que às vezes há regressões na descompilação de bytecode específico, à medida que a qualidade geral melhora.
Para Python 3.7 e 3.8, o código no decompyle3_ é geralmente melhor.
Ou tente outro descompilador Python específico como uncompyle2_, unpyc37_ ou pycdc_. Como os dois últimos funcionam de maneira diferente, bugs aqui geralmente não estão neles, e vice-versa.
Há uma classe interessante desses programas que está prontamente disponível para dar verificação mais forte: aqueles programas que, quando executados, testam a si mesmos. Nossa suíte de testes inclui esses.
E Python vem com outro conjunto de programas como este: sua suíte de testes para a biblioteca padrão. Temos algum código em :code:test/stdlib para facilitar esse tipo de verificação também.
O maior problema conhecido e possivelmente corrigível (mas difícil) tem a ver com o tratamento do fluxo de controle. (Python provavelmente tem o conjunto mais diverso e maluco de instruções compostas que já vi; há cláusulas "else" em loops e blocos try que suspeito que muitos programadores não conhecem.)
Todos os descompiladores Python que examinei têm problemas para descompilar o fluxo de controle do Python. Em alguns casos, podemos detectar uma descompilação errônea e relatar isso.
O suporte ao Python é muito bom para o Python 2
No extremo inferior das versões Python, a descompilação parece muito boa, embora não tenhamos testes automatizados para os testes distribuídos do Python. Além disso, não temos um interpretador Python para as versões 1.6 e 2.0.
Na série Python 3, o suporte ao Python é mais forte por volta de 3.4 ou 3.3 e diminui à medida que você se afasta dessas versões. Python 3.0 é estranho porque, de certa forma, se assemelha mais ao 2.6 do que ao 3.1 ou 2.7. Python 3.6 muda as coisas drasticamente ao usar códigos de palavra em vez de códigos de byte. Como resultado, o campo de deslocamento de salto em um argumento de instrução de salto foi reduzido. Isso torna as instruções :code:EXTENDED_ARG agora mais prevalentes em instruções de salto; anteriormente eram raras. Talvez para compensar as instruções :code:EXTENDED_ARG adicionais, otimização de salto adicional foi adicionada. Portanto, em suma, lidar com o fluxo de controle por meios ad hoc, como é feito atualmente, é pior.
Entre Python 3.5, 3.6 e 3.7, houve grandes mudanças nas instruções :code:MAKE_FUNCTION e :code:CALL_FUNCTION.
Python 3.8 remove as instruções :code:SETUP_LOOP, :code:SETUP_EXCEPT, :code:BREAK_LOOP e :code:CONTINUE_LOOP, o que pode dificultar a detecção de fluxo de controle, faltando a análise de fluxo de controle mais sofisticada que está planejada. Veremos.
Atualmente, nem todos os números mágicos do Python são suportados. Especificamente em algumas versões do Python, notavelmente Python 3.6, o número mágico mudou várias vezes dentro de uma versão.
Suportamos apenas versões lançadas, não versões candidatas. Note, no entanto, que o número mágico de uma versão lançada geralmente é o mesmo da última versão candidata antes do lançamento.
Existem também interpretadores Python personalizados, notavelmente o Dropbox, que usam seus próprios números mágicos e criptografam bytecode. Exceto pelo antigo interpretador Python 2.5 do Dropbox, esse tipo de coisa não é tratado.
Também não tratamos de código PJOrion_ ou de outra forma ofuscado. Para PJOrion tente: PJOrion Deobfuscator_ para desembaralhar o bytecode e obter bytecode válido antes de tentar esta ferramenta; pydecipher_ pode ajudar com isso.
Este programa não pode descompilar arquivos EXE do Microsoft Windows criados por Py2EXE_, embora possamos provavelmente descompilar o código depois que você extrair o bytecode corretamente. Pydeinstaller <https://github.com/charles-dyfis-net/pydeinstaller>_ pode ajudar a desempacotar empacotadores Pyinstaller.
Lidar com listas patologicamente longas de expressões ou instruções é lento. Não tratamos Cython_ ou MicroPython, que não usam bytecode.
Existem numerosos bugs na descompilação. E isso é verdade para todos os outros descompiladores CPython que encontrei, mesmo aqueles que afirmavam ser "perfeitos" em alguma versão específica como 2.4.
À medida que o Python avança, a descompilação também se torna mais difícil porque a compilação é mais sofisticada e a própria linguagem é mais sofisticada. Suspeito que haverá menos tentativas ad-hoc como unpyc37_ (que é baseado em um descompilador 3.3) simplesmente porque é mais difícil fazê-lo. A boa notícia, pelo menos do meu ponto de vista, é que acho que entendo o que é necessário para lidar com os problemas de forma mais robusta. Mas agora, até que o projeto seja melhor financiado, não pretendo fazer nenhum esforço sério para suportar as versões Python 3.8 ou 3.9, incluindo bugs que possam surgir. Imagino que em algum momento eu possa me interessar por isso.
Você pode facilmente encontrar bugs executando os testes contra a suíte de testes padrão que o Python usa para verificar a si mesmo. A qualquer momento, há dezenas de problemas conhecidos que são bastante bem isolados e que poderiam ser resolvidos se alguém dedicasse tempo para fazê-lo. O problema é que não há muitas pessoas que têm trabalhado na correção de bugs.
Alguns dos bugs em 3.7 e 3.8 são simplesmente uma questão de portar de volta as correções no decompyle3. Algum voluntário?
Você pode encontrar um bug que deseja relatar. Por favor, faça isso depois de ler How to report a bug <https://github.com/rocky/python-uncompyle6/blob/master/HOW-TO-REPORT-A-BUG.md>_ e siga as instruções ao abrir um issue <https://github.com/rocky/python-uncompyle6/issues/new?assignees=&labels=&template=bug-report.md>_.
Esteja ciente de que pode não receber minha atenção por um tempo. Se você patrocinar ou apoiar o projeto de alguma forma, priorizarei seus problemas acima da fila de outras coisas que eu possa estar fazendo. Em situações raras, posso fazer uma descompilação manual de bytecode mediante taxa. No entanto, isso é caro, geralmente além do que a maioria das pessoas está disposta a gastar.
uncompyle6 decompyle3 -- BlackHat 2024 Asia (vídeo <https://www.youtube.com/watch?v=NA77SFncppE>_). Muito obrigado aos Organizadores e Revisores por me deixarem falar. Esse tipo de coisa me incentiva a trabalhar em projetos como este.uncompyle6 estão incorretos, enquanto os do :code:uncompyle2 não, mas mais frequentemente o uncompyle6 está correto quando o uncompyle2 não está. Como o :code:uncompyle6 adere à precisão em vez do Python idiomático, o :code:uncompyle2 pode produzir código de aparência mais natural quando está correto. Atualmente :code: é levemente mantido. Veja seu _ para mais detalhes... _Cython: https://en.wikipedia.org/wiki/Cython .. _trepan: https://pypi.python.org/pypi/trepan3k .. _compiler: https://github.com/rocky/python-uncompyle6/wiki/How-does-this-code-work%3F .. _HISTORY: https://github.com/rocky/python-uncompyle6/blob/master/HISTORY.md .. _report_bug: https://github.com/rocky/python-uncompyle6/blob/master/HOW-TO-REPORT-A-BUG.md .. _debuggers: https://pypi.python.org/pypi/trepan3k .. _remake: https://bashdb.sf.net/remake .. _pycdc: https://github.com/zrax/pycdc .. _decompyle3: https://github.com/rocky/python-decompile3 .. _uncompyle2: https://github.com/wibiti/uncompyle2 .. _unpyc37: https://github.com/andrew-tavera/unpyc37 .. _this: https://github.com/rocky/python-uncompyle6/wiki/Deparsing-technology-and-its-use-in-exact-location-reporting .. |buildstatus| image:: https://circleci.com/gh/rocky/python-uncompyle6.svg?style=svg :target: https://app.circleci.com/pipelines/github/rocky/python-uncompyle6 .. |packagestatus| image:: https://repology.org/badge/vertical-allrepos/python:uncompyle6.svg :target: https://repology.org/project/python:uncompyle6/versions .. _PJOrion: http://www.koreanrandom.com/forum/topic/15280-pjorion-%D1%80%D0%B5%D0%B4%D0%B0%D0%BA%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5-%D0%BA%D0%BE%D0%BC%D0%BF%D0%B8%D0%BB%D1%8F%D1%86%D0%B8%D1%8F-%D0%B4%D0%B5%D0%BA%D0%BE%D0%BC%D0%BF%D0%B8%D0%BB%D1%8F%D1%86%D0%B8%D1%8F-%D0%BE%D0%B1%D1%84 .. _pydecipher: .. _Deobfuscator: .. _Py2EXE: .. |Supported Python Versions| image:: .. |Latest Version| image:: :target: .. |Pypi Installs| image::
uncompyle2tracker <https://github.com/wibiti/uncompyle2/issues>Como relatar um bug <https://github.com/rocky/python-uncompyle6/blob/master/HOW-TO-REPORT-A-BUG.md>_issue tracker <https://github.com/zrax/pycdc/issues>_ para detalhes. Atualmente levemente mantido.