本次投稿至华盟网,转自公众号黑白之道
出处:https://www.77169.net/html/272981.html
0x00 适用场景
本文所采用技术,仅用来实现自定义功能,适用场景仅为自己两台电脑使用聊天软件互发消息学习研究使用。
*本文仅限技术研究与讨论,严禁用于非法用途,否则产生的一切后果自行承担。
0x01 需求产生
某聊天软件有已读回执功能,可以通过查看标记,判断对方是否读了你的消息:
经常出现这样一种情况,联系人发来几条消息,啊T.T,我好想看看他说了什么:
然后点开看了一下,完蛋,又是借钱的:
这个辩解苍白无力~
那么伟大的需求就此诞生:如何偷偷看他消息,而不让他知道我已读了?
0x02 探究原理
盲猜原理一定是这样:
钉钉客户端查看了消息->客户端发送已读回执到服务器->服务器向消息发送者发送已读状态
那也就是说,只要我们使用的PC客户端不发送已读回执,服务器就永远不知道我们读了该条消息,对放也就不会知道我们看过了。
一开始想看有没有人造过轮子,于是github上翻了翻,但没什么收获。不过在翻的过程中发现了一个有意思的项目:dingtalk-interceptor,但该项目删除了,于是就去网上翻了翻,果然找到了相关的文章,针对的是老版的客户端(3.x),该版本就是Electron包装的一层网页,项目实现原理是通过拦截所有包含updateToRead的请求,从而实现永远未读消息的。
因此我们要寻找的突破口就在这个关键字上:updateToRead
0x03 环境准备
调试环境:
win10 2004 x64
x96dbg
ida7.0
顺便推荐个工具,可以让ida和od/xdbg/lldb等同步调试:
https://github.com/bootleg/ret-sync
聊天软件版本:
0x04 开始动手
首先用x64dbg启动主程序,主程序目录在.\main\current\路径下
通过不断尝试,发现该聊天软件的核心逻辑都在MainFrame.dll中,因此只针对MainFrame.dll进行搜索,关键字:updateToRead。
先定位到模块中:
然后在该模块中搜索updateToRead,并全部设置断点
发送两条消息并阅读测试,命中断点如下:
关闭其他断点,并用另一个号给调试的账号发送消息,并查看:
在第一个断点处,消息依然是未读状态
在第二个断点处,消息依然是未读状态
在第三个断点处,消息依然也是未读状态
在断到第四个断点处,消息依然是未读状态,但放过断点,消息就变成了已读:
解决方案有几个:
-
破坏原有数据包,使其服务器无法正确更新状态
-
在发送前拦截该数据包,使其不发送
-
不进入检测消息是否未读状态
此处通过调用该接口“/r/IDLMessageStatus/updateToReadV2”向该接口发送请求,来告诉服务器,消息已经被读了。此处可以直接暴力将接口修改,修改成错误的地址,也可以实现看了消息不显示已读。但直接通过该种方式修改,有两个弊端,
-
会向服务器发送一条数据,虽然服务器没能正确处理,但会返回一个异常。
-
客户端收到该错误响应后会认为自己与服务器断开连接,会经常认为自己掉线了从而发不出去消息
路线1走不通,因此继续寻找突破点,使用ida反编译该模块,查找该接口:
发现该接口是出自sub_1127B210函数:
进入sub_1127B210内容:
往上跟踪,发现sub_110BA580调用了sub_1127B210
往上跟踪,发现sub_107269E0调用了sub_110BA580
sub_107269E0函数内容:
其中包含多个接口,作用是向服务器发送一个包含消息id的请求包,从而实现消息已读。
此时已经基本了解大概情况,继续用x64dbg调试,在调试过程中发现在执行完sub_107269E0消息并未直接变成已读,而是继续向上层函数继续返回,直到返回了这样一个函数:
伪代码:
char __thiscall sub_10C77E70(_BYTE *this)
{
char *i; // esi
char result; // al
char v3; // [esp+8h] [ebp-10h]
char v4; // [esp+10h] [ebp-8h]
for ( i = this; ; sub_10C77EC0(i) )
{
result = i[20];
if ( result )
break;
sub_10C77060(i, (int)&v3);
if ( v3 )
sub_10C785D0(i, &v4);
else
sub_10C77210(i);
}
return result;
}
该函数sub_10C77E70在if ( v3 )处存在两个分支,一个分支调用sub_10C785D0(i, &v4),另一个分支即已读分支sub_10C77210(i),也就是我们的call所调用的函数。因此,v3是一个关键判断点
v3所对应汇编代码为:jz short loc_10C77EAC
偏移为:C77298
在C77298上下断点,通过修改寄存器zf标志位,使其进入sub_10C785D0(i, &v4)
修改后发现,后续发送的消息均不显示已读,且不会再进入该判断,因此只要让v3永远=True,即可永不进入sub_10C77210(i)分支,即修改该处条件跳转为jmp
修改后Patch到dll上即可。
特征码:C0 75 33 8D 45 F0 8B CE 50 E8 CE F1 FF FF 80 7D F0 00 8B CE
修改后:C0 EB 33 8D 45 F0 8B CE 50 E8 CE F1 FF FF 80 7D F0 00 8B CE
0x05 已读不回
0x07 免责申明
本文是作者闲暇之余,方便自己搞的一个小功能。如果对其他人/公司造成不好的影响请联系我们处理。
来源:freebuf.com 2021-02-22 17:36:56 by: LDrakura
请登录后发表评论
注册