kn0sky 发表于 2024-7-24 10:13

微信逆向:防撤回消息分析

本帖最后由 kn0sky 于 2024-7-24 10:29 编辑



依然是从日志出发,可以结合前几篇文章一块看

1. [微信逆向:hook 强制输出调试信息 - 『软件调试区』 - 吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn](https://www.52pojie.cn/thread-1940040-1-1.html)
2. [微信逆向:定位功能调用(以文本消息发送为例) - 『软件调试区』 - 吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn](https://www.52pojie.cn/thread-1940500-1-1.html)
3. [微信逆向:防多开检测原理和绕过 - 『软件调试区』 - 吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn](https://www.52pojie.cn/thread-1944151-1-1.html)

## 日志分析

从日志出发,清空日志,然后撤回消息,然后暂停打印新的日志,得到如下内容:(为了方便看,这里筛选出撤回相关的部分)

```c
网络相关的略
21      2.319700      7932      WeChat.exe      (2024-7-10:10:14:12:657 25604)-i/SyncMgr:net scene sync Finish. sync id: 75, accumulation: 0, sync ccount: 1
22      2.319873      7932      WeChat.exe      (2024-7-10:10:14:12:657 16196)-i/SyncMgr:start process cmdlist size : 1
23      2.319997      7932      WeChat.exe      (2024-7-10:10:14:12:657 16196)-i/SyncMgr:msg cmd count : 1
24      2.320204      7932      WeChat.exe      (2024-7-10:10:14:12:657 16196)-i/SyncMgr:doAddMsg srvid: 1454480870, msgtyp: 10002,cTime:1720577652,msgseq: 847233597 from : 用户1 to 用户2
25      2.320353      7932      WeChat.exe      (2024-7-10:10:14:12:657 16196)-i/SyncMgr:msg acctype 0 from user <NULL>
26      2.320499      7932      WeChat.exe      (2024-7-10:10:14:12:657 16196)-i/SyncMgr:Receive new xml : revokemsg

27      2.320652      7932      WeChat.exe      (2024-7-10:10:14:12:658 16196)-i/ChatRevokeMgr:rv 用户1 21907440767796979
28      2.321280      7932      WeChat.exe      (2024-7-10:10:14:12:658 16196)-i/SyncMgr:Update rv msg : 21907440767796979
29      2.321418      7932      WeChat.exe      (2024-7-10:10:14:12:658 16196)-i/ChatRevokeMgr:add or update rv msg : 21907440767796979 exist 1
30      2.321589      7932      WeChat.exe      (2024-7-10:10:14:12:659 16196)-i/MultiDBMsgMgr:DBName : MSG0.db Talker : 用户1, Id : 1
31      2.322783      7932      WeChat.exe      (2024-7-10:10:14:12:660 16196)-i/ChatMsgDeleteHelper:del msg attach id :21907440767796979 type 1 subtyp 0
34      2.323226      7932      WeChat.exe      (2024-7-10:10:14:12:660 16196)-i/ChatRevokeMgr:bIsSender 0, isTextType 1, reedit 0

35      2.323586      7932      WeChat.exe      (2024-7-10:10:14:12:660 25604)-i/ILinkTalkRoomMgr:getGroupVoipState, groupId 用户1 no banner, AllBannerCnt 0
36      2.323613      7932      WeChat.exe      (2024-7-10:10:14:12:660 16196)-i/MsgDBThreadWrapper:MyLaunch add task 18
37      2.323721      7932      WeChat.exe      (2024-7-10:10:14:12:661 25604)-i/SessionListItemUI:RefreshSessionGroipVoipStatus, name SYLPH status 0
38      2.323951      7932      WeChat.exe      (2024-7-10:10:14:12:661 10692)-i/MsgDBThreadWrapper:Task id 18 priority : 2 , wait time : 323 Micro Sec
39      2.323977      7932      WeChat.exe      (2024-7-10:10:14:12:661 16196)-i/ChatRevokeMgr:has msg 1 svrid 21907440767796979 subtype 0
```

差不多就筛成这样吧

这里有几条疑似的信息:

```c
26      2.320499      7932      WeChat.exe      (2024-7-10:10:14:12:657 16196)-i/SyncMgr:Receive new xml : revokemsg
27      2.320652      7932      WeChat.exe      (2024-7-10:10:14:12:658 16196)-i/ChatRevokeMgr:rv 用户1 21907440767796979
28      2.321280      7932      WeChat.exe      (2024-7-10:10:14:12:658 16196)-i/SyncMgr:Update rv msg : 21907440767796979
29      2.321418      7932      WeChat.exe      (2024-7-10:10:14:12:658 16196)-i/ChatRevokeMgr:add or update rv msg : 21907440767796979 exist 1
```

## 伪代码分析

就从这几条信息入手,rv应该是revoke(撤回)的缩写,搜索字符串定位:Receive new xml:

```c
      LOBYTE(v125) = 3;
      *((_QWORD *)&v125 + 1) = v21;
      v124 = *(_OWORD *)xmmword_184DED0B0;
      v123 = *(_OWORD *)xmmword_184DED0B0;
      v122 = *(_OWORD *)xmmword_184DED0B0;
      v121 = *(_OWORD *)xmmword_184DED0B0;
      v126 = *(_OWORD *)xmmword_184DED0B0;
      v22 = 2;
      log_message(
      2,
      (__int64)"D:\\Tools\\agent\\workspace\\MicroMsgWindowsV3911\\MicroMsgWin\\02_manager\\SyncMgr.cpp",
      3081,
      (__int64)"SyncMgr::GetNewXMLType",
      "SyncMgr",
      "Receive new xml : %s",
      &v125,
      v126,
      &v121,
      &v122,
      &v123,
      &v124);
      switch ( v3 )
      {
      case 7i64:
          v23 = 7i64;
          v24 = L"dynacfg";
          if ( *v21 >= 0x64u )
          {
            v25 = *v21 <= 0x64u;
            do
            {
            if ( !v25 )
                break;
            if ( v23 == 1 )
                goto LABEL_75;
            --v23;
            v26 = *(const wchar_t *)((char *)++v24 + (char *)v21 - (char *)L"dynacfg");
            v25 = v26 <= *v24;
            }
            while ( v26 >= *v24 );
          }
          goto LABEL_164;
      case 13i64:
```

这里是SyncMgr,同步消息的功能部分,GetNewXMLType应该意思是,同步消息通过xml结构进行,然后有很多类型,那么这个函数是判断出revoke msg的类型了

接下来往上一层追,判断出来类型了之后,应该就该执行操作了

```c
v30 = SyncMgr::GetNewXMLType(v56);
if ( !(unsigned __int8)sub_182318F20(v30, v8, a2, a3) )
{
    if ( v76 >= 0x10 )
    {
      v31 = Block;
      if ( v76 + 1 < 0x1000 || (v31 = (void *)*((_QWORD *)Block - 1), (unsigned __int64)(Block - v31 - 8) <= 0x1F) )
      {
      j_j_free_1_0(v31);
      return 0;
      }
LABEL_64:
      invalid_parameter_noinfo_noreturn();
    }
    return 0;
}
```

这里紧接着是一个if条件,是bool类型的函数,用了刚才判断类型的返回值v30作为函数第一个参数,应该是根据类型进行不同的操作

进去看看,这个函数不长,就全部贴出来了:

```c
char __fastcall SyncMgr::ProcessNewXMLMsg(int a1, __int64 a2, __int64 a3, _QWORD *a4)
{
int v7; // ebx
DWORD CurrentThreadId; // eax
__int64 v9; // rdi
char *v10; // rcx
char *v11; // rdx
__int64 v12; // rax
_DWORD *v13; // rbx
BOOL fPending; // BYREF
__int128 v16; // BYREF
__int128 v17; // BYREF
__int128 v18; // BYREF
__int128 v19; // BYREF
__int128 v20; // BYREF
int v21; // BYREF

v21 = a1;
v7 = dword_18590BCE4;
CurrentThreadId = GetCurrentThreadId();
v9 = v21;
if ( CurrentThreadId == v7 )
    goto LABEL_12;
if ( !InitOnceBeginInitialize(&stru_18593DE80, 0, fPending, 0i64) )
    goto LABEL_21;
if ( fPending )
{
    v10 = (char *)off_185756C40;
    v11 = (char *)off_185756C40 + 56;
    if ( off_185756C40 != (char *)off_185756C40 + 56 )
    {
      do
      {
      dword_18593DDE0[*(int *)v10] = 1;
      v10 += 4;
      }
      while ( v10 != v11 );
    }
    if ( !InitOnceComplete(&stru_18593DE80, 0, 0i64) )
LABEL_21:
      abort();
}
if ( dword_18593DDE0 != 1 )
{
LABEL_12:
    switch ( (_DWORD)v9 )
    {
      case 4:
      return sub_182300AE0(a2, a3, a4);       // 撤回
      case 0x21:
      sub_182301380(a2, a3, a4);
      break;
      case 0x24:
      sub_182301A90(a2, a3, a4);
      return 0;
      default:
      if ( (unsigned __int8)sub_182319190((unsigned int)v9, a2, a3) )
          return 1;
      break;
    }
    return 0;
}
LOBYTE(v16) = 0;
*((_QWORD *)&v16 + 1) = v9;
v12 = *(_QWORD *)(a3 + 48);
LOBYTE(v17) = 0;
*((_QWORD *)&v17 + 1) = v12;
*(_OWORD *)fPending = *(_OWORD *)xmmword_184DED0B0;
v18 = *(_OWORD *)xmmword_184DED0B0;
v19 = *(_OWORD *)xmmword_184DED0B0;
v20 = *(_OWORD *)xmmword_184DED0B0;
log_message(
    2,
    (__int64)"D:\\Tools\\agent\\workspace\\MicroMsgWindowsV3911\\MicroMsgWin\\02_manager\\SyncMgr.cpp",
    3550,
    (__int64)"SyncMgr::ProcessNewXMLMsg",
    "SyncMgr",
    "id %d xml type %d delay",
    &v17,
    &v16,
    &v20,
    &v19,
    &v18,
    (__int128 *)fPending);
v13 = (_DWORD *)a4;
if ( v13 == (_DWORD *)a4 )
{
    sub_18231F080((_DWORD)a4 + 56, (_DWORD)v13, (unsigned int)&v21, a2, a3);
}
else
{
    *(_QWORD *)fPending = a4;
    *v13 = v9;
    sub_183234350(v13 + 2, a2);
    sub_181B70FD0((__int64)(v13 + 26), a3);
    a4 += 1208i64;
}
return 0;
}
```

最先出现自定义函数调用的地方是switch-case语句那里,刚刚分析出来这里参数1是类型,看看参数1的传递:

```c
char __fastcall SyncMgr::ProcessNewXMLMsg(int a1, __int64 a2, __int64 a3, _QWORD *a4)

v21 = a1;

v9 = v21;

    switch ( (_DWORD)v9 )
```

刚好就是这个switch-case语句的条件,所以处理就在这里

接下来就有2种方法定位撤回消息是哪个分支:

1. 动态调试下断点,看撤回断下的时候这个函数开头rcx是多少
2. 静态分析,依次点进去看看,哪个函数调用出现了刚刚见过的日志

动调的话,可以看到此时该类型值为4,进入第一个函数分支

静态分析的话,在该函数内部可以看到如下日志打印参数:

```c
            log_message(
                2,
                (__int64)"D:\\Tools\\agent\\workspace\\MicroMsgWindowsV3911\\MicroMsgWin\\02_manager\\ChatRevokeMgr.cpp",
                377,
                (__int64)"ChatRevokeMgr::AddOrUpdateRevokeMsg",
                "ChatRevokeMgr",
                "add or update rv msg : %d exist %d",
                (__int128 *)v39,
                (__int128 *)v38,
                (__int128 *)v40,
                (__int128 *)v41,
                (__int128 *)v42,
                (__int128 *)v43);
```

格式符合刚刚接下来会出现的日志的样子

基本上这就是撤回操作的函数了

## 防撤回(核心代码)

知道撤回的逻辑了,接下来防撤回就很简单了

理一下这块的逻辑:

1. 判断收到的同步消息xml类型
2. 根据xml类型进行不同的处理

分析返回值发现,这个case分支的撤回函数的返回值是0

那么这里只需要在根据xml类型进行不同处理的函数这里进行hook,如果参数1为4,就直接返回0,即可

核心代码:

```c
#include "pch.h"
#include "selHook_ProcessNewXMLMsg.h"

_ProcessNewXMLMsg fProcessNewXMLMsg;

char __fastcall MyProcessNewXMLMsg(
      int a1,
      __int64 a2,
      __int64 a3,
      __int64* a4
) {
      if (a1 == 4) {
                return 0;
      }
      return fProcessNewXMLMsg(a1, a2, a3, a4);
}
```

## 效果演示:



## 参考资料:

* 【原文地址】:微信逆向:防撤回消息 - 我可是会飞的啊 (kn0sky.com)
* https://www.kn0sky.com/?p=a98a95f3-b554-4e3b-8778-c6f552149335

Redbell 发表于 2024-7-24 10:37

kn0sky 发表于 2024-7-24 10:33
你想改什么呢

比如以前对撤回的信息高亮显示之类的,然后如果有直接使用的方式,例如“编译升级”,对自己的微信改造

voop 发表于 2024-7-26 02:21

理论上因为(个人所有权)你可以去修改微信让它防撤回,当然理论上微信也可以因为这个(最终用户协议,就是一开始你点了同意那个)封禁账号。

xiaohali 发表于 2024-7-24 10:19

用ce逆向的?

Xiaosesi 发表于 2024-7-24 10:21

其他端的Receive new xml代码也是如此嘛

kn0sky 发表于 2024-7-24 10:31

xiaohali 发表于 2024-7-24 10:19
用ce逆向的?

这一篇主要是静态分析,动态用的x64dbg

Redbell 发表于 2024-7-24 10:32

虽然是有参考资料的,但也很厉害了...
所以可以自己篡改一下么?有什么可行操作么

kn0sky 发表于 2024-7-24 10:33

Redbell 发表于 2024-7-24 10:32
虽然是有参考资料的,但也很厉害了...
所以可以自己篡改一下么?有什么可行操作么

你想改什么呢

kn0sky 发表于 2024-7-24 10:41

Redbell 发表于 2024-7-24 10:37
比如以前对撤回的信息高亮显示之类的,然后如果有直接使用的方式,例如“编译升级”,对自己的微信改造
这不是改跳转防撤回,而是直接hook了撤回的call了,想怎么改,可操作空间很大的

Redbell 发表于 2024-7-24 10:50

kn0sky 发表于 2024-7-24 10:41
这不是改跳转防撤回,而是直接hook了撤回的call了,想怎么改,可操作空间很大的

我其实还是想实操,比如不光是分析,而是拿这个apk直接实操成自己改造之后的,虽然可能会通不过安全校验

Koriki 发表于 2024-7-24 11:06

感谢分享
页: [1] 2 3 4 5 6 7 8 9 10
查看完整版本: 微信逆向:防撤回消息分析