関連サイト
本書の関連ページが用意されています。
内容紹介
バイナリ解析とは、バイナリプログラムとそれに含まれているマシンコードやデータの性質を解析する科学と技術のことだ。セキュリティの脆弱性を狙う攻撃やマルウェアなど、悪意をもつソフトウェアに対処するには、バイナリプログラムの本当の性質を突き止める、そのプログラムが実際に実行することを突き止める必要がある。
本書は、バイナリプログラムの基礎知識、静的解析や動的解析といったバイナリ解析の基本から、ソースプログラムがなくてもバイナリプログラムの内容を書き換えるバイナリ計装といった高度な手法まで、リバースエンジニアリングの範囲を超えて実践的に解説する。
セキュリティエンジニアやセキュリティ研究者、ハッカーやペネトレーションテストの担当者、リバースエンジニア、マルウェアアナリスト、そしてコンピュータサイエンスの学生など、バイナリ解析に興味をもつすべての人を対象として、最も魅力的で最も手ごわいテーマであるバイナリ解析入門の決定版である。
書誌情報
- 著者: Dennis Andriesse(著), 株式会社クイープ, 遠藤美代子(訳)
- 発行日: 2022-01-29 (紙書籍版発行日: 2022-01-29)
- 最終更新日: 2022-01-29
- バージョン: 1.0.0
- ページ数: 479ページ(PDF版換算)
- 対応フォーマット: PDF, EPUB
- 出版社: アスキードワンゴ
対象読者
著者について
Dennis Andriesse
システム/ネットワークセキュリティの博士号を取得しており、日々研究でバイナリ解析を使っている。ROPなどの制御フローハイジャック攻撃を阻止する制御フロー整合性システムPathArmorのメインコントリビュータの1人。また、GameOver Zeus P2Pボットネットの撲滅に協力した攻撃開発者の1人でもある。
株式会社クイープ
1995年、米国サンフランシスコに設立。コンピュータシステムの開発、ローカライズ、コンサルティングを手がけている。2001年に日本法人を設立。主な訳書に『Python機械学習プログラミング 第3版』(インプレス)、『初めてのSQL 第3版』(オライリー・ジャパン)、『Rによる機械学習 第3版』『なっとく! AIアルゴリズム』(翔泳社)、『More Effective Agile』(日経BP)、『Pythonハッカーガイドブック』(マイナビ出版)などがある。ホームページはhttp://www.quipunet.com/。
目次
著者紹介
- テクニカルレビューアー紹介
本書に寄せて
まえがき
謝辞
はじめに
- バイナリ解析とは何か、なぜ必要か
- バイナリ解析が難しいのはなぜか
- 本書の対象読者
- 本書の内容
- 本書の使い方
- 命令セットアーキテクチャ
- アセンブリ構文
- バイナリフォーマットと開発プラットフォーム
- サンプルコードと仮想マシン
- 練習問題
第I部 バイナリフォーマット
第1章 バイナリを解剖する
- 1.1 Cのコンパイルプロセス
- 1.1.1 前処理フェーズ
- 1.1.2 コンパイルフェーズ
- 1.1.3 アセンブリフェーズ
- 1.1.4 リンクフェーズ
- 1.2 シンボルとストリップされたバイナリ
- 1.2.1 シンボル情報を調べる
- 1.2.2 ダークサイドに落ちたバイナリ:バイナリのストリップ
- 1.3 バイナリを逆アセンブルする
- 1.3.1 オブジェクトファイルの内容を調べる
- 1.3.2 完全なバイナリ実行ファイルを調べる
- 1.4 バイナリを読み込んで実行する
- 1.5 まとめ
第2章 ELFフォーマット
- 2.1 実行可能ヘッダー
- 2.1.1 e_ident配列
- 2.1.2 e_typeフィールド、e_machineフィールド、e_versionフィールド
- 2.1.3 e_entryフィールド
- 2.1.4 e_phoffフィールド、e_shoffフィールド
- 2.1.5 e_flagsフィールド
- 2.1.6 e_ehsizeフィールド
- 2.1.7 e_*entsizeフィールド、e_*numフィールド
- 2.1.8 e_shstrndxフィールド
- 2.2 セクションヘッダー
- 2.2.1 sh_nameフィールド
- 2.2.2 sh_typeフィールド
- 2.2.3 sh_flagsフィールド
- 2.2.4 sh_addrフィールド、sh_offsetフィールド、sh_sizeフィールド
- 2.2.5 sh_linkフィールド
- 2.2.6 sh_infoフィールド
- 2.2.7 sh_addralignフィールド
- 2.2.8 sh_entsizeフィールド
- 2.3 セクション
- 2.3.1 .initセクション、.finiセクション
- 2.3.2 .textセクション
- 2.3.3 .bssセクション、.dataセクション、.rodataセクション
- 2.3.4 .pltセクション、.gotセクション、.got.pltセクションと遅延バインディング
- 2.3.5 .rel.*セクション、.rela.*セクション
- 2.3.6 .dynamicセクション
- 2.3.7 .init_arrayセクション、.fini_arrayセクション
- 2.3.8 .shstrtabセクション、.symtabセクション、.strtabセクション、.dynsymセクション、.dynstrセクション
- 2.4 プログラムヘッダー
- 2.4.1 p_typeフィールド
- 2.4.2 p_flagsフィールド
- 2.4.3 p_offsetフィールド、p_vaddrフィールド、p_paddrフィールド、p_fileszフィールド、p_memszフィールド
- 2.4.4 p_alignフィールド
- 2.5 まとめ
第3章 入門:PEフォーマット
- 3.1 MS–DOSヘッダーとMS–DOSスタブ
- 3.2 PEシグネチャ、PEファイルヘッダー、PEオプションヘッダー
- 3.2.1 PEシグネチャ
- 3.2.2 PEファイルヘッダー
- 3.2.3 PEオプションヘッダー
- 3.3 セクションヘッダーテーブル
- 3.4 セクション
- 3.4.1 .edataセクションと.idataセクション
- 3.4.2 PEコードセクションのパディング
- 3.5 まとめ
第4章 libbfdを使ってバイナリローダーを作成する
- 4.1 libbfdとは何か
- 4.2 単純なバイナリ読み込みインターフェイス
- 4.2.1 Binaryクラス
- 4.2.2 Sectionクラス
- 4.2.3 Symbolクラス
- 4.3 バイナリローダーを実装する
- 4.3.1 libbfdを初期化してバイナリを開く
- 4.3.2 バイナリの基本的なプロパティをパースする
- 4.3.3 シンボルを読み込む
- 4.3.4 セクションを読み込む
- 4.4 バイナリローダーをテストする
- 4.5 まとめ
第II部 バイナリ解析の基礎
第5章 Linuxでの基本的なバイナリ解析
- 5.1 fileを使ってファイルの正体を突き止める
- 5.2 lddを使って依存関係を調べる
- 5.3 xxdを使ってファイルの内容を表示する
- 5.4 取り出したELFをreadelfでパースする
- 5.5 nmを使ってシンボルを解析する
- 5.6 stringsを使って手がかりを探る
- 5.7 straceとltraceを使ってシステムコールとライブラリ呼び出しを追跡する
- 5.8 objdumpを使って命令レベルの振る舞いを調べる
- 5.9 gdbを使って動的文字列バッファをダンプする
- 5.10 まとめ
第6章 逆アセンブリとバイナリ解析の基礎
- 6.1 静的逆アセンブリ
- 6.1.1 線形逆アセンブリ
- 6.1.2 再帰逆アセンブリ
- 6.2 動的逆アセンブリ
- 6.2.1 例:gdbを使ってバイナリ実行をトレースする
- 6.2.2 コードカバレッジ戦略
- 6.3 逆アセンブルしたコードとデータを構造化する
- 6.3.1 コードを構造化する
- 6.3.2 データを構造化する
- 6.3.3 逆コンパイル
- 6.3.4 中間表現
- 6.4 基本的な解析手法
- 6.4.1 バイナリ解析の特性
- 6.4.2 制御フロー解析
- 6.4.3 データフロー解析
- 6.5 コンパイラの設定は逆アセンブリにどのような影響を与えるか
- 6.6 まとめ
第7章 ELFにコードを注入する
- 7.1 16進数の編集を使ってバイナリを書き換える
- 7.1.1 off–by–oneバグの動作を確認する
- 7.1.2 off–by–oneバグを修正する
- 7.2 PRELOADを使って共有ライブラリの振る舞いを変更する
- 7.2.1 ヒープオーバーフローの脆弱性
- 7.2.2 ヒープオーバーフローを検出する
- 7.3 コードセクションを注入する
- 7.3.1 ELFセクションの注入:大まかな概要
- 7.3.2 elfinjectを使ってELFセクションを注入する
- 7.4 注入したコードを呼び出す
- 7.4.1 エントリポイントの変更
- 7.4.2 コンストラクタとデストラクタのハイジャック
- 7.4.3 GOTエントリのハイジャック
- 7.4.4 PLTエントリのハイジャック
- 7.4.5 直接呼び出しと間接呼び出しのリダイレクト
- 7.5 まとめ
第III部 高度なバイナリ解析
第8章 逆アセンブリのカスタマイズ
- 8.1 カスタム逆アセンブリを記述するのはなぜか
- 8.1.1 カスタム逆アセンブリのユースケース:難読化されたコード
- 8.1.2 カスタム逆アセンブラを記述するその他の理由
- 8.2 Capstone
- 8.2.1 Capstoneをインストールする
- 8.2.2 Capstoneを使って線形逆アセンブラを作成する
- 8.2.3 CapstoneのC APIを調べる
- 8.2.4 Capstoneを使って再帰逆アセンブラを作成する
- 8.3 ROPガジェットスキャナを実装する
- 8.3.1 リターン指向プログラミング
- 8.3.2 ROPガジェットを探索する
- 8.4 まとめ
第9章 バイナリ計装
- 9.1 バイナリ計装とは何か
- 9.1.1 バイナリ計装のAPI
- 9.1.2 静的バイナリ計装と動的バイナリ計装
- 9.2 静的バイナリ計装
- 9.2.1 int 3アプローチ
- 9.2.2 トランポリンアプローチ
- 9.3 動的バイナリ計装
- 9.3.1 DBIシステムのアーキテクチャ
- 9.3.2 Pinの概要
- 9.4 Pinを使ったプロファイリング
- 9.4.1 プロファイラのデータ構造とセットアップコード
- 9.4.2 関数のシンボルを調べる
- 9.4.3 基本ブロックを計装する
- 9.4.4 制御フロー命令を計装する
- 9.4.5 命令、制御の移動、システムコールをカウントする
- 9.4.6 プロファイラをテストする
- 9.5 Pinを使ってバイナリを自動的にアンパックする
- 9.5.1 実行可能パッカー
- 9.5.2 アンパッカーのデータ構造とセットアップコード
- 9.5.3 メモリ書き込みを計装する
- 9.5.4 制御フロー命令を計装する
- 9.5.5 メモリ書き込みを追跡する
- 9.5.6 元のエントリポイントを検出し、アンパックしたバイナリをダンプする
- 9.5.7 アンパッカーをテストする
- 9.6 まとめ
第10章 動的テイント解析
- 10.1 動的テイント解析とは何か
- 10.2 動的テイント解析の3つのステップ:テイントソース、テイントシンク、テイント伝播
- 10.2.1 テイントソースを定義する
- 10.2.2 テイントシンクを定義する
- 10.2.3 テイント伝播を追跡する
- 10.3 動的テイント解析を使ってハートブリードバグを検知する
- 10.3.1 ハートブリードとは
- 10.3.2 テイントを使ってハートブリードを検知する
- 10.4 動的テイント解析の設計要素:テイント粒度、テイントカラー、テイントポリシー
- 10.4.1 テイント粒度
- 10.4.2 テイントカラー
- 10.4.3 テイント伝播ポリシー
- 10.4.4 オーバーテイントとアンダーテイント
- 10.4.5 制御依存
- 10.4.6 シャドウメモリ
- 10.5 まとめ
第11章 libdftを使った実用的な動的テイント解析
- 11.1 libdftの概要
- 11.1.1 libdftの仕組み
- 11.1.2 テイントポリシー
- 11.2 動的テイント解析を使ってリモートコントロールハイジャックを検知する
- 11.2.1 テイント情報を調べる
- 11.2.2 テイントソース:受信したバイトをテイントする
- 11.2.3 テイントシンク:execveの引数をチェックする
- 11.2.4 制御フローハイジャック攻撃を検知する
- 11.3 暗黙的なフローを使ってDTAを逃れる
- 11.4 動的テイント解析を使ってデータ漏洩を検知する
- 11.4.1 テイントソース:開かれたファイルのテイントを追跡する
- 11.4.2 テイントシンク:ネットワーク送信でデータ漏洩を監視する
- 11.4.3 データ漏洩を検知する
- 11.5 まとめ
第12章 シンボリック実行
- 12.1 シンボリック実行の概要
- 12.1.1 シンボリック実行と具体値での実行
- 12.1.2 シンボリック実行の種類と制限
- 12.1.3 シンボリック実行のスケーラビリティを向上させる
- 12.2 Z3での制約解決
- 12.2.1 命令の到達可能性を検証する
- 12.2.2 命令の到達不能性を検証する
- 12.2.3 式の有効性を検証する
- 12.2.4 式を簡約化する
- 12.2.5 ビットベクトルを使ってマシンコードの制約をモデル化する
- 12.2.6 ビットベクトルのOpaque Predicateを解決する
- 12.3 まとめ
第13章 Tritonを使ったシンボリック実行
- 13.1 Tritonの概要
- 13.2 抽象構文木を使ってシンボリック状態を管理する
- 13.3 Tritonでの後ろ向きスライシング
- 13.3.1 Tritonのヘッダーファイルと設定
- 13.3.2 シンボリック設定ファイル
- 13.3.3 命令のエミュレーション
- 13.3.4 Tritonのアーキテクチャを設定する
- 13.3.5 後ろ向きスライスを計算する
- 13.4 Tritonを使ってコードカバレッジを改善する
- 13.4.1 シンボリック変数を作成する
- 13.4.2 新しいパスのモデルを取得する
- 13.4.3 コードカバレッジツールをテストする
- 13.5 脆弱性の自動エクスプロイト
- 13.5.1 脆弱なプログラム
- 13.5.2 脆弱なコールサイトのアドレスを突き止める
- 13.5.3 エクスプロイトジェネレータを構築する
- 13.5.4 rootシェルを手に入れる
- 13.6 まとめ
第IV部 付録
付録A 速習:x86アセンブリ
- A.1 アセンブリプログラムのレイアウト
- A.1.1 アセンブリの命令、ディレクティブ、ラベル、コメント
- A.1.2 コードとデータの分離
- A.1.3 AT&T構文とIntel構文
- A.2 x86命令の構造
- A.2.1 x86命令のアセンブリレベルの表現
- A.2.2 x86命令のマシンレベルの構造
- A.2.3 レジスタオペランド
- A.2.4 メモリオペランド
- A.2.5 即値
- A.3 一般的なx86命令
- A.3.1 オペランドの比較とステータスフラグの設定
- A.3.2 システムコールの実装
- A.3.3 条件付きジャンプの実装
- A.3.4 メモリアドレスのロード
- A.4 アセンブリの一般的なコード構造
- A.4.1 スタック
- A.4.2 関数呼び出しと関数フレーム
- A.4.3 条件分岐
- A.4.4 ループ
付録B libelfを使ってPT_NOTEを上書きする
- B.1 必要なヘッダー
- B.2 elfinjectで使われているデータ構造
- B.3 libelfを初期化する
- B.4 実行可能ヘッダーを取得する
- B.5 PT_NOTEセグメントを見つけ出す
- B.6 コードバイトを注入する
- B.7 注入したセクションのロードアドレスのアライメント
- B.8 .note.ABI-tagセクションヘッダーを上書きする
- B.9 注入したセクションに名前を付ける
- B.10 PT_NOTEプログラムヘッダーを上書きする
- B.11 エントリポイントを書き換える
付録C バイナリ解析ツール
- C.1 逆アセンブラ
- C.2 デバッガ
- C.3 逆アセンブリフレームワーク
- C.4 バイナリ解析フレームワーク
付録D 参考文献
- D.1 規格と参考資料
- D.2 論文と記事
- D.3 書籍