Windows NT 4.0 をいまさら VMware 上で動かす
祝 Windows NT 4.0 リリース 30 周年
AI バブルとかいう詐欺師連中がおっぱじめた戦争のせいで中古の DDR3 メモリですら値上がりする狂気の時代である。 スマホ買替で同じ値段でメモリ半減しかねんのに 2024 年発売の機種ですらアプデ無しサポート終了ブートローダーアンロック禁止って何だよ Hell Moto (ネットリ)はクソが。
こうなると OS も 64bit から 32bit そして最後は 8bit まで戻っていくのではないか、メモリなんて 640KB もあれば問題ないのである(ビル・ゲイツ言ってない)。
まだ異常者どもが fj.news.usage に隔離されてた昔を懐かしむべく表題の件やってみたが、Windows 10 のフラットデザインなんて NT 3.51 回帰したようなもんだし逆に新しく感じてしまうのである。 GUI の価値はプッシュボタンで決まるとは 粉末氏の言葉だが、それを今更ながら噛み締めるのである。
2007 年頃に一度全部 SP6a + SRP 以降にリリースされたパッチはすべて保管してあったのだが、今からInternet Archive で収集したい暇人のために暇人の俺が直リンのリストを まとめた のであなたも暇人であったらどうか活用して頂きたい。
ただいくつかもうキャッシュにすら残ってないファイルもあるし、i386 版以外は俺も保管してなかったから今から揃えるのは困難かもしれない。 NEC-9800はまだ誰かが持ってるだろうけど Alpha/MIPS/PPC は無理やろうなぁあと Terminal Server Edition とか、まぁ持ってないからどうでもいい。
不具合発生
ところがですね、何の操作もしていないのに CPU 使用率が 100% にずっと貼りついてしまうという謎現象が起きるのである。
おかげで大昔のトラウマが蘇ってしまった、とある地方スーパーの商品管理端末を作ってた時これが Windows NT 4.0 で動いてたんだがバッテリーがすぐ切れて仕事にならんってクレーム入ってな。 まだスマホやタブレットのような小型端末は使い物にならずラップトップ(死語)に産業用鉛蓄電池つなげて台車にドンと乗せてガラガラ押してた時代である。
そもそも Windows NT 4.0 って電源管理が ACPI 以前の実装なので各メーカーがカスタマイズした hal.dll の出来が悪いととにかくバッテリーが持たなかった思い出。 まぁクレームにまでエスカレートしたのは WaitForMultipleObjects の使い方間違えてビジーループしたアプリの暴走のせいなんだけどね。
今時はメニーコアなので CPU のひとつやふたつ持ってかれても気にならんかもしれんが、いまだに 2 コア 4 スレッドの SandyBridge おじさんにはちょっとツライんだ。 しかし世の中にはずいぶんと暇な人がいて hal.dll にバイナリパッチ当てて CPU 100% 問題を回避してるそうなのだ。
hal.dll にバイナリパッチを適用する
まずは %SYSTEMROOT%\System32 以下の hal.dll を開発マシンにコピーする、あるいは SP6a を展開してそこから hal.dll を引っこ抜くでもいい。 その場合は自分はインストール時に F5 押して SMP 対応カーネルに入れ替えてるので halmps.dll が書き換え対象となるので注意。
バイナリ編集作業の道具にはお金が余ってて仕方がない極悪人なら IDA Pro でも買うといい。 自分は無銭おじさんなので以前にちょっと紹介したフリーでオープンソースの Ghidra を使いますね。
手順を細かく説明する気はないので手短に、まず Ghidra でプロジェクトを作成し hal.dll をインポートしてバイナリ解析を行う、操作方法はマニュアル読んでくれ。
解析が終わると Listing というバイナリをアセンブラソース風に表示するウィンドウが現れるので、Search -> Program Text で HalProcessorIdle ラベルを探す。
- Search for … “HalProcessorIdle”
- Search Type … Listing Display
- Fields … Label にチェック
を検索ダイアログで指定し実行すると以下の疑似コードがヒットする。
**************************************************************
* FUNCTION *
**************************************************************
undefined __stdcall HalProcessorIdle(void)
assume FS_OFFSET = 0xffdff000
undefined <UNASSIGNED> <RETURN>
0x64c4 36 HalProcessorIdle
Ordinal_36 XREF[2]: Entry Point(*), 8001a114(*)
HalProcessorIdle
800164c4 fb STI
800164c5 c3 RET
800164c6 cc ?? CCh
800164c7 cc ?? CCh
見ての通り HalProcessorIdle ラベルの処理は STI 命令をよんで割り込み許可フラグを建てたら RET で呼び出し元に戻るだけの関数なんだが、こいつを
- STI 命令に続けて HLT 命令もよぶ
- CPU は次回の割り込み発生までお休みする
- これで CPU が常時 100% ブン回ること無いよね
と書き換えてやる、うーんなんともシンプルなワークアラウンド。
おあつらえ向きに 800164c6 ~ 800164c7 はアライメント調整(タイヤ館コピペ)のフィラーなので潰しても問題ない。 ここで HLT 命令をねじこむためにどっか JMP 命令で飛ばす必要があったりするとめんどくさいけど今回はイージーですな。
**************************************************************
* FUNCTION *
**************************************************************
undefined __stdcall HalProcessorIdle(void)
assume FS_OFFSET = 0xffdff000
undefined <UNASSIGNED> <RETURN>
0x64c4 36 HalProcessorIdle
Ordinal_36 XREF[2]: Entry Point(*), 8001a114(*)
HalProcessorIdle
800164c4 fb STI
800164c5 f4 HLT
800164c6 c3 RET
800164c7 cc ?? CCh
以上のように変更するだけでよい、ちなみに Listing から編集するより Window -> Bytes でバイナリエディタ開いてそっちで編集した方が作業は楽である。 えーっと Ghidra 使う意味あったんですかこれ(無いです)。
変更を保存したら File -> Export Program で hal.dll をエクスポートし適当な場所に保存する、この際に Format は Original File を選択すること。 この時 Ghidra は PE/COFF のチェックサムを再計算してくれるような親切さは持ち合わせておらんようなので別途 Visual Studio でも用意して
editbin /release hal.dll
と叩いて hal.dll のヘッダ情報を修正しないと壊れた PE/COFF と判定され hal.dll は動作しない。 この為だけにわざわざ Visual Studio Community を入れるのがメンドい人はいくらでも代替となるフリーの軽量なツールがあるので探してくれ。
改変した hal.dll をインストールする
%SYSTEMROOT%\System32 に放り込んで上書きして再起動でいい、サービスやドライバと違って hal.dll は起動時にメモリ上に置かれてるので使用中として上書き禁止になることもないのでな。
念には念を入れ安全策をとるならば %SYSTEMROOT%\System32\halvmw.dll としてコピーし、%SYSTEMDRIVE%\boot.ini を編集して以下の起動オプションを追加する。
multi(0)disk(0)rdisk(0)partition(1)\WINNT="Windows NT Workstation Version 4.00 VMware" /hal=halvmw.dll
boot.ini には読み取り専用属性がついてるので外しておく必要がある事には注意。
再起動してブートメニューから「Windows NT Workstation Version 4.00 VMware」を選択して起動する。 クラッシュせずホストのVMware Workstation VMX プロセスの CPU 使用率が劇的に低くなってれば成功である。
最後に
2000/XP では CPU 100% 問題は発生しないのだがそちらの hal.dll の HalProcessorIdle では NT 4.0 と同様 HLT 呼んでないんだよね。 なので CPU がお休みしてくれない真の原因は別の個所にあるっぽい。
これ以上の調査はリバースエンジニアリングは日本の法律上問題ないとはいえ EULA では明確に禁止されているので著作権的に俺の書くコードが汚染されかねないのでヤメ。 あいつらは Copilot やら Claude で俺らのオープンソースなコードを無断で学習してライセンスロンダリングで阿漕な商売してんのに何様なんだろうな。
たかが検索エンジン屋風情ににインデックス作るくらいならリーチに有利だからと著作権法でお目こぼしを与えたら、増長して勝手に AI 要約とやらでリーチすら消滅した地獄のようなインターネットにもはや用は無い。 お前のものは俺のもの、砂場から出て行けというのなら喜んで出ていきますよ俺は、 だから銃をとって叫べ 。