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
已在三臺電腦上測試(物理機)
理論上應(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-07C語言使用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-03C/C++哈希表優(yōu)化LeetCode題解997找到小鎮(zhèn)的法官
這篇文章主要為大家介紹了C/C++哈希表優(yōu)化題解997找到小鎮(zhèn)的法官示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12