
クロスバージョンのPythonバイトコード逆コンパイラ
|buildstatus| |Pypi Installs| |Latest Version| |Supported Python Versions|
|packagestatus|
.. contents::
ネイティブなPythonクロスバージョン逆コンパイラおよびフラグメント逆コンパイラです。 decompyle、uncompyle、uncompyle2の後継です。
BlackHat Asia 2024 <https://youtu.be/H-7ZNrpsV50?si=nOaixgYHr7RbILVS>_ でこの件について講演しました。
uncompyle6 はPythonバイトコードを同等のPythonソースコードに変換します。 Pythonバージョン1.0から3.8までのバイトコードを受け付け、24年以上のPythonリリースをカバーしています。DropboxのPython 2.5バイトコードといくつかのPyPyバイトコードも含まれています。
そう、言ってしまいましょう:このソフトウェアは素晴らしいです。普通のハッキーな逆コンパイラではありません。compiler_ テクノロジーを使い、プログラムは命令からプログラムの構文木を作成します。上位レベルではPythonのASTのようなノードが現れます。そのため、Pythonバイトコードのセクションで何が起こっているのかを実際に分類し理解することができます。
これに加えて、このソフトウェアが他のCPythonバイトコード逆コンパイラと異なる点は、ソースコードの フラグメント だけを逆パースし、特定のバイトコードオフセット周辺のソースコード情報を提供できることです。
私はツリーフラグメントを使って、trepan_ デバッガ_ の中で 実行時に コードのフラグメントを逆パースしています。そのために、バイトコードオフセットが記録され、ソースコードのフラグメントに関連付けられます。この目的は本来の意図と互換性がありますが、少し異なります。詳細は this_ を参照してください。
命令オフセットを与えられたPythonフラグメントの逆パースは、スタックトレースの表示に役立ち、実行時に行番号だけよりも詳細な位置を表示したい任意のプログラムに組み込むことができます。このコードは、ソースコード情報が存在せず、バイトコードのみがある場合にも使用できます。繰り返しますが、私のデバッガはこれを利用しています。
decompyle、uncompyle、uncompyle2、uncompyle3には多くのフォークが存在しました(そして今も存在します)。それらの多くは基本的に同じコードベースから派生しており、(ほぼ?)すべてがもはや積極的にメンテナンスされていません。あるものはPython 1.5-2.3の逆コンパイルに非常に優れ、別のものはPython 2.7に非常に優れていますが、それだけです。別のものはPython 3.2のみを扱い、さらに別のものはそれをパッチして3.3のみを扱います。おわかりいただけるでしょう。このコードはこれらのフォークすべてを統合し、 前進 しています。このコードベースは古いフォークよりも大幅にリファクタリングされ、クリーンアップされています。さらに実験的なリファクタリングが decompyle3_ で進行中です。
これは、すべてのPythonバージョンにわたって逆コンパイルの最良の結果を実証的に示しています。また、Pythonバージョンのサブセットのみを提供する別のプロジェクトが存在する場合でも、それらのバージョンに対しても一般的に実証的に優れた結果を示します。
どうやって判断するのか?そのPythonバージョンに付属するPythonバイトコードを取得し、それを逆コンパイルすることで判断します。正常に逆コンパイルできたものの中で、そのバイトコードバージョンのPythonインタプリタを実行して、結果のプログラムが構文的に正しいことを確認します。最後に、プログラムが自分自身のテストを持っている場合、逆コンパイルされたコードでチェックを実行できます。
私たちは自動化プロセスを使用してバグを見つけています。他の逆コンパイラのイシュートラッカーには、私たちが発見したいくつかのバグがあります。それらのほとんどは他の逆コンパイラで修正されていません。
gitリポジトリ内のコードは、Python 2.4から最新のPythonバージョンまで実行可能ですが、Python 3.0から3.2は除きます。これらの不足に対処するためのボランティアを歓迎します。
ただし、その方法は、連続するPythonバージョンをgitブランチに分離することです:
master Python 3.11以降(poetry installと新しいPythonイディオムを使用) python-3.6-to-3.10 Python 3.6から3.10(新しいf-strings、より現代的なイディオム、より現代的な型注釈を使用) python-3.3-to-3.5 Python 3.3から3.5(汎用Python 3) python-3.0-to-3.2 Python 3.0から3.2(初期のPython 3; 3.0はある領域ではPython 2.6に近く、Python 2.7には近くなかった) python-2.4-to-2.7 Python 2.4から2.7(汎用Python 2)
PyPyバージョン2.4以降も動作します。
読み取り可能なバイトコードファイルは、Pythonバージョン1.4、2.1-2.7、3.0-3.8、およびそれ以降のPyPyバージョンのバイトコードでテストされています。
最近のPythonリリース(Python 3.11+) の場合、PyPIから uncompyle6 の名前でインストールできます::
pip install uncompyle6
3.11より前のPythonリリース の場合、PyPIからインストールせず、GitHub Releasesセクションのファイルを使用してインストールしてください。古いPythonでは easy_install <https://python101.pythonlibrary.org/chapter29_pip.html#using-easy-install>_ を使用していました。しかし、これはPyPiや新しいPythonバージョンではサポートされなくなりました。逆に、poetry や pip(新しい方法)は古いPythonではサポートされていません。
uncompyle6を実行しているPythonバージョンがPython 2.4から2.7の場合、uncompyle6_24-x.y.z.tar.gz というtarballを使用してください。
uncompyle6を実行しているPythonバージョンがPython 3.0から3.2の場合、uncompyle6_30-x.y.z.tar.gz というtarballを使用してください。
uncompyle6を実行しているPythonバージョンがPython 3.3から3.5の場合、uncompyle6_33-x.y.z.tar.gz というtarballを使用してください。
uncompyle6を実行しているPythonバージョンがPython 3.6から3.11の場合、uncompyle6_36-x.y.z.tar.gz というtarballを使用してください。
uncompyle6を実行しているPythonバージョンが3.11以降の場合、uncompyle6-x.y.z.tar.gz というtarballを使用してください。
同じバージョン指定を持つeggやwheelも試せます。例えば、Python 3.9インストール用の uncompyle6-x.y.z-py39-none-any.whl などです。ただし、指定がないバージョンはPython 3.11以上を意味します。
同様に、_xx のないtarballはPython 3.11以降でのみ動作します。
Gitブランチ使用の根拠 +++++++++++++++++++++++++++++++
現在、この複雑さと多くの機能を持つPythonソースコードが、Python 2.7とPython 3.13+の両方で実行可能であることは不可能(実用的でないにせよ)です。言語は大きく乖離しており、パッケージングも大きく異なります。実際、Python 3.11+のパッケージング手法は、Python 2.7(およびそれ以前のPython 2.4まで)と互換性がなく、これらは「easy_install」を好んでいました。
ソーステキストからのインストール +++++++++++++++++++++++++++++++
ソースコードからインストールするには、適切なGitブランチを確認してください。Gitブランチ名については要件のセクションを参照してください。
適切なブランチを設定した後::
$ pip install -e . # ソースツリーから実行するよう設定
GNU Makefileも提供されているので、:code:make install(必要に応じてrootやsudo)で上記の手順を実行できます。
::
make check
正しいコマンドの設定と実行、およびテストを最速から最遅まで実行するためのGNU makefileが追加されました。
remake_ がインストールされている場合、:code:remake --tasks でテストを含むすべてのタスクのリストを表示できます。
実行:
::
$ uncompyle6 コンパイル済みPythonファイル-pyc-or-pyo
使い方のヘルプ:
::
$ uncompyle6 -h
古いバージョンのPythonでは、バイトコードを逆コンパイルし、そのバイトコードバージョンのPythonインタプリタを使用してコンパイルすることで、バイトコードを検証することが可能でした。これにより、生成されたバイトコードを元のバイトコードと比較できました。しかし、Pythonのコード生成が改善されるにつれて、これはもはや実行不可能になりました。
逆コンパイルプロセスの正確性のPython構文検証が必要な場合は、:code:--syntax-verify オプションを追加してください。ただし、Python構文は変更されます。バイトコードが構文チェックを行うPythonインタプリタに適したバイトコードである場合に、このオプションを使用する必要があります。
また、uncompyle6 の別のバージョンと結果を相互比較することもできます。全体的な品質が向上するにつれて、特定のバイトコードの逆コンパイルに回帰が生じることがあるためです。
Python 3.7および3.8の場合、decompyle3_ のコードが一般的に優れています。
または、uncompyle2_、unpyc37_、pycdc_ のような別の特定のPython逆コンパイラを試してください。後者の2つは異なる方法で動作するため、こちらのバグはそちらにはなく、その逆も同様です。
より強力な検証を容易に提供できるプログラムの興味深いクラスがあります:実行時に自分自身をテストするプログラムです。私たちのテストスイートにはこれらのプログラムが含まれています。
また、Pythonには標準ライブラリのテストスイートなど、このようなプログラムの別のセットが付属しています。この種のチェックを容易にするために、:code:test/stdlib にいくつかのコードがあります。
最大の既知の問題(修正可能かもしれないが困難)は、制御フローの処理に関連しています。(Pythonはおそらく私が見た中で最も多様で厄介な複合文セットを持っています;多くのプログラマーが知らないであろうループやtryブロックの「else」句があります。)
私が見たすべてのPython逆コンパイラは、Pythonの制御フローの逆コンパイルに問題を抱えています。場合によっては、誤った逆コンパイルを検出して報告することができます。
Python 2のサポートはかなり良好です。
Pythonバージョンの低い方では、逆コンパイルはかなり良いように見えますが、Pythonの配布テストに対する自動テストは実施していません。また、バージョン1.6および2.0用のPythonインタプリタもありません。
Python 3シリーズでは、Pythonサポートは3.4または3.3あたりで最も強力で、それらのバージョンから離れるにつれて低下します。Python 3.0は、ある意味で3.1や2.7よりも2.6に似ているという点で奇妙です。Python 3.6は、バイトコードではなくワードコードを使用することで状況を劇的に変更しました。その結果、ジャンプ命令の引数内のジャンプオフセットフィールドが縮小されました。これにより、:code:EXTENDED_ARG 命令がジャンプ命令でより一般的になり、以前は稀でした。追加の:code:EXTENDED_ARG 命令を補うために、追加のジャンプ最適化が追加された可能性があります。要するに、現在行われているようなアドホックな方法での制御フローの処理は、さらに悪化しています。
Python 3.5、3.6、3.7の間で、:code:MAKE_FUNCTION および :code:CALL_FUNCTION 命令に大きな変更がありました。
Python 3.8では :code:SETUP_LOOP、:code:SETUP_EXCEPT、:code:BREAK_LOOP、:code:CONTINUE_LOOP が削除され、より洗練された制御フロー分析(計画中)がない場合、制御フローの検出がさらに困難になる可能性があります。様子を見ましょう。
現在、すべてのPythonマジックナンバーがサポートされているわけではありません。特に、Python 3.6の一部のバージョンでは、マジックナンバーがバージョン内で数回変更されています。
リリース版のみをサポートし、候補版はサポートしません。 ただし、リリース版のマジックは通常、リリース前の 最後の 候補版と同じであることに注意してください。
また、カスタマイズされたPythonインタプリタ、特にDropboxのものがあり、独自のマジックを使用し、バイトコードを暗号化します。Dropboxの古いPython 2.5インタプリタを除き、この種のものは処理されません。
また、PJOrion_ や難読化されたコードも処理しません。PJOrionの場合は、PJOrion Deobfuscator_ を使用してバイトコードを解読し、有効なバイトコードを取得してからこのツールを試してください;pydecipher_ が役立つかもしれません。
このプログラムはPy2EXE_ によって作成されたMicrosoft Windows EXEファイルを逆コンパイルできませんが、おそらくバイトコードを適切に抽出した後でコードを逆コンパイルできます。Pydeinstaller <https://github.com/charles-dyfis-net/pydeinstaller>_ はPyinstallerバンドラーのアンパックに役立つかもしれません。
病的に長い式や文のリストの処理は遅いです。バイトコードを使用しないCython_ やMicroPython_ は処理しません。
逆コンパイルには多数のバグがあります。そして、それは私が出会った他のすべてのCPython逆コンパイラにも当てはまります。特定のバージョン(2.4など)で「完璧」と主張するものでさえもです。
Pythonが進化するにつれて、コンパイルがより洗練され、言語自体がより高度になるため、逆コンパイルも難しくなっています。unpyc37_ (3.3逆コンパイラに基づく)のようなアドホックな試みは、単に難しいために減少すると思います。良いニュースは、少なくとも私の立場からは、問題をより堅牢に解決するために必要なことを理解していることです。しかし今のところ、プロジェクトへの資金提供が改善されるまでは、Pythonバージョン3.8や3.9、およびそれらに含まれる可能性のあるバグをサポートするための真剣な努力をするつもりはありません。いつか興味を持つかもしれませんが。
Pythonが自身をチェックするために使用する標準テストスイートに対してテストを実行することで、簡単にバグを見つけることができます。どの時点でも、数十の既知の問題があり、それらはかなり孤立しており、時間をかければ解決できる可能性があります。問題は、バグ修正に取り組んでいる人がそれほど多くないことです。
3.7および3.8のバグの一部は、単にdecompyle3 の修正をバックポートするだけの問題です。ボランティアはいませんか?
バグに遭遇した場合は、報告してください。その際は、バグの報告方法 <https://github.com/rocky/python-uncompyle6/blob/master/HOW-TO-REPORT-A-BUG.md>_ を読んだ上で、イシューを開く際の指示 <https://github.com/rocky/python-uncompyle6/issues/new?assignees=&labels=&template=bug-report.md>_ に従ってください。
私の注意を引くまでに時間がかかる可能性があることを認識してください。プロジェクトをスポンサーまたはサポートする場合、私は他の行っていることのキューよりも優先してあなたのイシューを処理します。まれな状況では、有料でバイトコードの手動逆コンパイルを行うこともできます。ただし、これは高額であり、通常はほとんどの人が支払いたいと思う額を超えています。
uncompyle6 decompyle3 -- BlackHat 2024 Asia(ビデオ <https://www.youtube.com/watch?v=NA77SFncppE>_)。講演の機会を与えてくださった主催者とレビュアーの皆様に大きな感謝を。このようなことは、このようなプロジェクトに取り組む励みになります。uncompyle6 の結果が不正確で、:code:uncompyle2 の結果が正確である状況もありますが、:code:uncompyle2 が不正確な場合、:code:uncompyle6 が正確であることがより頻繁にあります。:code:uncompyle6 は正確性を重視するため、:code:uncompyle2 は正しい場合により自然なコードを生成できます。現在、:code: は軽度にメンテナンスされています。詳細はそのイシュー _ を参照してください。.. _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::
uncompyle2トラッカー <https://github.com/wibiti/uncompyle2/issues>バグの報告方法 <https://github.com/rocky/python-uncompyle6/blob/master/HOW-TO-REPORT-A-BUG.md>_イシュートラッカー <https://github.com/zrax/pycdc/issues>_ を参照してください。現在は軽度にメンテナンスされています。