0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

C言語でゼロから作るMS-DOS EXE

Last updated at Posted at 2025-09-22

初めに

MS-DOS上で動作するEXEファイルの構造を理解するために、今回はC言語を使ってEXEのバイナリを生成してみます。

Windows用のEXEファイルの先頭には、MS-DOS互換のMZヘッダが付いています。
このヘッダのおかげで、Windows用EXEをMS-DOS上で実行すると、以下のような文言が表示されます:
This program cannot be run in DOS mode.
前回の記事https://qiita.com/earthen94/items/acdaddaf36e44adffcd7
でも解説しましたが、当時はまだアセンブリの読み方が十分理解できていませんでした。
改めてバイナリを見直すと、EXEの中には文字列を出力するアセンブリ命令が機械語として組み込まれていることに気づきます。

EXEの生成

C:\Users\test\kaihatsu>gcc test.c -o test
C:\Users\test\kaihatsu>test.exe
test.c
#include <stdint.h>
#include <stdio.h>
#include <string.h>

#pragma pack(push, 1)  // 構造体の中身を0埋めしない

typedef struct {
    uint16_t e_magic;    // "MZ"
    uint16_t e_cblp;
    uint16_t e_cp;
    uint16_t e_crlc;
    uint16_t e_cparhdr;
    uint16_t e_minalloc;
    uint16_t e_maxalloc;
    uint16_t e_ss;
    uint16_t e_sp;
    uint16_t e_csum;
    uint16_t e_ip;
    uint16_t e_cs;
    uint16_t e_lfarlc;
    uint16_t e_ovno;
    uint8_t  reserved[8];
    uint16_t e_oemid;
    uint16_t e_oeminfo;
    uint8_t  reserved2[20];
    uint32_t e_lfanew;
} MZHeader;

#pragma pack(pop)

int main(void) {
    FILE *f = fopen("hello.exe", "wb");
    if(!f) return 1;

    MZHeader hdr;
    memset(&hdr, 0, sizeof(hdr));

    hdr.e_magic    = 0x5A4D; // "MZ"
    hdr.e_cblp     = 0x0090; // 最後のページのバイト数(512単位)
    hdr.e_cp       = 0x0001; // 1ページ ファイル全体のページ数(512バイト単位)
    hdr.e_cparhdr  = 0x0004; // ヘッダは 64バイト (4*16)
    hdr.e_sp       = 0x00B8; // DOS標準の初期SP
    hdr.e_ip       = 0x0000; // 初期IP = 0
    hdr.e_cs       = 0x0000; // 初期CS = 0
    hdr.e_lfarlc   = 0x0040; // リロケテーブルのオフセット

    // ヘッダを書き込む
    fwrite(&hdr, sizeof(hdr), 1, f);

    // ヘッダ後の位置 = e_cparhdr*16 バイト(64バイト)に合わせる
    uint8_t padding[64 - sizeof(hdr)];
    memset(padding, 0, sizeof(padding));
    fwrite(padding, sizeof(padding), 1, f);

    // コード部分(INT21h AH=9 で文字列出力)
    uint8_t code[] = {
        0x8C,0xC8,             // mov ax, cs
        0x8E,0xD8,             // mov ds, ax
        0xB4,0x09,             // mov ah, 9
        0xBA,0x10,0x00,        // mov dx, 0x0010  ← 文字列のオフセット
        0xCD,0x21,             // int 21h
        0xB8,0x4C,0x00,        // mov ax, 4C00h
        0xCD,0x21,             // int 21h
        // 文字列
        'H','e','l','l','o',',',' ','D','O','S',' ','w','o','r','l','d','!','$'
    };

    fwrite(code, sizeof(code), 1, f);

    fclose(f);
    printf("hello_mz.exe has been generated.\n");
    return 0;
}

動作確認

MS-DOS実機上で動かすとHello, DOS world!と表示されました。
image.png
c2fe8e41ec6788.jpg

追記

実際にMASMでコンパイルしたEXEのヘッダを解析しました。
https://qiita.com/earthen94/items/359a55b8403a5fc21b47

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?