日志分析
从日志出发,清空日志,然后撤回消息,然后暂停打印新的日志,得到如下内容:(为了方便看,这里筛选出撤回相关的部分)
网络相关的略
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种方法定位撤回消息是哪个分支:
- 动态调试下断点,看撤回断下的时候这个函数开头rcx是多少
- 静态分析,依次点进去看看,哪个函数调用出现了刚刚见过的日志
动调的话,可以看到此时该类型值为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);
格式符合刚刚接下来会出现的日志的样子
基本上这就是撤回操作的函数了
防撤回(核心代码)
知道撤回的逻辑了,接下来防撤回就很简单了
理一下这块的逻辑:
- 判断收到的同步消息xml类型
- 根据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