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


日志分析

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

网络相关的略
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

差不多就筛成这样吧

这里有几条疑似的信息:

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:

      LOBYTE(v125) = 3;
      *((_QWORD *)&v125 + 1) = v21;
      v124 = *(_OWORD *)xmmword_184DED0B0;
      v123 = *(_OWORD *)xmmword_184DED0B0;
      v122 = *(_OWORD *)xmmword_184DED0B0;
      v121 = *(_OWORD *)xmmword_184DED0B0;
      v126[0] = *(_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的类型了

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

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

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

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

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[4]; // [rsp+60h] [rbp-78h] BYREF
  __int128 v16; // [rsp+70h] [rbp-68h] BYREF
  __int128 v17; // [rsp+80h] [rbp-58h] BYREF
  __int128 v18; // [rsp+90h] [rbp-48h] BYREF
  __int128 v19; // [rsp+A0h] [rbp-38h] BYREF
  __int128 v20; // [rsp+B0h] [rbp-28h] BYREF
  int v21; // [rsp+E0h] [rbp+8h] 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[0] )
  {
    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[v9] != 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[8];
  if ( v13 == (_DWORD *)a4[9] )
  {
    sub_18231F080((_DWORD)a4 + 56, (_DWORD)v13, (unsigned int)&v21, a2, a3);
  }
  else
  {
    *(_QWORD *)fPending = a4[8];
    *v13 = v9;
    sub_183234350(v13 + 2, a2);
    sub_181B70FD0((__int64)(v13 + 26), a3);
    a4[8] += 1208i64;
  }
  return 0;
}

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

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,进入第一个函数分支

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

              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,即可

核心代码:

#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);
}

效果演示:

 

参考资料:

  • 【原文地址】:https://www.kn0sky.com/?p=a98a95f3-b554-4e3b-8778-c6f552149335
  • https://www.kn0sky.com/?p=a98a95f3-b554-4e3b-8778-c6f552149335

PVZ原版植物大战僵尸辅助例子

【病毒分析】Babyk加密器分析-NAS篇

获取更多资讯请加入交流群


    协助本站SEO优化一下,谢谢!
    关键词不能为空
评 论
更换验证码