应用是如何判断多开

一、通过查找窗口标题或者类名来判断程序是否正在运行。

二、通过互斥对象确定程序是否运行,大多数软件都是使用CreateMutexW 判断多开的。

三、内存映射物理文件,控制多开。

微信是使用 CreateMutexW 函数判断多开的。

CreateMutexW 是如何判断多开的。

微软 MSDN 文档

CreateMutexW  这个函数就是根据变量创建一个锁,下回再用相同的变量调用 CreateMutexW  的时候就可以控制是否允许多开。

如何找到微信中的 CreateMutexW 方法和互斥体

使用 procexp.exe 寻找互斥体,这个软件是微软官方出的 微软官网下载地址

登陆微信后

打开 Process Explorer

找到微信的进程 WeChat.exe

CTRL + L 查看微信这个进程占用的各种资源

根据这个字符串的名字判断出来的 _WeChat_App_Instance_Identity_Mutex_Name 这个字符串就是微信的互斥体

找到 CreateMutexW 这个函数

使用 OD 直接在内存中找 CreateMutexW 方法,并调试 CreateMutexW 方法,手动修改互斥体,启动多个微信实例。

使用 OD 打开微信

CTRL + G 搜索 CreateMutexW 方法

跳转到 CreateMutexW 方法后,在这个位置打一个断点 F2

打完断点之后继续运行

在堆栈窗口可以看到 CreateMutexW 方法传递的三个参数

第一个参数:NULL

第二个参数:FALSE

第三个参数:_WeChat_App_Instance_Identity_Mutex_Name

获取到 _WeChat_App_Instance_Identity_Mutex_Name 这个互斥体在内存中的地址 02BBB198

使用 CE 修改 _WeChat_App_Instance_Identity_Mutex_Name 这个字符串

打开CE后选择进程

选择当前进程

选择微信进程

打开

点击 手动添加地址

输入互斥体的内存地址

选择字符串

字符串长度设置成110

勾选 Unicode

选择数值这列,双击 _WeChat_App_Instance_Identity_Mutex_Name

在弹框中修改一下这个字符串

内存修改完成,回到 OD

F2 取消断点

继续运行,直到打开一个微信

这个微信他身上的互斥体就是手动修改的那个字符串

通过正常途径在启动一个微信

正常启动

正常登陆

代码思路

思路
PC:启动微信到登陆页面(那个页面都行,只要微信进程起来就可以了)
代码:
一、提升当前进程权限,提升到最大
二、获取当前操作系统中指定的所有进程(多开的时候会有多个微信进程,进程名字是一模一样的)
三、获取到系统中所有资源(内存中的资源,包含 文件,路径,锁,事件,线程等等)
四、通过指定的进程和已找到的资源,匹配到防多开的那个互斥体
4.1 干掉这个互斥体
PC:可以正常启动下一个微信 使用
启动微信后,如果想在启动一个微信,执行一遍代码就可以正常启动了
微信版本:3.4.5.27
已在三台电脑上测试(物理机)
理论上应该是所有版本的微信都能用 (只要这个互斥体的字符串不变) 缺点:直接修改了微信进程中的资源
/*
processName 微信进程名称
nutexName 微信互斥体字符串
*/
void CloseMutex(const WCHAR* processName, const WCHAR* nutexName) {
// 提升当前进程的访问权限
ElevatePrivileges();
// 找到所有的指定的进程 (多开的情况下会有多个)
vector<DWORD> pidList;
pidList = GetProcessIdsByName((WCHAR*)processName);
if (pidList.size() == 0)
{
// 没有开启或者没有找到指定的进程
return;
}
// 获取到操作系统所有进程的资源
LPVOID lp = GetSystemProcessHandleInfo();
// 遍历所有的指定进程
for (int i = 0; i < pidList.size(); i++)
{
// 遍历从系统中获取到的所有进程 与 指定进程进行匹配
// 遍历指定进程中的资源
// 找到互斥体
// 直接干掉这个互斥体
// 完成,后面就可以继续打开PC端微信了
EnumObjInfo(lp, pidList[i], processName);
}
}

提升权限

bool ElevatePrivileges() {
HANDLE hToken = NULL;
//打开当前进程的访问令牌
int hRet = OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &hToken);
if (hRet)
{
TOKEN_PRIVILEGES tp;
tp.PrivilegeCount = 1;
//取得描述权限的LUID
LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid);
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
//调整访问令牌的权限
AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);
CloseHandle(hToken);
}
return TRUE;
}

根据进程名字,获取到系统中全部的进程信息

vector<DWORD> GetProcessIdsByName(WCHAR* processName) {
vector<DWORD> pidList;
//HANDLE
HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hProcessSnap == FALSE)
{
return pidList;
}
PROCESSENTRY32 pe32;
pe32.dwSize = sizeof(PROCESSENTRY32); BOOL bRet = Process32First(hProcessSnap, &pe32);
while (bRet)
{
if (wcscmp(pe32.szExeFile, processName) == 0)
{
pidList.push_back(pe32.th32ProcessID);
}
bRet = Process32Next(hProcessSnap, &pe32);
}
CloseHandle(hProcessSnap);
return pidList;
}

获取系统中的所有资源

LPVOID GetSystemProcessHandleInfo()
{
ULONG cbBuffer = 0x4000;
LPVOID pBuffer = NULL;
NTSTATUS sts;
do
{
pBuffer = malloc(cbBuffer);
if (pBuffer == NULL)
{
return NULL;
}
memset(pBuffer, 0, cbBuffer);
hNtDLL = GetModuleHandle(TEXT("ntdll.dll"));
if (!hNtDLL)
return 0; ZWQUERYSYSTEMINFORMATION ZwQuerySystemInformation = (ZWQUERYSYSTEMINFORMATION)
GetProcAddress(hNtDLL, "ZwQuerySystemInformation");
sts = ZwQuerySystemInformation(SystemHandleInformation, pBuffer, cbBuffer, NULL);
if (sts == STATUS_INFO_LENGTH_MISMATCH)
{
free(pBuffer);
pBuffer = NULL;
cbBuffer = cbBuffer + 0x4000; // 初始分配的空间不足+4000h
}
} while (sts == STATUS_INFO_LENGTH_MISMATCH);
return pBuffer;
}

匹配到互斥体,并干掉他

void EnumObjInfo(LPVOID pBuffer, DWORD pid, const WCHAR* processName) {
char szType[128] = { 0 };
char szName[512] = { 0 };
DWORD dwFlags = 0;
POBJECT_NAME_INFORMATION pNameInfo;
POBJECT_NAME_INFORMATION pNameType;
PSYSTEM_HANDLE_INFORMATION_EX pInfo = (PSYSTEM_HANDLE_INFORMATION_EX)pBuffer;
ULONG OldPID = 0;
for (DWORD i = 0; i < pInfo->NumberOfHandles; i++)
{
if (OldPID != pInfo->Information[i].ProcessId)
{
if (pInfo->Information[i].ProcessId == pid)
{
HANDLE newHandle;
NtQueryObject p_NtQueryObject = (NtQueryObject)GetProcAddress(hNtDLL, "NtQueryObject");
if (p_NtQueryObject == NULL)
{
return;
}
DuplicateHandle(OpenProcess(PROCESS_ALL_ACCESS, FALSE, pInfo->Information[i].ProcessId), (HANDLE)pInfo->Information[i].Handle, GetCurrentProcess(), &newHandle, DUPLICATE_SAME_ACCESS, FALSE, DUPLICATE_SAME_ACCESS);
NTSTATUS status1 = p_NtQueryObject(newHandle, ObjectNameInformation, szName, 512, &dwFlags);
NTSTATUS status2 = p_NtQueryObject(newHandle, ObjectTypeInformation, szType, 128, &dwFlags); pNameInfo = (POBJECT_NAME_INFORMATION)szName;
pNameType = (POBJECT_NAME_INFORMATION)szType; if (strcmp(szName, "") && strcmp(szType, "") && status1 != 0xc0000008 && status2 != 0xc0000008)
{
if (wcsstr(pNameType->Name.Buffer, L"Mutant"))
{
pNameInfo = (POBJECT_NAME_INFORMATION)szName;
pNameType = (POBJECT_NAME_INFORMATION)szType;
if (wcsstr(pNameInfo->Name.Buffer, L"_WeChat_App_Instance_Identity_Mutex_Name"))
{
if (DuplicateHandle(OpenProcess(PROCESS_ALL_ACCESS, FALSE, pInfo->Information[i].ProcessId), (HANDLE)pInfo->Information[i].Handle, GetCurrentProcess(), &newHandle, 0, FALSE, DUPLICATE_CLOSE_SOURCE))
{
CloseHandle(newHandle);
};
}
}
}
}
}
}
}

C++ 微信多开的更多相关文章

  1. macOS 版微信小助手,支持微信多开、防撤回、远程控制mac、自动回复等等

    微信小助手 GitHub大牛提供的微信小助手是一款插件,该插件具备多开.防撤回.免手机认证登录.自动回复.远程控制自己的 macOS.群发等众多功能 GitHub网址:https://github.c ...

  2. 微信多开脚本(Windows,Mac)

    微信多开 以下内容仅用于学习使用.严禁用于非法用途,违者自负. Windows 多开 Windows 版本的微信在一些比较新的版本好像限制了多开,我们这里提供一个版本(也是官方的).https://p ...

  3. 目前用下来最溜的MacOS微信多开工具!

    一个生活微信,一个工作微信是很多上班族的基本配置. 但由于微信客户端在PC端上只能打开一个,这使得在上班时候就非常不便,一个号在PC端上登录,一个在手机上使用,但是上班时候又不能一直看手机,不然老板还 ...

  4. .Net微信网页开发之JSSDK使用步骤和配置信息timestamp(时间戳),nonceStr(随机串),signature(签名),access_token(接口调用凭据)的生成获取讲解

    前言: 因为接下来会有几篇关于微信JS-SDK功能使用的文章,主要会对微信分享,获取设备信息,获取地理位置,微信扫一扫这几个功能进行讲解.而这几个功能都是围绕着微信JS-SDK实现的,首先使用微信JS ...

  5. 微信多开简单实现 WeXinMoreOpen.bat

    新建一个 WeXinMoreOpen.bat 文件,内容如下 @echo off D: cd "D:\Program Files (x86)\Tencent\WeChat" sta ...

  6. macOS 微信多开插件

    macOS版本微信默认只能开一个,安装 WeChatTweak-macOS 插件即可实现多开. 效果图 安装步骤打开终端输入一下命令: git clone https://github.com/Sun ...

  7. PC微信多开

    1.桌面上面新建一个  多开.txt . 2.将下面的内容拷贝进去 TASKKILL /F /IM wechat.exestart "" "E:\wechat\WeCha ...

  8. 微信企业号开发之weixin://preInjectJSBridge/fail

    最近几天遇到个奇怪的问题,目前只有在Andriod平台上出现:weixin://preInjectJSBridge/fail 不止我一个人遇到这个问题,群里也有好几个问了这个问题.这个问题直接导致我们 ...

  9. iOS银联,支付宝,微信,ping++开发文档

    银联支付 银联支付目测只需两个参数 1.tn 其实就是订单号 2.mode 是测试环境还是线上环境 开发步骤 1.首先客户端浏览商品,点击下单,请求到达商户后台 2.商户后台在提交订单信息到银联后台 ...

随机推荐

  1. Java 8 的内存结构

    Java8内存结构图 虚拟机内存与本地内存的区别 Java虚拟机在执行的时候会把管理的内存分配成不同的区域,这些区域被称为虚拟机内存,同时,对于虚拟机没有直接管理的物理内存,也有一定的利用,这些被利用 ...

  2. 一个不常遇到的HbuilderX自动化测试运行问题

    晚上10点左右,刚好说想研究一下uniapp项目中怎么进行自动测试,于是跟着插件的官方教程开始配置测试环境,写好了一个简单的测试脚本,然后图形化操作IDE运行测试,却报错了一大片错误信息,如下所示: ...

  3. uniapp动态修改导航栏

    1.修改导航栏buttons 如图动态修改角标 <template> <view> </view> </template> <script> ...

  4. Spring @Component 注解的使用

    使用说明 这个注解用于声明当前的类是一个组件类,Spring 会通过类路径扫描来自动侦测和自动装配这些组件,创建一个个 bean 后,注册到 Spring 容器中. 带 @Component 注解的类 ...

  5. 在动态组件上使用 keep-alive

    ----------------------html.js.style----------------------------------------------- <!DOCTYPE html ...

  6. Centos下查看cpu核数

    1.概念物理CPU:实际Server中插槽上的CPU个数.物理cpu数量:可以数不重复的 physical id 有几个. 2.逻辑CPULinux用户对 /proc/cpuinfo 这个文件肯定不陌 ...

  7. Centos 7 上 查看MySQL当前使用的配置文件my.cnf的方法

    my.cnf是mysql启动时加载的配置文件,一般会放在mysql的安装目录中,用户也可以放在其他目录加载.总的来说,my.cnf类似与window中my.ini 使用locate my.cnf命令可 ...

  8. CentOS 7 使用unzip解压zip文件提示未找到命令的解决方法

    故障现象: 解决方法: 如果你使用unzip命令解压.zip文件,提示未找到命令,可能是你没有安装unzip软件,下面是安装方法 [root@localhost www]# yum install - ...

  9. java 封装 总结

    1.前言 老是被问什么是java 封装...很基础的一个问题 ,其实我们一直在写的东西但不知道怎么称呼. 比如 在entity实体类 里面老用到的 getter 和 setter 方法其实就是封装的方 ...

  10. 创建react开发环境

    准备工作 1.下载node.js(http://nodejs.cn/download/)推荐下载长期支持的版本 2.下载cnpm(https://jingyan.baidu.com/article/9 ...