프로그램의 중복 실행을 막는 방법은 여러가지가 있습니다.
그 중 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 이란 분이 아주 상세하게 설명해 놓으셨습니다.
'리버싱' 카테고리의 다른 글
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 |