吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 3894|回复: 46
上一主题 下一主题
收起左侧

[调试逆向] 微信逆向:防多开检测原理和绕过

  [复制链接]
跳转到指定楼层
楼主
kn0sky 发表于 2024-7-15 13:37 回帖奖励

本文仅用于学习研究,请勿用于非法用途和商业用途!如因此产生任何法律纠纷,均与作者无关!

本篇主要从日志和反编译代码的角度,分析一下定位到防多开功能,以及原理和绕过

绕过代码不是本文重点,选自网上的一种有趣的方法进行介绍(参考资料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,这里能打印出日志,说明条件应该是默认就满足了的,不管也行

绕过防多开检测

知道原理后,这里处理互斥体检测方法就很多了,例如:

  • hook CreateMutexW函数,检查第三个参数,如果是硬编码的_WeChat_App_Instance_Identity_Mutex_Name,就修改一下这个命名

    • 需要在第一次创建互斥体之前劫持
  • 或者注入模块启动的时候做一件事,关闭这个命名互斥体

    • 获取到句柄,然后关闭句柄
  • 或者直接patch这里的跳转逻辑,让LastError == 183失效
  • 给这个互斥体设置权限,拒绝访问

最后这个思路很有趣,代码来自参考资料[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

免费评分

参与人数 14吾爱币 +29 热心值 +14 收起 理由
AxiaoWyaoA + 1 + 1 用心讨论,共获提升!
willJ + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
wholdcat + 1 + 1 非常棒,很详细. 推荐大佬可以再来姐妹篇,例如企业微信钉钉篇等.
SVIP008 + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
allspark + 1 + 1 用心讨论,共获提升!
梦入神机 + 3 + 1 我很赞同!
ouiazrael + 1 + 1 谢谢@Thanks!
scz + 1 + 1 谢谢
JackLSQ + 1 + 1 用心讨论,共获提升!
shanzhanzhe + 1 + 1 谢谢@Thanks!
wtujoxk + 3 + 1 用心讨论,共获提升!
GenW + 5 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
debug_cat + 2 + 1 谢谢@Thanks!
zxczxc19941102 + 1 + 1 鼓励转贴优秀软件安全工具和文档!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

推荐
 楼主| kn0sky 发表于 2024-7-18 12:38 |楼主
aaa140 发表于 2024-7-18 08:46
大佬问一下,那种快速点击启动2个微信的办法,为啥也可以实现双开,根据这个源码的思路

我猜可能是两个微信同时进入了创建互斥体对象的函数里,然后一个创建成功,一个触发了非183的错误,以至于没有进入检测到多开然后退出的分支
推荐
cooltnt 发表于 2024-7-19 11:48
我记得哪里看到过钉钉多开,就是用进程管理器杀掉2个进程中的一个,就可以多开,不知道微信能不能这么做
沙发
yycmd 发表于 2024-7-15 13:55
3#
摩天小马 发表于 2024-7-15 13:57
作者分析的非常清晰
4#
bester 发表于 2024-7-15 14:04
你最后给的那段源码,不适合企业微信,企业微信的逻辑又不一样
5#
apjsip 发表于 2024-7-15 14:08
谢谢分享!楼主厉害啦
6#
可坏 发表于 2024-7-15 14:13
Niubility,成功多开!
7#
Eserialize 发表于 2024-7-15 14:27
感谢楼主!楼主厉害啦!正想学习一下呢
8#
 楼主| kn0sky 发表于 2024-7-15 14:31 |楼主
bester 发表于 2024-7-15 14:04
你最后给的那段源码,不适合企业微信,企业微信的逻辑又不一样

这里还没分析企微呢(
9#
jia222 发表于 2024-7-15 14:56
分析的很清晰  不错
10#
mcdh17 发表于 2024-7-15 15:09
防多开的原理阐释的蛮清楚的  对于小白很友好  学习了
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-21 17:08

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表