解除游戏多开限制,关闭互斥体句柄

游戏限制多开有很多种方法

比如说遍历窗口,遍历进程,配置文件,注册表,互斥体,mac地址,ip,公共文件,内存映射等等.方法很多.

但是绝大部分游戏限制多开都是采用的互斥体.

这节课我们来讲解一下关闭互斥体句柄来实现多开.例子为CQYH

(这里的防护建议是,增加多种多开限制的方法 以及 逻辑中增加多该互斥体的使用,这样可以避免直接被恶意关闭)

一.验证限制多开

首先我们打开游戏

然后再开第二个窗口

发现已经检测到我们开了一个窗口,只是没有进行限制

再开第三个窗口的时候出现了限制

很明显限制了多开,只能开2个

二.工具查看互斥体并关闭

我们可以使用工具来查看互斥体,大家可以用XT,PCH等等工具

也可以直接到公众号  任鸟飞逆向  资源下载中下载以下工具

打开软件,找到我们的进程,右键查看句柄

 出现了很多句柄, 找到Mutant类型的句柄

发现有很多,没有名字的可以不管

剩下有名字的句柄有20-30个

我们逐一关闭,看看关闭哪个之后就可以多开窗口了

发现关闭这个就可以了,名字是  AOD_Game

 可以开3个窗口了

三,编写代码,关闭互斥体句柄

添加一个头文件"Mutex.h"

用作关闭互斥体句柄

#pragma once
#include <string.h>
#include <windows.h>
#include <winternl.h>
#include <TlHelp32.h>

// 定义需要的宏
#define STATUS_SUCCESS 0x00UL
#define STATUS_INFO_LENGTH_MISMATCH 0xC0000004

#define SystemHandleInformation 16
#define SE_DEBUG_PRIVILEGE 0x14

// 定义需要用到的结构体
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;

// 声明未导出API
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>

// 提升进程权限
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;
}

// 初始化未导出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;
}

// 关闭指定Mutex  有问题关注公众号  任鸟飞逆向
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, &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);
      //进程有效句柄从4开始,每次以4递增
      unsigned short hndNum = 4;
      for (int idx = 0; idx < (int)procHndNum; hndNum += 4)
      {
        //判断是否为有效句柄,返回TRUE,就是有效句柄
        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);

          //找到互斥体 比较名字
          if (wcscmp(objTypeInfo->ObjectName.Buffer, L"Mutant") == 0)
          {
            if (objNameInfo->ObjectName.Length != 0 && wcsstr(objNameInfo->ObjectName.Buffer, Mutex_name) != 0)
            {
              printf("%ws\n", 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;
}

然后我们调用


closeMutexHandle(sm[i].pid,L"AOD_Game");

四,调整窗口位置进行多开

然后我们进行窗口布局

好的,我们的效果实现了.

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
下面是通过 C++ 代码实现关闭指定进程中的指定互斥句柄,通过互斥名称判断的思路: 1. 首先需要获取指定互斥名称的互斥句柄。可以通过 `OpenMutex()` 函数来打开一个已存在的互斥。 ```c++ HANDLE hMutex = NULL; hMutex = OpenMutex(MUTEX_ALL_ACCESS, FALSE, "mutex name"); ``` 2. 如果获取到互斥句柄,则说明该互斥存在。可以使用 `ReleaseMutex()` 函数释放该互斥,从而关闭指定进程中的指定互斥句柄。 ```c++ if (hMutex != NULL) { ReleaseMutex(hMutex); CloseHandle(hMutex); } ``` 3. 如果没有获取到互斥句柄,则说明该互斥不存在或者该进程未创建该互斥。在这种情况下,可以通过 `EnumProcesses()` 函数获取当前系统中所有进程的 ID,然后逐个遍历所有进程,查找指定进程 ID,再通过 `OpenProcess()` 函数打开该进程,最后通过 `OpenMutex()` 函数获取该进程中的互斥句柄。 ```c++ DWORD pid = 0; DWORD aProcesses[1024], cbNeeded, cProcesses; if (!EnumProcesses(aProcesses, sizeof(aProcesses), &cbNeeded)) { // handle error } cProcesses = cbNeeded / sizeof(DWORD); for (DWORD i = 0; i < cProcesses; i++) { if (aProcesses[i] == target_process_id) { HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, target_process_id); if (hProcess != NULL) { // search for mutex handle in the target process // ... CloseHandle(hProcess); } break; } } ``` 请注意,这只是一种思路,具实现需要根据具情况进行调整和修改。同时,需要特别注意在处理进程和互斥时的权限问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值