本文转载于52pojie
0x00 前言
不知道大家有没有多个微信号,我反正有一两三个。
现在电脑端微信使用频率也比较高,主要用于大文件传输,或者手机电脑文件互传等等,除了不能收红包和看朋友圈,貌似电脑端没其他毛病。
哦,还有个毛病,只能开一个微信,只能开一个,开一个,一个…
不管这些有的没的,今天的主题是,怎么样在电脑上开多个微信客户端!
0x01 分析
了解过单实例的同学,应该都知道大概是怎么实现的单开。简单说下,大都通过判断Mutex、Event、File等等是否已经存在,存在则退出当前开启进程(说明已经有一个进程了),这样也就是单实例了。
那只要找到微信是通过什么标志来实现单实例的,然后干掉这个标志即可。然后…基于这个思路,我们上工具。
使用procexp找到微信进程,然后翻了一遍句柄。找到疑是的一段句柄。
感觉这两个都像,不管了,上pchunter,kill掉句柄试一下。
经过尝试,发现_WeChat_App_Instance_Identity_Mutex_Name是单实例标志(kill句柄后可以开第二个客户端),WeChat_GlobalConfig_Multi_Process_Mutex没用。
既然如此,那开始码代码吧。
0x02 代码
可能的方案:
- 找微信判断标识的代码位置,然后直接patch掉,或者整个dll进去patch。然后大致去翻了一下,貌似代码在wechatwin.dll,然后加了vmp壳,所以就不折腾这个了。
- 直接通过代码kill掉这个Mutex的句柄(类似Pchunter操作),然后就可以开启第二个实例了,貌似明显更有优势啊。
- 额,如果觉得无所谓,每次开之前用pchunter关一次句柄也行,下面就不用看了…
这里选择第二个方案,开始代码。
流程:
- 枚举句柄,找到_WeChat_App_Instance_Identity_Mutex_Name的mutant
- duplicate句柄到本进程,然后close
- 启动微信
下面是主要代码:
#include <Windows.h> #include <TlHelp32.h> #include <Shlwapi.h> #pragma comment(lib, "shlwapi") #include "main.h" #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; } HANDLE DuplicateHandleEx(DWORD pid, HANDLE h, DWORD flags) { HANDLE hHandle = NULL; HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); if(hProc) { if(!DuplicateHandle(hProc, (HANDLE)h, GetCurrentProcess(), &hHandle, 0, FALSE, /*DUPLICATE_SAME_ACCESS*/flags)) { hHandle = NULL; } } CloseHandle(hProc); return hHandle; } int GetProcIds(LPWSTR Name, DWORD* Pids) { PROCESSENTRY32 pe32 = {sizeof(pe32)}; int num = 0; HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if(hSnap) { if(Process32First(hSnap, &pe32)) { do { if(!wcsicmp(Name, pe32.szExeFile)) { if(Pids) { Pids[num++] = pe32.th32ProcessID; } } } while(Process32Next(hSnap, &pe32)); } CloseHandle(hSnap); } return num; } BOOL IsTargetPid(DWORD Pid, DWORD* Pids, int num) { for(int i=0; i<num; i++) { if(Pid == Pids[i]) { return TRUE; } } return FALSE; } int PatchWeChat() { DWORD dwSize = 0; POBJECT_NAME_INFORMATION pNameInfo; POBJECT_NAME_INFORMATION pNameType; PVOID pbuffer = NULL; NTSTATUS Status; int nIndex = 0; DWORD dwFlags = 0; char szType[128] = {0}; char szName[512] = {0}; ElevatePrivileges(); DWORD Pids[100] = {0}; DWORD Num = GetProcIds(L"WeChat.exe", Pids); if(Num == 0) { return 0; } if(!ZwQuerySystemInformation) { goto Exit0; } pbuffer = VirtualAlloc(NULL, 0x1000, MEM_COMMIT, PAGE_READWRITE); if(!pbuffer) { goto Exit0; } Status = ZwQuerySystemInformation(SystemHandleInformation, pbuffer, 0x1000, &dwSize); if(!NT_SUCCESS(Status)) { if (STATUS_INFO_LENGTH_MISMATCH != Status) { goto Exit0; } else { // 这里大家可以保证程序的正确性使用循环分配稍好 if (NULL != pbuffer) { VirtualFree(pbuffer, 0, MEM_RELEASE); } if (dwSize*2 > 0x4000000) // MAXSIZE { goto Exit0; } pbuffer = VirtualAlloc(NULL, dwSize*2, MEM_COMMIT, PAGE_READWRITE); if(!pbuffer) { goto Exit0; } Status = ZwQuerySystemInformation(SystemHandleInformation, pbuffer, dwSize*2, NULL); if(!NT_SUCCESS(Status)) { goto Exit0; } } } PSYSTEM_HANDLE_INFORMATION1 pHandleInfo = (PSYSTEM_HANDLE_INFORMATION1)pbuffer; for(nIndex = 0; nIndex < pHandleInfo->NumberOfHandles; nIndex++) { if(IsTargetPid(pHandleInfo->Handles[nIndex].UniqueProcessId, Pids, Num)) { // HANDLE hHandle = DuplicateHandleEx(pHandleInfo->Handles[nIndex].UniqueProcessId, (HANDLE)pHandleInfo->Handles[nIndex].HandleValue, DUPLICATE_SAME_ACCESS ); if(hHandle == NULL) continue; Status = NtQueryObject(hHandle, ObjectNameInformation, szName, 512, &dwFlags); if (!NT_SUCCESS(Status)) { CloseHandle(hHandle); continue; } Status = NtQueryObject(hHandle, ObjectTypeInformation, szType, 128, &dwFlags); if (!NT_SUCCESS(Status)) { CloseHandle(hHandle); continue; } pNameInfo = (POBJECT_NAME_INFORMATION)szName; pNameType = (POBJECT_NAME_INFORMATION)szType; WCHAR TypName[1024] = {0}; WCHAR Name[1024] = {0}; wcsncpy(TypName, (WCHAR*)pNameType->Name.Buffer, pNameType->Name.Length/2); wcsncpy(Name, (WCHAR*)pNameInfo->Name.Buffer, pNameInfo->Name.Length/2); // 匹配是否为需要关闭的句柄名称 if (0 == wcscmp(TypName, L"Mutant")) { if (wcsstr(Name, L"_WeChat_App_Instance_Identity_Mutex_Name")) { CloseHandle(hHandle); hHandle = DuplicateHandleEx(pHandleInfo->Handles[nIndex].UniqueProcessId, (HANDLE)pHandleInfo->Handles[nIndex].HandleValue, DUPLICATE_CLOSE_SOURCE ); if(hHandle) { printf("+ Patch wechat success!\n"); CloseHandle(hHandle); } else { printf("- Patch error: %d\n", GetLastError()); } goto Exit0; } } CloseHandle(hHandle); } } Exit0: if (NULL != pbuffer) { VirtualFree(pbuffer, 0, MEM_RELEASE); } return 0; } void OpenWeChat() { //HKEY_CURRENT_USER\Software\Tencent\WeChat InstallPath = xx HKEY hKey = NULL; if(ERROR_SUCCESS != RegOpenKey(HKEY_CURRENT_USER, L"Software\\Tencent\\WeChat", &hKey)) { return; } DWORD Type = REG_SZ; WCHAR Path[MAX_PATH] = {0}; DWORD cbData = MAX_PATH*sizeof(WCHAR); if(ERROR_SUCCESS != RegQueryValueEx(hKey, L"InstallPath", 0, &Type, (LPBYTE)Path, &cbData)) { goto __exit; } PathAppend(Path, L"WeChat.exe"); ShellExecute(NULL, L"Open", Path, NULL, NULL, SW_SHOW); __exit: if(hKey) { RegCloseKey(hKey); } } int main() { //TEST //\Sessions\1\BaseNamedObjects\_WeChat_App_Instance_Identity_Mutex_Name //HANDLE hMutex = CreateMutexA(NULL, FALSE, "_WeChat_App_Instance_Identity_Mutex_Name"); //HANDLE hMutex = OpenMutexA(MUTEX_ALL_ACCESS, FALSE, "_WeChat_App_Instance_Identity_Mutex_Name"); //DUPLICATE_CLOSE_SOURCE printf("------------------------------------------------------------\n"); printf("--------------- WeChat电脑端多开器 -------------------------\n"); printf("--------------- 2017年5月14日 Anhkgg -----------------------\n"); printf("--------------- CopyRight (C) 2017 by Anhkgg ---------------\n"); printf("------------------------------------------------------------\n\n"); PatchWeChat(); printf("+ Patch Finish!\n"); printf("+ Start new wechat!\n"); OpenWeChat(); printf("+ Exit...\n"); //getchar(); return 0; }
代码就这样,有注释,就不再啰嗦。
完整代码,请看后面的地址。
0x03 总结
一个小玩意,供大家一笑。
编译好的可执行文件:
https://github.com/anhkgg/multi_wechat_pc/raw/master/WeChat%E5%A4%9A%E5%BC%80.exe
源码地址:https://github.com/anhkgg/multi_wechat_pc
博客原文:https://anhkgg.github.io/wechat-multi-pc
- 文章2305
- 用户1336
- 访客11456237
没有努力,天份不代表什么。
MySQL 数据库优化
This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its de
免ROOT实现模拟点击任意位置
Mobaxterm终端神器
CreateProcessW要注意的细节问题
Autonomous NAT Traversal
【教程】win10 彻底卸载edge浏览器
eclipse工程基于Xposed的一个简单Hook
排名前5的开源在线机器学习
Mac OS最简单及(Karabiner)快捷键设置
发一款C++编写的麻将
VMware NAT端口映射外网访问虚拟机linux
独家发布最新可用My-AutoPost——wordpress 采集器