C++ 微信多開的實現(xiàn)
應(yīng)用是如何判斷多開
一、通過查找窗口標(biāo)題或者類名來判斷程序是否正在運行。
二、通過互斥對象確定程序是否運行,大多數(shù)軟件都是使用CreateMutexW 判斷多開的。
三、內(nèi)存映射物理文件,控制多開。
微信是使用 CreateMutexW 函數(shù)判斷多開的。
CreateMutexW 是如何判斷多開的。
微軟 MSDN 文檔
CreateMutexW 這個函數(shù)就是根據(jù)變量創(chuàng)建一個鎖,下回再用相同的變量調(diào)用CreateMutexW 的時候就可以控制是否允許多開。
如何找到微信中的 CreateMutexW 方法和互斥體
使用 procexp.exe 尋找互斥體,這個軟件是微軟官方出的 微軟官網(wǎng)下載地址

登陸微信后
打開 Process Explorer
找到微信的進(jìn)程 WeChat.exe
CTRL + L 查看微信這個進(jìn)程占用的各種資源
根據(jù)這個字符串的名字判斷出來的_WeChat_App_Instance_Identity_Mutex_Name 這個字符串就是微信的互斥體
找到 CreateMutexW 這個函數(shù)
使用 OD 直接在內(nèi)存中找 CreateMutexW 方法,并調(diào)試 CreateMutexW 方法,手動修改互斥體,啟動多個微信實例。

使用 OD 打開微信

CTRL + G 搜索 CreateMutexW 方法

跳轉(zhuǎn)到 CreateMutexW 方法后,在這個位置打一個斷點 F2

打完斷點之后繼續(xù)運行
在堆棧窗口可以看到 CreateMutexW 方法傳遞的三個參數(shù)
第一個參數(shù):NULL
第二個參數(shù):FALSE
第三個參數(shù):_WeChat_App_Instance_Identity_Mutex_Name

獲取到 _WeChat_App_Instance_Identity_Mutex_Name 這個互斥體在內(nèi)存中的地址 02BBB198
使用 CE 修改 _WeChat_App_Instance_Identity_Mutex_Name 這個字符串

打開CE后選擇進(jìn)程
選擇當(dāng)前進(jìn)程
選擇微信進(jìn)程
打開

點擊 手動添加地址
輸入互斥體的內(nèi)存地址
選擇字符串
字符串長度設(shè)置成110
勾選 Unicode

選擇數(shù)值這列,雙擊 _WeChat_App_Instance_Identity_Mutex_Name
在彈框中修改一下這個字符串
內(nèi)存修改完成,回到 OD

F2 取消斷點
繼續(xù)運行,直到打開一個微信
這個微信他身上的互斥體就是手動修改的那個字符串

通過正常途徑在啟動一個微信
正常啟動
正常登陸
代碼思路
思路
PC:啟動微信到登陸頁面(那個頁面都行,只要微信進(jìn)程起來就可以了)
代碼:
一、提升當(dāng)前進(jìn)程權(quán)限,提升到最大
二、獲取當(dāng)前操作系統(tǒng)中指定的所有進(jìn)程(多開的時候會有多個微信進(jìn)程,進(jìn)程名字是一模一樣的)
三、獲取到系統(tǒng)中所有資源(內(nèi)存中的資源,包含 文件,路徑,鎖,事件,線程等等)
四、通過指定的進(jìn)程和已找到的資源,匹配到防多開的那個互斥體
4.1 干掉這個互斥體
PC:可以正常啟動下一個微信使用
啟動微信后,如果想在啟動一個微信,執(zhí)行一遍代碼就可以正常啟動了
微信版本:3.4.5.27
已在三臺電腦上測試(物理機(jī))
理論上應(yīng)該是所有版本的微信都能用 (只要這個互斥體的字符串不變)缺點:直接修改了微信進(jìn)程中的資源
/*
processName 微信進(jìn)程名稱
nutexName 微信互斥體字符串
*/
void CloseMutex(const WCHAR* processName, const WCHAR* nutexName) {
// 提升當(dāng)前進(jìn)程的訪問權(quán)限
ElevatePrivileges();
// 找到所有的指定的進(jìn)程 (多開的情況下會有多個)
vector<DWORD> pidList;
pidList = GetProcessIdsByName((WCHAR*)processName);
if (pidList.size() == 0)
{
// 沒有開啟或者沒有找到指定的進(jìn)程
return;
}
// 獲取到操作系統(tǒng)所有進(jìn)程的資源
LPVOID lp = GetSystemProcessHandleInfo();
// 遍歷所有的指定進(jìn)程
for (int i = 0; i < pidList.size(); i++)
{
// 遍歷從系統(tǒng)中獲取到的所有進(jìn)程 與 指定進(jìn)程進(jìn)行匹配
// 遍歷指定進(jìn)程中的資源
// 找到互斥體
// 直接干掉這個互斥體
// 完成,后面就可以繼續(xù)打開PC端微信了
EnumObjInfo(lp, pidList[i], processName);
}
}提升權(quán)限
bool ElevatePrivileges() {
HANDLE hToken = NULL;
//打開當(dāng)前進(jìn)程的訪問令牌
int hRet = OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &hToken);
if (hRet)
{
TOKEN_PRIVILEGES tp;
tp.PrivilegeCount = 1;
//取得描述權(quán)限的LUID
LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid);
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
//調(diào)整訪問令牌的權(quán)限
AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);
CloseHandle(hToken);
}
return TRUE;
}根據(jù)進(jìn)程名字,獲取到系統(tǒng)中全部的進(jìn)程信息
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;
}獲取系統(tǒng)中的所有資源
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);
};
}
}
}
}
}
}
}
到此這篇關(guān)于C++ 微信多開的實現(xiàn)的文章就介紹到這了,更多相關(guān)C++ 微信多開內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++實現(xiàn)LeetCode(133.克隆無向圖)
這篇文章主要介紹了C++實現(xiàn)LeetCode(133.克隆無向圖),本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07
C語言使用strcmp()函數(shù)比較兩個字符串的實現(xiàn)
這篇文章主要介紹了C語言使用strcmp()函數(shù)比較兩個字符串的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01
淺析string類字符串和C風(fēng)格字符串之間的區(qū)別
string類是標(biāo)準(zhǔn)庫的類,并不是內(nèi)置類型,標(biāo)準(zhǔn)庫就像是我們自己定義的類差不多的,string類型對象沒有標(biāo)配'\0'結(jié)尾的2013-09-09
詳解C++中stoi/stol/stoll函數(shù)的用法
這篇文章主要為大家詳細(xì)介紹了C++中stoi、stol、stoll函數(shù)的具體用法,文中的示例代碼講解詳細(xì),對我們學(xué)校C++有一點的幫助,需要的可以參考一下2023-03-03
C/C++哈希表優(yōu)化LeetCode題解997找到小鎮(zhèn)的法官
這篇文章主要為大家介紹了C/C++哈希表優(yōu)化題解997找到小鎮(zhèn)的法官示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12

