Pythonで開発されたシステムのソースコードが失われた、担当者が退職して設計意図が分からなくなった、あるいはPyInstallerでパッケージングされた実行バイナリの内部ロジックを解析したい——そのような課題を抱えている方は少なくありません。Pythonはスクリプト言語であるため「ソースコードがあれば読める」と思われがちですが、実際の現場では.pyc(バイトコード)配布やCython・PyInstallerによるコンパイル済みバイナリとして提供されるケースが多く、解析は決して単純ではありません。
本記事では、Pythonのリバースエンジニアリングの全工程を、Python固有の特性・ツール・注意点も含めて体系的に解説します。解析の進め方から法的リスクの回避策、外注と内製の判断軸まで網羅していますので、初めてPythonシステムのリバースエンジニアリングに取り組む方から、プロジェクトを担当するIT部門のご担当者まで、広くお役立ていただける内容です。
▼全体ガイドの記事
・Pythonのリバースエンジニアリングの完全ガイド
PythonのリバースエンジニアリングとはL

リバースエンジニアリングとは、既存のシステムやソフトウェアを解析し、その内部構造・設計意図・業務ロジックを復元する技術的プロセスです。Pythonの場合は他の言語と異なる固有の特性があり、解析のアプローチも変わります。まずPythonリバースエンジニアリングの基本的な概念と適用場面を理解しておくことが重要です。
他言語との違い・Python固有の特性
Pythonはスクリプト言語であるため、通常は.pyファイルとしてソースコードが読める状態で配布されます。この点はCOBOLやC言語のバイナリ解析とは根本的に異なります。しかし現実には、PyInstallerやPy2exe、cx_Freezeなどのツールで単一の実行ファイル(EXE)にパッケージングされた状態で配布されることが多く、その場合はバイナリ解析が必要になります。また、Cythonでコンパイルされた.soや.pydファイルは実質的にC拡張モジュールとなり、逆コンパイルは困難を極めます。
さらに、.pyではなく.pyc(バイトコード)のみ配布するケースでは、uncompyle6やdecompile3などのPythonバイトコード逆コンパイルツールが有効です。C#やJavaの中間言語(IL/バイトコード)と同様に比較的高精度で元のソースに近い形まで復元できますが、変数名の一部消失やコメントの欠落は避けられません。Pythonリバースエンジニアリングの難易度は「純粋な.pyか」「.pycか」「PyInstallerバイナリか」「Cython拡張か」によって大きく異なることを最初に把握しておく必要があります。
主な用途(レガシー移行・脆弱性診断・仕様書復元)
Pythonのリバースエンジニアリングが必要になる場面は大きく4つに分類されます。第一に、データサイエンティストや自動化スクリプト担当者が退職し、引き継ぎドキュメントが皆無のまま業務Pythonスクリプトが残されてしまった「担当者退職後の引き継ぎ問題」です。第二に、PyInstallerでパッケージングされた社内ツールの内部ロジックを復元する「業務システム解析」です。
第三に、セキュリティ診断の一環として、Python製Webアプリや自動化スクリプトの脆弱性を発見する「セキュリティ監査」です。第四に、AIモデル・機械学習パイプラインのコードを解析し、モデルの学習ロジックを再現・移植する「ML/AIシステムの移行」という新興用途も増えています。特にMLOpsの現場では、Jupyter Notebookや実験スクリプトから本番パイプラインを再構築する必要が生じるケースが多く、Pythonリバースエンジニアリングの重要性が高まっています。
Pythonのリバースエンジニアリングの6工程

Pythonのリバースエンジニアリングは、場当たり的に進めると途中で方向を見失ったり、成果物の品質が担保できなかったりします。以下の6工程に沿って体系的に進めることで、解析の抜け漏れを防ぎ、後工程の開発に活用できる成果物を得ることができます。
工程1:対象選定・目的明確化
最初の工程では、解析対象のPythonシステムを正確に特定し、リバースエンジニアリングの目的を書面で明確化します。「業務仕様書の復元なのか」「セキュリティ脆弱性の発見なのか」「モダナイゼーション(新システムへの移行)のための設計情報取得なのか」によって、後続工程での解析深度・成果物の形式が大きく変わります。
また、対象システムのPythonバージョン・依存ライブラリ・実行形態(純粋.py / .pyc / PyInstallerバイナリ / Cython拡張)を事前に棚卸しておくことが重要です。PyInstaller製EXEファイルが対象の場合、まずpyinstxtractorでアーカイブをアンパックし、.pycファイルを取り出す前処理が必要になります。この段階で対象の全ファイル一覧と依存関係を整理しておくと、工程2以降がスムーズに進みます。
工程2:解析環境・ツール準備(Python特化ツール)
Python解析に必要なツールは対象形態によって異なります。.pycファイルの逆コンパイルにはuncompyle6(Python 2〜3.8対応)またはdecompile3(Python 3.9以降対応)が定番です。PyInstallerバイナリの場合はpyinstxtractorでEXEを解凍してから.pycを取り出し、その後逆コンパイルします。バイナリ全体の構造解析にはBinwalk、制御フロー解析や難読化済みコードの解読にはGhidraまたはIDA Proが使われます。
動的解析には、Pythonデバッガ(pdb / pdbpp)やWireshark(ネットワーク通信解析)を活用します。Belkin N300ルーターのファームウェアダンプ事例では、UARTインターフェース経由でのアクセスに際し、Pythonスクリプトで4,096バイト×512回に分割して読み出すことで、一度に大量転送するとクラッシュする制限を回避し、約2時間で200万バイト全量のプレーンテキスト化に成功しています。このようにPythonは解析ツールとしても、解析対象としても柔軟に活用されます。解析環境はインターネット非接続の隔離環境を用意し、解析専用マシンで実施することが法的観点からも推奨されます。
工程3:静的解析(逆コンパイル・逆アセンブル)
静的解析では、システムを実行せずにコードの構造を分析します。.pyファイルが入手できている場合は、まずAST(抽象構文木)解析でクラス・関数・モジュールの依存関係を把握します。.pycファイルの場合はdisモジュールでバイトコードを逆アセンブルし、uncompyle6等で可読なPythonコードに逆コンパイルします。
PyInstallerバイナリの場合、pyinstxtractorでアーカイブをアンパックすると、メインの.pycとともにすべての依存.pycが展開されます。展開後は各.pycを個別に逆コンパイルし、import関係を整理してモジュール構造を再構築します。Cython拡張(.so / .pyd)については、Ghidraの逆コンパイラでC言語レベルのコードを確認し、Python APIとの境界部分(PyArg_ParseTuple等の呼び出し)を手がかりに引数・戻り値の型と処理内容を推定します。静的解析の段階では、変数名の一部がgen_varなどの汎用名に置換されている場合がありますが、コンテキストとコメントから命名を補完することが重要です。
工程4:動的解析(デバッガ・実行トレース)
動的解析では、実際にシステムを実行しながらその振る舞いを観察します。Pythonのpdbデバッガやptraceを使ったシステムコール追跡(straceコマンド)、Wiresharkによるネットワーク通信解析が主なアプローチです。難読化されたコードでも、実行時にメモリ上で展開された実際の値を観察することで、静的解析では分からなかった処理内容を把握できます。
特に機械学習パイプラインの解析では、モデルのforwardパス(推論処理)をフックして中間層の出力を記録する手法が有効です。PyTorchの場合はforward hookを、TensorFlowの場合はtf.functionをwrapしてトレースすることで、学習ロジックの詳細を動的に把握できます。動的解析の結果は静的解析の結果と突き合わせることで、解析精度が大幅に向上します。実行ログ・通信ログ・メモリダンプは後の成果物化工程でも重要な根拠資料となるため、必ず記録しておきます。
工程5:抽象化(Design Recovery:実装→設計→仕様)
静的・動的解析で得られた情報を元に、実装レベルのコードから設計レベルの情報(クラス図・シーケンス図・データフロー図)、さらに仕様レベルの情報(業務フロー・ユースケース)へと段階的に抽象化します。このDesign Recoveryのプロセスこそが、リバースエンジニアリングで最も価値を生む工程です。
Pythonシステムの場合、コードからは「How(どう動くか)」は分かりますが、「Why(なぜその実装になっているか)」は当時の担当者にしか分かりません。データサイエンティストや自動化スクリプト担当者が退職した後の引き継ぎ問題では、この「Why」の欠落が最大のリスクです。抽象化工程では、業務部門の担当者にインタビューを行い、「コードからは読み取れないが業務上重要な制約や例外処理の背景」を補完することが必須です。この工程を省略すると、復元した仕様書が不完全なまま新システム開発に進んでしまい、本番稼働後に業務ルール違反のバグが多発するリスクがあります。
工程6:成果物化(仕様書・新システム設計)
最終工程では、解析・抽象化の結果を具体的な成果物にまとめます。成果物の粒度は目的によって異なり、「フローチャート」「業務仕様書(画面遷移図・DB設計含む)」「詳細設計書(新システム開発に直接使えるレベル)」の3段階が一般的です。粒度が上がるにつれてコストと期間は増加しますが、後工程での開発効率も大幅に向上します。
Pythonシステムの場合、自動生成ツール(Sphinx・pdocなど)でAPIドキュメントを自動生成し、そこに手作業で業務ロジックの背景(Why)を付記する方法が効率的です。機械学習パイプラインの場合は、モデルアーキテクチャ図・特徴量エンジニアリング手順・評価指標の定義も成果物に含める必要があります。成果物のレビューは、元の担当者や業務部門の有識者が行い、解析内容の正確性を確認してから最終版を確定します。
Python固有の注意点と典型的な失敗パターン

Pythonリバースエンジニアリングには他言語にはない固有の落とし穴が存在します。「Pythonはスクリプト言語だから簡単に解析できる」という先入観が最大の危険です。実際の現場で頻繁に見られる失敗パターンと、難読化・暗号化への対処方法を解説します。
典型的な失敗パターン
最も多い失敗は、「コードは読めたが業務ルールの意図が分からず、移行後にバグが多発した」というケースです。Pythonの自動化スクリプトや機械学習パイプラインには、当時の担当者が暗黙知として持っていた特殊なデータ処理ルール(特定顧客への例外処理、季節性を考慮した分岐条件など)が埋め込まれていることが多く、コードを読むだけではそのビジネス背景が分かりません。
次に多い失敗は、「Pythonバージョンの違いによる逆コンパイル失敗」です。uncompyle6はPython 3.9以降の.pycに対応していないため、新しいバージョンのバイトコードは別ツール(decompile3)を使う必要があります。Pythonのマイナーバージョンが変わるだけでバイトコードのオペコードが変化することがあり、逆コンパイラのバージョン選定を誤ると不完全なコードしか得られません。また、PyInstaller 4.x以降ではアーカイブ形式が変わっているため、古いpyinstxtractorでは正しくアンパックできない場合があります。
難読化・暗号化への対処
Pythonコードの難読化手法には、変数名・関数名の意味のない文字列への置換(例:l1I0O形式)、文字列の動的生成(eval/exec経由での実行)、PyArmorによるバイトコード暗号化などがあります。PyArmorは.pyファイルをランタイム暗号化し、専用のbootstrap loader経由でのみ実行可能にするため、静的解析だけでは解読が非常に困難です。
