PE判定ツール
PE判定ツール
「あるファイルがPEフォーマットか」を判定するツールをCで書いてみました。
参考
PE勉強会#2の資料より引用。PEヘッダについての説明。
1. 0000 からMZ の2 文字が入っているか確認。ただしこれだけではDOS のEXE と共通のためPE であるとは断定できない。ちなみにMZ はDOS 開発責任者Mark Zbikowski 氏のイニシャルだそうです。
2. 003C〜003F の4 バイトは、リトルエンディアンでPE ヘッダの開始位置を示している。
3. 2 のアドレスを見て、先頭にPE の2 文字が入っているか確認。その次に00 00 が続くか確認。この4 バイトがPE の印で、PE シグネチャと呼ばれている。
ソースコード
#include <stdio.h> // judge.c void usage( char **argv ) { printf( "usage : %s <binary file>\n", argv[ 0 ] ) ; return ; } int main( int argc, char **argv ) { FILE *fp ; int c ; unsigned int i = 0, head_addr = 0, valid = 0 ; if( argc != 2 ) { usage( argv ) ; return -1 ; } if( ! ( fp = fopen( argv[ 1 ], "rb" ) ) ) { printf( "failed to open file.\n" ) ; return -1 ; } while( ( c = getc( fp ) ) != EOF ) { if( i == 0 ) { if( c != 77 ) break ; } else if( i == 1 ) { if( c != 90 ) break ; } else if( i >= 60 && i <= 63 ) { head_addr += ( c << ( ( i - 60 ) * 8 ) ) ; } else if( head_addr ) { if( i == head_addr ) { if( c != 80 ) break ; } else if( i == head_addr + 1 ) { if( c != 69 ) break ; } else if( i == head_addr + 2 ) { if( c != 0 ) break ; } else if( i == head_addr + 3 ) { if( c == 0 ) valid = 1 ; break ; } else if( i > head_addr + 3 ) { break ; } } i++ ; } if( valid ) printf( "this file is PE.\n" ) ; else printf( "this file is not PE.\n" ) ; fclose( fp ) ; return 0 ; }
実行例
% gcc judge.c -o judge.exe
% ./judge.exe ./a.exe
this file is PE.
% ./judge.exe ./judge.c
this file is not PE.
ついでに16新数ダンプツール
ついでにこんなものも。バイナリファイルを16進数にダンプするツールです。
ソースコード
#include <stdio.h> #include <getopt.h> // dump.c void usage( char **argv ) { printf( "usage : %s [-s] <binary file>\n", argv[ 0 ] ) ; printf ( " -s display characters.\n" ) ; return ; } int main( int argc, char **argv ) { FILE *fp ; int c, opt ; unsigned int i = 0, display = 0 ; while( ( opt = getopt( argc, argv, "hs" ) ) != -1 ) { switch( opt ) { case 'h' : usage( argv ) ; return 0 ; case 's' : display = 1 ; break ; default : usage( argv ) ; return 0 ; } } if( argc != 2 && argc != 3 ) { usage( argv ) ; return -1 ; } if( ! ( fp = fopen( argv[ optind ], "rb" ) ) ) { printf( "failed to open file.\n" ) ; return -1 ; } while( ( c = getc( fp ) ) != EOF ) { if( i % 16 == 0 ) { printf( "%04X : ", i ) ; } printf( "%02X ", c ) ; if( display ) { if( c > 32 && c < 127 ) { printf( "%c ", c ) ; } else { printf( ". " ) ; } } if( i % 16 == 15 ) printf( "\n" ) ; i++ ; } if( i % 16 != 0 ) printf( "\n" ) ; fclose( fp ) ; return 0 ; }
実行例
% gcc dump.c -o dump.exe % ./dump.exe ./a.exe | head 0000 : 4D 5A 90 00 03 00 00 00 04 00 00 00 FF FF 00 00 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 0050 : 69 73 20 70 72 6F 67 72 61 6D 20 63 61 6E 6E 6F 0060 : 74 20 62 65 20 72 75 6E 20 69 6E 20 44 4F 53 20 0070 : 6D 6F 64 65 2E 0D 0D 0A 24 00 00 00 00 00 00 00 0080 : 50 45 00 00 4C 01 08 00 FA 3C 9C 4D 00 32 00 00 0090 : D7 02 00 00 E0 00 07 03 0B 01 02 15 00 1C 00 00 % ./dump.exe -s ./a.exe | head 0000 : 4D M 5A Z 90 . 00 . 03 . 00 . 00 . 00 . 04 . 00 . 00 . 00 . FF . FF . 00 . 00 . 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 L CD . 21 ! 54 T 68 h 0050 : 69 i 73 s 20 . 70 p 72 r 6F o 67 g 72 r 61 a 6D m 20 . 63 c 61 a 6E n 6E n 6F o 0060 : 74 t 20 . 62 b 65 e 20 . 72 r 75 u 6E n 20 . 69 i 6E n 20 . 44 D 4F O 53 S 20 . 0070 : 6D m 6F o 64 d 65 e 2E . 0D . 0D . 0A . 24 $ 00 . 00 . 00 . 00 . 00 . 00 . 00 . 0080 : 50 P 45 E 00 . 00 . 4C L 01 . 08 . 00 . FA . 3C < 9C . 4D M 00 . 32 2 00 . 00 . 0090 : D7 . 02 . 00 . 00 . E0 . 00 . 07 . 03 . 0B . 01 . 02 . 15 . 00 . 1C . 00 . 00 .
終わりに
まぁなんというか力技です。もっと綺麗に書けそうな気がします。標準入力で渡してパイプで処理できるようにしようかとも思ったのですが。
久々にCが書きたかっただけだったりもします。