吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 6848|回复: 75
上一主题 下一主题
收起左侧

[调试逆向] 微信逆向:定位功能调用(以文本消息发送为例)

  [复制链接]
跳转到指定楼层
楼主
kn0sky 发表于 2024-7-3 14:44 回帖奖励
本帖最后由 kn0sky 于 2024-7-3 19:51 编辑


本文仅用于学习研究,请勿用于非法用途和商业用途!如因此产生任何法律纠纷,均与作者无关!

授人以鱼不如授人以渔,定位功能call的方法思路是通用的,这一套流程对定位其他功能也适用
第二篇,以分析文本消息发送call为例,写代码调用实现发文本消息的功能

有什么想看什么主题,可以在评论区回复,随缘更新

环境准备

  • wechat x64版本
  • x64dbg
  • ida 或者 ghidra
  • debugview++
  • Visual Studio 2022

定位发消息 CALL

查看点击发送消息时候出现的日志里有如下几条:

(2024-7-3:10:28:46:698 43868)-i/MainWnd:btn click: sendBtn

(2024-7-3:10:28:46:702 43868)-i/SendMessageMgr:sendTextMsg

(2024-7-3:10:28:46:831 43868)-i/SendMessageMgr:send msg ok. msgId=

log太多了,中间略去了ui相关的,网络相关的log内容,这里有个很可疑的东西出现:SendMessageMgr,这个东西疑似是发消息操作相关的

去字符串里搜索该串:

Address        Length        Type        String
.rdata:0000000184EFAFD0        00000011        C        SendMessageMgr()
.rdata:0000000184EFB0E0        0000001A        C        SendMessageMgr::eventProc
.rdata:0000000184EFB100        0000001F        C        SendMessageMgr::createThumbJpg
.rdata:0000000184EFB138        0000001C        C        SendMessageMgr::sendTextMsg
.rdata:0000000184EFB250        00000020        C        SendMessageMgr::~SendMessageMgr
.rdata:0000000184EFB270        00000012        C        ~SendMessageMgr()
.rdata:0000000184EFB290        00000058        C        D:\\Tools\\agent\\workspace\\MicroMsgWindowsV3911\\MicroMsgWin\\02_manager\\SendMessageMgr.cpp
.rdata:0000000184EFB2E8        0000001F        C        SendMessageMgr::SendMessageMgr
.rdata:0000000184EFB308        0000000F        C        SendMessageMgr
.rdata:0000000184EFB7B8        0000001B        C        SendMessageMgr::trySendMP4
.rdata:0000000184EFB840        0000002C        C        SendMessageMgr::removeVideoHeaderAndMoovSwp
.rdata:0000000184EFB930        0000001D        C        SendMessageMgr::sendImageMsg
.rdata:0000000184EFB968        00000023        C        SendMessageMgr::createSendThumbJpg
.rdata:0000000184EFB990        00000023        C        SendMessageMgr::creatSendMiddleJpg
.rdata:0000000184EFBC20        0000002A        C        SendMessageMgr::processCompressedVideoMsg
.rdata:0000000184EFBC68        00000031        C        SendMessageMgr::tryDealWithOtherCompressVideoMsg
.rdata:0000000184EFBCD8        0000001E        C        SendMessageMgr::forwardEmjMsg
.rdata:0000000184EFBD18        00000020        C        SendMessageMgr::forwardVideoMsg
.rdata:0000000184EFBD88        00000021        C        SendMessageMgr::OnSendVideoAdded
.rdata:0000000184EFBEA0        00000026        C        SendMessageMgr::startTransVideoThread
.rdata:0000000184EFBF10        0000001F        C        SendMessageMgr::OnSendImgAdded
.rdata:0000000184EFBF60        00000018        C        SendMessageMgr::sendMsg
.rdata:0000000184EFBF78        0000001B        C        SendMessageMgr::forwordMsg
.data:00000001857B2910        00000029        C        .?AV?$DynamicHandler@VSendMessageMgr@@@@
.data:00000001857B2998        00000015        C        .?AVSendMessageMgr@@
.data:00000001857B29C0        0000002D        C        .?AV?$mmAccountSingleton@VSendMessageMgr@@@@

这里应该是多种发送消息的地方:

  • SendMessageMgr::sendTextMsg
  • SendMessageMgr::trySendMP4
  • SendMessageMgr::sendImageMsg
  • SendMessageMgr::sendMsg

当前的重点是找到文本消息发送的调用,重点去看SendMessageMgr::sendTextMsg,但是发现交叉引用没有,另一个疑似发送文本消息的是SendMessageMgr::sendMsg,这个有交叉引用:

      log_message(
        2,
        (__int64)"D:\\Tools\\agent\\workspace\\MicroMsgWindowsV3911\\MicroMsgWin\\02_manager\\SendMessageMgr.cpp",
        2208,
        (__int64)"SendMessageMgr::sendMsg",
        "SendMessageMgr",
        "xml parse msg failed",
        (__int128 *)v74,
        (__int128 *)Block,
        (__int128 *)v94,
        (__int128 *)v92,
        &v120,
        &v91);

这里有个信息是xml parse msg failed,应该是解析xml格式的内容,同函数内上下浏览,发现还有个字符串:

    v22 = sub_182617210(
            Block,
            L"<msgsource><sec_msg_node><alnode><fr>%d</fr></alnode></sec_msg_node></msgsource>",
            1i64);

应该是拼接xml格式的函数吧?

这里猜测当前的函数就算发送消息的函数,这个xml应该是发送消息时候组装的数据结构

x64dbg 下断这个函数,发送消息试试:

成功断下,rdx是发送目标的wxid,r8是消息内容

经测试,当从调试器里修改消息内容,就会发送被修改过的消息内容,当从调试器里修改wxid,就会发送给修改过的wxid,是这个call没错了

分析发消息 CALL 参数

向上找一层瞅一眼调用时候的参数:

            SendMessageMgr::sendMsg((__int64)v189, (__int64)&Block, v12 + 8, v12 + 80, 1, 1, *(_DWORD *)(v12 + 4), 0i64);
            sub_181B50D80(v189);

在发消息函数下断查看8个参数分别是什么:

  • a1:应该是个类的对象
  • a2:目标wxid指针,wchar_t类型
  • a3:目标消息指针,wchar_t类型
  • a4:指向0的指针,疑似缓冲区
  • a5:1
  • a6:1
  • a7:0
  • a8:0

v12变量应该也是个对象或者结构体,然后参数取值其中的内容

下面的函数紧接着的这个函数也用了v189变量,进去瞅一眼:

void __fastcall sub_181B50D80(char *a1)
{
  void *v2; // rcx
  void *v3; // rcx
  void *v4; // rcx
  void *v5; // rcx
  void *v6; // rcx
  void *v7; // rcx
  void *v8; // rcx
  void *v9; // rcx
  void *v10; // rcx
  void *v11; // rcx
  void *v12; // rcx
  void *v13; // rcx
  void *v14; // rcx
  void *v15; // rcx
  void *v16; // rcx
  void *v17; // rcx
  void *v18; // rcx
  void *v19; // rcx
  void *v20; // rcx
  void *v21; // rcx
  void *v22; // rcx

  *(_QWORD *)a1 = &ChatMsg::`vftable';
  sub_181B51B90(a1 + 1080, a1 + 1080, *(_QWORD *)(*((_QWORD *)a1 + 135) + 8i64));
  j_j_free_1_0(*((void **)a1 + 135));
  sub_181B51510(a1 + 448);
  v2 = (void *)*((_QWORD *)a1 + 50);
  if ( v2 )
  {
    free(v2);
    *((_QWORD *)a1 + 50) = 0i64;
  }
  *((_QWORD *)a1 + 51) = 0i64;
  v3 = (void *)*((_QWORD *)a1 + 52);
  if ( v3 )
  {
    free(v3);
    *((_QWORD *)a1 + 52) = 0i64;
    *((_DWORD *)a1 + 106) = 0;
  }
  v4 = (void *)*((_QWORD *)a1 + 46);
  if ( v4 )
  {
    free(v4);
    *((_QWORD *)a1 + 46) = 0i64;

这应该是个析构函数,疑似ChatMsg相关类的析构,里面是取虚表之后,对各种字段进行释放

对这个虚表查看交叉引用查看到疑似构造函数:

__int64 __fastcall sub_181B59670(__int64 a1)
{
  void *v2; // rcx
  void *v3; // rcx
  _QWORD *v4; // rax

  *(_QWORD *)a1 = &InstanceCounter<ChatMsg,1000>::`vftable';
  sub_181B59010();
  *(_QWORD *)a1 = &ChatMsg::`vftable';
  *(_QWORD *)(a1 + 32) = 0i64;
  *(_DWORD *)(a1 + 40) = 0;
  *(_QWORD *)(a1 + 48) = 0i64;
  *(_QWORD *)(a1 + 56) = 0i64;
  *(_QWORD *)(a1 + 64) = 0i64;
  *(_QWORD *)(a1 + 72) = 0i64;
  *(_QWORD *)(a1 + 80) = 0i64;
  *(_QWORD *)(a1 + 88) = 0i64;
  *(_DWORD *)(a1 + 96) = 0;
  *(_QWORD *)(a1 + 104) = 0i64;
  *(_QWORD *)(a1 + 112) = 0i64;
  *(_QWORD *)(a1 + 120) = 0i64;
  *(_DWORD *)(a1 + 128) = 0;
  v2 = *(void **)(a1 + 104);
  if ( v2 )
  {
    free(v2);
    *(_QWORD *)(a1 + 104) = 0i64;
  }
  *(_QWORD *)(a1 + 112) = 0i64;
  v3 = *(void **)(a1 + 120);
  if ( v3 )
  {
    free(v3);
    *(_QWORD *)(a1 + 120) = 0i64;
    *(_DWORD *)(a1 + 128) = 0;
  }
  *(_QWORD *)(a1 + 136) = 0i64;
  *(_QWORD *)(a1 + 144) = 0i64;
  *(_QWORD *)(a1 + 152) = 0i64;
  *(_DWORD *)(a1 + 160) = 0;
  *(_QWORD *)(a1 + 168) = 0i64;
  *(_DWORD *)(a1 + 176) = 0;
  *(_QWORD *)(a1 + 184) = 0i64;
  *(_DWORD *)(a1 + 192) = 0;
  *(_QWORD *)(a1 + 200) = 0i64;
  *(_DWORD *)(a1 + 208) = 0;
  *(_QWORD *)(a1 + 216) = 0i64;
  *(_QWORD *)(a1 + 224) = 0i64;
  *(_QWORD *)(a1 + 232) = 0i64;
  *(_DWORD *)(a1 + 240) = 0;
  *(_QWORD *)(a1 + 248) = 0i64;
  *(_QWORD *)(a1 + 256) = 0i64;
  *(_QWORD *)(a1 + 264) = 0i64;
  *(_DWORD *)(a1 + 272) = 0;
  *(_QWORD *)(a1 + 280) = 0i64;
  *(_QWORD *)(a1 + 288) = 0i64;
  *(_QWORD *)(a1 + 296) = 0i64;
  *(_DWORD *)(a1 + 304) = 0;
  *(_QWORD *)(a1 + 320) = 0i64;
  *(_QWORD *)(a1 + 328) = 0i64;
  *(_QWORD *)(a1 + 336) = 0i64;
  *(_DWORD *)(a1 + 344) = 0;
  *(_BYTE *)(a1 + 352) = 0;
  *(_DWORD *)(a1 + 356) = 0;
  *(_BYTE *)(a1 + 360) = 0;
  *(_QWORD *)(a1 + 368) = 0i64;
  *(_QWORD *)(a1 + 376) = 0i64;
  *(_QWORD *)(a1 + 384) = 0i64;
  *(_DWORD *)(a1 + 392) = 0;
  *(_QWORD *)(a1 + 400) = 0i64;
  *(_QWORD *)(a1 + 408) = 0i64;
  *(_QWORD *)(a1 + 416) = 0i64;
  *(_DWORD *)(a1 + 424) = 0;
  *(_QWORD *)(a1 + 432) = 0i64;
  *(_QWORD *)(a1 + 440) = 0i64;
  sub_181B59470(a1 + 448);
  *(_DWORD *)(a1 + 1040) = 255;
  *(_BYTE *)(a1 + 1044) = 0;
  *(_DWORD *)(a1 + 1048) = 0;
  *(_BYTE *)(a1 + 1052) = 0;
  *(_QWORD *)(a1 + 1064) = 0i64;
  *(_QWORD *)(a1 + 0x438) = 0i64;
  *(_QWORD *)(a1 + 0x440) = 0i64;
  v4 = operator new(0x70ui64);
  *v4 = v4;
  v4[1] = v4;
  v4[2] = v4;
  *((_WORD *)v4 + 12) = 257;
  *(_QWORD *)(a1 + 0x438) = v4;
  *(_QWORD *)(a1 + 0x448) = 0i64;
  *(_QWORD *)(a1 + 8) = 0i64;
  *(_QWORD *)(a1 + 24) = 0i64;
  *(_QWORD *)(a1 + 16) = 0i64;
  *(_QWORD *)(a1 + 0x420) = 0i64;
  *(_BYTE *)(a1 + 0x430) = 0;
  *(_BYTE *)(a1 + 312) = 0;
  return a1;
}

假如这是真的构造函数,说明这个类的大小是0x450字节,sendmsg这里是第一次用到这个变量,说明这里传入一个大小0x450的缓冲区即可,然后调用完sendmsg再调用下面的析构去释放了,填充行为会在sendmsg里面进行:

  sub_181B70FD0(a1, v121);
  if ( v23 )
    free(v23);
  if ( v25 )
    free(v25);
  sub_181B50D80(v121);
  return a1;

内容:

__int64 __fastcall sub_181B70FD0(__int64 a1, __int64 a2)
{
  *(_QWORD *)a1 = &InstanceCounter<ChatMsg,1000>::`vftable';
  sub_181B59010();
  *(_QWORD *)a1 = &ChatMsg::`vftable';
  *(_QWORD *)(a1 + 8) = *(_QWORD *)(a2 + 8);
  *(_DWORD *)(a1 + 16) = *(_DWORD *)(a2 + 16);
  *(_DWORD *)(a1 + 20) = *(_DWORD *)(a2 + 20);
  *(_QWORD *)(a1 + 24) = *(_QWORD *)(a2 + 24);
  *(_QWORD *)(a1 + 32) = *(_QWORD *)(a2 + 32);
  *(_DWORD *)(a1 + 40) = *(_DWORD *)(a2 + 40);
  *(_QWORD *)(a1 + 48) = *(_QWORD *)(a2 + 48);
  *(_DWORD *)(a1 + 56) = *(_DWORD *)(a2 + 56);
  *(_DWORD *)(a1 + 60) = *(_DWORD *)(a2 + 60);
  *(_DWORD *)(a1 + 64) = *(_DWORD *)(a2 + 64);
  *(_DWORD *)(a1 + 68) = *(_DWORD *)(a2 + 68);
  sub_182615020((void *)(a1 + 72), (void *)(a2 + 72));
  sub_182615020((void *)(a1 + 104), (void *)(a2 + 104));
  sub_182615020((void *)(a1 + 136), (void *)(a2 + 136));
  sub_18260DBF0(a1 + 168, a2 + 168);
  sub_18260DBF0(a1 + 184, a2 + 184);
  sub_18260DBF0(a1 + 200, a2 + 200);
  sub_182615020((void *)(a1 + 216), (void *)(a2 + 216));
  sub_182615020((void *)(a1 + 248), (void *)(a2 + 248));
  sub_182615020((void *)(a1 + 280), (void *)(a2 + 280));
  *(_BYTE *)(a1 + 312) = *(_BYTE *)(a2 + 312);
  sub_182615020((void *)(a1 + 320), (void *)(a2 + 320));
  *(_BYTE *)(a1 + 352) = *(_BYTE *)(a2 + 352);
  *(_DWORD *)(a1 + 356) = *(_DWORD *)(a2 + 356);
  *(_BYTE *)(a1 + 360) = *(_BYTE *)(a2 + 360);
  sub_182615020((void *)(a1 + 368), (void *)(a2 + 368));
  sub_182615020((void *)(a1 + 400), (void *)(a2 + 400));
  *(_DWORD *)(a1 + 432) = *(_DWORD *)(a2 + 432);
  *(_DWORD *)(a1 + 436) = *(_DWORD *)(a2 + 436);
  *(_DWORD *)(a1 + 440) = *(_DWORD *)(a2 + 440);
  *(_DWORD *)(a1 + 444) = *(_DWORD *)(a2 + 444);
  sub_181B71240(a1 + 448, a2 + 448);
  *(_DWORD *)(a1 + 1040) = *(_DWORD *)(a2 + 1040);
  *(_BYTE *)(a1 + 1044) = *(_BYTE *)(a2 + 1044);
  *(_DWORD *)(a1 + 1048) = *(_DWORD *)(a2 + 1048);
  *(_BYTE *)(a1 + 1052) = *(_BYTE *)(a2 + 1052);
  *(_QWORD *)(a1 + 1056) = *(_QWORD *)(a2 + 1056);
  *(_QWORD *)(a1 + 1064) = *(_QWORD *)(a2 + 1064);
  *(_BYTE *)(a1 + 1072) = *(_BYTE *)(a2 + 1072);
  sub_181B728E0(a1 + 1080, a2 + 1080);
  *(_DWORD *)(a1 + 1096) = *(_DWORD *)(a2 + 1096);
  *(_DWORD *)(a1 + 1100) = *(_DWORD *)(a2 + 1100);
  return a1;
}

猜测没错,应该是这样了

那么接下来的问题就是v12是什么?

网上找,发现v12赋值的地方:v12 = *a2

  if ( *(int *)(a1 + 56) <= 0 && *(int *)(a3 + 8) <= 0 || (v11 = a2[1], v12 = *a2, (v13 = (v11 - *a2) / 104) == 0) )

v12来自参数a2的第一个成员,返回上一层查看这个参数来自一个复杂的函数调用,于是就先不管了,直接用硬编码调用试试看

现在的参数:

  • a1:缓冲区:0x450 字节
  • a2:目标wxid指针,wchar_t类型
  • a3:目标消息指针,wchar_t类型
  • a4:缓冲区:大小未知
  • a5:1
  • a6:1
  • a7:0
  • a8:0

代码实现(核心)

实现调用微信的call完成发消息

这里主要就是找到call以及分析参数,找好参数构造好直接调用就行

注意一点就是,这里用的字符串都是结构体,第一个成员是wchar_t指针,第二个成员是长度

struct message {
    wchar_t* msg;
    int len;
};

typedef INT64(__fastcall* _sendMsg)(
    char* a1,
    message* target_wxid,
    message* message,
    char* buffer,
    int a5,
    int a6,
    int a7,
    INT64 a8);

typedef void(__fastcall* _ChatMsg_destruct)(char* a1);

_sendMsg fsendMsg;
_ChatMsg_destruct fChatMsg_destruct;

void sendMsg() {
    HMODULE hMod = GetModuleHandleA("WeChatWin.dll");
    fsendMsg = (_sendMsg)((unsigned long long)hMod + FUNCTION_SendMsg);
    fChatMsg_destruct = (_ChatMsg_destruct)((unsigned long long)hMod + FUNCTION_ChatMsg_Destructor);

    char* a1 = (char*)calloc(1,0x450);
    wchar_t* str = L"filehelper";
    wchar_t* str2 = L"start";
    message a2,a3;
    a2.msg = str;
    a2.len = wcslen(str);
    a3.msg = str2;
    a3.len = wcslen(str2);
    char* a4 = (char*)calloc(1,0x450);
    fsendMsg(a1, &a2, &a3, a4, 1, 1, 0, 0);
    fChatMsg_destruct(a1);
        free(a1);
}

实现效果

成功把start发送给文件助手:

参考资料

免费评分

参与人数 20威望 +2 吾爱币 +118 热心值 +19 收起 理由
victos + 1 + 1 谢谢@Thanks!
jjm1178583316 + 1 + 1 我很赞同!
guoguoapple + 1 我很赞同!
CowB + 1 + 1 我很赞同!
billsmiless + 2 + 1 鼓励转贴优秀软件安全工具和文档!
Wyiyun777 + 1 我很赞同!
Sayon + 2 + 1 我很赞同!
seakei + 1 用心讨论,共获提升!
eijop252023 + 1 谢谢@Thanks!
allspark + 1 + 1 用心讨论,共获提升!
willJ + 2 + 100 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
xiaomumu + 1 + 1 谢谢@Thanks!
DQQQQQ + 1 + 1 我很赞同!
Whitetime + 1 + 1 我很赞同!
allenzhu + 1 + 1 用心讨论,共获提升!
呀云宝 + 1 + 1 用心讨论,共获提升!
wuai234 + 1 + 1 谢谢@Thanks!
snrtdwss + 1 + 1 发企业微信转圈是啥问题呢?
52bulesky + 1 + 1 谢谢@Thanks!
uuwatch + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!

查看全部评分

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

推荐
 楼主| kn0sky 发表于 2024-7-17 10:32 |楼主
lhy888xh 发表于 2024-7-17 00:05
请问 FUNCTION_SendMsg 和 FUNCTION_ChatMsg_Destructor 的偏移怎么计算?

找到函数,然后用函数开头的地址减去当前模块基地址

免费评分

参与人数 1吾爱币 +3 热心值 +1 收起 理由
lhy888xh + 3 + 1 热心回复!

查看全部评分

推荐
奈空林 发表于 2024-7-15 08:17
了解你的需求,定位功能在微信里主要用于服务号,通过API实现推送地理位置,不是普通聊天使用。
沙发
wtujoxk 发表于 2024-7-3 15:20
本帖最后由 wtujoxk 于 2024-7-4 09:33 编辑

非常感谢,这V587,再求一篇获取联系人

本文的0x450怎么来的,没看懂,希望能解答!
3#
uuwatch 发表于 2024-7-3 15:20
那要是想发送图片呢?
4#
 楼主| kn0sky 发表于 2024-7-3 15:40 |楼主
uuwatch 发表于 2024-7-3 15:20
那要是想发送图片呢?

去逆出来发图片的地方就行
5#
zege1733 发表于 2024-7-3 15:50
感谢分享
6#
阿菊不在 发表于 2024-7-3 16:38
已经搞好&#128076;&#127995;
7#
snrtdwss 发表于 2024-7-3 16:38
给企业微信发文本 转圈呀 怎么破
8#
inkoo 发表于 2024-7-4 08:43
不明觉厉
9#
 楼主| kn0sky 发表于 2024-7-4 09:31 |楼主
snrtdwss 发表于 2024-7-3 16:38
给企业微信发文本 转圈呀 怎么破

分析分析呗(
10#
li63033 发表于 2024-7-4 14:36
可以可以,很强
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

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

GMT+8, 2024-7-23 05:46

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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