posted by 블르샤이닝 2011. 8. 8. 14:59
728x90

프로그램의 중복 실행을 막는 방법은 여러가지가 있습니다.
그 중 3가지를 소개하고 제가 사용하는 방법을 알려드리려 합니다.

1. FindWindow() API를 이용하는 방법
    - 이 API는 RegisterClass() 또는 RegisterClassEx() API를 이용하여 등록된 클래스명이나 프로그램명(Title)을 이용하여 특정 윈도우를 찾는 함수입니다.
    - 가장 간단한 코드로 구현할 수 있습니다. 그러나 문제점을 갖고 있어 사용을 권장하진 않습니다.
    - 문제점1: 클래스명이나 프로그램명은 중복될 확률이 높아 우리가 원하는 프로그램을 찾아내지 못합니다. (예를들면 탐색기의 프로그램명은 폴더명에 따라 바뀌기 때문에 혼동되기 쉽습니다.)
    - 문제점2: 프로그램을 아주 빠른 속도로 두번 연속 실행시킬 경우 정상적으로 동작을 안할 수 있습니다.
    - 문제점3: 레이스 컨디션(Race Condition: 두 개의 쓰레드가 실행될 때 어떤 스레드가 먼저 처리되느냐에 따라 결과가 달라지는 것)이 존재합니다.

// MyApp.cpp : Defines the class behaviors for the application.
#include "stdafx.h"
...

BOOL CMyApp::InitInstance()
{
    HWND  hwndClone;

    hwndClone = ::FindWindow(NULL, _T("프로그램 이름 (My App. Title)"));
    if (hwndClone != NULL)
    {
        ::SetForegroundWindow(hwndClone);
        if (::IsIconic(hwndClone))
            ::ShowWindow(hwndClone, SW_SHOWDEFAULT);
        return FALSE;
    }



    ...
}



2. 뮤텍스(Mutex)를 이용하는 방법
    - 커널 객체인 뮤텍스는 단 하나의 쓰레드만이 소유할 수 있다는 성질을 이용합니다. (뮤텍스에 대한 자세한 설명은 'Windows API 정복, 가남사' 또는 'TCP/IP 소켓 프로그래밍, 프리렉'을 참조하세요.)
    - 뮤텍스를 생성하기 위해서는 CreateMutext() API를 이용합니다. 이 API의 세번째 인자에 뮤텍스 이름을 넘기게 되어 있는데, 그 이름이 중복될 경우 원치않는 동작을 할 수 있습니다. 이를 해결하기 위해서 뮤텍스 이름으로 GUID 값을 넘기면 해결할 수 있습니다.

// MyApp.h : main header file for the MyApp application
...

class CMyApp : public CWinApp
{
    ...

protected:
    HANDLE  m_hMutex;
};


// MyApp.cpp : Defines the class behaviors for the application.
#include "stdafx.h"
...

BOOL CMyApp::CMyApp()
{
    m_hMutex = NULL;

    ...
}

BOOL CMyApp::InitInstance()
{
    m_hMutex = ::CreateMutex(NULL, FALSE, _T("My App. Name-D51099F9-0FC6-4124-BC61-09B288101D0E"));
    if (::GetLastError() == ERROR_ALREADY_EXISTS)
    {
        AfxMessageBox(_T("App.는 중복 실행될 수 없습니다."));
        return 0;
    }

    ...
}

BOOL CMyApp::ExitInstance()
{
    if (m_hMutex != NULL)
        ::CloseHandle(m_hMutex);

...
}



3. 공유 메모리를 이용하는 방법
    - 특정 데이터 세그먼트 영역에 공유할 변수를 만들어 그 변수 값에 따라 처리하는 방법입니다.

// MyApp.cpp : Defines the class behaviors for the application.
#include "stdafx.h"
...

#pragma data_seg(".Shared")          // 'Shared'란 이름으로 'Data Segment'를 만든다.
long  g_lInstanceCount = 0;
#pragma data_seg()

#pragma comment(linker, "/SECTION:.Shared,rws")

...

BOOL CMyApp::InitInstance()
{
    if (g_lInstanceCount > 0)
    {
        AfxMessageBox(_T("App.는 중복 실행될 수 없습니다."));
        return 0;
    }

    g_lInstanceCount ++;

    ...
}

BOOL CMyApp::ExitInstance()
{
    g_lInstanceCount --;

    ...
}



위 3가지 방법 중에서 FindWindow() API를 이용하는 방법을 제외하면 기존 인스턴스에 대한 정보를 얻지 못해 기존 인스턴스를 윈도우즈에서 최상단(z-order)에 위치하도록 하지 못하고 있습니다. 이를 해결하기 위해 전 뮤텍스와 공유 메모리를 이용한 방법을 혼용하였습니다.
공유 메모리 영역에 최초로 실행된 인스턴스의 메인 윈도우 핸들 값을 저장하고 그 값을 이용하여 인스턴스를 컨트롤합니다.

// MyApp.h : main header file for the MyApp application
...

class CMyApp : public CWinApp
{
    ...

protected:
    HANDLE  m_hMutex;
};


// MyApp.cpp : Defines the class behaviors for the application.
#include "stdafx.h"
...

#pragma data_seg(".Shared")          // 'Shared'란 이름으로 'Data Segment'를 만든다.
HWND  g_hwndInstance = NULL;
#pragma data_seg()

#pragma comment(linker, "/SECTION:.Shared,rws")

...

BOOL CMyApp::CMyApp()
{
    m_hMutex = NULL;

    ...
}

BOOL CMyApp::InitInstance()
{
    m_hMutex = ::CreateMutex(NULL, FALSE, _T("My App. Name-D51099F9-0FC6-4124-BC61-09B288101D0E"));
    if (::GetLastError() == ERROR_ALREADY_EXISTS)
    {
        if (g_hwndInstance != NULL)
        {
            ::SetForegroundWindow(g_hwndInstance);
            if (::IsIconic(g_hwndInstance))
                ::ShowWindow(g_hwndInstance, SW_SHOWDEFAULT);
        }
        return FALSE;
    }

    ...

    if (g_hwndInstance == NULL)
        g_hwndInstance = m_pMainWnd;

    ...
}

BOOL CMyApp::ExitInstance()
{
    if (m_hMutex != NULL)
        ::CloseHandle(m_hMutex);

추신. http://www.flounder.com/nomultiples.htm에 가보시면 Joseph M. Newcomer 이란 분이 아주 상세하게 설명해 놓으셨습니다.

728x90

'리버싱' 카테고리의 다른 글

unpack_dragonarmour  (0) 2011.09.06
Simple IDA Pro plugin to look up APIs on MSDN  (0) 2011.08.17
SHELLCODE 의 코드 이해하는 방법  (0) 2011.07.29
CVE-2011-0222_WinXP_Exploit  (0) 2011.07.29
ida 단축키  (0) 2011.07.28