C/C++ 运行单一实例

背景

病毒木马在使用各种手段植入到用户计算机后,也会使用浑身解数使自己被用户执行激活。但是,如果病毒木马自己被多次重复运行,系统中存在多分病毒木马的进程,那么,这就有可能增加被暴露的风险。所以,要想解决上述问题,就要确保系统上只运行一个病毒木马的进程实例。

确保运行一个进程实例的实现方法有很多,可以是通过扫描进程列表来实现,也可以通过枚举程序窗口的方式来实现或者可以通过共享全局变量来实现。接下来,本文将介绍一种被病毒木马广泛使用而且使用简单的方法,即通过创建系统命名互斥对象的方式实现。

函数介绍

1. CreateMutex函数

创建或打开一个已命名或未命名的互斥对象。

HANDLE WINAPI CreateMutex(
  _In_opt_ LPSECURITY_ATTRIBUTES lpMutexAttributes,
  _In_     BOOL                  bInitialOwner,
  _In_opt_ LPCTSTR               lpName
);

参数

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。

实现原理

通常情况下,系统上的进程是相互独立的,每个进程都拥有自己独立资源和地址空间,进程间互不影响。所以,同一个程序可以重复运行,在系统上的进程互不影响。但是,在一些特殊的情况,需要程序在系统上只存在一份进程实例,这就引出了进程互斥的问题。

微软提供了CreateMutex函数来实现创建或者打开一个已命名或未命名的互斥对象,程序每次运行的时候,通过判断系统上是否存在相同的命名互斥对象来确定程序是否重复运行。

CreateMutex函数一共有三个参数,第一个参数表示互斥对象的安全设置,是一个指向SECURITY_ATTRIBUTES结构的指针,在该程序中直接设置为NULL即可。第二个参数表示线程是否获得互斥锁对象的初始所有权,在该程序中,无论该参数为TRUE或者FALSE,均不影响程序的正常执行。第三个参数表示互斥对象的名称,对于通过过互斥对象来判断进程实例是否重复运行的程序来说,该参数一定要设置,而且设置的名称要保证唯一性。

程序的判断原理是通过CreateMutex函数创建一个命名互斥对象,如果对象创建成功,而且通过调用GetLastError函数获取的返回码的值为ERROR_ALREADY_EXISTS,则表示该命名互斥对象存在,即程序重复运行。否则,认为是首次运行程序。

编码实现

// 判断是否重复运行
BOOL IsAlreadyRun()
{
    HANDLE hMutex = NULL;
    hMutex = ::CreateMutex(NULL, FALSE, "TEST");
    if (hMutex)
    {
        if (ERROR_ALREADY_EXISTS == ::GetLastError())
        {
            return TRUE;
        }
    }
    return FALSE;
}

测试

直接运行上面的程序,第一次运行的时候,程序提示“NOT Already Run!“,如图2-1所示,意思是系统上没有该实例运行。接着继续双击执行程序,这次程序提示”Already Run!!!!“,如图2-2所示,意思是系统上已经存在该实例正在运行着。所以,程序成功判断出程序是否重复运行。

img

img

小结

这个程序实现起来不难,关键是熟悉对CreateMutex函数的调用。在调用CreateMutex函数来创建命名的互斥对象的时候,注意互斥对象的名称不要与现有事件、信号量或者文件映射对象等的名称相同,否则创建互斥对象失败。

在实现的过程中,特别要注意,程序一定不要调用CloseHandle函数来关闭CreateMutex函数创建出来的互斥对象的句柄,否则会导致互斥对象判断失败。因为CloseHandle函数会关闭互斥对象的句柄,释放资源。这样,系统上便不会存在对应的命名互斥对象了。这样,通过CreateMutex来创建命名互斥对象都不会重复的。

//网络素材仅限收藏 方便学习

posted @ 2021-06-06 15:44  New_HackerHK  阅读(165)  评论(0编辑  收藏  举报