2010年06月22日

Windows実行形式のMachineタイプを判別する方法

はてなブックマークに登録

粗忽な性格の筆者の場合、Windows 上であるプログラムの 32 ビット版・64 ビット版のふたつを開発していると、ときどき「あれ、このバイナリはどっちだっけ??」という状態になることがあります。開発を行っているプラットフォームが 32 ビットであればクロスコンパイルした 64 ビットバイナリをそのまま実行することはできないためパッと判別できますが、64 ビット環境だとかしこい仮想機構 (WOW64: Windows 32bit On Windows 64bit) のおかげで 32ビットコードもすんなり実行できてしまうので逆にちょっと面倒です。

64 ビット環境の場合は所定のバイナリを実行してタスクマネージャで確認すれば、32 ビットプロセスならプロセスイメージ名の末尾に「*32」の符丁が表示されるためそれで見分けることもできますが、わざわざそのためにプロセスを起動するというのも何だか妙な話だし、また、バイナリが DLL の場合にはその方法は使えません。好ましいのは「バイナリの内容をチェックして判別する」というもっとも単純なやり方だと思いました。また、そういうツールがあれば 32 ビット環境でも 64 ビット環境でも所定のバイナリを手軽に識別できるはずです。

Windows に標準でそういう機能がないかとざっと見渡したところではどうも見当たらず、ネット上にもそれらしいツールが見つからなかったため簡単なコードを書いてみることにしました。 だいぶ前にこのブログに Windows 実行形式のヘッダ構成を書いたことがあり、その中のどこかにそういう情報があった記憶がありました。あらためて探してみるとこれでした。
MSDN: IMAGE_FILE_HEADER Structure
|Members
|  Machine
|    IMAGE_FILE_MACHINE_I386  (0x014c) x86
|    IMAGE_FILE_MACHINE_IA64  (0x0200) Intel IPF
|    IMAGE_FILE_MACHINE_AMD64 (0x8664) x64
そのまんまですね^^;

これらの情報をもとに C 言語で短い Windows プログラムを書きました。 引数で渡されたファイルを読んで実行形式であれば Machine タイプを表示する内容です。 ビルドずみのバイナリ (x86 & x64)
筆者は SendTo フォルダにショートカットをコピーし右クリックメニューから呼び出して使っています。
//
// 引数で渡された Windows 実行形式ファイルの Machine タイプを判別する
//
#if !defined(UNICODE)
#define UNICODE
#define _UNICODE
#endif
 
#include <windows.h>
#include <stdio.h>
#include <sys/stat.h>
 
#pragma comment(lib, "user32.lib")
 
// IMAGE_FILE_HEADER から Machine タイプを取得
int MyCheckMachineType(LPCWSTR pszFileName)
{
  FILE *fp;
  struct _stat st;
  IMAGE_DOS_HEADER idh;
  IMAGE_NT_HEADERS inh;
 
  if (_wstat(pszFileName, &st) < 0 ||
    _wfopen_s(&fp, pszFileName, L"rb") != 0) {
    return -1;
  }
  if (fread(&idh, sizeof(IMAGE_DOS_HEADER), 1, fp) < 1 ||
    idh.e_magic != IMAGE_DOS_SIGNATURE || // "MZ"
    idh.e_lfanew <= 0 || idh.e_lfanew >= st.st_size) {
    fclose(fp);
    return -2;
  }
  if (fseek(fp, idh.e_lfanew, SEEK_SET) != 0 ||
    fread(&inh, sizeof(IMAGE_NT_HEADERS), 1, fp) < 1 ||
    inh.Signature != IMAGE_NT_SIGNATURE) { // "PE\0\0"
    fclose(fp);
    return -3;
  }
  fclose(fp);
 
  switch (inh.FileHeader.Machine) {
  case IMAGE_FILE_MACHINE_I386:
    return 0;
  case IMAGE_FILE_MACHINE_AMD64:
    return 1;
  case IMAGE_FILE_MACHINE_IA64:
    return 2;
  }
  return 3; // Unknown
}

// 現プロセスが WOW64 下で実行中のプロセスか
BOOL MyIsWow64Process()
{
  typedef BOOL (WINAPI *DEC_ISWOW64PROCESS)(HANDLE, BOOL*);
  BOOL bWow64Process = FALSE;
  DEC_ISWOW64PROCESS pIsWow64Process = (DEC_ISWOW64PROCESS)
    GetProcAddress(GetModuleHandle(L"Kernel32.dll"),"IsWow64Process");
  if (!pIsWow64Process ||
    !pIsWow64Process(GetCurrentProcess(), &bWow64Process)) {
    return FALSE;
  }
  return bWow64Process;
}
 
int APIENTRY wWinMain(HINSTANCE hInst, HINSTANCE hPrevInst,
            LPWSTR lpCmdLine, int nCmdShow)
{
  int sts;
  WCHAR *pName;
  WCHAR *m[] = { L"x86", L"x64", L"IA64", L"Unknown"};
  WCHAR *e[] = { L"broken?", L"not executable", L"open error"};
#ifdef WIN32
  if (MyIsWow64Process()) {
    MessageBox(NULL,
      L"WOW64 によるリダイレクトの影響を避けるために\n" \
      L"Windows x64 環境では x64 版を使用して下さい", 
      L"Warning", MB_OK);
  }
#endif
  if (__argc < 2) {
    return 0;
  }
  sts = MyCheckMachineType(__wargv[1]);
  pName = wcsrchr(__wargv[1], L'\\') + 1;
  if (sts >= 0 && sts <= 3) {
    MessageBox(NULL, m[sts], pName, MB_ICONINFORMATION|MB_TOPMOST);
  } else {
    MessageBox(NULL, e[sts+3], pName, MB_TOPMOST);
  }
  return 0;
}

たとえばこのプログラムでこのプログラム自身の x86, x64 バイナリをチェックすると図のように表示されます。

(tanabe)
klab_gijutsu2 at 19:59│Comments(0)TrackBack(0)win 

トラックバックURL

この記事にコメントする

名前:
URL:
  情報を記憶: 評価: 顔   
 
 
 
Blog内検索
Archives
このブログについて
DSASとは、KLab が構築し運用しているコンテンツサービス用のLinuxベースのインフラです。現在5ヶ所のデータセンタにて構築し、運用していますが、我々はDSASをより使いやすく、より安全に、そしてより省力で運用できることを目指して、日々改良に勤しんでいます。
このブログでは、そんな DSAS で使っている技術の紹介や、実験してみた結果の報告、トラブルに巻き込まれた時の経験談など、広く深く、色々な話題を織りまぜて紹介していきたいと思います。
最新コメント