iOSシステム解析(二)Mach-Oバイナリファイル解析

5465 ワード

もっと読む
原文は【クラウド技術ブログを聴く】から来ました.http://blog.tingyun.com/web/article/detail/1341
 
0 x 01 Mach-Oフォーマットは簡単に紹介します.
Mach-OファイルフォーマットはOS XとiOSシステム上の実行可能ファイルフォーマットで、windowsのPEファイルとLinux(他のUnix like)のELFファイルと似ています.Mach-Oのフォーマットと関連内容を徹底的に把握しないと、xnuカーネルを深く研究することはできません.
Mach-Oファイルのフォーマットは下図の通りです.
下記のいくつかの部分から構成されています.
1.ヘッド:Mach-Oの基本情報を保存しています.プラットフォーム、ファイルタイプ、LoadCommundsの個数などが含まれています.
2.LoadCommunds:この部分はHeaderに続き、Mach-Oファイルを読み込む時にここのデータを使ってメモリの分布を確定します.
3.Data:各segmentの具体的なデータはここに保存されています.ここには具体的なコード、データなどが含まれています.
0 x 02 FATバイナリデータ、データ構造定義は\
1.最初の段はmagicの魔数です.ここでは大きさに注意して、読んだ後、0 xCAFEBABEかそれとも0 xBEBAFECAかを確認してください.これによって後続のバイトのバイト順を変えなければなりません. 最初の4 byteは0 xBEBAFECAであり、fatとして説明されていることがわかる.
2.第二段はarch countで、このアプリまたはdSYSMにはどのCPUアーキテクチャが含まれていますか?例えば、armv 7、arm 64など、この例では2(後4 byte)です. 0 x 00 00 00 02)は、2つのcpuアーキテクチャを含むことを示す. 
  `sizeof(struct fat-header) = 8byte`
3.後段にはcputtype(0 x)が含まれています. 0 C 00 00 01)、cpusbtype(0 x 00 00)、offset(0 x 00 10 00) 00)、size(0 x 00 F 0 27 00)などのデータは、fatの構造定義に基づいて順次読み、ここで説明する必要があるのは、CPUアーキテクチャが一つしか含まれていないなら、このfatヘッダの定義がないので、この部分をスキップして直接にArchデータを読み取ることができるということである.
   `sizeof(struct fat-arch) = 20byte`
4.fatヘッダから読み出されたoffsetデータによって、ファイルに対応するarchデータの位置にジャンプすることができます.もちろん一つのアーキテクチャだけであれば、オフセット量の計算は不要です.下図に解析の関数を与えます.
0 x 03 Mach Headerバイナリデータ
magicによって32-bitか64-bitかを区別できます.64-bitは4バイト分の予約フィールドが多くなりました.ここではバイト順に注意する必要があります.つまり、magicを判断して、バイト順を変換する必要があるかどうかを判断します. 
`sizeof(struct mach-header-64) = 32byte`  ; `sizeof(struct mach-header) = 28byte`
mach-headerとmach-header_によると64の定義は、Headersの主な役割は、Mach-Oファイルの迅速な位置決めを支援するためのシステムの動作環境、ファイルタイプであることが明らかになっています.
FileType 
Mach-Oファイルは実行可能ファイルだけでなく、他の内容を実現するために使われています.
1.カーネル拡張
2.ライブラリファイル
3.CoreDump
4. その他
以下はいくつかの素晴らしいファイルタイプです.
1.MH-OBJECT   コンパイル中に発生した objファイル(gcc-c xx.c.はxx.oファイルを生成します)
2.MH-EXECUTABLE バイナリファイル(/usr/bin/ls)が実行できます.
3.MH-CSORE     CoreDump(崩壊時のDumpファイル)
4.MH-DYLIB ダイナミックライブラリ(/usr/lib/中の共有ライブラリファイル)
5.MH-DYLINKER コネクタlinker(/usr/lib/dyldファイル)
6.MH-KEXT-BUNNDLE  カーネル拡張ファイル(自分で開発した簡単なカーネルモジュール)
flags
Mach-O headersはまたいくつかの重要なdyldのロードパラメータを含んでいます.
1.MH-NOUNDEFS  ターゲットには未定義の記号がありません.リンクの依存性はありません.
2.MH-DYLDLINK    ターゲットファイルはdyldの入力ファイルで、再度静的にリンクできません.
3.MH-PIIE     ランダムなアドレス空間を許可する(ASLRをオープンする -\>Address Space Layout Randomization)
4.MH-ALOW-STCK-EXECUTION  スタックメモリはコードを実行できます.一般的にはデフォルトで閉じられています.
5.MH-NO-HEAP-EXECUTION  ヒープメモリがコードを実行できませんでした.
0 x 04 LoadCommunds
Load Commundsは直接にHeaderの後に付いています.すべてのcommandがメモリを占有する合計はMach-O Headerの中ですでに与えられました.Headerをロードした後にLoadCommandを解析して次のデータをロードします.定義は以下の通りです
cmdフィールド
cmdフィールドの種類によって、異なる関数を使用してロードされます.簡単なリストを作ってみてください.カーネルコードの中では違ったcommandタイプにはどのような役割がありますか?
1.LC-SGMENT;LC-SGMENT-64  カーネルでは、load-segment関数で処理されます.(segmentのデータをロードして、プロセスのメモリ空間にマッピングします.)
2.LC-LIOA D-DYLINKER   カーネルでは、load-dylinker関数で処理されます(呼び出し/usr/lib/dyldプログラム).
3.LC-UIDは、カーネル内でload-uuid関数によって処理される(128 bitの一意IDをロードする)
4.LC-THREAD カーネルでは、load-thread関数によって処理されます(MACHスレッドを開いていますが、スタック空間は割り当てられていません).
5.LC-UNIXTHREADは、カーネルでは、load-unixthread関数で処理されます(UNIX posixスレッドを開く).
6.LC-ODE-SIGNATUREは、カーネル内でload-code-signature関数によって処理されます(デジタル署名を行います).
7.LC-ENCRYPTION-InFOは、カーネルでset-code-unprotect関数によって処理されます(バイナリファイルを暗号化します).
UUIDバイナリデータ   128 byte
UUIDは16バイト(128 bit)のデータの一部であり、ファイルの一意の識別であり、前述の記号化の際には、このUUIDはAppバイナリファイルのUUIDと一致しなければならず、正しい記号化が可能となる.dwarfdumpで調べたUUIDはこのデータです.この部分のデータを読み込む時にCommand構造で読みました.つまり、第一段(0 x 000001 B)は次のデータタイプ、第二段(0 x 00008)はデータのサイズ(Commandデータを含む)を表します. 
SymTabバイナリデータ
1.符号表のデータブロック構造は、前の二段は依然としてCommandデータである.後の4つのセグメントは、それぞれ、シンボルのファイル内のオフセット量(0 x 001 DF 5 E 0)、シンボルの個数(0 x 001 DF 5 E 0)、文字列のファイル内のオフセット量(0 x 0020 C 3 A 0)、文字列の表サイズ(0 x 000729 A 8)である. 
2.次はSegmentとSectionのデータブロックを読みます.上で読んだデータブロックの構造と同じようにCommand構造から読み、下の図に示すSegmentデータとSectionデータはバイナリファイルで連続しています.つまり、Segmentデータの後ろには複数の対応するアクションデータが続きます.Segment構造のnsectsにより、Segmentのデータ総数が決定されます. 
3.ここで簡単なMach-O解析ツールを書きました. [https://github.com/liutianshx2012/Tmacho」(https://github.com/liutianshx2012/Tmacho)
Segmentデータ
データをロードする時、主にロードするのはLC-SGMET生きているLC-SGMENT_です.64です.他のSegmentの用途はここでは深く追究しません.
LCSEGMENTおよびLC-SGMENT-64は以下の図を定義します.
 
ここでは大半のデータが、カーネルの仮想メモリへのSegmentマッピングを支援するために使用されていることがわかる.
nsectsフィールドは、Segmentの中にどれぐらいのsecetionがあるかを示しています.sectionは具体的に有用なデータの保管場所です.
TEXTのvmaddrはつまりプログラムのロードアドレスです.—DWARFはDWARFデータブロックの情報を示し、dSYSMはDWARFフォーマットのデータ構造であることを示している. 
` sizeof(struct segment-command) = 56byte   ;   sizeof(struct segment-command-64) = 72byte`
セレクションデータ
Sectionデータから、debug-info、debug-pubnames、debug-lineなどのデバッグ情報を見つけられます.これらのデバッグ情報を通じて、プログラム中の記号の先頭アドレス、変数タイプなどの情報を見つけられます.私たちが記号化したいなら、これらのデータを解析してほしい情報を得ることができます.
Symbolデータ
SymTabのデータからSymbolのファイル内の位置と個数を得ることができ、Symbolブロックデータにはシンボルの先頭アドレス、文字列のオフセットなどのデータが含まれています.この部分のデータ構造は\および\を参照することができます.この部分のデータを全部読み込めば、すべての記号データ、つまり次のデータを読み取ることができます. 
Symbol Stringデータ
1.SymTabとSymboのデータを通じて、各記号文字列のファイル中のオフセット量とサイズを得ることができます.各記号データは0で終わる文字列です. 
2.上記の二つの部分のデータの組み合わせを通して、各smboのプログラム中のロードアドレスを得ることができます.これらのデータは後でシンボルを作る仕事にとても役立ちます.
3.これで、dSYSMファイルにおけるヘッダデータの読み込みが完了します.ヘッダデータにはそれぞれのデータ構造の定義があり、読み取りが比較的容易であり、データを解析する際にはバイト順の問題に注意し、32-bitと64-bitのデータ構造の違い、バイト長の違い、DWARFバージョンの違い、各データブロック間の緊密な関係があり、1バイトの読み取り偏差は後続データの読み取りエラーを引き起こします.差の厘毛とは、千里をも失うことである.