首页
社区
课程
招聘
分析wx防撤回原理
发表于: 2020-2-15 08:44 10055

分析wx防撤回原理

2020-2-15 08:44
10055

搜一搜网上的文章,很多以前的大佬都是通过hook掉 updateWithOnConflict函数,当type=1000时,判断出撤回信息的id,然后重新插入数据库,并修改“xx撤回一条信息”为“xx撤回信息失败”。

猜想这个过程可能分为三步

1.接收到某某发出的一条信息

2.接收到某某撤回了一条信息,删除或者替换这条信息

3.插入文字“某某撤回了一条信息文字”


通过monitor方法回溯或者直接hook函数 updateWithOnConflict作为突破口,因为发送消息肯定要与数据库交互,而处理数据库信息很有可能用到这个函数,手动狗头


我们先正常发一条消息,查看hook结果

函数原型updateWithOnConflict(String table, ContentValues values, String whereClause, String[] whereArgs, int conflictAlgorithm)


向测试手机发送haha,拼接数据库语句为

update rconversation set  msgType=1 flag=1579779746000 content=haha digestUser= digest=haha lastSeq=707367243 msgCount=33 isSend=0 hasTrunc=1 unReadCount=1 conversationTime=1579779746000 username=wxid_dajiadebaba status=3 where  username=wxid_dajiadebaba


hook update start

a1:rconversation

a2:msgType=1 flag=1579779746000 content=haha digestUser= digest=haha lastSeq=707367243 msgCount=33 isSend=0 hasTrunc=1 unReadCount=1 conversationTime=1579779746000 username=wxid_dajiadebaba status=3

a3:username=?

a4:wxid_dajiadebaba

a5:0



堆栈信息如下,这个下面分析撤回的堆栈信息时要用


下面我们撤回这条信息,发现updateWithOnConflict这个函数被调用了3次,也就是说一次撤回包含着3次数据库更新


hook update start

a1:message

a2:msgId=38 type=10000 content="baba" 撤回了一条消息

a3:msgId=?

a4:38

a5:0

hook update start

a1:rconversation

a2:msgType=10000 flag=1579779746000 digestUser= digest="baba" 撤回了一条消息 isSend=0 hasTrunc=1 unReadCount=0 conversationTime=1579779746000 content="baba"  撤回了一条消息 username=wxid_dajiadebaba status=3

对比a2:msgType=1 flag=1579779746000 content=haha digestUser= digest=haha lastSeq=707367243 msgCount=33 isSend=0 hasTrunc=1 unReadCount=1 conversationTime=1579779746000 username=wxid_dajiadebaba status=3

a3:username=?

a4:wxid_dajiadebaba

a5:0

hook update start

a1:rconversation

a2:UnReadInvite=0 atCount=0

a3:username= ?

a4:wxid_dajiadebaba

a5:0



拼接数据库语句为

update message set :msgId=38 type=10000 content="baba" 撤回了一条消息 where msgId=38

update rconversation set msgType=10000 flag=1579779746000 digestUser= digest="baba" 撤回了一条消息 isSend=0 hasTrunc=1 unReadCount=0 conversationTime=1579779746000 content="baba"  撤回了一条消息 username=wxid_dajiadebaba status=3 where msgId=38 where username=wxid_dajiadebaba

update rconversation set UnReadInvite=0 atCount=0 where username=wxid_dajiadebaba



这里msgType=10000 很重要,我们刚才成功发送的msgType=1,放过来对比一下,content从haha变成"baba"  撤回了一条消息

a2:msgType=10000 flag=1579779746000 digestUser= digest="baba" 撤回了一条消息

isSend=0 hasTrunc=1 unReadCount=0 conversationTime=1579779746000 content="baba"  撤回了一条消息 username=wxid_dajiadebaba status=3

a22:msgType=1 flag=1579779746000 content=haha digestUser= digest=haha lastSeq=707367243 msgCount=33

isSend=0 hasTrunc=1 unReadCount=1 conversationTime=1579779746000 username=wxid_dajiadebaba status=3


第一句update message这个表,还附带 msgId,这个 msgId应该就是待撤回的msg的id,猜测这一句的功能就是通过 msgId删除msg,替换为"baba" 撤回了一条消息。

第二句就是更新界面了。

第三句,字面意思就是更新 rconversation未读信息的计数,跟防撤回关系不大。

其实到了这里,我们已经可以通过判断 msgType是否=1000判断是否需要撤回信息,

如果要撤回,先得到要撤回的msgId,调用相关函数重新插入数据库就能实现防撤回了,

类似的文章如下:

简书大佬的防撤回https://www.jianshu.com/p/fb16ea7b28bf

很明显,updateWithOnConflict必定有上层函数直接判断是否撤回,如果判断撤回,调用更下面的逻辑才会到updateWithOnConflict和delete类似函数完成撤回功能的删除和更新。如果我们hook上层函数,可以直接屏蔽掉撤回功能,而不需要先通过判断 msgType是否=1000判断是否需要撤回信息,如果要撤回,得到要撤回的msgId,再调用相关函数重新插入数据库

于是,我们再一次分析函数流程,撤回haha这句消息,打印updateWithOnConflict堆栈如下



这里先点出重点函数吧,后面再具体分析流程,懒得看同学的就看到这里结束吧,

关键函数在com.tencent.mm.model.f类里面,为什么关注这个model呢,因为正常接受信息没有走这个类,还有com.tencent.mm.plugin.messenger.foundation类,应该在更上层,

类比django的mvc框架,猜想model也是封装了和数据库交互的上层,

1.com.tencent.mm.model.f.a(SourceFile:145)   m34535a;

2.com.tencent.mm.model.f.a(SourceFile:352)  mo7343a;

函数mo7343a调用了m34535a,这里我们直接看mo7343a,南极公司对jadx做了小动作,导致反编译失败,具体原因估计还是变量太多,但我们还有gjden 大佬的神器gda,这是真正的大佬,给大佬打个广告http://www.gda.wiki:9090/blog_list0.php,虽然也不算完美,一大堆的寄存器变量,但是肯定比看smali好多了,我们把gda反编译的代码粘过来看


我们一眼就看到了这一句p0.equals("revokemsg"),revokemsg不就是撤回消息吗,很明显,p0=revokemsg时,跳转进入撤回的逻辑,

p1是一个Map,v5 = p1.get(".sysmsg.revokemsg.newmsgid"); v6 = p1.get(".sysmsg.revokemsg.replacemsg");存储着msgid和replacemsg,

而这句话实现了具体功能 f.a(c.ajB().ak(p1.get(".sysmsg.revokemsg.session"), bo.getLong(v5, 0)), p2, v6, "MicroMsg.BigBallSysCmdMsgConsumer");


我们直接hook这个函数,当p0=revokemsg时直接返回就可以屏蔽掉撤回了;粗略一看,这个函数还控制着添加联系人,更新包等功能,大家有兴趣可以具体跟一下。







[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

最后于 2021-3-7 12:35 被挤蹭菌衣编辑 ,原因:
收藏
免费 1
支持
分享
最新回复 (6)
雪    币: 2510
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2
感谢分享
2020-2-15 10:25
0
雪    币: 483
活跃值: (682)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
感谢分享
2020-2-15 16:15
0
雪    币: 6
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
4
感谢分享
2020-2-17 11:37
0
雪    币: 1274
活跃值: (162)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
感谢感谢
2020-2-18 20:45
0
雪    币: 4861
活跃值: (4843)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
谢谢啦!
2020-3-1 20:00
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
7
感谢分享
2020-3-6 12:05
0
游客
登录 | 注册 方可回帖
返回
//