ソフトウェア開発 Win32プログラミング

自殺プログラム

戻る


「自殺プログラム」とは、自分自身を削除するプログラムのことだ。 インストーラなどを作成している場合、プログラム自体がプログラムを削除しなければいけない状況に直面することがある。 そんなとき、自殺プログラムを使う。

プログラムの実行中は、EXEファイルに共有ロックがかかっているため、 プロセスが終了するまで、プログラム自身のファイルを削除することはできない。 しかしプロセスが終了すると、プロセスのすべての処理が終了してしまう。 どうやって自分自身を削除するか。

バッチファイル(拡張子が bat のファイル)を用いた解決策がある。次のソースコードを見てもらいたい。

#include <windows.h>
#include <stdio.h>

static void WriteSz(HANDLE hFile, LPSTR pszFmt, ...)
{
    va_list args;
    DWORD cb;
    CHAR sz[1024];

    va_start(args, pszFmt);
    wvsprintfA(sz, pszFmt, args);
    WriteFile(hFile, sz, lstrlen(sz) * sizeof(CHAR), &cb, NULL);

    va_end(args);
}

BOOL Suicide(VOID)
{
    CHAR szBat[MAX_PATH];
    CHAR szBatShort[MAX_PATH];
    HANDLE hBatFile;
    CHAR szModule[MAX_PATH];
    CHAR szModuleShort[MAX_PATH];
    STARTUPINFO si;
    PROCESS_INFORMATION pi;
    TCHAR szCmdLine[1024];
    TCHAR szComSpec[MAX_PATH];

    GetTempPathA(MAX_PATH, szBat);
    lstrcatA(szBat, "suicide.bat");
    DeleteFileA(szBat);
    hBatFile = CreateFileA(szBat, GENERIC_WRITE, FILE_SHARE_READ, NULL, 
                           CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hBatFile == INVALID_HANDLE_VALUE)
        return FALSE;

    GetModuleFileNameA(NULL, szModule, MAX_PATH);
    GetShortPathNameA(szModule, szModuleShort, MAX_PATH);
    GetShortPathNameA(szBat, szBatShort, MAX_PATH);
    WriteSz(hBatFile, ":loop\r\n");
    WriteSz(hBatFile, "del %s\r\n", szModuleShort);
    WriteSz(hBatFile, "if exist %s goto loop\r\n", szModuleShort);
    WriteSz(hBatFile, "del %s\r\n", szBatShort);
    CloseHandle(hBatFile);

    ZeroMemory(&si, sizeof(STARTUPINFO));
    si.cb = sizeof(STARTUPINFO);
    si.dwFlags = STARTF_USESHOWWINDOW;
    si.wShowWindow = SW_HIDE;

    if (!GetEnvironmentVariable(TEXT("COMSPEC"), szComSpec, MAX_PATH))
    {
        OSVERSIONINFO ovi;
        ZeroMemory(&ovi, sizeof(OSVERSIONINFO));
        ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
        GetVersionEx(&ovi);
        if (ovi.dwPlatformId == VER_PLATFORM_WIN32_NT)
            lstrcpy(szComSpec, TEXT("cmd.exe"));
        else
            lstrcpy(szComSpec, TEXT("command.com"));
    }
    wsprintf(szCmdLine, TEXT("\"%s\" /C \"%hs\""), szComSpec, szBat);

    if (CreateProcess(NULL, szCmdLine, NULL, NULL, TRUE, 
        CREATE_NEW_CONSOLE | IDLE_PRIORITY_CLASS, NULL, NULL, &si, &pi))
    {
        CloseHandle(pi.hProcess);
        CloseHandle(pi.hThread);
        return TRUE;
    }
    return FALSE;
}

#ifdef UNITTEST
INT WINAPI WinMain(
    HINSTANCE   hInstance, 
    HINSTANCE   hPrevInstance,
    LPSTR       pszCmdLine,
    INT         nCmdShow)
{
    Suicide();
    return 0;
}
#endif

WriteString関数は、ファイルハンドルでprintfのような処理を行っている。 Suicide関数が主な「自殺準備」処理を行っている。

このプログラムは、大まかに次のような手順で「自殺」を行う。

  1. 次のようなバッチファイルを普通に作成する。
  2. バッチファイル実行に必要なCOMSPEC環境変数を参照する。
  3. 低い優先順位でバッチファイルを実行する(SW_HIDEを指定しているので表示されない)。
  4. プロセスを終了する(プロセスが終了しても、バッチファイルの実行は終了していない)。
  5. バッチファイルのプロセスがEXEファイルを削除する。
  6. その後、バッチファイル自身がバッチファイルを削除する。

GetTempPathで取得した一時フォルダのパスには必ず '\\' がついているので、つける必要はない。 GetModuleFileNameで実行中のプログラムのフルパス名を取得できる。

CreateProcessで取得したpi.hProcessとpi.hThreadは、使わないのですぐ閉じる必要がある。 閉じてもプロセスは終了しない。バッチファイルなので、CREATE_NEW_CONSOLEを指定する。

低い優先順位(IDLE_PRIORITY_CLASS)でバッチを実行するのは、ループでビジーウェイトにならないようにするため。

ソース: suicide.zip


戻る

©片山博文MZ
katayama.hirofumi.mz@gmail.com

inserted by FC2 system