让应用程序只启动一次

    在编写应用程序,有时候会有 让应用程序只启动一次的 要求。

一. 命名的内核对象

    利用 内核中的命名对象 名称不能重复这一特性,来实现 应用程序的只启动一次的请求。

    以Mutex为例,可以利用CreateMutex函数:

HANDLE CreateMutex(
  LPSECURITY_ATTRIBUTESlpMutexAttributes,    // 指向安全属性的指针
  BOOLbInitialOwner,                         // 初始化互斥对象的所有者,TRUE表示创建锁线程所拥有这个锁,反之则表示没有线程占用这个锁
  LPCTSTRlpName                              // 指向互斥对象名
  );
    使用锁来实现应用程序启动一次:

	//在应用程序入口处,创建一个"TestForApp"的锁
	HANDLE g_hMtx = CreateMutex(NULL, FALSE, "TestForApp");
	//如果已经存在这个锁,则认为应用程序已经启动
	if (GetLastError() == ERROR_ALREADY_EXISTS)
	{
		printf("应用程序实例已经启动!\n");
		return -1;
	}
	//应用程序内容
        //...

二.  唯一的锁名

    第一节中给出的例子,如果另外的应用程序刚好也创建了一个名为"TestForApp"名字的锁,将会导致此应用程序无法启动。那如何保证锁名的唯一性呢?

2.1 GUID保证锁名唯一性

   GUID(Global Unique Identifier,全球唯一标示符),它由128位的整数表示,表现为格式:"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"(每一个X表示0-9或者A-F的字符),其用来保证不同计算机,不同时间产生唯一的标示符。

   可以利用GUID生成一个唯一的名字,作为锁名;

2.2 GUID产生锁名

        GUID guid;
	CoCreateGuid(&guid);
	printf("%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X\n",
		guid.Data1, guid.Data2, guid.Data3,
		guid.Data4[0], guid.Data4[1],
		guid.Data4[2], guid.Data4[3],
		guid.Data4[4], guid.Data4[5],
		guid.Data4[6], guid.Data4[7]);
	//运行产生GUID"D5F78344-5051-4CB3-9CA7-A5A4B5AAEBD1"
将"D5F78344-5051-4CB3-9CA7-A5A4B5AAEBD1" GUID取代 "TestForApp"作为锁名,来保证锁名的唯一性

三. 避免Dos(Denial Of  Service, 拒绝服务攻击)

3.1 攻击方法

    第二节中的方法,虽然避免了锁名冲突的可能性,但并没有避免 有目的制造冲突。

    通过Process Explorer工具查看到应用程序所创建的内核对象的名称,如下图所示,能够显示刚创建的名为"D5F78344-5051-4CB3-9CA7-A5A4B5AAEBD1"锁。


黑客很可能利用这个锁名,在应用服务程序启动之前,先创建这个锁,从而导致应用服务程序启动失败。

3.2 解决方法

创建专有的命名空间,专有命名空间就类似于 在内核名称之前在加上一个目录名称,ProcessExplorer中显示为 "\..\锁名",而不会暴露专有命名空间名字。

专有命名空间关联一个 边界描述符 (Boundary Descriptor), 边界描述符 至少包含一个SID;

个人理解 边界描述符 就是 根据SID和边界描述符名称划分的一个域。

//参考<<windows核心编程>>
#include<windows.h>
#include <Sddl.h>
#include<stdio.h>
#include<string.h>
#include <strsafe.h>

int main ()
{
	//创建边界描述符
	PCTSTR g_szBoundary = TEXT("TestForBoundary");
	HANDLE g_hBoundary = CreateBoundaryDescriptor(g_szBoundary, 0);

	//添加管理员组SID到边界描述符
	BYTE localAdminSID[SECURITY_MAX_SID_SIZE];
	PSID pLocalAdminSID = &localAdminSID;
	DWORD cbSID = sizeof(localAdminSID);
	if (!CreateWellKnownSid(WinBuiltinAdministratorsSid, NULL, pLocalAdminSID, &cbSID))
	{
		printf("CreateWellKnownSid Failed!\n");
		return -1;
	}

	if (!AddSIDToBoundaryDescriptor(&g_hBoundary, pLocalAdminSID))
	{
		printf("AddSIDToBoundaryDescriptor Failed!\n");
		return -1;
	}

	//产生安全信息
	SECURITY_ATTRIBUTES sa;
	sa.nLength = sizeof(sa);
	sa.bInheritHandle = FALSE;
	if (!ConvertStringSecurityDescriptorToSecurityDescriptor(TEXT("D:(A;;GA;;;BA)"),
		SDDL_REVISION_1, &sa.lpSecurityDescriptor, NULL))
	{
		printf("ConvertString Failed!\n");
		return -1;
	}

	//创建专有命名空间 "TestForNamespace"
	PCTSTR g_szNamespace = TEXT("TestForNamespace");
	HANDLE g_hNamespace = CreatePrivateNamespace(&sa, g_hBoundary, g_szNamespace);
	if (g_hNamespace == NULL)   
	{
		//这里没有进行处理,如果已经创建,可以打开专有命名空间
		//OpenPrivateNamespace API
		printf("CreatePrivateNamespace Failed!\n");
		return -1;
	}
	LocalFree(sa.lpSecurityDescriptor);

	//创建锁
	TCHAR szMutexName[64];
	StringCchPrintf(szMutexName, _countof(szMutexName), TEXT("%s\\%s"),
		g_szNamespace, TEXT("TestForApp"));
	HANDLE g_hSingleton = CreateMutex(NULL, FALSE, szMutexName);
	if (GetLastError() == ERROR_ALREADY_EXISTS)
	{
		printf("应用程序实例已经启动!\n");
		return -1;
	}
	return 0;
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值