Windows プログラムに埋め込まれた MS-DOS スタブのメッセージを表示する

MS-DOS スタブの「This program cannot be run in DOS mode.」というメッセージを表示するプログラムを作ります。

動作確認環境

  • Windows 10 Home 21H1, 64bit
  • Visual Studio Community 2019

プログラムの作成

既存のほとんどのプログラムには、0x4e バイト目から「This program cannot be run in DOS mode.」という文字列が埋め込まれています。

例 1. メモ帳

4D 5A 90 00 03 00 00 00 04 00 00 00 FF FF 00 00  MZ..............
B8 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00  ........@.......
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00 00 00 00 00 00 00 00 00 00 00 00 F8 00 00 00  ................
0E 1F BA 0E 00 B4 09 CD 21 B8 01 4C CD 21 54 68  ........!..L.!Th
69 73 20 70 72 6F 67 72 61 6D 20 63 61 6E 6E 6F  is program canno
74 20 62 65 20 72 75 6E 20 69 6E 20 44 4F 53 20  t be run in DOS 
6D 6F 64 65 2E 0D 0D 0A 24 00 00 00 00 00 00 00  mode....$.......
<後略>

例 2. Excel

4D 5A 90 00 03 00 00 00 04 00 00 00 FF FF 00 00  MZ..............
B8 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00  ........@.......
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00 00 00 00 00 00 00 00 00 00 00 00 28 01 00 00  ............(...
0E 1F BA 0E 00 B4 09 CD 21 B8 01 4C CD 21 54 68  ........!..L.!Th
69 73 20 70 72 6F 67 72 61 6D 20 63 61 6E 6E 6F  is program canno
74 20 62 65 20 72 75 6E 20 69 6E 20 44 4F 53 20  t be run in DOS 
6D 6F 64 65 2E 0D 0D 0A 24 00 00 00 00 00 00 00  mode....$.......
<後略>

この文字列は、MS-DOS で当該プログラムを起動したときに実行される「MS-DOS スタブ」の一部です。

MS-DOS のエミュレーターである DOSBox (https://www.dosbox.com/) からメモ帳を起動すると、MS-DOS スタブが実行され、「This program cannot be run in DOS mode.」と表示されます。「このプログラムは(Windows プログラムなので)DOS モードからは実行できません」という意味です。

Windows では MS-DOS スタブは利用されませんが、メモリには読み込まれます。具体的には、プログラムのベースアドレス + 0x4e に配置されます。

プログラムのベースアドレスは Windows の ASLR 機能によりランダム化されますが、実行時にプログラム内から GetModuleHandle(NULL) で取得できます。
したがって、以下のプログラムで目的の文字列が表示できます。

// DosModeMsg.c
#include <stdio.h>
#include <windows.h>

int main()
{
    char *p = (char *)GetModuleHandle(NULL);
    printf("%s", p + 0x4e);

    return 0;
}

ビルドして、Windows のコマンドプロンプトから実行します。

「This program cannot be run in DOS mode.」は期待通りですが、次行に「$」が表示されてしまいました。これは、文字列を出力する MS-DOS のファンクションコール (ah=09h, int 21h) の「文字列の末尾は ‘$’」という仕様に合わせて文字列が定義されているためです。
そこで、「$」の直前までの 42 文字を出力するよう、printf の書式指定子を変更します。

    printf("%.42s", p + 0x4e);

不要な $ が消えました。

以上、だから何だという話ではありますが、MS-DOS スタブのメッセージを表示する Windows プログラムでした。

なお、MS-DOS スタブ自体を実行することはできません。MS-DOS スタブが文字列の出力に使っている int 21h を Windows から実行すると、0xC0000005 (Access violation) の例外が発生します。