
Ein versionsübergreifender Python-Bytecode-Dekompiler
|buildstatus| |Pypi Installs| |Latest Version| |Supported Python Versions|
|packagestatus|
.. contents::
Ein nativer Python-kreuzversionsfähiger Decompiler und Fragment-Decompiler. Der Nachfolger von decompyle, uncompyle und uncompyle2.
Ich habe darüber einen Vortrag auf der BlackHat Asia 2024 <https://youtu.be/H-7ZNrpsV50?si=nOaixgYHr7RbILVS>_ gehalten.
uncompyle6 übersetzt Python-Bytecode zurück in äquivalenten Python-Quellcode. Es akzeptiert Bytecode von Python-Version 1.0 bis Version 3.8, was über 24 Jahre Python-Veröffentlichungen abdeckt. Wir beinhalten Dropbox' Python 2.5 Bytecode und einige PyPy-Bytecodes.
Ok, ich sage es: Diese Software ist erstaunlich. Sie ist mehr als ein gewöhnlicher, hackiger Decompiler. Mithilfe der compiler_-Technologie erstellt das Programm einen Parse-Baum des Programms aus den Instruktionen; Knoten auf den oberen Ebenen, die ein wenig so aussehen, wie das, was von einem Python-AST kommen könnte. So können wir wirklich klassifizieren und verstehen, was in Abschnitten von Python-Bytecode vor sich geht.
Darauf aufbauend ist ein weiterer Unterschied zu anderen CPython-Bytecode-Decompilern, dass er nur Fragmente von Quellcode deparsen und Quellcode-Informationen um einen bestimmten Bytecode-Offset herum liefern kann.
Ich verwende die Baumfragmente, um Codefragmente zur Laufzeit in meinen trepan_-Debuggers_ zu deparsen. Dazu werden Bytecode-Offsets aufgezeichnet und mit Fragmenten des Quellcodes verknüpft. Dieser Zweck ist zwar mit der ursprünglichen Absicht vereinbar, aber dennoch etwas anders. Siehe this_ für weitere Informationen.
Python-Fragment-Deparsing, gegeben einen Instruktions-Offset, ist nützlich für die Anzeige von Stack-Traces und kann in jedes Programm integriert werden, das einen Ort detaillierter als nur eine Zeilennummer zur Laufzeit anzeigen möchte. Dieser Code kann auch verwendet werden, wenn keine Quellcode-Informationen existieren und nur Bytecode vorhanden ist. Auch hier nutzen meine Debugger dies.
Es gab (und gibt immer noch) mehrere Forks von decompyle, uncompyle, uncompyle2, uncompyle3. Viele von ihnen stammen im Grunde von derselben Codebasis, und (fast?) alle werden nicht mehr aktiv gepflegt. Einer war wirklich gut im Dekompilieren von Python 1.5-2.3, ein anderer ist wirklich gut für Python 2.7, aber nur das. Ein anderer handhabt nur Python 3.2; ein weiterer hat das gepatcht und handhabt nur 3.3. Du verstehst, worauf ich hinauswill. Dieser Code bringt all diese Forks zusammen und geht voran. Es gibt eine ernsthafte Refaktorisierung und Bereinigung in dieser Codebasis im Vergleich zu diesen alten Forks. Noch experimentellere Refaktorisierung findet in decompyle3_ statt.
Dieser Code macht nachweislich das Beste beim Dekompilieren von Python über alle Python-Versionen hinweg. Und selbst wenn es ein anderes Projekt gibt, das nur Dekompilierung für eine Teilmenge von Python-Versionen bietet, schneiden wir im Allgemeinen auch für diese nachweislich besser ab.
Wie können wir das feststellen? Indem wir Python-Bytecode nehmen, der mit dieser Python-Version ausgeliefert wird, und ihn dekompilieren. Unter den erfolgreich dekompilierten können wir dann sicherstellen, dass die resultierenden Programme syntaktisch korrekt sind, indem wir den Python-Interpreter für diese Bytecode-Version ausführen. Schließlich können wir in Fällen, in denen das Programm einen Selbsttest hat, die Prüfung am dekompilierten Code durchführen.
Wir verwenden automatisierte Prozesse, um Fehler zu finden. In den Issue-Trackern anderer Decompiler werden Sie mehrere Fehler finden, die wir im Laufe der Zeit gefunden haben. Sehr wenige davon sind in den anderen Decompilern behoben.
Der Code im Git-Repository kann von Python 2.4 bis zur neuesten Python-Version ausgeführt werden, mit Ausnahme von Python 3.0 bis 3.2. Freiwillige sind willkommen, diese Mängel zu beheben, falls der Wunsch dazu besteht.
Die Art und Weise, wie dies geschieht, ist die Aufteilung aufeinanderfolgender Python-Versionen in Git-Zweige:
master Python 3.11 und höher (verwendet poetry-Installation und neuere Python-Idiome) python-3.6-to-3.10 Python 3.6 bis 3.10 (verwendet neuere f-Strings, modernere Idiome und modernere Typannotationen) python-3.3-to-3.5 Python 3.3 bis 3.5 (Generisches Python 3) python-3.0-to-3.2 Python 3.0 bis 3.2 (Frühes Python 3; 3.0 war in einigen Bereichen näher an Python 2.6 als an Python 2.7) python-2.4-to-2.7 Python 2.4 bis 2.7 (Generisches Python 2)
PyPy ab Version 2.4 funktioniert ebenfalls.
Die Bytecode-Dateien, die es lesen kann, wurden auf Python-Bytecodes der Versionen 1.4, 2.1-2.7, 3.0-3.8 und späteren PyPy-Versionen getestet.
Für neuere Python-Veröffentlichungen (Python 3.11+) können Sie von PyPI mit dem Namen uncompyle6 installieren::
pip install uncompyle6
Für Python-Veröffentlichungen vor 3.11 installieren Sie nicht von PyPI, sondern verwenden Sie eine Datei aus dem GitHub Releases section. Älteres Python verwendete früher easy_install <https://python101.pythonlibrary.org/chapter29_pip.html#using-easy-install>_. Dies wird jedoch nicht mehr auf PyPi oder neueren Python-Versionen unterstützt. Und umgekehrt werden poetry noch pip (die neueren Methoden) auf älteren Pythons nicht unterstützt.
Wenn die Python-Version, auf der Sie uncompyle6 ausführen, zwischen Python 2.4 und 2.7 liegt, verwenden Sie ein Tarball namens uncompyle6_24-x.y.z.tar.gz.
Wenn die Python-Version, auf der Sie uncompyle6 ausführen, zwischen Python 3.0 und 3.2 liegt, verwenden Sie ein Tarball namens uncompyle6_30-x.y.z.tar.gz.
Wenn die Python-Version, auf der Sie uncompyle6 ausführen, zwischen Python 3.3 und 3.5 liegt, verwenden Sie ein Tarball namens uncompyle6_33-x.y.z.tar.gz.
Wenn die Python-Version, auf der Sie uncompyle6 ausführen, zwischen Python 3.6 und 3.11 liegt, verwenden Sie ein Tarball namens uncompyle6_36-x.y.z.tar.gz.
Wenn die Python-Version, auf der Sie uncompyle6 ausführen, 3.11 oder höher ist, verwenden Sie ein Tarball namens uncompyle6-x.y.z.tar.gz.
Sie können auch Eggs oder Wheels mit derselben Versionsbezeichnung ausprobieren, z.B. uncompyle6-x.y.z-py39-none-any.whl für eine Python 3.9-Installation. *Beachten Sie jedoch, dass die Version ohne die Bezeichnung Python 3.11 oder höher bedeutet.
Ebenso funktioniert ein Tarball ohne _xx nur ab Python 3.11 oder höher.
Begründung für die Verwendung von Git-Zweigen +++++++++++++++++++++++++++++++++++++++++++++
Es ist derzeit unmöglich (wenn nicht unpraktisch), einen Python-Quellcode dieser Komplexität und mit so vielen Funktionen zu haben, der sowohl Python 2.7 als auch Python 3.13+ ausführen kann. Die Sprachen haben sich so weit auseinanderentwickelt, und das Paketieren ist grundverschieden. Tatsächlich ist die Verpackungspraxis für Python 3.11+ inkompatibel mit Python 2.7 (und davor zurück bis Python 2.4), das "easy_install" bevorzugte.
Installation aus dem Quelltext ++++++++++++++++++++++++++++++
Um aus dem Quellcode zu installieren, stellen Sie sicher, dass Sie den richtigen Git-Zweig haben. Siehe den Abschnitt Anforderungen für die Git-Zweignamen.
Nach dem Setzen des richtigen Zweigs::
$ pip install -e . # set up to run from source tree
Ein GNU Makefile wird ebenfalls bereitgestellt, so dass :code:make install (möglicherweise als root oder sudo) die obigen Schritte ausführt.
::
make check
Ein GNU-Makefile wurde hinzugefügt, um das Einrichten und Ausführen des richtigen Befehls und das Ausführen von Tests vom schnellsten zum langsamsten zu erleichtern.
Wenn Sie remake_ installiert haben, können Sie die Liste aller Aufgaben einschließlich Tests über :code:remake --tasks sehen.
Führen Sie aus:
::
$ uncompyle6 compiled-python-file-pyc-or-pyo
Für Hilfe zur Verwendung:
::
$ uncompyle6 -h
In älteren Python-Versionen war es möglich, Bytecode zu überprüfen, indem man ihn dekompilierte und dann mit dem Python-Interpreter für diese Bytecode-Version kompilierte. Nachdem dies geschehen war, konnte der erzeugte Bytecode mit dem ursprünglichen Bytecode verglichen werden. Da jedoch die Codegenerierung von Python besser wurde, war dies nicht mehr praktikabel.
Wenn Sie eine Python-Syntax-Überprüfung der Korrektheit des Dekompilierungsprozesses wünschen, fügen Sie die Option :code:--syntax-verify hinzu. Da sich die Python-Syntax jedoch ändert, sollten Sie diese Option verwenden, wenn der Bytecode der richtige Bytecode für den Python-Interpreter ist, der die Syntax überprüfen wird.
Sie können die Ergebnisse auch mit einer anderen Version von uncompyle6 vergleichen, da es manchmal Regressionen beim Dekompilieren bestimmter Bytecode-Arten gibt, während die Gesamtqualität steigt.
Für Python 3.7 und 3.8 ist der Code in decompyle3_ im Allgemeinen besser.
Oder probieren Sie einen anderen spezifischen Python-Decompiler wie uncompyle2_, unpyc37_ oder pycdc_. Da die beiden letzteren anders arbeiten, treten Fehler hier oft nicht dort auf und umgekehrt.
Es gibt eine interessante Klasse dieser Programme, die leicht verfügbar ist, um eine stärkere Verifizierung zu ermöglichen: diejenigen Programme, die sich selbst testen, wenn sie ausgeführt werden. Unsere Testsuite enthält solche.
Und Python wird mit einem weiteren Satz solcher Programme ausgeliefert: seiner Testsuite für die Standardbibliothek. Wir haben etwas Code in :code:test/stdlib, um diese Art der Überprüfung ebenfalls zu erleichtern.
Das größte bekannte und möglicherweise behebbare (aber schwierige) Problem betrifft die Handhabung des Kontrollflusses. (Python hat wahrscheinlich die vielfältigste und verrückteste Sammlung zusammengesetzter Anweisungen, die ich je gesehen habe; es gibt "else"-Klauseln bei Schleifen und try-Blöcken, von denen ich vermute, dass viele Programmierer nichts davon wissen.)
Alle Python-Decompiler, die ich mir angesehen habe, haben Probleme beim Dekompilieren des Python-Kontrollflusses. In einigen Fällen können wir eine fehlerhafte Dekompilierung erkennen und melden.
Die Python-Unterstützung ist ziemlich gut für Python 2
Am unteren Ende der Python-Versionen scheint die Dekompilierung ziemlich gut zu sein, obwohl wir keine automatisierten Tests für die verteilten Tests von Python haben. Außerdem haben wir keinen Python-Interpreter für die Versionen 1.6 und 2.0.
In der Python-3-Serie ist die Python-Unterstützung am stärksten um 3.4 oder 3.3 herum und nimmt ab, je weiter Sie sich von diesen Versionen entfernen. Python 3.0 ist seltsam, da es in mancher Hinsicht eher 2.6 ähnelt als 3.1 oder 2.7. Python 3.6 ändert die Dinge drastisch, indem es Wortcodes anstelle von Bytecodes verwendet. Dadurch wurde das Sprungversatzfeld in einem Sprungbefehl-Argument reduziert. Dies macht die :code:EXTENDED_ARG-Anweisungen jetzt häufiger in Sprungbefehlen; zuvor waren sie selten. Vielleicht wurde als Ausgleich für die zusätzlichen :code:EXTENDED_ARG-Anweisungen eine zusätzliche Sprungoptimierung hinzugefügt. Zusammenfassend ist die Handhabung des Kontrollflusses durch Ad-hoc-Methoden, wie sie derzeit praktiziert wird, schlechter.
Zwischen Python 3.5, 3.6 und 3.7 gab es große Änderungen an den :code:MAKE_FUNCTION- und :code:CALL_FUNCTION-Anweisungen.
Python 3.8 entfernt die Anweisungen :code:SETUP_LOOP, :code:SETUP_EXCEPT, :code:BREAK_LOOP und :code:CONTINUE_LOOP, was die Erkennung von Kontrollflüssen erschweren könnte, da die geplante anspruchsvollere Kontrollflussanalyse fehlt. Wir werden sehen.
Derzeit werden nicht alle Python-Magic-Numbers unterstützt. Insbesondere in einigen Python-Versionen, vor allem Python 3.6, hat sich die Magic Number innerhalb einer Version mehrmals geändert.
Wir unterstützen nur veröffentlichte Versionen, keine Kandidatenversionen. Beachten Sie jedoch, dass die Magic einer veröffentlichten Version normalerweise dieselbe ist wie die letzte Kandidatenversion vor der Veröffentlichung.
Es gibt auch angepasste Python-Interpreter, vor allem Dropbox, die ihre eigene Magic verwenden und Bytecode verschlüsseln. Mit Ausnahme von Dropbox' altem Python-2.5-Interpreter wird diese Art von Dingen nicht behandelt.
Wir behandeln auch keinen PJOrion_- oder anderweitig verschleierten Code. Für PJOrion versuchen Sie: PJOrion Deobfuscator_, um den Bytecode zu entschlüsseln und gültigen Bytecode zu erhalten, bevor Sie dieses Tool verwenden; pydecipher_ könnte dabei helfen.
Dieses Programm kann keine Microsoft Windows EXE-Dateien dekompilieren, die von Py2EXE_ erstellt wurden, obwohl wir den Code wahrscheinlich dekompilieren können, nachdem Sie den Bytecode ordnungsgemäß extrahiert haben. Pydeinstaller <https://github.com/charles-dyfis-net/pydeinstaller>_ könnte beim Entpacken von Pyinstaller-Bundlern helfen.
Die Verarbeitung pathologisch langer Listen von Ausdrücken oder Anweisungen ist langsam. Wir behandeln kein Cython_ oder MicroPython, die keinen Bytecode verwenden.
Es gibt zahlreiche Fehler bei der Dekompilierung. Und das gilt für alle anderen CPython-Decompiler, die ich gesehen habe, selbst für diejenigen, die behaupteten, auf einer bestimmten Version wie 2.4 "perfekt" zu sein.
Mit dem Fortschreiten von Python wird auch die Dekompilierung schwieriger, da die Kompilierung ausgefeilter wird und die Sprache selbst ausgefeilter wird. Ich vermute, dass es weniger Ad-hoc-Versuche wie unpyc37_ (das auf einem 3.3-Decompiler basiert) geben wird, einfach weil es schwieriger ist. Die gute Nachricht, zumindest aus meiner Sicht, ist, dass ich glaube zu verstehen, was nötig ist, um die Probleme robuster anzugehen. Aber im Moment, bis das Projekt besser finanziert ist, habe ich nicht vor, ernsthafte Anstrengungen zu unternehmen, um Python-Versionen 3.8 oder 3.9 zu unterstützen, einschließlich möglicher Fehler. Ich stelle mir vor, dass ich irgendwann daran interessiert sein könnte.
Sie können leicht Fehler finden, indem Sie die Tests gegen die Standard-Testsuite laufen lassen, die Python zur Selbstprüfung verwendet. Zu jeder Zeit gibt es Dutzende bekannter Probleme, die ziemlich gut isoliert sind und gelöst werden könnten, wenn jemand die Zeit dafür aufwenden würde. Das Problem ist, dass es nicht so viele Leute gibt, die an der Fehlerbehebung gearbeitet haben.
Einige der Fehler in 3.7 und 3.8 sind einfach eine Frage des Backportings der Korrekturen in decompyle3. Irgendwelche Freiwilligen?
Sie könnten auf einen Fehler stoßen, den Sie melden möchten. Bitte tun Sie dies, nachdem Sie So melden Sie einen Fehler <https://github.com/rocky/python-uncompyle6/blob/master/HOW-TO-REPORT-A-BUG.md>_ gelesen haben, und folgen Sie den Anweisungen beim Öffnen eines Issues <https://github.com/rocky/python-uncompyle6/issues/new?assignees=&labels=&template=bug-report.md>_.
Seien Sie sich bewusst, dass es möglicherweise eine Weile dauert, bis es meine Aufmerksamkeit erregt. Wenn Sie das Projekt auf irgendeine Weise sponsorn oder unterstützen, werde ich Ihre Issues priorisieren, bevor ich andere Dinge erledige. In seltenen Fällen kann ich gegen eine Gebühr eine manuelle Dekompilierung von Bytecode durchführen. Dies ist jedoch teuer und liegt normalerweise über dem, was die meisten Menschen ausgeben möchten.
uncompyle6 decompyle3 -- BlackHat 2024 Asia (video https://www.youtube.com/watch?v=NA77SFncppE`_). Ein großes Dankeschön an die Organisatoren und Gutachter, dass ich sprechen durfte. Solche Dinge ermutigen mich, an Projekten wie diesem zu arbeiten.uncompyle6 falsch sind, die von :code:uncompyle2 jedoch nicht, aber häufiger ist uncompyle6 korrekt, wenn uncompyle2 es nicht ist. Da :code:uncompyle6 Genauigkeit über idiomatisches Python stellt, kann :code:uncompyle2 bei Korrektheit natürlicher aussehenden Code erzeugen. Derzeit wird :code: leicht gepflegt. Siehe seinen Issue _ für weitere Details... _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>So melden Sie einen Fehler <https://github.com/rocky/python-uncompyle6/blob/master/HOW-TO-REPORT-A-BUG.md>_Issue-Tracker <https://github.com/zrax/pycdc/issues>_ für Details. Derzeit leicht gepflegt.