リバースエンジニアリングへの道
出田 守です。
最近、情報セキュリティに興味を持ち、『リバースエンジニアリング-Pythonによるバイナリ解析技法』という本(以降、「教科書」と呼びます)を読みました。
「こんな世界があるのか!かっこいい!」と感動し、私も触れてみたいということでド素人からリバースエンジニアリングができるまでを書いていきたいと思います。
ちなみに、教科書ではPython言語が使用されているので私もPython言語を使用しています。
ここを見ていただいた諸先輩方からの意見をお待ちしております。
軌跡
環境
OS: Windows10 64bit Home (日本語)
CPU: Intel® Core™ i3-6006U CPU @ 2.00GHz × 1
メモリ: 2048MB
Python: 3.6.5
私の環境は、普段Ubuntu16.04を使っていますが、ここではWindows10 64bitを仮想マシン上で立ち上げております。
ちなみに教科書では、Windowsの32bitで紹介されています。
Intel Deveroper's Manual [1] Chapter1 - Chapter2
前回まで、デバッガの基本的な処理について学びました。
次に教科書から少し脱線して逆アセンブラを作ってみたいと思いました。そこで、CPUがどのようにしてバイナリから命令を実行しているのか調べるためと、ついでにCPUについて把握しておきたかったので『Intel Developer's Manual』を読んでみることにしました。
ですので今回からは『Intel Deveroper's Manual』(以下, IDMと書かせていただきます)について簡単にまとめていきたいと思います。
今回から使用するIDMは2018/5/18更新のものを参照していきます。
間違っている箇所は、ぜひ修正依頼をお寄せくださると助かります。
Chapter1 ABOUT THIS MANUAL
1.3 Notational Convertions
この節ではこのマニュアルでのデータ構造表記や命令シンボリック表現、16進数、2進数表記について説明しています。
1.3.1 Bit and Byte Order
メモリのデータ構造は下に向かって小さいアドレスが現れます。アドレスは上に向かっていくにつれて増加していきます。ビット位置は右から左に行くにつれて増加していきます。セットされたビットはそのビット位置の2の累乗に等しい値をもちます。IntelのCPUは最下位WORDバイトから番号付けします。これをリトルエンディアンとよびます。
1.3.2 Reserved Bits and Software Compatibility
多くのレジスタやメモリレイアウトには予約ビットがあります。予約ビットは未定義ではなく、予測不可能なビットとして扱われるべきです。
ソフトウェアは以下のガイドラインにしたがって予約ビットを扱うべきです。
- 予約ビットを含むテストをするときは、予約ビットの状態に依存しないでください。テスト前に予約ビットはマスクしておきます。
- メモリやレジスタに保持する際に予約ビットの状態に依存しないでください。
- 予約ビットに書き込まれた情報に依存しないでください。
- レジスタに読み込む場合、予約ビットはいつもドキュメントで示された値で読み込むべきです。以前読み込んだ同じレジスタでそれらをリロードします。
1.3.2.1 Instruction Operands
命令がシンボルで表現される場合、IA-32アセンブリ言語のサブセットが使用されます。このサブセットは以下のような形式です。
label: mnemonic argument1, argument2, argument3
2つのオペランドがある場合、右オペランドがソースで、左オペランドがデスティネーションです。アセンブリ言語によっては左右逆になる場合もあるようです。
1.3.3 Hexadecimal and Binary Numbers
16進数表記は、16進数文字列と末尾に「H」で表現されます。(例: 0F82EH)
2進数表記は、0と1で時々末尾に「B」で表現されます。(例: 1010B)
1.3.4 Segmented Addressing
プロセッサはバイトアドレッシングを使用します。つまりメモリはByte列としてアクセスします。一つまたは複数のバイトにアクセスしようと、バイトアドレスはバイトまたはバイトメモリの位置を特定するために使用されます。アドレス可能なメモリの範囲をアドレススペースとよびます。
プロセッサはまた、セグメントアドレスをサポートします。セグメントアドレスはプログラムで多くの独立したアドレススペースを持つことが出来るアドレッシング形態です。例えばスタックなど。コードアドレスは常にコードスペースを、スタックアドレスは常にスタックスペースを参照します。セグメント内のByteアドレスを指定するには以下のようにします。
Segment-register:Byte-address
例:
DS:FF79H DSレジスタ内のアドレスを指定
CS:EIP CSレジスタ内のEIPに格納された命令アドレスを指定
1.3.5 A New Syntax for CPUID, CR, and MSR Values
CPUID命令はCR(Control Register)のbitをチェックし、MSR(Model spesific registers)を読み込み、機能フラグやステータス、システム情報を取得できます。
1.3.6 Exceptions
例外とは、通常命令がエラーを起こした時に発生するイベントです。例えば0で割ろうとした時など。それ以外に前回までに学んだブレークポイントによる例外もあります。
いくつかの例外はエラーコードをはきます。エラーコードから追加の情報を得ることが出来ます。例外とエラーコードを表示するための表記は以下です。
#PF(fault code)
この例では、障害のタイプを命名エラーコードが報告されている条件で、ページフォルトを指します。
条件によってはエラーコードが正しく報告されないことがあるようです。
#GP(0)
この例では一般保護例外の場合エラーコードは0です。
Chapter2 INTEL 64 AND IA-32 ARCHITECTURES
2.1 BRIEF HISTORY OF INTEL ® 64 AND IA-32 ARCHITECTURE
Intelアーキテクチャの歴史を要約します。
1978年のプロセッサに向けたオブジェクトコードは最新のプロセッサでも実行できるらしいです。すごい互換性ですね!
2.1.1 16-bit Processors and Segmentation (1978)
IA-32アーキテクチャが出る前は16bitプロセッサの8086と8088がありました。
8086には16bitレジスタと16bitの外部データバスがあり、20bitのアドレッシングで1MByteのアドレススペースを与えます(?)
8088も8bitの外部データバスを持っている以外は8086と似ているようです。
8086/8088はIA-32へセグメンテーションを導入した。セグメンテーションでは、16bitセグメントレジスタは最大64KByteのメモリセグメントへのポインタも含まれます。
一度に4つのセグメントレジスタを使用することで、8086/8088はセグメント間を切り替えずに最大256KByteのアドレスを指定可能です。
セグメントレジスタと追加の16bitポインタを使用して形成できる20bitのアドレスは、合計1MByteのアドレス範囲を提供します。
2.1.2 The Intel ® 286 Processor (1982)
Intel286はIA-32に保護モード動作を導入した。保護モードでは、セグメントレジスタの内容をディスクリプタ・テーブルへのセレクタまたはポインタとして使用します。
ディスクリプタは最大16MByteの物理メモリを持つ24bitベースアドレスとセグメント切り替えベースでの仮想メモリ管理をサポートし、他いくつかの保護メカニズムを提供します。
それらメカニズムは以下です。
- セグメントリミットをチェック
- 読み取り専用と実行専用のセグメントオプション
- 4つの権限レベル
2.1.3 The Intel386 TM Processor (1985)
Intel386はIA-32で初めての32bitプロセッサです。Intel386はオペランドの保持とアドレッシングのために32bitレジスタを導入しました。下位16bitは、前世代までのプロセッサの互換性を保つために16bitプロセッサの特性を保持しています。さらに8086/8088用に作成されたプログラムを効率よく実行するために仮想8086モードが用意されています。
加えて、Intel386は以下のサポートもしています。
- 32bitアドレスバスは最大4GByteの物理メモリをサポート
- セグメントメモリモデルとフラットメモリモデル
- 仮想メモリ管理のために固定された4KByteのページングを提供
- 並列ステージを提供
2.1.4 The Intel486 TM Processor (1989)
Intel486は5つの並列ステージを使いIntel386の命令をデコードし実行するのでさらに並列実行の性能が向上しました。それぞれのステージを使い最大5つの異なる命令を実行することができるそうです。
さらに、以下をサポートしています。
- 8KByteのオンチップの第一キャッシュで1クロックあたりの命令実行速度が向上
- x87 FPUが統合
- 節電とシステム管理機能
2.1.5 The Intel ® Pentium ® Processor (1993)
Pentiumは第二の実行パイプラインを導入し、スーパースカラ性能を向上させたようです。(2つのパイプラインはuとvで知られており、1クロックで一緒に実行させることができます。)
スーパースカラとは、同種または異種の命令を並列に実行させるアーキテクチャのこと。
オンチップの第一キャッシュが倍増し、一つの8KByteはコードに、もうひとつの8KByteはデータに使われました。データキャッシュはMESIプロトコルをサポートし、Intel486で以前から使用されていたライトスルーキャッシュに加えて、ライトバックキャッシュで効率化しました。
MESIプロトコルはメモリ一貫性を保つためのプロトコルのようです。
ループ構造のパフォーマンス向上のために分岐予測をオンチップ分岐テーブルに加えました。
さらに、以下をサポートしています。
- 仮想8086モードの向上に4MByteと4KByteのページを使用できるように拡張
- 128bitと256bitの内部データパスにより、内部データ転送速度が向上
- バースト可能な外部データバスを64bitに増加
- マルチプロセッサをサポートするためのAPIC
APICはマルチプロセッサ版割り込みコントローラらしいです。 - 2つのプロセッサをサポートするためのデュアルプロセッサモード
その後、Intel MMXテクノロジを導入しました。Intel MMXテクノロジは、単一命令、複数データ(SIMD)実行モデルを使用して、64bitレジスタに格納されたパックされた整数データの並列計算を実行します。
2.1.6 The P6 Family of Processors (1995-1999)
P6ファミリーはマイクロアーキテクチャによって新しい標準パフォーマンスを設定しました。
2.1.7 The Intel ® Pentium ® 4 Processor Family (2000-2006)
Pentium4はIntel NetBurstマイクロアーキテクチャをベースにしています。
Pentium4はStreaming SIMD Extensions 2(SSE2)を導入しました。ハイパースレッディングをサポートしているPentium4の3.40GHzはStreaming SIMD Extensions 3(SSE3)を導入しました。
2.1.8 The Intel ® Xeon ® Processor (2001- 2007)
XeonもIntel NetBurstマイクロアーキテクチャをベースにしています。
このプロセッサはマルチプロセッササーバとハイパフォーマンスワークステーションとして使うためにデザインされています。
2.1.9 The Intel ® Pentium ® M Processor (2003-2006)
以前のIA-32モバイルプロセッサよりもハイパフォーマンスで省電力に強化されたプロセッサです。
2.1.10 The Intel ® Pentium ® Processor Extreme Edition (2005)
Pentium Extreme Editionではデュアルコアが導入されました。
このテクノロジはマルチスレッディングを提供します。
2.1.11 The Intel ® Core TM Duo and Intel ® Core TM Solo Processors (2006-2007)
Core Duoは電源を長持ちさせるよう強化しました。
- Intel ® Smart Cacheにより2コア間のデータ共有が効率化
- SIMD実行とデコード性能の向上
- Intel ® Dynamic Power Coordination and Enhanced Intel ® Deeper Sleepにより消費電力の削減
- Intel ® Advanced Thermal ManagerによりThermalセンサのデジタル化
- 電力に最適化された667 MHzバスのサポート
2.1.12 The Intel ® Xeon ® Processor 5100, 5300 Series and Intel ® Core TM 2 Processor Family (2006)
The Intel Xeon processor 3000, 3200, 5100, 5300, and 7300, Intel Pentium Dual-Core, Intel Core 2 Extreme, Intel Core 2 Quad processors, Intel Core 2 Duoは64アーキテクチャをサポート。
2.1.13 The Intel ® Xeon ® Processor 5200, 5400, 7400 Series and Intel ® Core TM 2 Processor Family (2007)
The Intel Xeon processor 5200, 5400, and 7400 series, Intel Core 2 Quad processor Q9000 Series, Intel Core 2 Duo E8000 Seriesも64アーキテクチャをサポート。
2.1.14 The Intel ® Atom TM Processor Family (2008)
(省略)
2.1.15 The Intel ® Atom TM Processor Family Based on Silvermont Microarchitecture (2013)
(省略)
2.1.16 The Intel ® Core TM i7 Processor Family (2008)
- Intel ® Turbo Boost Technologyによりサーマルヘッドルームのパフォーマンスを向上
- Intel ® HyperThreading Technologyとクアッドコアを組み合わせて4コアと8コアを使います。
- SSE4.1とSSE4.2の命令セットをサポート
- 第二世代Intel Virtualization Technology
2.1.1 The Intel ® Xeon ® Processor 7500 Series (2010)
(省略)
2.1.18 2010 Intel ® Core TM Processor Family (2010)
(省略)
2.1.19 The Intel ® Xeon ® Processor 5600 Series (2010)
(省略)
2.1.20 The Second Generation Intel ® Core TM Processor Family (2011)
(省略)
2.1.21 The Third Generation Intel ® Core TM Processor Family (2012)
第三世代のIntel Core i7, i5 and i3プロセッサはIvy Bridgeをベースにしています。
2.1.22 The Fourth Generation Intel ® Core TM Processor Family (2013)
第四世代のIntel Core i7, i5 and i3プロセッサはHaswellをベースにしています。
2.2 MORE ON SPECIFIC ADVANCES
主要な技術について説明
2.2.1 P6 Family Microarchitecture
P6 Family Microarchitectureはアドバンスドトランスファキャッシュとよばれるレベル2キャッシュを載せています。
このアーキテクチャは3ウェイスーパースカラパイプラインアーキテクチャです。3ウェイスーパースカラとはプロセッサが1クロック毎にデコード、ディスパッチ、実行(リタイア)を完了できることです。
このアーキテクチャのメインは動的実行です。
- Deep Branch predicitionはパイプラインをフルに保つために分岐を超えて命令をデコード出来ます。これには最適化された分岐予測アルゴリズムを使用します。
- Dynamic data flow analysisは依存関係を判断し、アウトオブオーダー命令実行の機会を検出するためのプロセスを通じて、データフローのリアルタイム分析が必要です。アウトオブオーダー実行コアは多くの命令を監視し、データの完全性を保ちながら、複数の実行ユニットに最適な順序でそれらの命令を実行することが出来ます。
- Speculative execution(投機的実行)はまだ解決されていない分岐を命令を実行し、最終的に元の順に結果をコミットすることが出来ます。投機的実行を可能にするために命令の実行とディスパッチを結果のコミットメントから分離します。アウトオブオーダー実行コアは命令プールから全ての実行可能命令のデータフローを解析し結果を一時レジスタに保存します。リタイヤメントユニットは、他の命令または未解決の分岐予測とのデータ依存性をもたない完了命令について、命令プールを線形検索する。完了した命令が見つかるとリタイアメントユニットは結果をメモリand/orレジスタにオリジナルの順序でコミットします。
2.2.2 Intel NetBurst ® Microarchitecture
- The Rapid Execution Engine
- 算術論理ユニット(ALU)はプロセッサ周波数の2倍で実行
- 基本整数演算はクロックの1/2でディスパッチできる
- Hyper-Pipelined Technology
- ディープパイプラインは業界最高水準のクロックレートを実現
- 今後のリーダーシップを継続するための周波数ヘッドルームとスケーラビリティ
- Advanced Dynamic Execution
- Deep, out-of-order, speculative execution engine
- フライト(?)で最大126命令
- パイプラインで最大48の読み込みと24の保存
- 強化された分岐予測
- 深い分岐関連での誤予測のペナルティを軽減
- 高度な分岐予測アルゴリズム
- 4Kエントリ分岐ターゲット配列
- Deep, out-of-order, speculative execution engine
- 新しいキャッシュサブシステム
- レベル1キャッシュ
- 高度なExecution Trace Cacheはデコードした命令を保存
- Execution Trace Cacheは、メイン実行ループからデコーダのレイテンシを削除
- Execution Trace Cacheは、プログラム実行フローのパスを1行に統合
- 低レイテンシデータキャッシュ
- レベル2キャッシュ
- フルスピード、統合8ウェイレベル2オンダイダイレクト転送キャッシュ
- プロセッサ周波数で帯域幅とパフォーマンスの向上
- レベル1キャッシュ
- インテルNetBurstマイクロアーキテクチャーシステムバスへの高性能、クワッドポンプバスインターフェース
- 並列性を可能にするスーパースカラ
- レジスタ名のスペース制限を避けるために名前を変更してハードウェアレジスタを拡張
- 64Byteのキャッシュラインサイズ(セクタあたり最大2本のデータを転送)
2.2.2.1 The Front End Pipeline
フロントエンドはプログラム順序でアウトオブオーダ実行コアへ命令を供給します。
(以下省略 )
2.2.2.2 Out-Of-Order Execution Core
アウトオブオーダの命令を実行するためのアウトオブオーダ実行コアは並列性を実現するための重要な要素です。
この機能により、プロセッサは命令を並べ替えることができるため、1つのマイクロオペレーションが遅延すると、他のマイクロオペレーションがその周囲を進むことができるようになります。 プロセッサは、マイクロオペレーションの流れを滑らかにするためにいくつかのバッファを使用します。
2.2.2.3 Retirement Unit
リタイヤメントユニットは、実行されたマイクロオペレーションの結果をアウトオブオーダー実行コアから受信し、その結果を処理して、元のプログラム順序に従ってアーキテクチャ状態が更新されます。
2.2.3 Intel ® Core TM Microarchitecture
インテルCoreマイクロアーキテクチャーには、シングルスレッドワークロードとマルチスレッドワークロードの高性能で電力効率の高いパフォーマンスを実現する機能が導入されています。
(以下省略)
2.2.4 Intel ® Atom TM Microarchitecture
(省略)
2.2.5 Intel ® Microarchitecture Code Name Nehalem
(省略)
2.2.6 Intel ® Microarchitecture Code Name Sandy Bridge
(省略)
2.2.7 SIMD Instructions
Intel MMXテクノロジプロセッサファミリを搭載したPentium IIおよびPentiumから、SIMD(single-instruction multiple-data)動作を実行するためにIntel 64およびIA-32アーキテクチャに6つの拡張が導入されました。
これらの拡張には、MMXテクノロジ、SSE拡張、SSE2拡張、SSE3拡張、補足SSE拡張3、およびSSE4が含まれます。 これらの拡張の各々は、パックされた整数および/またはパックされた浮動小数点データの要素に対するSIMD演算を実行する命令群を提供します。
SIMD整数演算では64bitMMXレジスタまたは128bitXMMレジスタが使用できます。SIMD浮動小数点演算では128bitXMMレジスタが使用されます。
MMX命令は、MMXレジスタに配置されたパックされたバイト、ワード、またはダブルワード整数に対してSIMD演算を実行します。これらの命令は、整数配列および整数データのストリームで動作するアプリケーションで、SIMD処理に適したアプリケーションで役立ちます。
SSE命令は、XMMレジスタおよびMMXレジスタに格納されているパックされた整数に含まれるパックされた単精度浮動小数点値で動作します。
いくつかのSSE命令は、状態管理、キャッシュ制御、およびメモリ順序付け操作を提供します。 その他のSSE命令は、単精度浮動小数点データ要素の配列(3Dジオメトリ、3Dレンダリング、およびビデオエンコードおよびデコードアプリケーション)で動作するアプリケーションを対象としています。
SSE2命令は、XMMレジスタに含まれるパックされた倍精度浮動小数点値と、MMXおよびMMXレジスタに含まれるパックされた整数で動作します。
SSE2整数命令は、新しい128ビットSIMD整数演算を追加し、既存の64ビットSIMD整数演算を128ビットXMM機能に拡張することにより、IA-32 SIMD演算を拡張します。SSE2命令はまた、新しいキャッシュ制御およびメモリ順序付け動作を提供します。
SSE3は、ストリーミングSIMD Extenderのパフォーマンスを向上させる13の命令を提供します。
ストリーミングSIMD拡張命令2テクノロジ、およびx87-FP数学的性能を提供します。
SSSE3はSIMD整数演算のパフォーマンスを向上させる32の命令を提供します。
SSE4は54命令を提供します。そのうち47命令がSSE4.1の命令と呼ばれています。その他7命令はSSE4.2の命令と呼ばれています。
AESNIとPCLMULQDQは7つの新しい命令を導入しています。それらのうちの6つは、AES暗号化/復号化規格(AESNIと呼ばれる)に基づくアルゴリズムを加速するためのプリミティブです。
PCLMULQDQ命令は、汎用ブロック暗号化を高速化します。この暗号化では、最大64ビット幅の2進数に対してキャリーレス乗算を実行できます。
インテル®64アーキテクチャーでは、4世代の128bitSIMD拡張で最大16のXMMレジスタにアクセスできます。IA-32アーキテクチャでは、8つのXMMレジスタが提供されます。
2.2.8 Intel ® Hyper-Threading Technology
Hyper-Threading TechnologyはIA-32プロセッサのマルチタスク環境下でマルチスレッドシステムとマルチスレッドアプリケーションまたはシングルスレッドアプリケーションを実行を向上させます。この技術により、単一の物理プロセッサは、共有実行リソースを使用して2つ以上の別個のコードストリーム(スレッド)を同時に実行することが可能になります。
(以下省略)
2.2.9 Multi-Core Technology
マルチコアテクノロジは、物理パッケージに2つ以上の実行コアを提供することにより、ハードウェアのマルチスレッド機能を強化します。
(以下省略)
2.2.10 Intel ® 64 Architecture
Intel 64アーキテクチャーは、ソフトウェアの線形アドレス空間を64bitに拡張し、物理アドレス空間を最大46bitまでサポートします。この技術では、IA-32eモードと呼ばれる新しい動作モードも導入されています。
IA-32eモードは2つのサブモードがあります。
- 互換モードは32bitソフトウェアをほとんど変更せずに64bitOSで実行できます。
- 64bitモードは64bitアドレススペースで書かれたアプリケーションを64bitOSで実行できます。
64bitモードでは以下を使用できます。
- 64bitフラット線形アドレッシング
- 8つの追加汎用レジスタ(GPR)
- 8つのSSE(SSE, SSE2, SSE3 and SSSE3)用追加レジスタ
- 64bitワイドGPRと命令ポインタ
- 均一なバイトレジスタアドレッシング
- 高速割り込み優先順位付けメカニズム
- 新しい命令ポインタ相対アドレス指定モード
2.2.11 Intel ® Virtualization Technology (Intel ® VT)
インテル®64およびIA-32アーキテクチャー用のIntel ® Virtualization Technologyは、仮想化をサポートする拡張機能を提供します。この拡張機能はVirtual Machine Extensions(VMX)と呼ばれます。VMXを搭載したIntel 64またはIA-32プラットフォームは、複数の仮想システム(または仮想マシン)として機能できます。
まとめ
- Chapter1ではマニュアルの表記方法などを記載
- Chapter2ではプロセッサの歴史を記載
- プロセッサの歴史を見ると結構前半に主要な技術が開発されていることを知った
かなり省略したところもあります。今後必要になれば埋めていこうと思います。