はじめに
最近私は低レイヤーの勉強をしています.
ある時, 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)で分けした.
File Header
-
NumberOfSections
セクションの数 -
TimeDataStamp
1970年1月1日00:00からの秒数
ファイルが作成された日時を示す -
PointerToSymbolTable
-
NumberOfSymbols
-
SizeOfOptionalHeader
Optionalheader
のサイズ -
Characteristics
ファイルの特性を示すフラグ -
IMAGE_FILE_RELOCS_STRIPPED(0x0001)
ベース再配置情報が含まれないことを意味する -
IMAGE_FILE_EXECUTABLE_IMAGE(0x0002)
実行可能ファイルであることを意味する -
IMAGE_FILE_LINE_NUMS_STRIPPED(0x0004)
ファイルから行番号が削除される -
IMAGE_FILE_LOCAL_SYMS_STRIPPED(0x0008)
ファイルにシンボルテーブルが含まれない -
IMAGE_FILE_AGGRESSIVE_WS_TRIM(0x0010)
Windows2000以降は廃止された -
IMAGE_FILE_LARGE_ADDRESS_AWARE(0x0020)
2GBを超えるアプリのアドレスをサポートする
x64の場合は既定でフラグが立っている -
NULL(0x0040)
-
IMAGE_FILE_BYTES_REVERSED_LO(0x0080)
-
IMAGE_FILE_32BIT_MACHINE(0x0100)
x86の場合のみにフラグが立つ -
IMAGE_FILE_DEBUG_STRIPPED(0x0200)
デバッグ情報を含まない -
IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP(0x0400)
リムーバブルメディアから実行するときに, スワップファイル上にコピーして実行する -
IMAGE_FILE_NET_RUN_FROM_SWAP(0x0800)
ネットワーク上から実行するときに, スワップファイル上にコピーして実行する -
IMAGE_FILE_SYSTEM(0x1000)
-
IMAGE_FILE_DLL(0x2000)
DLLファイルであるを意味する -
IMAGE_FILE_UP_SYSTEM_ONLY(0x4000)
単一のCPUのマシンでのみ実行可能である -
IMAGE_FILE_BYTES_REVERSED_HI(0x8000)
-o
Optional Headerを出力します.
このHeaderは実行時に必要な情報がたくさん含まれています.
Optional Headerの中でもさらに3の主要な部分があります.
それを見やすくするために[--<header part>--]
を付けました.
問題点として, これを出力した際にとても長くなってしまうので,
さらにオプションを増やして, header part
ごとに出力できるようにしたいです.
Standard Fields
実行ファイルをロードする際に必要な情報が含まれている
Details
0x010Bの場合はx86, 0x020Bの場合はx64
これからのサイズが変わる - **MajorLinkerVersion** - **MinorLinkerVersion**
リンカーのバージョン - **SizeOfCode**
実行可能なコードのサイズ - **SizeOfInitializedData**
初期化済みのデータサイズ - **SizeOfUninitializedData**
未初期化のデータサイズ - **AddressOfEntryPoint**
実行を始めるEntry Pointのアドレス
mainではない - **BaseOfCode**
コード領域のベースアドレス - **BaseOfData**
初期化済みのデータの開始アドレス
x86のみが持っている
###Windows-Specific Fields
リンカーとローダに必要な情報が含まれている
Details
ファイルがロードされると望ましいアドレス
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
エクスポート情報(.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
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を作りたいと思います.
##参考