让应用程序只启动一次
站长推荐:NSetup一键部署软件
一键式完成美化安装包制作,自动增量升级,数据统计,数字签名。应对各种复杂场景,脚本模块化拆分,常规复杂的脚本代码,图形化设置。无需专业的研发经验,轻松完成项目部署。(www.nsetup.cn)
在编写应用程序,有时候会有 让应用程序只启动一次的 要求。
一. 命名的内核对象
利用 内核中的命名对象 名称不能重复这一特性,来实现 应用程序的只启动一次的请求。
以Mutex为例,可以利用CreateMutex函数:
1 2 3 4 5 |
HANDLE CreateMutex( LPSECURITY_ATTRIBUTESlpMutexAttributes, // 指向安全属性的指针 BOOLbInitialOwner, // 初始化互斥对象的所有者,TRUE表示创建锁线程所拥有这个锁,反之则表示没有线程占用这个锁 LPCTSTRlpName // 指向互斥对象名 ); |
使用锁来实现应用程序启动一次:
1 2 3 4 5 6 7 8 9 10 |
//在应用程序入口处,创建一个"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产生锁名
1 2 3 4 5 6 7 8 9 |
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和边界描述符名称划分的一个域。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
//参考<<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; } |
学习日记,兼职软件设计,软件修改,毕业设计。
本文出自 学习日记,转载时请注明出处及相应链接。
本文永久链接: https://www.softwareace.cn/?p=544