EXEファイルを小さくする方法

2026/1/12

導入

「Hello World」を表示するだけのC++プログラムをコンパイルして、生成されたEXEファイルのサイズを見て、思わずため息をついた経験がある人も多いと思います。 普通にコンパイルすると、たった数行のコードに対し、数百KB〜数MBもの余計なものが勝手についてきます。

マルウェア開発において、ファイルサイズは小さければ小さいほど有利です。転送時間を短縮でき、パッカーやクリプターを通した際のオーバーヘッドも減らせるからです。

今回は、MinGW (GCC) を前提に、標準ライブラリ(CRT)を完全に排除し、数KBレベルまでEXEを小さくする手法を紹介します。

サンプルコード

C++の標準機能(iostreamstring)は使いません。これらをインクルードした瞬間、巨大なライブラリがリンクされるからです。 代わりに、Windows APIを直接叩きます。

#include <Windows.h>

// CRTのprintfなどは使えないため、WinAPIで代用
void PrintfW(const wchar_t* format, ...) {
    wchar_t buffer[1024];
    DWORD bytesWritten;
    va_list args;

    va_start(args, format);
    wvsprintfW(buffer, format, args);
    va_end(args);

    HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
    WriteConsoleW(hStdOut, buffer, lstrlenW(buffer), &bytesWritten, NULL);
}

// 独自のエントリーポイントを定義
extern "C" void EntryPoint() {
    PrintfW(L"Hello, World!\n");
    ExitProcess(0);
}

コンパイルコマンド

ポイントはコードだけでなく、コンパイルオプションにもあります。以下のコマンドでコンパイルします。

g++ main.cpp -nostdlib -fno-threadsafe-statics -fno-exceptions -fno-rtti -Wl,-e,EntryPoint -lkernel32 -luser32 -s -Os -Qn -Wall

オプションの解説

なぜこれで小さくなるのか? 各オプションの「意味」を理解することが重要です。

1. スタートアップコードの排除

これが最も強力なオプションです。通常、GCCは main() 実行前の準備を行うスタートアップコードや、便利な機能が詰まった標準ライブラリを自動的にリンクします。このオプションはそれらをすべて無効化し、CRTや標準ライブラリの依存を完全に排除します。

2. C++特有のオーバーヘッドを排除する

C++の便利な機能は、裏で大量のコードを生成します。これらをすべてOFFにします。

3. エントリーポイントの変更

リンカ(Linker)に対し、「プログラムの開始地点は main ではなく EntryPoint だ」と伝えます。CRTを排除した以上、標準の main は使えません。

4. 最適化と情報の削除

結果

この手法を使うことで、通常は数百KB以上になる「Hello World」を、数KB〜十数KB程度まで縮小できます(ビルド環境によって差はあります)。