日記移転先(゜▽゜)

という名前の適当な日記ブログ


| Home | Index | Links | About |

Entries

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

EXEヘッダ/PEヘッダ/NEヘッダ


PEヘッダ以外の扱いが適当な所が多いような気がしたのでメモ書きしてみる。


PEヘッダの解説の為のEXEヘッダの扱いは適当だ。
まあMicrosoftのPECOFFの資料からして省略してるし(´д`;)

とりあえずPE関連のリンクから。

とりあえず基本はMicrosoftの資料。x64なPE32+も書いてる。
次のリンクは日本語訳(?)。元になる版が古いようだがMSの英語だけを読むより楽になる。
CodeZineのは順に解説してるのでまあこれから読むのが一番か?作る人の視点で書いてる為か
解析の資料に読むにゃここだけじゃ解り難い箇所がある。

typedef struct _IMAGE_DOS_HEADER {  // DOS .EXE header
    WORD   e_magic;                 // +00 Magic number
    WORD   e_cblp;                  // +02 Bytes on last page of file
    WORD   e_cp;                    // +04 Pages in file
    WORD   e_crlc;                  // +06 Relocations
    WORD   e_cparhdr;               // +08 Size of header in paragraphs
    WORD   e_minalloc;              // +0A Minimum extra paragraphs needed
    WORD   e_maxalloc;              // +0C Maximum extra paragraphs needed
    WORD   e_ss;                    // +0E Initial (relative) SS value
    WORD   e_sp;                    // +10 Initial SP value
    WORD   e_csum;                  // +12 Checksum
    WORD   e_ip;                    // +14 Initial IP value
    WORD   e_cs;                    // +16 Initial (relative) CS value
    WORD   e_lfarlc;                // +18 File address of relocation table
    WORD   e_ovno;                  // +1A Overlay number
    WORD   e_res[4];                // +1C Reserved words
    WORD   e_oemid;                 // +26 OEM identifier (for e_oeminfo)
    WORD   e_oeminfo;               // +28 OEM information; e_oemid specific
    WORD   e_res2[10];              // +2A Reserved words
    LONG   e_lfanew;                // +3E File address of new exe header
  } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
PEヘッダの解説で使われるIMAGE_DOS_HEADERはWinNT.hにある↑の通り。サイズは0x40=64バイト。 NEヘッダを持つファイルも同じくe_lfanewに位置が入るので先頭64バイトは固定と見てよい。 だが実際のDOSでは0x1A=28バイト以降は上の構造体に入るような内容にはなっていない。 28バイト以降はリロケーションテーブル(とリンカが埋め込んだ少量のデータ)がここに重なる事が多い。
LHAの自己解凍書庫の判別用文字列辺り等もこの領域と重なる。

要はPE/NEヘッダ付きの最近の物とDOS時代のEXEファイルを一つのデータの塊として見たい場合 それぞれに対応させる必要がある。冒頭には書かなかったが今回の目的はそれだ。
EXEファイル1くくりで解説するのに上の構造体だけを挙げるだけで 「EXEファイルの説明」としている幾つかのサイトはよくもまあこんなものでと(略
※先に上げたサイトの事ではない。それらはPE関連の解説だ。

DOSでのこの先頭28バイト部分の資料は探してみると意外と少ない。 構造についてはIMAGE_DOS_HEADERに含まれているしRalf Brown's Interrupt Listにもある (DLした物ならINTERRUP.Gファイル辺り)。
ざっと調べた所DOSの内部構造を扱った書籍等はPSPとかには詳しいのになぜかEXEファイルの頭の所は書いてないってのが多かった。 まともに書いてるのを一つ挙げるとこれ

DOS時代だと明確に名前で定義されたものが一般的でないので上の構造体の名前を使う事にする。
で、DOSのEXEのファイルサイズを得る場合'MZ'に続く+02のe_cblpと+04のe_cpを使う。
512を1ページとしてc_cpがページ数、e_cblpは最終ページが1ページ未満の場合のバイト数となる。
  filesize = idh.e_cp*512 +idh.e_cblp +((idh.e_cblp)? -512 : 0);
で計算できる。
安易にe_cpを常に-1すると512バイト単位丁度のファイルに対して512バイトの計算違いが生じる。
※DOSのBCで512の倍数のサイズになる実行ファイルを作成してみたがe_cblpは0になった。

e_cp,e_cblpによるファイルサイズの計算はPE/NEヘッダ付きのファイルには使えない。
以下はPEヘッダを含むファイルの例。(何故か古いnotepad.exe)
ADDR: +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F 0123456789ABCDEF
0000: 4D 5A 90 00 03 00 00 00 04 00 00 00 FF FF 00 00 MZ..............
0010: B8 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00 ク.......@.......
0020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0030: 00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 00 ................
0040: 0E 1F BA 0E 00 B4 09 CD 21 B8 01 4C CD 21 54 68 ..コ..エ.ヘ!ク.Lヘ!Th
0050: 69 73 20 70 72 6F 67 72 61 6D 20 63 61 6E 6E 6F is program canno
0060: 74 20 62 65 20 72 75 6E 20 69 6E 20 44 4F 53 20 t be run in DOS
0070: 6D 6F 64 65 2E 0D 0D 0A 24 00 00 00 00 00 00 00 mode....$.......
0080: 50 45 00 00 4C 01 05 00 A4 52 5A 35 00 00 00 00 PE..L...、RZ5....
0090: 00 00 00 00 E0 00 0E 01 0B 01 03 0A 00 40 00 00 .............@..
00A0: 00 70 00 00 00 00 00 00 CC 10 00 00 00 10 00 00 .p......フ.......
00B0: 00 50 00 00 00 00 40 00 00 10 00 00 00 10 00 00 .P....@.........
00C0: 04 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 ................
00D0: 00 D0 00 00 00 04 00 00 A7 54 01 00 02 00 00 00 .ミ......ァT......
00E0: 00 00 10 00 00 10 00 00 00 00 10 00 00 10 00 00 ................
00F0: 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 ................
e_cblp=0x0090,e_cp=0x0003であるがこれで計算すると意味の無いサイズになる。
MSのリンカを使うと多分90 00 30 00固定。Borland系だとまた別の数字になる。
e_cpはDOSでの実行部分を含むサイズ以上になっている事は期待してよさそうだ。
e_cblpはリンカのバージョンかとも思ったがPEヘッダの方にあるリンカのバージョンとずれる場合もある。
※色々と検証してみたらDOS実行部分/PEヘッダの境を指す綺麗な値が入ったEXEファイルもあった。
e_cparhdrがEXEヘッダのパラグラフ数だがPE/NEヘッダへのe_lfanewが有効なら4以上のはず(?)。

上のダンプは古いnotepad.exeなので無いのだが最近の物には間にまたよく解らないデータが入っている。
4バイトで区切ると近い数値なのでリンカが何かのアドレスでも吐いてるのかとも思ったのだが違うようだ。
詳しく調べなかったが以下辺りで触れられているのを発見したのでリンク。
#リンク切れ対策にリンク先のリンク先も併記。

PEヘッダ型のファイルのサイズはPEヘッダ開始位置とPEヘッダのサイズとセクション領域のサイズの合計で得られる。
  • e_lfanew
  • 4('PE00')+sizeof(IMAGE_FILE_HEADER)+追加ヘッダサイズ(ifh.SizeOfOptionalHeader)
でセクション領域の位置が判るのでセクションヘッダ内の一番最後のIMAGE_SECTION_HEADERを見る。
そこから得られる位置にサイズを足せばファイルサイズとなる。
「最後」はIMAGE_FILE_HEADERにセクション数(NumberOfSections)があるのでそこから得る。
0178: [.text ] = 00001000 ~ 00005000
0180: [.data ] = 00005000 ~ 00006000
0188: [.idata] = 00006000 ~ 00007000
0190: [.rsrc ] = 00007000 ~ 0000C000
0198: [.reloc] = 0000C000 ~ 0000D000←これ
この辺りのヘッダに含むテーブルって必ずアドレス若い順に入ってるかどうかが気になるのだがどうなんだろう? 色々調べるとデータ上はokだがアドレスの配置がおかしい場合ウイルス関連かもしれないので律儀に従いすぎるのも駄目なのかな?


PEヘッダを含む/含まないEXEファイルの処理は大体これで問題ないがWindows3.1時代の(?)NEヘッダ型ってのもある。 今時だとVector辺りに拾いに行かないと遭遇しそうにもないようだがXPでもfonts内に古いフォント形式で残っていたりもする。
とりあえずNEヘッダを概要以上に踏み込んで触れてる所は少ない(゜д゜)
今だとWindows3.1関連の資料を漁るか英語のサイトを読むしかないようだ。
調べてみるとどうしても必要じゃなきゃ触りたくないような構造だった(ToT

まずWinNT.hに構造体がある。IMAGE_OS2_HEADERだ。
typedef struct _IMAGE_OS2_HEADER {      // OS/2 .EXE header
    WORD   ne_magic;                    // Magic number
    CHAR   ne_ver;                      // Version number
    CHAR   ne_rev;                      // Revision number
    WORD   ne_enttab;                   // Offset of Entry Table
    WORD   ne_cbenttab;                 // Number of bytes in Entry Table
    LONG   ne_crc;                      // Checksum of whole file
    WORD   ne_flags;                    // Flag word
    WORD   ne_autodata;                 // Automatic data segment number
    WORD   ne_heap;                     // Initial heap allocation
    WORD   ne_stack;                    // Initial stack allocation
    LONG   ne_csip;                     // Initial CS:IP setting
    LONG   ne_sssp;                     // Initial SS:SP setting
    WORD   ne_cseg;                     // Count of file segments
    WORD   ne_cmod;                     // Entries in Module Reference Table
    WORD   ne_cbnrestab;                // Size of non-resident name table
    WORD   ne_segtab;                   // Offset of Segment Table
    WORD   ne_rsrctab;                  // Offset of Resource Table
    WORD   ne_restab;                   // Offset of resident name table
    WORD   ne_modtab;                   // Offset of Module Reference Table
    WORD   ne_imptab;                   // Offset of Imported Names Table
    LONG   ne_nrestab;                  // Offset of Non-resident Names Table
    WORD   ne_cmovent;                  // Count of movable entries
    WORD   ne_align;                    // Segment alignment shift count
    WORD   ne_cres;                     // Count of resource segments
    BYTE   ne_exetyp;                   // Target Operating system
    BYTE   ne_flagsothers;              // Other .EXE flags
    WORD   ne_pretthunks;               // offset to return thunks
    WORD   ne_psegrefbytes;             // offset to segment ref. bytes
    WORD   ne_swaparea;                 // Minimum code swap area size
    WORD   ne_expver;                   // Expected Windows version number
  } IMAGE_OS2_HEADER, *PIMAGE_OS2_HEADER;

そしてMicrosoftの資料。
これがまた読みにくい。37h以降がReservedとなっているので古いかも?
Interrupt Listも一緒に読むと良い。データが一部分けられたりして項目数が違ったりする点に注意か?

今度はapp932.fon(Terminalフォント)をサンプルに見てみる。
#適当な実行ファイルじゃないのは後述するテーブル部分が小さい為。
ADDR: +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F
0080: 4E 45 05 01 16 01 02 00 0B A5 8F 5B 00 83 00 00
0090: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00A0: 23 00 40 00 40 00 0C 01 16 01 16 01 98 01 00 00
00B0: 00 00 04 00 00 00 02 08 1E 00 64 00 00 00 0A 03
位置はe_lfanewで得る。64バイト分読む。

64バイトの構造体よりその次にあるテーブルのアドレスが取得/計算できる。
0080: [0040] Segmented Exe Header
00C0: [0000] Segment Table
00C0: [00CC] Resource Table
018C: [000A] Resident Name Table
0196: [0000] Module Reference Table
0196: [0000] Inported Names Table
0196: [0002] Entry Table
0198: [0023] Non-Resident Name Table
アドレスを並べて差を取ってサイズも併記すると↑のようになる。
構造体に含むオフセットは大体'NE'部分からだが一部'MZ'からのオフセットがあるので注意。
テーブル以降に続くデータのサイズを得るならResource Tableを調べる。

ADDR_____Shift__TypeID__Count_Offset__Size___Flag__ResID___Res1___Res2__
000000C0: 0004                                                          = x16
000000C2:        8007   0001                               0000   0000
000000CA:                      0020   005E   0C50   00C4   0000   0000  [00000200:000005E0]
000000D6:        8008   000C                               0000   0000
000000DE:                      0082   00C0   1C30   8001   0000   0000  [00000820:00000C00]
000000EA:                      0142   0109   1C30   8002   0000   0000  [00001420:00001090]
000000F6:                      024B   00C9   1C30   8003   0000   0000  [000024B0:00000C90]
00000102:                      0314   0109   1C30   8004   0000   0000  [00003140:00001090]
0000010E:                      041D   028A   1C30   8005   0000   0000  [000041D0:000028A0]
0000011A:                      06A7   0260   1C30   8006   0000   0000  [00006A70:00002600]
00000126:                      0907   0099   1C30   8007   0000   0000  [00009070:00000990]
00000132:                      09A0   00C9   1C30   8008   0000   0000  [00009A00:00000C90]
0000013E:                      0A69   0119   1C30   8009   0000   0000  [0000A690:00001190]
0000014A:                      0B82   016A   1C30   800A   0000   0000  [0000B820:000016A0]
00000156:                      0CEC   030A   1C30   800B   0000   0000  [0000CEC0:000030A0]
00000162:                      0FF6   03AA   1C30   800C   0000   0000  [0000FF60:00003AA0]
0000016E:        8010   0001                               0000   0000
00000176:                      13A0   0020   0C30   8001   0000   0000  [00013A00:00000200]
00000182: 0000
#↑は面倒なのでWORD列扱いにした為2つに分けたが資料上のリザーブ部分はDWORD。
Resource Table内のテーブルは頭から見ていくしかない。最後のテーブルは0000になる。
この後に文字列も入っているとあるが無い場合もある。
ここに含まれるオフセットはファイル先頭からの位置になる。

最後のテーブル部分のオフセットとサイズをシフトして足せばファイルのサイズが得られる。


以上でDOSの実行ファイル、PE/NEヘッダを含むファイルのファイルサイズ部分は取得できる。
実際の用途として'MZ'ヘッダを持つファイルに連結された物があるかの調べたり
実行ファイルが途中で切れていたり後ろにゴミが付いてないかの調べるのに使う。

後ろにデータが付いてるものとしては主に1ファイルで解凍できる自己解凍書庫等がそうだ。
派生物として1ファイルのインストーラ型の実行ファイルも多くは同様。
他にはHSPで生成したも実行ファイルなんかもそうだ。
それらは実行ファイルに埋め込まれているのではなく後ろに連結したデータを参照する形で利用している。


PE/NE型ファイルのDOS実行部分の逆アセ結果やら等もあるんだが長くなったので今回は省略。



新規
←管理者にだけ表示する場合チェック



トラックバック用URL:
http://r2089.blog36.fc2.com/tb.php/119-9cd3e546

Appendix

profile

那駕〇

Author:那駕〇

recent entries

アクセスの多い記事

過去の記事


義援金募集

FC2「東北地方太平洋沖地震」義援金募集につきまして

ブロとも申請フォーム

この人とブロともになる

カウンタ

※ここより下の部分に掲示板とかまとめとかいうリンクがあってもそれはfc2blogの広告なので当blog内コンテンツではないです(゜д゜)
まとめ
....
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。