PE判定ツール

はじめに

先日のエントリでも書きましたがPE勉強会#2に参加してきました。

「ただ学ぶだけでは勿体無い。得た知識を基に何か作らねば」と思い、こんなものを作ってみました。

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が書きたかっただけだったりもします。