吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 3353|回复: 74
收起左侧

[调试逆向] 微信逆向:发送图片消息分析

  [复制链接]
kn0sky 发表于 2024-8-1 16:06

前言

上次有同学评论说想看发送图片的分析,这不就来了嘛,本人技术水平有限,有啥问题欢迎指出,没啥问题欢迎留言互动(

之前的分析文章:

  1. 微信逆向:hook 强制输出调试信息 - 『软件调试区』 - 吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn
  2. 微信逆向:定位功能调用(以文本消息发送为例) - 『软件调试区』 - 吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn
  3. 微信逆向:防多开检测原理和绕过 - 『软件调试区』 - 吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn
  4. 微信逆向:防撤回消息分析 - 『软件调试区』 - 吾爱破解 - 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 是个结构体

  • a2 是个缓冲区

  • a3 是个 wxid 的引用

  • a4 是个图片路径的引用

  • a5 是 1(如果这么想的话,就踩坑了!下面介绍)

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给变量的命名,都表明这很可能是结构体

  • 结合上下文代码,进入函数后,发现有对a5成员的访问(最大访问到 0x48 偏移):v9 = *(_QWORD *)(a5 + 0x48);

  • ida给变量命名是,连号意味着是内存上也连着的

  • 每次正常情况下,v185+8的地方都是个可读地址

再次看伪代码,这里的连续赋值的变量刚好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;

}

效果展示

执行代码,自动发送图片出去:

image-20240710175059-23hdz9v.png

参考资料

免费评分

参与人数 13吾爱币 +21 热心值 +11 收起 理由
willJ + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
happyxuexi + 1 + 1 谢谢@Thanks!
AuroraVerses + 1 + 1 热心回复!
root000001 + 1 + 1 热心回复!
kingscu + 1 热心回复!
wawaxiuxiu + 1 热心回复!
knx888 + 1 + 1 我很赞同!
allspark + 1 + 1 用心讨论,共获提升!
phr + 1 + 1 我很赞同!
wtujoxk + 3 + 1 我很赞同!
DancingRain + 1 + 1 谢谢@Thanks!
SVIP9大会员 + 1 + 1 谢谢@Thanks!
gqdsc + 1 + 1 谢谢@Thanks!

查看全部评分

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

sunzhw 发表于 2024-8-1 19:37
牛人,学习一下,点赞
zzylyzs 发表于 2024-8-9 01:14
楼主可以逆向一下消息接收吗,有消息来的时候先执行了 SyncMgr::doAddMsg 然后会调用 SyncMgr::ConvertAddMsgToChatMsg 这个函数
我HOOK了这个函数,它有3个参数,第二个参数应该是解析消息结果的结构体指针,分析了一下大概结构体是
+38 -> 消息类型 1 = 消息 51 = xml 通知  47 = xml 表情包

+48 ->消息来源
+50 ->文本长度
+54 ->总来源长度
+88 ->消息地址
+90 ->文本长度
+94 ->总消息长度

虽然这样已经可以获取到wxid 和 消息内容了,但是在私聊和群聊的时候,都不能判断是自己发的消息同步过来的,还是其他人发的,请教一下楼主是如何hook消息来源通知的
gqdsc 发表于 2024-8-1 16:27
LODGE 发表于 2024-8-1 16:33
大佬的世界对小白来说是天书
556699 发表于 2024-8-1 16:41
小白一脸懵,啥都没看懂
Clangy 发表于 2024-8-1 17:02
强!!!
happy1every1day 发表于 2024-8-1 17:17
我啥时候能这样啊
DancingRain 发表于 2024-8-1 17:35
学会了&#128536;
phr 发表于 2024-8-1 18:27
牛!!!
csvip 发表于 2024-8-1 19:00
大神牛!!!
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

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

GMT+8, 2024-12-14 15:54

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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