pyinstallerで作成されたexeをpyファイルに戻す方法を調べると複数のパターンがあって、なかなかうまくいく方法に出会うことができませんでしたが、唯一なんとかなった方法を書き残します
ただ完全に復元できるわけではなく、かなり欠損もあり過度な期待はせず参考ソースが抽出できるぐらいでお願いします!!
exeからpycファイルを抽出する
pyinstxtractorを利用するのでGithubからインストール
https://github.com/extremecoders-re/pyinstxtractor
引数にpyファイルに戻したいexeファイルを指定してpyinstxtractor.pyを実行して・・・
なんですが、下部にあるリンク(pyinstxtractor-ng or pyinstxtractor-web)ならもっと簡単に利用できるので一旦省略
https://github.com/extremecoders-re/pyinstxtractor
- pyinstxtractor-ng: A standalone binary version of pyinstxtractor. This tool doesn’t require Python to run and can extract all supported versions of PyInstaller. It also supports encrypted pyinstaller executables.
- pyinstxtractor-web: pyinstxtractor running in the web browser, powered by Go & GopherJS.
pyinstxtractor-ng の場合
リンク先のGithubに目当てのファイルが・・無い!!
もう一つ先のリンクにあるので↓の通りreleasesへ遷移
最新版のところから「pyinstxtractor-ng.exe」をダウンロードしてくる
pyinstxtractor-ng.exeの使い方
使い方はとても簡単。exeファイルを「pyinstxtractor-ng.exe」へドラッグ&ドロップするだけ!
{exeファイル名}_extractedのフォルダが作成されて、その中にフォルダ&ファイルが多数ありますが、拡張子が.pycになっているexeファイルと同じ名前のファイルがあるのでこれを次工程へ
pyinstxtractor-web の場合
リンク名が”pyinstxtractor-web”なのWEBページへのリンクかと思いきや行き着く先はGo言語で構成するpyinstxtractorのGithubページへ ※私Goは全くわかりません・・
なんですが、大事なのは少し下にある今度こそWEBページのリンク
行き着く先はpyinstxtractor-ng.exeと同じことができるWEBページ
やることは簡単
“ファイルを選択”から対象のexeファイルを選択して”Process”をクリックするだけ
中央の枠にログが表示されて最終的に{exeファイル名}_extracted.zipがダウンロードされるので解凍すればあとは同じ
.pycファイルを.pyファルにする
次はpycdcを使うので再びGithubへ
https://github.com/extremecoders-re/decompyle-builds
pycdcように見えてじゃないけどここが正解
pyinstxtractor-ngの時と同じくreleasesのリンクへ
「pycdc.exe」をダウンロードしてくる
pycdcの使い方
pyinstxtractor-ng.exeと違ってドラック&ドロップではできない
コマンドプロンプトとかで対象のpycファイルを引数にしてログ出力として実行する
cd {ファイルがあるフォルダ}
pycdc.exe {pycファイル}>{出力するpyファイル}
※2行目の各ファイルをフォルダ込みのフルパスで書くなら1行目の"cd {ファイルがあるフォルダ}"は不要
この出力したpyファイルがexeから戻した目的ファイル!
もとのpyファイルとどれくらい違うのか
細かいところは他にもあるけど影響大きそうなものはこんな感じ
“#WARNING: Decompyle incomplete”以下の欠損
これが一番ダメージデカい・・
この文字があると同一def内の以降のソースが消し飛ぶ
diffを取るとこんな感じ
コメントがなくなる
なくなるコメントは#で始まる一行コメントのみ
「”’」か「”””」で囲んだ複数行コメントは残ってる
引数が「**kwargs」になりがち
(これは動きそうなので見逃せそう)
# ↓これが
f = csv.DictReader(csv_file, delimiter=',', doublequote=True,
lineterminator='\r\n', quotechar='"', skipinitialspace=True)
# ↓こうなる
f = csv.DictReader(csv_file, ',', True, '\r\n', '"', True, **('delimiter', 'doublequote', 'lineterminator', 'quotechar', 'skipinitialspace'))
引数の解釈がおかしい時がある
そもそも型指定しなければ良いのかな・・?
# ↓これが
def findItemsAdvancedAPI(self, SellerId: str, page_no: int, ItemIdList: list):
# ↓こうなる
def findItemsAdvancedAPI(self = None, SellerId = None, page_no = None, ItemIdList = ('SellerId', str, 'page_no', int, 'ItemIdList', list)):
ネストされたifなどが(間違って?)まとめられる
元のソースが微妙な気もするけど・・
# ↓これが
if res['findItemsAdvancedResponse']['searchResult']['@count'] == '1':
item = items
if item['globalId'] == 'EBAY-US':
if str(item['itemId']) not in ItemIdList:
if self.GetItemDetail(str(item)) == 2:
return 2
else:
for item in items:
if item['globalId'] == 'EBAY-US':
if str(item['itemId']) not in ItemIdList:
if self.GetItemDetail(str(item['itemId'])) == 2:
return 2
return 1 if totalPages > page_no else 0
# ↓こうなる
if res['findItemsAdvancedResponse']['searchResult']['@count'] == '1':
item = items
if item['globalId'] == 'EBAY-US' and str(item['itemId']) not in ItemIdList and self.GetItemDetail(str(item)) == 2:
return 2
for item in items:
if item['globalId'] == 'EBAY-US' and str(item['itemId']) not in ItemIdList and self.GetItemDetail(str(item['itemId'])) == 2:
return 2
if totalPages > page_no:
return 1
return None
あとがき
何もないよりは絶対に良いけどこれで復元と言うには少し微妙・・・
人様が作ったexeをデコンパイルして改修することは難しいってことですね
コメント