利用命名对象来防止运行一个应用程序的多个实例

原始方法:

  在应用程序入口(_tmain或_tWinMain)函数中创建(Create*)一个命名对象(具体创建什么类型无关紧要),Create*返回后,再调用一下GetLassError。如果返回ERROR_ALREADY_EXISTS,表明应用程序的另一个实例正在运行,新的实例就可以退出了。

例码:

int main()
{
	HANDLE h = CreateMutex(NULL,FALSE,TEXT("showerrorMutex"));
	if (ERROR_ALREADY_EXISTS == GetLastError())
	{
		cout<<"A old app exit!"<<endl;
		CloseHandle(h);
		return 0;
		}
	/*  ..........其他代码..........*/

	CloseHandle(h); /*关闭句柄*/
	return 0;
}

 

安全风险:

     任何进程--即使是最低权限的进程--都能用任何指定的名称来创建一个对象,所以攻击者很容易写一个应用程序来创建一个同名的内核对象。如果它先于单实例应用程序启动,上面“单实例”的应用程序就变成一个“无实例”的应用程序--始终都是已启动就退出。这种攻击就是拒绝服务(DoS)的一种。

扩展方法:

    通过专有命名空间,我自己也不是很理解。只贴上代码,详细见<Windows 核心编程>:

#include "Resource.h"

#include "../CommonFiles/CmnHdr.h"
#include <WindowsX.h>
#include <Sddl.h>
#include <tchar.h>
#include <strsafe.h>

//主对话框句柄
HWND g_hDlg;

HANDLE g_hBoundary = NULL;
HANDLE g_hNameSpace = NULL;
HANDLE g_hSingleTon =NULL;

BOOL g_bNamespaceOpened = FALSE;
PCTSTR g_szBoundary = TEXT("3_Boundary");
PCTSTR g_szNameSpace = TEXT("3-NameSpace");

#define DETAILS_CTRL GetDlgItem(g_hDlg,IDC_EDIT_DETAILS)

void addText(PCTSTR pszFormat,...)
{
	va_list argList;
	va_start(argList,pszFormat);
	
	TCHAR sz[20*1024];
	Edit_GetText(DETAILS_CTRL,sz,_countof(sz));
	_vstprintf_s(_tcschr(sz, TEXT('\0')), _countof(sz) - _tcslen(sz), pszFormat, argList);
	Edit_SetText(DETAILS_CTRL,sz);

	va_end(argList);

	return ;
}

VOID checkInstances()
{
	g_hBoundary = CreateBoundaryDescriptor(g_szBoundary,0);

	BYTE localAdminSID[SECURITY_MAX_SID_SIZE];
	PSID pLocalAdminSID = &localAdminSID;
	DWORD cbSID = sizeof(localAdminSID);
	if(!CreateWellKnownSid(WinBuiltinAdministratorsSid,NULL,pLocalAdminSID,&cbSID))
	{
		addText(TEXT("AddSIDToBoundary failed : %u\r\n"),GetLastError());
		return;
	}
	if (!AddSIDToBoundaryDescriptor(&g_hBoundary,pLocalAdminSID))
	{
		addText(TEXT("AddSIDto boundary descriptor failed : %u\r\n",GetLastError()));
		return;
	}

	SECURITY_ATTRIBUTES sa;
	memset(&sa, 0, sizeof(SECURITY_ATTRIBUTES));
	sa.bInheritHandle = FALSE;
	sa.nLength = sizeof(SECURITY_ATTRIBUTES);
	
	if (!ConvertStringSecurityDescriptorToSecurityDescriptor(TEXT("D:"),	//去掉D:后面的括号
						SDDL_REVISION_1, &sa.lpSecurityDescriptor, NULL))
	{
		addText(TEXT("Security Descriptor create failed : %u\r\n", GetLastError()));
		return;
	}

	g_hNameSpace = CreatePrivateNamespace(&sa,g_hBoundary,g_szNameSpace);
	
	LocalFree(sa.lpSecurityDescriptor);

	DWORD dwLastError = GetLastError();
	if (NULL == g_hNameSpace)
	{
		if (dwLastError == ERROR_ACCESS_DENIED)
		{
			addText(TEXT("Access denied when creating namespace : %u\r\n",GetLastError()));
			addText(TEXT("You must be running as Administrator .\r\n"));
			return ;
		}
		else
		{
			if(dwLastError == ERROR_ALREADY_EXISTS)
			{
				addText(TEXT("Create private namespace failed : %u\r\n"),GetLastError());
				g_hNameSpace = OpenPrivateNamespace(g_hBoundary,g_szBoundary);
				if (NULL == g_hNameSpace)
				{
					addText(TEXT("and open private namespace failed : %u\r\n"),GetLastError());
					return ;
				}
				else
				{
					addText(TEXT(" but open private namespace succeeded\r\n"));
					return;
				}
			}
			else
			{
				addText(TEXT("Unexpected error occured: %u\r\n"),GetLastError());
				return ;
			}
		}
	}
	TCHAR szMutexName[64];
	StringCchPrintf(szMutexName,_countof(szMutexName),TEXT("%s\\%s"),g_szNameSpace,TEXT("Singleton"));
	g_hSingleTon = CreateMutex(NULL,FALSE,szMutexName);
	if (ERROR_ALREADY_EXISTS == GetLastError())
	{
		addText(TEXT("Another instance  is running.\r\n"));
		addText(TEXT("-----Impossible to access application features.\r\n"));
	}
	else
	{
		addText(TEXT("First instance of This app! \r\n"));
		addText(TEXT("---->access application features now.\r\n"));
	}
}
VOID Dlg_OnCommand(HWND hwnd,int id,HWND hwndCtrl,UINT codeNotify)
{
	switch(id)
	{
	case IDOK:
	case IDCANCEL:
		EndDialog(hwnd,id);
		break;
	}
	return ;
}
BOOL Dlg_InitDlg(HWND hwnd,HWND hwndFocus,LPARAM lParam)
{
	chSETDLGICONS(hwnd,IDI_SINGLETON);	//设置对话框图标
	g_hDlg = hwnd;
	(VOID)checkInstances();	//检查是否已经存在实例

	return TRUE;
}
/************************************************************************/
/* 
	主对话框处理函数
*/
/************************************************************************/
INT_PTR WINAPI dlg_Proc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
	switch (uMsg)
	{
		chHANDLE_DLGMSG(hwnd,WM_COMMAND,Dlg_OnCommand);
		chHANDLE_DLGMSG(hwnd,WM_INITDIALOG,Dlg_InitDlg);
	}
	return FALSE;
}
int APIENTRY _tWinMain( __in HINSTANCE hInstance, __in_opt HINSTANCE hPrevInstance, __in_opt LPTSTR lpCmdLine, __in int nShowCmd )
{
	/* 忽略无关参数 */
	UNREFERENCED_PARAMETER(hPrevInstance);
	UNREFERENCED_PARAMETER(lpCmdLine);
	
	//show main window
	DialogBox(hInstance,MAKEINTRESOURCE(IDD_SINGLETON),NULL,dlg_Proc);
	if (NULL != g_hSingleTon )
	{
		CloseHandle(g_hSingleTon);
	}
	if (NULL != g_hNameSpace )
	{
		if (g_bNamespaceOpened)
		{
			ClosePrivateNamespace(g_hNameSpace, 0);
		}
		else
		{
			ClosePrivateNamespace(g_hNameSpace, PRIVATE_NAMESPACE_FLAG_DESTROY);
		}
	}
	if (NULL != g_hBoundary)
	{
		DeleteBoundaryDescriptor(g_hBoundary);
	}
	return 0;
}
注意:此代码是我抄出来的,系统配置:Win7 64位系统VS2013 的win32,在调用ConvertStringSecurityDescriptorToSecurityDescriptor时,本来一直失败(security descriptor create failed),后来在网上搜把其第一个参数(TEXT("D:()"))中的括号去掉了,函数就返回成功了,我也不知是什么原因,在此仅作提示。



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值