x64マシンでpy2exeを使ってx86バイナリを吐き出した話

今回の記事(というかメモ)

pythonのコードをバイナリにできるのは知っていたが、実際に使ったことはなかったのでやってみた。手元のマシンはx64、pythonもx64用しかなかったので、このままではx64バイナリしか吐けない。そこで今回はx64マシンに新たにx86用pythonをインストールし、pythonコードからバイナリを吐き出すユーティリティである「py2exe」(もちろんx86用)を導入してx86バイナリを出力させる。

py2exe導入手順

py2exeはpythonコードからWindows実行形式ファイル(以下、バイナリ)を出力するpythonのユーティリティである。導入にはインストーラを用いる。pipを使わない理由は、pipでインストールできるpy2exeはpython3対応のもののみだから(手元では現在python2.7を使用している)。
インストーラはここから入手する。適当なバージョンを選択し、ダウンロード、インストールを行う。この時、x64のpythonしかない環境でx86のpy2exeをインストールしようとすると、「Python version 2.x required, which was not found in the registry」という感じのエラーが出る。手元にはpython2.7(x64)しかインストールされていないため、レジストリの参照に失敗していると考えられる。これはx86のpythonをダウンロード、インストールすることで解決する。バージョンは2.7で揃えた(特に意味は無いが)。ちゃんと見つかるとこんな感じの画面になる(図1)。あとは通常通りインストールを行えば良い。

py2exeインストール正常進行の途中画面

使ってみる

py2exeでバイナリを吐き出すには次の手順を踏む。

  1. バイナリにしたいpythonのコードを書く
  2. setup.pyを書く
  3. バイナリを出力

(1)はそのまま。(2)のsetup.pyだが、py2exeではバイナリ出力のオプションなどをこのファイル内に記述しなければならない。具体的には、

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# -*- coding: utf-8 -*-
from distutils.core import setup
import py2exe
option = {
"compressed": True,
"optimize": 2,
"bundle_files": 2
}
setup(
options = {
"py2exe": option
},
console = [
{"script": "test.py"}
],
zipfile = None
)

このように。「option」では出力圧縮の有無(compressed[True|False])、最適化の有無(optimize[0|1|2])、ランタイムDLLを含めるか(bundle_files[1|2])を指定できる。「console」では対象のpythonコードを指定する(test.pyの部分は適宜変更)。Zipファイルを生成する場合は「zipfile」にファイル名を指定する。他にも除外するDLLのオプション指定などができるらしいが、今回は試していない。
setup.pyの記述が終わったら(3)実行。

1
python setup.py py2exe

これでdistディレクトリ内にバイナリが出力されているのが確認できるはずである。fileコマンドで確認してみると、

1
2
$ file test.exe
test.exe: PE32 executable (console) Intel 80386, for MS Windows

x86バイナリが出力できている。
なお、zipfileがNoneの状態でbunfle_filesが1(ランタイムDLLを含める)だと、Runtime Errorが出てバイナリが生成できなかった。zipfileに適当なファイル名を指定するか、bundle_filesを2(含めない)として実行すればエラー自体は消えるがスタンドアロンのバイナリは出力できなかった。

出力されたバイナリ

今回バイナリを出力したpythonコードは

1
2
# -*- coding: utf-8 -*-
print "Hello, World!"

これだけ。IDA Pro Free(x86バイナリを出力したかった理由の一つ)で開いてStringsタブを確認してみる。が、Hello, Worldの文字列は見当たらない。VM上のLinuxで

1
strings test.exe | grep Hello

を実行すると「Hello, World(」の文字が出力される。知識不足でなぜIDAのstringsでは確認できないのかわからなかった。

最後に

ランタイムDLLを含めるオプションで実行したにも関わらずスタンドアロンバイナリを出力できなかった。x86バイナリになったことでIDA Pro Freeでファイルを開けたのは良かったものの、出力されたバイナリの構造が知識不足でよくわからず…。バイナリ力がまだまだ足りないので、これからも精進したい。py2exeでのバイナリ出力はこれから活用していくか正直まだわからないが、一つメモとして残しておきたかったので記事に残した。

参考資料