はじめに
最近私は低レイヤーの勉強をしています.
ある時, IATがおかしいと実行時に怒られてしまいました.
私はその時, IATを直すことができなかったのでPEを理解したいと思い,
readpe
を自作しようと思いました.
readpe
はELFフォーマットを見やすくするためのreadelf
の
PE版です.
今回実装したオプションは,
-h
, -o
, -S
です.
私は普段
mingw-w64
というコンパイラを使っているのですが,
それを使うとMicrosoftのドキュメントの説明と
異なる点があったため,Visual Studio
のコンパイラを使いました.
PEとは
Windowsで使用される実行ファイルのファイルフォーマット
COFFとも呼ばれる
MS-DOS Header
前方互換をとるためのHeaderである
DOS Header
-
Sigunature
0x5A4D(MZ)が格納されている -
Pointer to PE Header (0x3C)
PE Headerの先頭のアドレスが格納される
DOS Stub
MS-DOS上で実行するためのコード
通常の場合は処理されず, MS-DOSの場合のみに処理される
ここから
それではreadpeについて説明したいと思います.
readpe
はx86/x64に対応しています.
ソースコードはここから
hello_x86.exe
今回の解析対象はhello_x86.exe
です.
コンパイル前のファイルは下記の通りです.
#include <stdio.h>
int main() {
printf("Hello,World!\n");
return 0;
}
これをcl hello_x86.c -o hello_x86
コマンドでコンパイルします.
-h
COFF Headerを出力します.
これを見ることで, このファイルがいつコンパイルされたかなどの
基本的な情報がわかります.
このオプションでは見やすいようにするために,
フラグの部分を赤(1)と緑(0)で分けした.
NumberOfSections TimeDataStamp PointerToSymbolTable NumberOfSymbols SizeOfOptionalHeader CharacteristicsFile Header
- Machine
どのマシーンを対象として作られているかを示す
基本的には0x8664(x64), 0x014C(x86)
セクションの数
1970年1月1日00:00からの秒数
ファイルが作成された日時を示す
Optionalheader
のサイズ
ファイルの特性を示すフラグ
ベース再配置情報が含まれないことを意味する
実行可能ファイルであることを意味する
ファイルから行番号が削除される
ファイルにシンボルテーブルが含まれない
Windows2000以降は廃止された
2GBを超えるアプリのアドレスをサポートする
x64の場合は既定でフラグが立っている
x86の場合のみにフラグが立つ
デバッグ情報を含まない
リムーバブルメディアから実行するときに, スワップファイル上にコピーして実行する
ネットワーク上から実行するときに, スワップファイル上にコピーして実行する
DLLファイルであるを意味する
単一のCPUのマシンでのみ実行可能である
-o
Optional Headerを出力します.
このHeaderは実行時に必要な情報がたくさん含まれています.
Optional Headerの中でもさらに3の主要な部分があります.
それを見やすくするために[--<header part>--]
を付けました.
問題点として, これを出力した際にとても長くなってしまうので,
さらにオプションを増やして, header part
ごとに出力できるようにしたいです.
Standard Fields
実行ファイルをロードする際に必要な情報が含まれている
Details
- Magic
0x010Bの場合はx86, 0x020Bの場合はx64
これからのサイズが変わる
- MajorLinkerVersion
- MinorLinkerVersion
リンカーのバージョン
- SizeOfCode
実行可能なコードのサイズ
- SizeOfInitializedData
初期化済みのデータサイズ
- SizeOfUninitializedData
未初期化のデータサイズ
- AddressOfEntryPoint
実行を始めるEntry Pointのアドレス
mainではない
- BaseOfCode
コード領域のベースアドレス
- BaseOfData
初期化済みのデータの開始アドレス
x86のみが持っている
Windows-Specific Fields
リンカーとローダに必要な情報が含まれている
Details
- ImageBase
ファイルがロードされると望ましいアドレス
EXEでは0x004000000, DLLでは0x10000000
- SectionAlignment
メモリにロードされるときに各セクションの開始アドレスがSectionAlignment
の倍数になる
- FileAlignment
ファイル内で各セクションの開始アドレスがFileAlignment
の倍数になる
- MajorOperatingSystemVersion
- MinorOperatingSystemVersion
OSのバージョン
- MajorImageVersion
- MinorImageVersion
ファイルのバージョン
- MajorSubsystemVersion
- MinorSubsystemVersion
サブシステムのバージョン
- Win32VersionValue
今は使われていない
- SizeOfImage
ファイルをロードした際のメモリを占めるサイズ
- SizeOfHeaders
すべてのファイルヘッダーのサイズ
- CheckSum
ロード時にファイルを確認する時に使う
- Subsystem
ファイルのターゲットとなるサブシステム
GUIなら0x02, CUIなら0x03
- DllCharacteristics
- IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA(0x0020)
64bitのアドレス空間でASLRを有効
- IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE(0x0040)
ASLRを有効
- IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY(0x0080)
コードのデジタル署名を強制的にする
- IMAGE_DLLCHARACTERISTICS_NX_COMPAT(0x0100)
DEPが有効
- IMAGE_DLLCHARACTERISTICS_ NO_ISOLATION(0x0200)
アプリケーションの分離を持たない
- IMAGE_DLLCHARACTERISTICS_ NO_SEH(0x0400)
構造化例外ハンドラーを持たない
- IMAGE_DLLCHARACTERISTICS_ NO_BIND(0x0800)
バインドできない
- IMAGE_DLLCHARACTERISTICS_APPCONTAINER(0x1000)
AppContainer 上で実行される必要がある
- IMAGE_DLLCHARACTERISTICS_ WDM_DRIVER(0x2000)
WDMドライバーであることを表す
- IMAGE_DLLCHARACTERISTICS_GUARD_CF(0x4000)
制御フローガードが有効である
- IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE(0x8000)
ターミナルサーバとの互換性がある
- SizeOfStackReserve
スタック領域として予約するサイズ
- SizeOfStackCommit
スタック領域としてコミットするサイズ
- SizeOfHeapReserve
ヒープ領域として予約するサイズ
- SizeOfHeapCommit
ヒープ領域として予約するサイズ
- LoaderFlags
今は使われていない
- NumberOfRvaAndSizes
Data Directories
のサイズ
Data Directories
上位の4バイトはアドレスを指していて, 下位4バイトはサイズを表している
Details
- Export Table
エクスポート情報(.edata)
- Import Table
インポート情報(.idata)
- Resouce Table
リソース(.rsrc)
- Exception Table
例外情報(.pdata)
- Certificate Table
セキュリティ情報
- Base Relocation Table
再配置情報(.reloc)
- Debug
デバッグ情報(.debug)
- Architecture
今は使われていない
- Global Ptr
グローバルポインター
構造体サイズは0
- TLS Table
TLS情報(.tls)
- Load Config Table
ロード構成情報
- Bound Import
バインドされたインポート情報
- IAT
インポートアドレステーブル
- Delay Import Descriptor
遅延インポート情報
- CLR Runtime Header
.NETメタデータ(.cormeta)
- Reserved, must be zero
今は使われていない
-S
Section Headerを出力します.
これを見ることで, 各セクションの位置や,
読み込み可能や書き込み可能などのフラグがわかります.
格子状にすることで見やすくなっています.
charactorisics
の部分が見にくいのですがいい方法が思い浮かばなかったため,
とりあえずはそのままにしました.
Section Header
- Name
ASCIIで8バイト使う
余ると0x00が入る
- VirtualSize
メモリにロードされた時に, このセクションがどれだけ占めるサイズ
- VirtualAddress
メモリにロードされた時に, このセクションが配置されるアドレス
- SizeOfRawData
ファイル内でのこのセクションサイズ
- PointerToRawData
ファイル内でこのセクションのアドレス
- PointerToRelocations
- PointerToLinenumbers
- NumberOfRelocations
- NumberOfLinenumbers
PEでは使われない
- Charactorisics
- IMAGE_SCN_CNT_CODE (0x00000020)
実行可能コードを含んでいる
- IMAGE_SCN_CNT_INITIALIZED_DATA (0x00000040)
初期化済みのデータが含まれている
- IMAGE_SCN_CNT_UNINITIALIZED_DATA (0x00000080)
未初期化済みのデータが含まれている
- IMAGE_SCN_LNK_INFO (0x00000200)
コメントやその他のデータを含まれている
.drectveセクションにある
オブジェクトファイルにのみ有効
- IMAGE_SCN_LNK_REMOVE (0x00000800)
リンク時にイメージから取り除かれる
オブジェクトファイルにのみ有効
- IMAGE_SCN_LNK_COMDAT (0x00001000)
COMDATデータが含まれる
オブジェクトファイルにのみ有効
- IMAGE_SCN_GPREL (0x00008000)
グローバルポインターを介して参照するデータがある
- IMAGE_SCN_ALIGN_1BYTES (0x00100000)
- IMAGE_SCN_ALIGN_2BYTES (0x00200000)
- IMAGE_SCN_ALIGN_4BYTES (0x00300000)
- IMAGE_SCN_ALIGN_8BYTES (0x00400000)
- IMAGE_SCN_ALIGN_16BYTES (0x00500000)
- IMAGE_SCN_ALIGN_32BYTES (0x00600000)
- IMAGE_SCN_ALIGN_64BYTES (0x00700000)
- IMAGE_SCN_ALIGN_128BYTES (0x00800000)
- IMAGE_SCN_ALIGN_256BYTES (0x00900000)
- IMAGE_SCN_ALIGN_512BYTES (0x00A00000)
- IMAGE_SCN_ALIGN_1024BYTES (0x00B00000)
- IMAGE_SCN_ALIGN_2048BYTES (0x00C00000)
- IMAGE_SCN_ALIGN_4096BYTES (0x00D00000)
- IMAGE_SCN_ALIGN_8192BYTES (0x00E00000)
オブジェクトファイルにのみ有効
- IMAGE_SCN_LNK_NRELOC_OVFL (0x01000000)
拡張再配置が含まれている
- IMAGE_SCN_MEM_DISCARDABLE (0x02000000)
必要に応じて破棄できる
- IMAGE_SCN_MEM_NOT_CACHED (0x04000000)
メモリ上でキャッシュできない
- IMAGE_SCN_MEM_NOT_PAGED (0x08000000)
メモリ上でページングできない
- IMAGE_SCN_MEM_SHARED (0x10000000)
メモリ上で共有できる
- IMAGE_SCN_MEM_EXECUTE (0x20000000)
実行可能
- IMAGE_SCN_MEM_READ (0x40000000)
読み込み可能
- IMAGE_SCN_MEM_WRITE (0x80000000)
書き込み可能
まとめ
今回の学習により, 自分でPEを解析することができると思います.
次はPackerを作りたいと思います.