There are many ways to limit the game
For example, traversing windows, traversing processes, configuration files, registry, mutexes, mac addresses, ip, public files, memory mapping, etc. There are many methods.
But the vast majority of games are limited by the use of mutexes.
In this lesson, we will explain how to close the mutex handle to achieve multiple openings. The example is CQYH
(The protection suggestion here is to add a variety of ways to limit multiple openings and increase the use of multiple mutexes in the logic, so as to avoid being directly maliciously closed)
1. Multiple authentication restrictions
First we open the game
Then open a second window
It is found that we have detected that we have opened a window, but there is no restriction
Restriction occurs when opening the third window
It is obvious that more openings are restricted, only 2 can be opened
2. The tool checks the mutex and closes it
We can use tools to view mutexes, you can use XT, PCH and other tools
You can also directly download the following tools from the official account Ren Niaofei reverse engineering resource download
Open the software, find our process, right click to view the handle
There are many handles, find the handle of Mutant type
Found that there are many, no name can be ignored
There are 20-30 handles with names left
Let’s close them one by one to see which one is closed and then we can open more windows
It is found that closing this is enough, the name is AOD_Game
Can open 3 windows
Three, write code, close the mutex handle
Add a header file “Mutex.h”
Used as a close mutex handle
#pragma once #include <string.h> #include <windows.h> #include <winternl.h> #include <TlHelp32.h> // define the required macros #define STATUS_SUCCESS 0x00UL #define STATUS_INFO_LENGTH_MISMATCH 0xC0000004 #define SystemHandleInformation 16 #define SE_DEBUG_PRIVILEGE 0x14 // Define the structure that needs to be used typedef enum _OBJECT_INFORMATION_CLASSEX { ObjBasicInformation = 0, ObjNameInformation, ObjTypeInformation, } OBJECT_INFORMATION_CLASSEX; typedef enum _PROCESSINFOCLASSEX { ProcessHandleInformation = 20, }PROCESSINFOCLASSEX; typedef struct _SYSTEM_HANDLE { ULONG ProcessId; BYTE ObjectTypeNumber; BYTE Flags; USHORT Handle; PVOID Object; ACCESS_MASK GrantAccess; }SYSTEM_HANDLE; typedef struct _SYSTEM_HANDLE_INFORMATION { DWORD HandleCount; SYSTEM_HANDLE Handles[1]; }SYSTEM_HANDLE_INFORMATION; typedef struct _OBJECT_NAME_INFORMATION { UNICODE_STRING ObjectName; }OBJECT_NAME_INFORMATION; // Declares that the API is not exported typedef NTSTATUS(WINAPI* ZwQueryInformationProcessProc)(HANDLE, PROCESSINFOCLASSEX, LPVOID, DWORD, PDWORD); ZwQueryInformationProcessProc ZwQueryInformationProcess; typedef NTSTATUS(WINAPI* ZwQuerySystemInformationProc)(DWORD, PVOID, DWORD, DWORD*); ZwQuerySystemInformationProc ZwQuerySystemInformation; typedef NTSTATUS(WINAPI* ZwQueryObjectProc)(HANDLE, OBJECT_INFORMATION_CLASSEX, PVOID, ULONG, PULONG); ZwQueryObjectProc ZwQueryObject; typedef NTSTATUS(WINAPI* RtlAdjustPrivilegeProc)(DWORD, BOOL, BOOL, PDWORD); RtlAdjustPrivilegeProc RtlAdjustPrivilege; typedef DWORD(WINAPI* ZwSuspendProcessProc)(HANDLE); ZwSuspendProcessProc ZwSuspendProcess; typedef DWORD(WINAPI* ZwResumeProcessProc)(HANDLE); ZwResumeProcessProc ZwResumeProcess; #pragma warning (disable: 6011) #pragma warning (disable: 6001) #pragma warning (disable: 6387) #include <stdio.h> // Elevate process privileges BOOL ElevatePrivileges() { HANDLE hToken; TOKEN_PRIVILEGES tkp; tkp.PrivilegeCount = 1; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) return FALSE; LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tkp.Privileges[0].Luid); tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; if (!AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(TOKEN_PRIVILEGES), NULL, NULL)) { return FALSE; } return TRUE; } // Initialize unexported API BOOL GetUnDocumentAPI() { ZwSuspendProcess = (ZwSuspendProcessProc) GetProcAddress(GetModuleHandle("ntdll.dll"), "ZwSuspendProcess"); ZwQuerySystemInformation = (ZwQuerySystemInformationProc) GetProcAddress(GetModuleHandle("ntdll.dll"), "ZwQuerySystemInformation"); ZwQueryObject = (ZwQueryObjectProc) GetProcAddress(GetModuleHandle("ntdll.dll"), "ZwQueryObject"); ZwResumeProcess = (ZwResumeProcessProc) GetProcAddress(GetModuleHandle("ntdll.dll"), "ZwResumeProcess"); ZwQueryInformationProcess = (ZwQueryInformationProcessProc) GetProcAddress(GetModuleHandle("ntdll.dll"), "ZwQueryInformationProcess"); if ((ZwSuspendProcess == NULL) || \ (ZwQuerySystemInformation == NULL) || \ (ZwQueryObject == NULL) || \ (ZwResumeProcess == NULL) || \ (ZwQueryInformationProcess == NULL)) return FALSE; return TRUE; } // Close the specified Mutex. If there is a problem, follow the official account. Ren Niaofei reverse engineering BOOL closeMutexHandle(UINT Proc_id, const wchar_t* Mutex_name) { HANDLE duplicateHnd, sourceHnd = 0; DWORD procHndNum; SYSTEM_HANDLE* currnetHnd; DWORD buffLen = 0x1000; NTSTATUS status; SYSTEM_HANDLE_INFORMATION* buff = (SYSTEM_HANDLE_INFORMATION*) malloc(buffLen); UINT count = 0; if ((ElevatePrivileges() == FALSE) || (GetUnDocumentAPI() == FALSE)) return FALSE; do { status = ZwQuerySystemInformation(SystemHandleInformation, buff, buffLen, & amp; buffLen); if (status == STATUS_INFO_LENGTH_MISMATCH) { free(buff); buff = (SYSTEM_HANDLE_INFORMATION*)malloc(buffLen); } else break; } while (1); OBJECT_NAME_INFORMATION* objNameInfo = (OBJECT_NAME_INFORMATION*) malloc(0x1000); OBJECT_NAME_INFORMATION* objTypeInfo = (OBJECT_NAME_INFORMATION*) malloc(0x1000); for (int idx = 0; idx < (int)buff->HandleCount; idx ++ ) { currnetHnd = &(buff->Handles[idx]); if (currnetHnd->ProcessId == Proc_id) { sourceHnd = OpenProcess(PROCESS_ALL_ACCESS | PROCESS_DUP_HANDLE | PROCESS_SUSPEND_RESUME, FALSE, Proc_id); (ZwSuspendProcess)(sourceHnd); (ZwQueryInformationProcess)(sourceHnd, ProcessHandleInformation, &procHndNum, sizeof(DWORD), NULL); //The effective handle of the process starts from 4 and increments by 4 each time unsigned short hndNum = 4; for (int idx = 0; idx < (int)procHndNum; hndNum + = 4) { //Judge whether it is a valid handle, return TRUE, it is a valid handle if (!DuplicateHandle(sourceHnd, (HANDLE)hndNum, GetCurrentProcess(), &duplicateHnd, 0, FALSE, DUPLICATE_SAME_ACCESS)) { continue; } else { memset(objNameInfo, 0, 0x1000); memset(objTypeInfo, 0, 0x1000); ZwQueryObject((HANDLE)duplicateHnd, ObjNameInformation, objNameInfo, 0x1000, NULL); ZwQueryObject((HANDLE)duplicateHnd, ObjTypeInformation, objTypeInfo, 0x1000, NULL); // find mutex compare name if (wcscmp(objTypeInfo->ObjectName. Buffer, L"Mutant") == 0) { if (objNameInfo->ObjectName.Length != 0 & amp; & amp; wcsstr(objNameInfo->ObjectName.Buffer, Mutex_name) != 0) { printf("%ws\ ", objNameInfo->ObjectName.Buffer); CloseHandle(duplicateHnd); if (DuplicateHandle(sourceHnd, (HANDLE)hndNum, GetCurrentProcess(), &duplicateHnd, 0, FALSE, DUPLICATE_CLOSE_SOURCE)) { CloseHandle(duplicateHnd); (ZwResumeProcess)(sourceHnd); return TRUE; } } count + + ; if (count == 20) { return FALSE; } } CloseHandle(duplicateHnd); idx++; } } } } return FALSE; }
Then we call
closeMutexHandle(sm[i].pid,L"AOD_Game");
Fourth, adjust the window position to open more
Then we do the window layout
Ok, our effect is achieved.