Hello World入門したかった
# はじめに
タイトル通り、「Hello World」入門をしたかったお話です。
某UNIX環境でHello Worldを解析していく本のパクリかよと思われたかもしれませんが、今回の場合はWindows環境で行いました。決してパクリではありません。
更に、今回の検証ではただ単にHello Worldを出力するプログラムを解析するだけでなくCでコンパイルした一般的なバイナリと、Rustでコンパイルしたバイナリ
そしてPy2exeでexe化したバイナリを比較していくことにしました。というよりもこの三つのバイナリを解析したいという思いつきからはじめた検証です。
というのも、同じ処理を行うバイナリならば、コンパイルを行うことで同じバイナリを生成するのではないか、もし異なるモノであるならどこに差があるのかを調べてみたいと思ったため、
Revのリハビリも兼ねて調べてみることにしました。
とあるLTで発表したスライドも参考にして頂ければ良いかなと思います。
Hello world(kari)
環境について
今回の検証では、MBP上にVBoxでWindows7環境を構築して行いました。
ツールに関しては静的解析には「Stirling」,PE Header解析にはvxPEViewer, 動的解析にはOllydbgと少しだけIDA freeを使用しました。
検証メモ
以下のメモが検証を行ったまとめになります。
Hello World Memo
C,rust,pythonにてバイナリを生成時に最適化オプション無効
- cl -Od hello.c(C)
- rustc -C opt-level=3 -C debug_assertions=no hello.rs(Rust)
- optimize : 1(python)
Stirling
* Cを基準にそれぞれのバイナリを比較・素材を収集 *
C-Python
- C-pythonでは、ヘッダーとヘッダー・ボディ間のnullバイト列の空白以外はほぼ異なるバイナリ
- バイナリ列中にIsDebuggerPresentやGetLastError等の一般的な関数が列挙されている
-> Cバイナリにも同じような関数名を列挙した箇所がある - pythonバイナリのヘッダー付近に「python27」等の文字列が現れる
-> python側でエラー処理を行っている?(error 等の文字列が多く現れているため) -> 相似していないのではなく、python側の処理分ズレている可能性あり(サイズが大幅に肥大化しているのもその影響かも) - 一部タグらしき形式の文字列あり
- 最終部辺りで「pyo」等のpython関連と思しき文字列あり
- そもそも最適化が適応されていない?
C-Rust
- Python同様ヘッダー以外は異なったバイナリ
-> 若干python以上に異なる部分が多い - stringsを用いると.idataなどの文字列が多く出力される
- GCCなども多くみられる
- 前半部はほぼ解読不可なもので、おそらく後半部に重要な処理がある? -> 後半部はrust文らしきものが多くある
- rust特有の命令(Cではない命令)が含まれている.
- error処理が大半
- Python同様、わかる形で「Hello World!」は格納されていない
Rust-Python
- これまでと同じく特に似通った個所は無し
まとめ
どのバイナリもヘッダー以外は独自の構造をしているため似通った個所は見当たらない。そもそもバイナリのサイズがそれぞれ違うため相似しているとは思えない。...が、強いて最も似通ったバイナリ構造をしているものを挙げるならばC-Python
vxPEView, stud_PE
* Cを基準にそれぞれのバイナリの構造を比較 *
* 同時に関連性のありそうなファイルも解析を行う *
C-Python
- 些細な違いはあるものの、タイムスタンプ等の違いの出ておかしくない部分が大半
- Pythonと読み込んでいるライブラリに違いがあり、MSVCR90.dllを読み込んでいる -> MSVCR90.dllはアプリケーションを動作させるためのライブラリ(コンパイラの違いが原因?)
- .relocセクションがなく.rsrcが存在している
-> .relocは再配置情報、rsrcはリソース情報 -> これかの違いからヘッダー以下の構造が変わっている? - 同じくkernel32.dllを読み込んでいるものの、利用している関数に違いがある
- msvcr90.dllには_snprintfやexit関数が読み込まれている
-> Cではコンパイル時に基本的な関数は組み込まれているから? - 実行時にpython27.dllを必要とするにも関わらず実際には読み込まれていない
- .textセクション中のSizeofRawdataの数値に違いがある
-> その分アセンブリのコード量が多い
-> 変換処理が含まれている?
C-Rust
- 明らかに読み込んでいるdllの量がCに比べて多い -> python27.dllと似ている?
- おそらく呼び出されることはないであろうwinsock2を扱うws2_32.dllの含まれていることから予め大方のライブラリを読み込んでいる?
- セクション数が以上に多い ->
- SizeOfRawdataが以上に大きい
* これより下は関連性のありそうなファイルの解析結果 *
-------------------------------------------------------------------
select.pyd
- 「.pyd」pythonスクリプトをWindows用にコンパイルしたファイルの拡張子
- Ollydbgで読み込みが行えることからおそらくdllと同じ
- vxPEViewを用いると、複数のdllを読み込んでいる模様
-> その一つに「pyhton27.dll」を読み込んでいることから、何らかの関連性がある?
-> 「kernel32.dll」はほとんどのバイナリで読み込まれるため省略
-> 「ws2_32.dll」はwinsock2を扱うためのライブラリ
-> 「select」という名称から、socket関連のdllかも?
-> python27.dllは主に初期化やエラー処理を行う内容? -> socket関連なら初期化・エラーは必要な処理 - その他の要素は普通のdllと差はない
unicodedata.pyd, bz2.pyd
- select.dllと同様、python27.dllは読み込まれているが、ws2_32.dllが読み込まれていない点から、上記の仮説は正しいと思われる。
- 同じくpython27.dllが読まれててエラー処理が含まれているが、「format」や「string」などの文字列操作系の処理と思われる関数が含まれている
-> 内部でCやシステムコールラッパ等に変換している?
-> PyXX_xxxxxx : XX部が処理の内容(ErrやOS等), xx部がその具体的な処理名
-> snprintfなどのCで用いられる関数も含まれていることから上記の仮説no 辻褄があう
_hashkub.pyd
- ほとんど上記のpydファイルと差はないが、一部違うdllを読み込んでいる
pyd まとめ
おそらくpythonスクリプトを変換するためのdllであると考えられる。
本来コンパイラがCソースコードを構文解析しているものを、pythonスクリプトを議事Cソースコードへ変換してコンパイラに渡す、もしくはpythonスクリプトを直接アセンブリに変換していると思われる。そのためにpython27.dllではC言語もしくはアセンブリへの変換処理が含まれていると思われる。
Python27.dll
- kernel32.dll, user32.dll, msvcr90.dllなどの一般的によく利用されるdllの他にshell.dllというdllが含まれている。
- shell32.dll/shellexecute : 外部アプリケーションを操作する関数
-> 何らかの実行ファイルを実行・終了するために利用する? - pythonスクリプトをexeファイルに変換時にスクリプトで利用される関数を抽出して新たな小型のdllを生成している?
-> pydがそれに該当する?
-> もしくは各pydから読み込まれて利用される-------------------------------------------------------------------
Ollydbg,IDA Free
* ここでは各実行ファイルのみ解析を行い、dllの解析は行わないこととする *
* なお、解析に関しては一度Cの実行ファイルを解析した後に比較を行う *
C
- 「call 0095154B」を最初に実行しているが、メインルーチンでないため、おそらく初期化などの処理を行なっている?
- 引数として「Hello World」と1(stdout)を渡してcallしている処理があるものの、実際のメインルーチンではない模様
- ImageBase範囲外で主な処理を行なっている?
- メインルーチンと思われる00400000番台のアドレスを経由せず、様々なアドレスへジャンプしながら最終的にwriteconsoleA関数を呼び出すことで画面出力を行なっている
- アドレス値がImageBaseと異なるのは環境の問題? -> その影響からか、直接printf関数を呼び出さずにwriteconsoleAを呼び出している?
- WriteFile => WriteCosoleという流れから、UNIX系統で用いられるglibのwrite() => writeシステムコールラッパの構造に似ている?
- コンソール上に文字列(Hello World!)を表示させる最終的な関数はWriteConsoleAであり、表面上ではWriteFile関数を呼び出すことで表示させている
-> WriteFileがWriteConsoleのラッパの役割を果たしている?
C-Python
- 開始時点ではCファイルのようにWriteConsole関数もWriteFile関数もない -> 実行していくとWriteConsoleA関数を呼び出す処理がある
- _writeという名前で呼び出される
-> glibでも存在するwriteシステムコールラッパと同等? - Cファイルとは違い、ImageBase範囲内でコードが実行されている -> Cファイルで最適化無効が効いてない?
- PyEval_EvalcodeEx => PyFrame_New => etc...を経由する処理から、表面上はPython27.dll内で処理が行われている?
-> 内部で様々なpyと付く関数の定義が成されている
-> そもそもvxpeViewerで読み込まれていなかったはずが、実際にはアクセスしている点から、python27.dllはあらゆる関数をpythonスクリプトから変換する役割を持つという仮説の信ぴょう性が高くなった - printf関数をkernel32.dllからimportしている
-> 変換の際に利用されている? - python27.dllで定義されている関数は内部で既存の関数を呼び出している点から標準関数のラッパである可能性が濃厚 -> ループ処理を繰り返すことで変換を行なっている? -> python27.dllを書き換えることで任意の処理に改ざんできる?
- WriteFileを呼び出して内部でWriteConsoleAを呼び出すルーチンはCと同じ
-> PyFile_WriteObjectが一番外側 && ラッパの関数
C-Rust
- CともPythonとも異なる初期コードを持っている
-> call => jmpという流れは同一 - C,Pythonと違い、エントリーポイントが正常な位置に存在している? -> いくつかアドレスを経由しているが、これまでのファイルよりも本来の処理に近い形をしている
- Stirlingでは発見できなかったが、セクション内に「Hello World!」が存在している?
- WriteConsoleWを使用している -> 最適化の問題?
- Cと異なり、WriteFIleを経由せずに直接(一応Rustではラッパを作成しているかも)WriteConcsole関数を呼び出している
-> 自前で関数ラッパを作成している
-> vxPEViewerでの仮説が正しい可能性が濃厚
-> ファイルサイズのわりに実行する処理がさほど多くないのも其のため?
まとめ
僕の検証が必ずしも正しいというわけではなく、あくまでこうなっているのかなという想像のお話なのでもしツッコミや鉞を投げてくださる人がいれば教えていただければと思います。
検証の感想としては、同じ処理を行うとしてもやはりその再現方法は様々でありそこでパッカーや難読かなどの手法にも応用できるものがありそうだと感じました。
また、Rustやpy2exeを用いる場合とCでコンパイルした場合とではファイルのサイズに大幅な差が生じていることからCがベアメタル環境で用いられているのもそのためなのかなと思ったり、初歩的なプログラムながら学ぶことが多く色々な想像ができる検証でした。