本帖最后由 ahxhs 于 2024-07-06 08:59 编辑
本文仅用于学习研究,请勿用于非法用途和商业用途!如因此产生任何法律纠纷,均与作者无关! 授人以鱼不如授人以渔,定位功能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); }
注:若转载请注明大神论坛来源(本贴地址)与作者信息。
|