VC++11製バイナリをXPで動かす

2012年4月1日現在、VisualC++11 Betaで作成したバイナリはXPで動作しません。
今回は、それを強引にXPで動くようにしてしまう方法を紹介します。

なぜXPで動かないのか?

理由は簡単で

  • 動作対象OSがVista以上
  • ランタイムでVista以降のAPIを使用

だから。

動作対象OSをXPに変更

exeファイルには動作対象のOSを指定するサブシステムバージョンというものが存在し、それに満たない環境では動作しないようになっています。
本来ならリンカオプションのsubsystemで変更できるのですが、VC++11からはXP(5.1)以下を選択できません。
なので、強引にexe冒頭で指定されているサブシステムバージョンを5.1に書き換えることで回避します。

具体的には
ここを05 00 01 00と書き換えるとXP以上という意味になります。

書き換えている場所について詳しく知りたい人は、IMAGE_NT_HEADERSのOptionalHeader.MajorSubsystemVersionとOptionalHeader.MinorSubsystemVersionでググってください。

Windowsのバージョン番号はwikipediaの「Windows のタイムライン」を参照

Vista以降のAPI呼び出しを止める

動作対象のOSをXP以上にしたとしても、XPに存在しないAPIを呼んでいては起動すらできません。
これを回避する方法はいくつかあるのですが、ここではダミーのKernel32.dllを作成し、そこに必要なAPIを独自実装することで回避することにします。

exeファイルには当然リンクするべきdll名が記述されています。
そのうちKERNEL32.dllを書き換え、ダミーのDLL名(ここではKernelXP.dllとします)に変更し、そのDLL越しにKERNEL32.dllを呼ぶように細工します。
これは単にexeファイル内からKERNEL32.dllという文字列を探し、書き換えるだけで可能です。

つぎにダミーのDLLの作り方ですが、まず完成品がこちら。
KernelXP.zip

作り方としては、XPのKERNEL32.dllでエクスポートされている関数の一覧を作り
※エクスポート関数一覧の取得方法はDllFuncList.zipなどを参照
それらをすべてKERNEL32.dllへ受け流すだけのDLLを作成。
その後exeファイルをXPで実行してみて、APIが見つからないエラーが出るたびに、そのダミー実装を作るを繰り返すだけ。
詳しくは、DLLインジェクションやダミーDLLなどでググると見つかるかと思います。

終わりに

今回はダミーのDLLを経由させる方法で実現しましたが、一般的にはIATを書き換える方が多いようです。
いつかそちらについても言及してみたいなと思いつつ、今回はここまで。

th123_aiVer0.95リリース

変更点の大部分をすでに覚えていないのだが、何とか書き出してみる。

Ver0.95

  • get_obj_dataで返るHPがおかしかった不具合の修正
  • 環境変数act_blockの追加
  • コンパイル環境の変更
  • get_special_dataの13と14が動作するように
  • PANIC後の再読み込みが機能していなかったのを修正
  • 非想天則1.10aに対応

get_obj_dataはたぶん何か計算ミスってたんだと思います。

act_blockは従来までの*_frame系がリセットされて判別できない問題に対応するため
frameがリセットされるたびにインクリメントされている値を入れました。
ちなみに、自力計算ではなくth123.exeの中から取得しています。

コンパイル環境は何度か変えました。
最新はVC++11Beta Ultimate。
VC++11ではXP向けバイナリは作れないんですが、無理やり力技で何とかしてます。

get_special_dataはどうやったかは覚えてないけど動くようになったらしいです、たぶん。

PANIC後の再読み込みがなぜか動いてなかったので修正。
なぜかファイル名のリセットがかかっていて、再読み込みしようとして失敗して落ちるというアホかましてました。

1.10a対応はちゃんとアドレスサーチして入れました。
が、結果は有志の方が作ってくれたiniとほぼ一致していたので無駄でしたね。

get_hitareaにバグあるらしいので、近いうちに直した版をリリース予定。
一週間以内には出るんじゃないかな。
たぶん、たぶんね。