概要
Pythonを勉強している際にバイトコードの生成方法と、バイトコードからバイトコード命令及びソースコードを逆生成する方法を確認したかったのですが、一度にまとまっているページが見つからなかったので備忘録として残しておきます。
今回はCPythonを使用しています。Pythonのバージョンは3.6.8です。
ソースファイルのコンパイル
ファイルのコンパイルは2つの方法があります。
1.ソースファイルもしくはディレクトリを一括でコンパイルする場合
コマンドプロンプトを起動して下記コマンドを実行します。
ファイル単体でのコンパイル及び、ディレクトリ配下一括でのコンパイルが可能です。
compileall --- Python ライブラリをバイトコンパイル
python -m compileall 【ファイル名もしくはディレクトリパス】
2.ソースファイルを単体でコンパイルする場合
Pythonインタプリタを起動して下記コードを実行します。
こちらの場合は下のURLに詳しく記載されていますが、コンパイル時のオプションを指定する事が可能です。
py_compile --- Python ソースファイルのコンパイル
import py_compile
py_compile.compile(【ファイル名】)
逆アセンブラ
コンパイルを実行した場合、通常の方法では確認不可能なバイトコードのファイルとして保存されます。
この時に、インタプリタがどのように実行しているのかを解析したい場合に逆アセンブリを実行してバイトコード命令を確認する事が可能です。
1.関数の逆アセンブラ
import dis
dis.dis(【関数名】)
2.バイトコードの逆アセンブラ
バイトコードを逆アセンブラする場合は、一度Pythonオブジェクトへ整列化をする必要があります。
整列化を実行した後、整列化されたデータをdis.dis()
へ渡します。
バイトコードの生成時と逆アセンブラ実行時のPythonのバージョンが異なる場合、オブジェクトの整列化が失敗する可能性が高いので必ず生成時と同じPythonのバージョンで実行するようにします。
marshal --- 内部使用向けの Python オブジェクト整列化
※追記
ほとんどのサイトとPEP3147ではヘッダは32ビット=4バイト × 2 = 8バイトと記載されているのですが、自分の環境では8バイトに設定するとエラーが発生しました。
stackoverflowだと12バイトに変更されているとの記載があったのですが、変更された記述のある公式のドキュメントが確認できていません。
import dis, marshal
code = open(【ファイル名】, "rb").read()[12:] # ヘッダ以降のデータを読み込む
code = marshal.loads(code)
dis.dis(code)
バイトコードの逆コンパイル
pycからpyファイルに逆コンパイルを実行したい場合はuncompyle6を使用します。Pythonのバージョンが1.3から3.7まで使用可能です。
uncompyle2というライブラリもありますが、こちらはPython2.7までしか使用できません。
逆コンパイルはまだ未実施なので、実行したら追記をします。