本文仅用于学习研究,请勿用于非法用途和商业用途!如因此产生任何法律纠纷,均与作者无关!
本篇主要从日志和反编译代码的角度,分析一下定位到防多开功能,以及原理和绕过
绕过代码不是本文重点,选自网上的一种有趣的方法进行介绍(参考资料0)
日志分析定位
打开一个wechat,然后用调试器打开另一个,在载入wechatwin.dll的时候注入模块打印日志,然后运行起来得到如下内容:
2 6.090196 63260 WeChat.exe (2024-7-9:10:15:32:389 42164)-i/AppletDesktopLaunchMgr:reg path = D:\Software\others\WeChat
3 6.090407 63260 WeChat.exe (2024-7-9:10:15:32:389 42164)-i/AppletDesktopLaunchMgr:same regPath
4 6.090681 63260 WeChat.exe (2024-7-9:10:15:32:390 42164)-i/FinderliveIpcMgr:process fake url: <NULL>
5 6.090837 63260 WeChat.exe (2024-7-9:10:15:32:390 42164)-w/FinderliveIpcMgr:unmatch fake url
6 6.090950 63260 WeChat.exe (2024-7-9:10:15:32:390 42164)-i/WeChat:GetLastError 183
7 6.091207 63260 WeChat.exe (2024-7-9:10:15:32:390 42164)-i/WeChat:mainwnd is null? 0
8 6.106538 63260 WeChat.exe (2024-7-9:10:15:32:405 42164)-e/MicroMsgWin:Already running
9 6.139109 63260 WeChat.exe (2024-7-9:10:15:32:437 42164)-i/GlobalConfigStorage:C:\Users\Admin\Documents default path is useful
10 6.143698 63260 WeChat.exe (2024-7-9:10:15:32:442 42164)-i/FileUtils:defautSaveDir is user document dir: C:\Users\Admin\Documents
11 6.144656 63260 WeChat.exe (2024-7-9:10:15:32:443 42164)-i/ConfigInfoStorage:ConfigFilePath : C:\Users\Admin\Documents\WeChat Files\All Users\config\config.data, filesize: 530
12 6.145636 63260 WeChat.exe (2024-7-9:10:15:32:444 42164)-i/ConfigInfoStorage:read configfile size: 530
13 6.145935 63260 WeChat.exe (2024-7-9:10:15:32:444 42164)-i/WebViewMgr:WebViewMgr()
14 6.146118 63260 WeChat.exe (2024-7-9:10:15:32:445 42164)-i/WebViewMgr:unInit()
15 6.341011 63260 WeChat.exe <process started at 10:14:25.263 has terminated with exit code -1 (0xffffffff)>
有一条有趣的日志:
8 6.106538 63260 WeChat.exe (2024-7-9:10:15:32:405 42164)-e/MicroMsgWin:Already running
代码原理分析
搜索字符串:
.rdata:00000001849622D0 0000001E C pingCheck is already running.
.rdata:0000000184962338 0000001C C dnsCheck is already running
.rdata:00000001849623A0 0000001D C tcpCheck is already running.
.rdata:0000000184962408 0000001E C httpCheck is already running.
.rdata:0000000184962478 00000024 C tracerouteCheck is already running.
.rdata:00000001849624F0 00000020 C reqbufCheck is already running.
.rdata:0000000184966DF8 0000002E C DnsCheck is already running, skip this action
.rdata:0000000184966F60 0000002F C HttpCheck is already running, skip this action
.rdata:00000001849670A0 0000002F C PingCheck is already running, skip this action
.rdata:0000000184967280 00000034 C ReqBufferCheck is already running, skip this action
.rdata:0000000184967360 0000002E C TcpCheck is already running, skip this action
.rdata:0000000184967458 00000035 C TracerouteCheck is already running, skip this action
.rdata:0000000184F05D30 00000010 C already running
.rdata:00000001850A7AD0 00000010 C Already running
最后一条就是我们要定位的地方,交叉引用到:
v64 = v113[0];
if ( (unsigned __int8)sub_1830F0E30() )
{
if ( !LOBYTE(v113[0]) )
{
LABEL_103:
pExceptionObject = *(_OWORD *)xmmword_184DED0B0;
v117 = *(_OWORD *)xmmword_184DED0B0;
v116 = *(_OWORD *)xmmword_184DED0B0;
v115 = *(_OWORD *)xmmword_184DED0B0;
v114 = *(_OWORD *)xmmword_184DED0B0;
v118 = *(_OWORD *)xmmword_184DED0B0;
log_message(
4,
(__int64)"D:\\Tools\\agent\\workspace\\MicroMsgWindowsV3911\\MicroMsgWin\\MicroMsgWin.cpp",
695,
(__int64)"StartWechat",
"MicroMsgWin",
"Already running",
&v118,
&v114,
&v115,
&v116,
&v117,
&pExceptionObject);
LABEL_162:
sub_1830F9150(v113);
ShutdownMMMojo();
unknown_libname_1695(lpLibFileName);
goto LABEL_165;
}
LABEL_108:
memset(ClassName, 0, sizeof(ClassName));
if ( GetClassNameW(hWnd, ClassName, 256) )
{
v65 = 0i64;
while ( ClassName[v65] == aWechatloginwnd[v65] && ClassName[v65 + 1] == aWechatloginwnd[v65 + 1] )
{
v65 += 2i64;
if ( v65 == 20 )
goto LABEL_113;
}
}
goto LABEL_103;
}
这应该就是StartWechat函数,这里有2个if条件
第一个是要满足sub_1830F0E30返回true,第二个是满足v113=0
首先看这个函数,第一部分:
v1 = v0;
Block[0] = v0;
if ( !v0 )
{
sub_181B49DE0(&pExceptionObject, "out of memory");
CxxThrowException(&pExceptionObject, (_ThrowInfo *)&_TI2_AVruntime_error_std__);
}
*v0 = 0;
HIDWORD(Block[1]) = 40;
wcsncpy(v0, L"_WeChat_App_Instance_Identity_Mutex_Name", 0x28ui64);
v1[40] = 0;
LODWORD(Block[1]) = 40;
GetCurrentProcessId();
v2 = &DirectoryName;
if ( *v1 )
v2 = v1;
MutexW = CreateMutexW(0i64, 0, v2);
v4 = 0;
if ( MutexW )
{
LastError = GetLastError();
BYTE8(v29) = 0;
v30[0] = LastError;
*(_OWORD *)&v30[1] = *(_OWORD *)xmmword_184DED0B0;
v31 = *(_OWORD *)xmmword_184DED0B0;
v32 = *(_OWORD *)xmmword_184DED0B0;
v33 = *(_OWORD *)xmmword_184DED0B0;
*(_OWORD *)lParam = *(_OWORD *)xmmword_184DED0B0;
log_message(
2,
(__int64)"D:\\Tools\\agent\\workspace\\MicroMsgWindowsV3911\\MicroMsgWin\\10_platform\\WeChat.cpp",
444,
(__int64)"WeChat::preInstanceAlreadyRun",
"WeChat",
"GetLastError %d",
(__int128 *)((char *)&v29 + 8),
(__int128 *)lParam,
&v33,
&v32,
&v31,
(__int128 *)&v30[1]);
if ( LastError == 183 )
{
v4 = 1;
CloseHandle(MutexW);
}
else
{
hObject = MutexW;
}
}
free(v1);
这里用了互斥体MutexW,通过CreateMutexW创建的,这个函数:用于创建或打开命名的或未命名的互斥体对象。
HANDLE CreateMutexW(
[in, optional] LPSECURITY_ATTRIBUTES lpMutexAttributes,
[in] BOOL bInitialOwner,
[in, optional] LPCWSTR lpName
);
第三个参数标识这是个命名互斥体,返回值:
- 如果函数成功,则返回值是新创建的互斥对象的句柄。
- 如果函数失败,则返回值为 NULL。 要获得更多的错误信息,请调用 GetLastError。
- 如果互斥体是命名互斥体,并且对象在调用此函数之前就存在,则返回值是现有对象的句柄, 而 GetLastError函数返回 ERROR_ALREADY_EXISTS。
这里明显是满足了这里的第三条,GetLastError返回183
这个函数剩下的部分需要满足v4=1,满足这个条件需要GetLastError返回183
关于v113,这里能打印出日志,说明条件应该是默认就满足了的,不管也行
绕过防多开检测
知道原理后,这里处理互斥体检测方法就很多了,例如:
最后这个思路很有趣,代码来自参考资料[0],以这个为例,这里下面介绍这个代码:
#include <iostream>
#include <aclapi.h>
#include <shlwapi.h>
#include <windows.h>
#pragma comment(lib,"Shlwapi.lib")
void enableMultiWeChat()
{
HANDLE hMutex = CreateMutexW(NULL, FALSE, L"_WeChat_App_Instance_Identity_Mutex_Name");
SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
PSID pEveryoneSID = NULL;
char szBuffer[4096] = { 0 };
PACL pAcl = (PACL)szBuffer;
AllocateAndInitializeSid(&SIDAuthWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &pEveryoneSID);
InitializeAcl(pAcl, sizeof(szBuffer), ACL_REVISION);
AddAccessDeniedAce(pAcl, ACL_REVISION, MUTEX_ALL_ACCESS, pEveryoneSID);
SetSecurityInfo(hMutex, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, pAcl, NULL);
}
int main()
{
enableMultiWeChat();
//WinExec("D:\\ProgramFiles\\Tencent\\WeChat\\WeChat.exe", SW_SHOW);
return 0;
}
AllocateAndInitializeSid用于分配SID,SID是Windows系统中用于唯一标识用户、组或计算机的安全标识符。代码中创建了一个表示“Everyone”的SID,用于在ACL中指定拒绝访问的对象。
紧接着的InitializeAcl和AddAccessDeniedAce,初始化并创建了访问控制项,ACL用于定义哪些用户或组能不能访问某个资源
SetSecurityInfo用于设置该句柄的权限,让任何人不能访问
所以当wecaht执行到CreateMutexW的时候,会因为没有权限返回NULL,从而不执行后面的代码以及返回0,从而绕过了多开检测
效果展示
参考资料
【原文链接】:https://www.kn0sky.com/?p=53495e9e-1f6e-4439-aed6-2561887af85a