程序防多开之四:共享区间检测

本文介绍了在Windows系统中,通过共享区间实现程序防多开的技术,包括进程数量检测、全局对象检测,以及使用共享内存存储进程路径和计数的方法,确保同一程序实例不超过设定的最大数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        之前章节中我们讲了几种针对程序多开的检测:

        程序防多开之一:进程数量检测

        程序防多开之二:程序窗口检测

        程序防多开之三:全局对象检测

       

        在Windows系统中,每个进程在创建时,都会加载系统的共享区间,在内核中的名称是ShareSection.

        共享区间可以实现对同一个程序或者DLL数据共享,避免在系统加载的时候重复载入,导致资源负载浪费.

        因此我们可以利用此种方式实现进程数据同步,对程序是否多开进行检测.

        共享区间在程序中的实现方式:

#pragma data_seg("_gdata")
//告诉编译器,接下来的全局变量(例如 g_PidArray)应该被放置在一个名为 "_gdata" 的数据段中
DWORD g_PidArray[10] = {0};
#pragma data_seg()
//关闭这个设置,意味着在此之后定义的全局变量将回到默认的数据段
#pragma comment(linker,"/SECTION:_gdata,RWS")
//指示链接器将 "_gdata" 数据段设置为读写共享(RWS)模式

        以上代码指示编译器在生产程序或者dll文件时,提供一块共享区间内存,区间内存名字为"_gdata",下面我们开始针对程序运行期间,对此区间的数据进程操作.

        首先我们得有一个获取进程路径的函数,之前文章中已经贴过代码,此处再次给出:

//获取进程的完整路径,并返回路径文本长度
static DWORD myGetProcessPath(DWORD nPID, LPWSTR lpPath, DWORD nSize)
{
	if (!nPID || !lpPath)
		return 0;
	DWORD nRet = 0;
	HANDLE handle = NULL;
	__try {
		do
		{
			wsprintfW(lpPath, L"");
			handle = ::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, nPID);
			if (!handle)
				break;
			::QueryFullProcessImageNameW(handle, 0, lpPath, &nSize);
			nRet = wcslen(lpPath);
		} while (FALSE);

	}
	__except (EXCEPTION_EXECUTE_HANDLER) { ; }

	if (handle)
		CloseHandle(handle);
	return nRet;
}

        

        以上代码的作用仅是获取程序的完整路径,我们今天依然要用到它.如果您怕上述的功能被作弊者使坏导致失效,可以利用其他方式实现,此处不再祥举例程.

        现在开始实现检测代码:

//遍历共享内存区域进程数组,并且获取当前运行程序的启动总数
LONG GetProcessCountByShareMemory()
{
	//获取自身进程完整路径
	DWORD nCurrPID = GetCurrentProcessId();
	WCHAR strCurrPath[MAX_PATH] = { 0 };
	DWORD nRet = myGetProcessPath(nCurrPID, strCurrPath, sizeof(strCurrPath));
	if (!nRet)
	{
		//获取自身进程完整路径失败!
		return -1;
	}

	//取共享区域内存指针和长度
	DWORD* ppPidArray = g_PidArray;
	DWORD nBufLen = sizeof(g_PidArray);
	
	LONG nCount = 0;
	int nWriteIndex = -1;//用来标记写入共享区域内存的数组下标
	//通过数据长度,解析出数组成员数
	DWORD nPidConts = nBufLen / sizeof(DWORD);
	__try
	{
		//遍历PID数组
		for (size_t i = 0; i < nPidConts; i++)
		{
			DWORD nPid = ppPidArray[i];
			
			//如果当前进程PID已经记录在共享内存中,则设置写入下标为最大数+1(用是否数组越界判断是否写入)
			if (nPid == nCurrPID)
				nWriteIndex = nPidConts + 1;
			
				
			
			if (nPid == 0)
			{
				//仅修改一次写入下标为当前索引
				if (nWriteIndex < 0)
					nWriteIndex = i;
				continue;
			}
				

			//查询当下PID所在路径是否和本进程路径相同
			WCHAR strPath[MAX_PATH] = { 0 };
			DWORD nRet = myGetProcessPath(nPid, strPath, sizeof(strPath));
			if (!nRet)
			{	//如果进程路径不相同,可能是旧进程关闭,这里记录索引,以便在数组写满后可以覆写数据
				if (nWriteIndex < 0)
					nWriteIndex = i;
			}
			if (nRet)
			{
				if (wcscmp(strPath, strCurrPath) == 0)
				{
					nCount++;
				}
			}
		}
	}
	__except (EXCEPTION_EXECUTE_HANDLER) { ; }

	//判断是否在共享内存进程数组中写入本进程PID
	if (nWriteIndex < nPidConts && nWriteIndex >= 0)
	{
		g_PidArray[nWriteIndex] = nCurrPID;
		nCount++;
	}
		

	return nCount;
}

DWORD CheckCountShareMemory(DWORD nMaxCount)
{
	BOOL isLimit = FALSE;
	ULONG nSize = 0;
	DWORD* pPidArray = NULL;
	__try {

		do
		{

			//在共享区间中读取数据,获取当前程序的启动数量.
			LONG nCount = GetProcessCountByShareMemory();

			//进程数量超出最大数
			if (nCount > nMaxCount)
				isLimit = TRUE;

			//进程数量=0或者-1均表示系统环境异常,此处直接返回TRUE.
			if (nCount <= 0)
				isLimit = TRUE;

		} while (FALSE);

	}
	__except (EXCEPTION_EXECUTE_HANDLER) { ; }

	
	return isLimit;
}

        代码解释:

        在函数CheckCountShareMemory()中我们实现调用GetProcessCountByShareMemory()来获取当前程序的运行数量,如果超出我们规定的最大启动数,则返回TRUE.

        函数GetProcessCountByShareMemory()通过遍历共享区间内存数据g_PidArray来获取当前程序的所有PID.

        在遍历期间,判断当前进程的PID是否已经共享,如果没有共享,则会将当前PID写入共享区间内存中.

        同时也会判断共享区间内存中的PID是否已经失效,例如旧进程是否关闭(通过程序路径判断).

        然后将所有存活的当前程序数量记录,并且返回准确的数量.

        请注意:

        通过此种方式实现的数据共享,仅能维持在程序是相同路径的情况下才会共享.

        如果将此程序复制到其他目录或者修改名字,那么共享区间将会重新开辟.

        我们这里仅使用此种方式实现对同一个进程的检测,本专栏以后将会详解如何针对修改文件名/路径躲避多开如何实现检测,此处不再详解.

  

   最后的调用:

int main()
{
    
     //限制多开数量
     LONG nMaxCount = 2;

     //判断进程数量是否超出限制数量
     BOOL isLimit = CheckCountShareMemory(nMaxCount);

     if (isLimit)
         MessageBoxA(0, "客户端开启数量超出限制!", "提示", 0);

    system("pause");
}

运行效果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

°默然

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值