运行单一实例
运行单一实例
0x00 前言
诸如上线后的维权操作,运行过多的进程可能会造成c/s端通讯异常,而过多的危险程序进程也会增加暴露风险,所以说可以给程序加一个api,通过创建一个系统命名互斥对象来实现。又比如在自启维权操作时,可以保证马已经在运行的时候,不用再次运行payload加载部分,避免过多的操作造成暴露风险。
0x01 函数介绍
1.CreateMutex函数
创建或打开一个已命名或者未命名的护持对象。
2.函数定义
HANDLE WINAPI CreateMutex(
_In_opt_ LPSECURITY_ATTRIBUTES lpMutexAttributes,
_In_ BOOL bInitialOwner,
_In_opt_ LPCTSTR lpName
);
3.参数与返回值
参数
lpMutexAttributes [in, optional]
指向SECURITY_ATTRIBUTES结构的指针。如果此参数为NULL,则该句柄不能由子进程继承。
bInitialOwner [in]
如果此值为TRUE并且调用者创建了互斥锁,则调用线程将获得互斥锁对象的初始所有权。否则,调用线程不会获得互斥锁的所有权。
lpName [in, optional]
互斥对象的名称。该名称仅限于MAX_PATH字符。名称比较区分大小写。如果lpName为NULL,则会创建不带名称的互斥体对象。
如果lpName与现有事件,信号量,等待定时器,作业或文件映射对象的名称匹配,则该函数将失败,并且GetLastError函数返回ERROR_INVALID_HANDLE。这是因为这些对象共享相同的名称空间。
该名称可以具有“Global”或“Local”前缀以在全局或会话名称空间中显式创建对象。名称的其余部分可以包含除反斜杠字符(\)以外的任何字符。
返回值
如果函数成功,则返回值是新创建的互斥对象的句柄。
如果函数失败,返回值为NULL。 要获得扩展的错误信息,请调用GetLastError。
如果互斥锁是一个已命名的互斥锁,并且该对象在此函数调用之前就存在,则返回值是现有对象的句柄,GetLastError返回ERROR_ALREADY_EXISTS。
0x02 编码实现
#include <iostream>
#include <Windows.h>
// 判断是否重复运行
BOOL IsAlreadyRun()
{
HANDLE hMutex = NULL;
hMutex = ::CreateMutex(NULL, FALSE, "TEST");
if (hMutex)
{
if (ERROR_ALREADY_EXISTS == ::GetLastError())
{
return TRUE;
}
}
return FALSE;
}
int main()
{
// 判断是否重复运行
if (IsAlreadyRun())
{
printf("Already Run!!!!\n");
}
else
{
printf("NOT Already Run!\n");
}
system("pause");
return 0;
}
0x03 关于报错
Tips:
关于如果Visual Studio 2019 出现const char *“ 类型的实参与 “LPCWSTR“ 类型的形参不兼容这类错误。
解决方法:
右击项目文件 — 单击属性 — 配置属性 — 高级 — 高级属性 — 字符集
使用 Unicode 字符集”改为“使用多字节字符集
0x04 实测
0x05 关于原理思考与学习记录
CreateMutex函数一共的三个参数,主要起作用的是第三个,第三个参数表示互斥对象的名称,程序的主要逻辑就是通过互斥对象来判断进程实例是否重复运行,而如果实现该参数必须设置并保证唯一性。
而第一个参数表示互斥对象的安全设置,是一个指向SECURITY_ATTRIBUTES结构的指针,在该程序中直接设置为NULL即可。
第二个参数表示线程是否获得互斥锁对象的初始所有权,在该程序中,无论该参数为TRUE或者FALSE,均不影响程序的正常执行。
我们编写运行单一实例时,可以通过if判断语句判断程序是否已经在运行。
首先通过CreateMutex创建一个被命名的互斥对象,其次通过调用GetLastError函数获取的返回码的值是否为ERROR_ALREADY_EXISTS,如果是则表示该命名互斥对象存在,即程序重复运行,否则认为是首次运行程序。
另外就是通过CreateMutex创建完进程互斥对象,不能通过CloseHandle等方法关闭进程句柄,否则就相当于关闭了互斥对象的句柄,释放资源没有任何作用了。