前言
上次有同学评论说想看发送图片的分析,这不就来了嘛,本人技术水平有限,有啥问题欢迎指出,没啥问题欢迎留言互动(
之前的分析文章:
- 微信逆向:hook 强制输出调试信息 - 『软件调试区』 - 吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn
- 微信逆向:定位功能调用(以文本消息发送为例) - 『软件调试区』 - 吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn
- 微信逆向:防多开检测原理和绕过 - 『软件调试区』 - 吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn
- 微信逆向:防撤回消息分析 - 『软件调试区』 - 吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn
前景回顾
发送图片消息延续上次发送文本消息的分析(参考资料[0]),从日志中得到的字符串关键词SendMessageMgr,搜索找到疑似发送图片的字符串:
.rdata:0000000184EFB930 0000001D C SendMessageMgr::sendImageMsg
交叉引用发现,全部来自同一个函数的调用:
Direction Type Address Text
Up o SendMessageMgr__sendImageMsg+14F lea r9, aSendmessagemgr_8; "SendMessageMgr::sendImageMsg"
Up o SendMessageMgr__sendImageMsg+254 lea r9, aSendmessagemgr_8; "SendMessageMgr::sendImageMsg"
Up o SendMessageMgr__sendImageMsg+CFF lea r9, aSendmessagemgr_8; "SendMessageMgr::sendImageMsg"
Up o SendMessageMgr__sendImageMsg+DF4 lea r9, aSendmessagemgr_8; "SendMessageMgr::sendImageMsg"
Up o SendMessageMgr__sendImageMsg+F5F lea r9, aSendmessagemgr_8; "SendMessageMgr::sendImageMsg"
Up o SendMessageMgr__sendImageMsg+10C4 lea r9, aSendmessagemgr_8; "SendMessageMgr::sendImageMsg"
Up o SendMessageMgr__sendImageMsg+1328 lea r9, aSendmessagemgr_8; "SendMessageMgr::sendImageMsg"
Up o SendMessageMgr__sendImageMsg+1636 lea r9, aSendmessagemgr_8; "SendMessageMgr::sendImageMsg"
Up o SendMessageMgr__sendImageMsg+195E lea r9, aSendmessagemgr_8; "SendMessageMgr::sendImageMsg"
这个函数十有八九就是发送图片的了
日志分析
抓一下发送图片时候的调试信息:(这里是筛出来的和发送图片相关的几个)
(2024-7-10:14:11:48:318 02532)-i/SendMessageMgr: sendImageMsg. mmbuf.size:18286
(2024-7-10:14:11:48:362 02532)-i/SendMessageMgr:send image progress(183 of 18288). msgId=360289069701268057
(2024-7-10:14:11:48:919 02532)-i/SendMessageMgr:send image ok. msgId=360289069701268057
刚好和函数中的日志匹配,任取一条函数中的日志:
log_message(
2,
(__int64)"D:\\Tools\\agent\\workspace\\MicroMsgWindowsV3911\\MicroMsgWin\\02_manager\\SendMessageMgr.cpp",
717,
(__int64)"SendMessageMgr::sendImageMsg",
"SendMessageMgr",
" sendImageMsg. mmbuf.size:%d",
&v310,
&v309,
&v308,
&v313,
&v312,
&v311);
再次确认了这个函数就是用来发送图片的
伪代码分析
接下来对这个函数继续分析,开始的第一部分如下:
v249 = a4;
v7 = a2;
*(_QWORD *)v280 = a2;
v276 = a1;
v257 = (char *)a2;
v8 = a5;
if ( !*(_BYTE *)(sub_181B50D00() + 2040) )
goto LABEL_7;
sub_181BE0050();
if ( (*(_BYTE *)(sub_181B50D00() + 1272) & 1) != 0 )
{
sub_181BE0050();
sub_182615020(v340, a3);
sub_1824CA8B0(v10, v9);
}
if ( !(unsigned __int8)sub_1825E3A70(a4) )
{
v274[0] = *(_OWORD *)xmmword_184DED0B0;
v243[0] = *(_OWORD *)xmmword_184DED0B0;
v244 = *(_OWORD *)xmmword_184DED0B0;
*(_OWORD *)v271 = *(_OWORD *)xmmword_184DED0B0;
*(_OWORD *)v268 = *(_OWORD *)xmmword_184DED0B0;
v261 = *(_OWORD *)xmmword_184DED0B0;
log_message(
4,
(__int64)"D:\\Tools\\agent\\workspace\\MicroMsgWindowsV3911\\MicroMsgWin\\02_manager\\SendMessageMgr.cpp",
710,
(__int64)"SendMessageMgr::sendImageMsg",
"SendMessageMgr",
" File not found.",
&v261,
(__int128 *)v268,
(__int128 *)v271,
&v244,
v243,
v274);
v14 = sub_181B4A130(v13, v12);
v15 = 17;
LABEL_6:
sub_182223DF0(v14, 95, v15, 1, 1);
LABEL_7:
sub_181B59670(v7);
return v7;
}
这里的调试信息表示,文件未找到,进入log_message的条件是sub_1825E3A70(a4),把关键功能放在if里已经不是第一次见了,这里直接猜测这个函数是类似打开图片的操作,而这里的a4是参数,刚好又是个指针类型(void*)
继续往下看:
LOBYTE(v11) = 1;
v16 = sub_1825E89F0(a4, v11);
v258 = (void *)v16;
LOBYTE(v306) = 0;
*((_QWORD *)&v306 + 1) = v16;
v311 = *(_OWORD *)xmmword_184DED0B0;
v312 = *(_OWORD *)xmmword_184DED0B0;
v313 = *(_OWORD *)xmmword_184DED0B0;
v308 = *(_OWORD *)xmmword_184DED0B0;
v309 = *(_OWORD *)xmmword_184DED0B0;
v310 = v306;
log_message(
2,
(__int64)"D:\\Tools\\agent\\workspace\\MicroMsgWindowsV3911\\MicroMsgWin\\02_manager\\SendMessageMgr.cpp",
717,
(__int64)"SendMessageMgr::sendImageMsg",
"SendMessageMgr",
" sendImageMsg. mmbuf.size:%d",
&v310,
&v309,
&v308,
&v313,
&v312,
&v311);
if ( !v16 )
{
v14 = sub_181B4A130(v18, v17);
v15 = 18;
goto LABEL_6;
}
if ( (unsigned int)(v16 - 1) > 0x77FF )
{
if ( (unsigned int)(v16 - 30721) > 0x117FF )
{
if ( (unsigned int)(v16 - 102401) > 0x257FF )
{
if ( (int)v16 <= 256000 )
goto LABEL_19;
v19 = sub_181B4A130(v18, v17);
v20 = 53;
}
else
{
v19 = sub_181B4A130(v18, v17);
v20 = 52;
}
}
else
{
v19 = sub_181B4A130(v18, v17);
v20 = 51;
}
}
else
{
v19 = sub_181B4A130(v18, v17);
v20 = 50;
}
这里的功能应该是sub_1825E89F0,参数又是a4,调试信息是输出图片缓冲区大小,那这个函数多半是获取图片大小的,如果获取失败跳转到LABEL_6,然后就是对于不同大小的图片分别进行处理,并设置v20的值
再往下就是创建缩略图,发送图片这些操作了,中间有好多没有调试信息的部分,有兴趣可以自行分析,这块内容本文不做重点
向上追一层,看看参数是什么:
__int64 __fastcall sub_1822C13B0(int a1, __int64 a2, int a3, __int64 a4, __int64 a5)
{
...
v18 = *(_DWORD *)a5;
...
SendMessageMgr::sendImageMsg(a1, a2, a3, a4, (__int64)&v18);
if ( Block )
{
free(Block);
Block = 0i64;
}
v23 = 0i64;
if ( v24 )
free(v24);
}
else
{
sub_181B59670(a2);
}
return a2;
}
这里v18是a5解引用,参数传递的又是v18的地址,那就是直接把a5传入了呗
参数一模一样的的传递
继续向上看:
ChatMsg_construct_2((__int64)chatmsg_obj);
sub_1824EC670(chatmsg_obj, v12 + 40);
sub_18260DBF0(&v182, &qword_18590DA90);
sub_18260DBF0(&v180, &qword_18590DA90);
HIDWORD(v145) = v9 | 6;
v185 = 1;
v186 = 0i64;
v187 = 0;
v188 = 0i64;
v189 = 0i64;
v191 = 0i64;
v192 = 0;
v190 = 0i64;
v193 = &v180;
v194 = &v182;
if ( v180 )
{
free(v180);
v180 = 0i64;
v181 = 0;
}
if ( v182 )
{
free(v182);
v182 = 0i64;
v183 = 0;
}
v186 = chatmsg_obj;
v48 = SendMessageMgr_Constructor();
sendImageMsg_2(v48, (__int64)v215, (int)&Block, v12 + 8, (__int64)&v185);// 发送图片
参数5个,很面熟啊,附近发现上次见过的类似的文本消息发送函数的调用:
SendMessageMgr::sendMsg(
(__int64)chatmsg_obj,
(__int64)&Block,
v12 + 8,
v12 + 80,
1,
1,
*(_DWORD *)(v12 + 4),
0i64);
ChatMsg_destruct(chatmsg_obj);
goto LABEL_82;
}
看来是找对地方了
a1 从 sub_181B4F500 中获取即可,该函数的内容:
__int64 sub_181B4F500()
{
__int64 result; // rax
__int64 v1; // rbx
void *v2; // [rsp+40h] [rbp+8h]
result = qword_1859374D0;
if ( !qword_1859374D0 )
{
EnterCriticalSection(&stru_185941F38);
v1 = qword_1859374D0;
if ( !qword_1859374D0 )
{
v2 = operator new(0x150ui64);
qword_1859374D0 = SendMessageMgr((__int64)v2);
sub_182614D30(sub_181B4FF00);
v1 = qword_1859374D0;
}
LeaveCriticalSection(&stru_185941F38);
return v1;
}
return result;
}
返回了个SendMessageMgr的对象,中间调用的那个是构造函数
a2 的参数来自 v215,在上面发现 v215 的其他使用:
sub_1820D84F0(v12 + 80, (__int64)v215, v12 + 8, (__int64)&Block);
ChatMsg_destruct(v215);
说明 v215 是之前见过的 ChatMsg 结构体(之前的分析见参考资料[0]),大小是0x450
函数和参数都找齐了,可以去写代码了
踩坑:a5是结构体
写代码发现参数都是对的,但是就是报错,有异常访问,在排查过程中,突然意识到一个点,这个参数a5,可能不是一个指向1的指针,而是个结构体!!!
结合上下文代码,以及调试正常情况,ida给变量的命名,都表明这很可能是结构体
再次看伪代码,这里的连续赋值的变量刚好10个,刚好对应到内部函数对a5最大访问到0x48偏移处,所以可以认为这就是结构体的初始化
v185 = 1;
v186 = 0i64;
v187 = 0;
v188 = 0i64;
v189 = 0i64;
v191 = 0i64;
v192 = 0;
v190 = 0i64;
v193 = &v180;
v194 = &v182;
if ( v180 )
{
free(v180);
v180 = 0i64;
v181 = 0;
}
if ( v182 )
{
free(v182);
v182 = 0i64;
v183 = 0;
}
v186 = chatmsg_obj;
这里的v186来自一个构造函数,其他的不是1就是0或者指向0的指针
ChatMsg_construct_2((__int64)chatmsg_obj);
内部是构造函数,在之前文章内见过的(参考资料[0]),大小为0x450的结构体
到此才是真正的参数找齐了!
代码实现(核心代码)
#include "pch.h"
#include "selSendImageMsg.h"
_ChatMsg_construct fChatMsg_construct;
_SendMessageMgr_Constructor fSendMessageMgr_Constructor;
_SendImageMsg fSendImageMsg;
struct ImageMsg {
__int64 v185; // 1
__int64 v186; // ChatObj
__int64 v187;
__int64 v188;
__int64 v189;
__int64 v191;
__int64 v192;
__int64 v190;
__int64* v193; // ptr -> 0
__int64* v194; // ptr -> 0
};
__int64 sendImageMsg(wchar_t* target_wxid, wchar_t* msgPath) {
//fSendMessageMgr_Constructor = (_SendMessageMgr_Constructor)AOBScan_SundayEx(FUNCTION_SendMessageMgr_Constructor);
//fSendImageMsg = (_SendImageMsg)AOBScan_SundayEx(FUNCTION_SendImageMsg);
fChatMsg_construct = (_ChatMsg_construct)((__int64)LoadLibraryA("wechatwin.dll") + FUNCTION_ChatMsg_Constructor_o);
fSendMessageMgr_Constructor = (_SendMessageMgr_Constructor)((__int64)LoadLibraryA("wechatwin.dll") + FUNCTION_SendMessageMgr_Constructor_o);
fSendImageMsg = (_SendImageMsg)((__int64)LoadLibraryA("wechatwin.dll") + FUNCTION_SendImageMsg_o);
__int64 a1 = fSendMessageMgr_Constructor();
__int64 a2 = (__int64)calloc(1, 0x1000);
WxString a3 = { 0 };
a3.str = target_wxid;
a3.len = wcslen(a3.str);
a3.len2 = wcslen(a3.str);
WxString a4 = { 0 };
a4.str = msgPath;
a4.len = wcslen(a4.str);
a4.len2 = wcslen(a4.str);
char* chatObj = (char*)calloc(1, 0x450);
fChatMsg_construct((__int64)chatObj);
__int64 v180 = 0;
__int64 v182 = 0;
ImageMsg a5 = { 0 };
a5.v185 = 1;
a5.v186 = (__int64)chatObj;
a5.v193 = &v180;
a5.v194 = &v182;
fSendImageMsg(a1, a2, (__int64) &a3, (__int64) &a4, (__int64)&a5);
return 0;
}
效果展示
执行代码,自动发送图片出去:
参考资料