LoginSignup
8
1

More than 3 years have passed since last update.

PEを理解したかったのでreadpeってものを作った

Last updated at Posted at 2019-12-19

はじめに

最近私は低レイヤーの勉強をしています.

ある時, IATがおかしいと実行時に怒られてしまいました.
私はその時, IATを直すことができなかったのでPEを理解したいと思い,
readpeを自作しようと思いました.

readpeELFフォーマットを見やすくするための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について説明したいと思います.
readpex86/x64に対応しています.

ソースコードはここから

hello_x86.exe

今回の解析対象はhello_x86.exeです.
コンパイル前のファイルは下記の通りです.

hello_x86.c
#include <stdio.h>

int main() {
    printf("Hello,World!\n");
    return 0;
}

これをcl hello_x86.c -o hello_x86コマンドでコンパイルします.

-h

COFF Headerを出力します.

これを見ることで, このファイルがいつコンパイルされたかなどの
基本的な情報がわかります.

このオプションでは見やすいようにするために,
フラグの部分を赤(1)と緑(0)で分けした.

キャプチャ.PNG

File Header

- Machine
    どのマシーンを対象として作られているかを示す
    基本的には0x8664(x64), 0x014C(x86)
  • 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ごとに出力できるようにしたいです.

キャプチャ.PNG

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の部分が見にくいのですがいい方法が思い浮かばなかったため,
とりあえずはそのままにしました.

キャプチャ.PNG

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を作りたいと思います.

参考

8
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
1