0%

QQ 8.8.17 防撤回、无限打开闪照

概述

之前写过一篇QQ8.1.3防撤回原理,每次更新后,都需要适配被混淆的类名。

今天更新了最新版QQ(8.8.17),发现使用到的类名都未被混淆,不过包结构有了一定变化(可能是重构了?),需要重新适配。

于是尝试使用objection分析撤回提示的实现,并编写Xposed模块。

防撤回

与之前一样,设置com.tencent.mobileqq.app.message.QQMessageFacade.a(ArrayList, boolean)方法的返回值为空即可

显示撤回提示

定位到关键类com.tencent.mobileqq.msg.api.impl.MessageFacadeImpl,其中提供有QQMessageFacade类的接口,包括删除消息、增加消息等功能。

其中增加消息有以下两个函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
@Override  // com.tencent.mobileqq.msg.api.IMessageFacade
public void addMessage(MessageRecord arg2, String arg3) {
this.getMessageFacade().a(arg2, arg3);
}

@Override // com.tencent.mobileqq.msg.api.IMessageFacade
public void addMessage(MessageRecord arg9, String arg10, boolean arg11, boolean arg12, boolean arg13, boolean arg14) {
if(this.checkAppRuntimeInValid()) {
return;
}

this.getMessageFacade().a(arg9, arg10, arg11, arg12, arg13, arg14);
}

通过以前对旧版qq的分析可知,撤回消息的流程为:删除本地消息记录-创建撤回提示-增加撤回提示到本地

因此,使用objection hook addMessage函数,打印参数和调用栈:

1
android hooking watch class_method com.tencent.mobileqq.msg.api.impl.MessageFacadeImpl.addMessage --dump-args --dump-backtrace

首先使用另一个qq发送消息,发现调用的是第一个函数addMessage(MessageRecord, String)

使用另一个qq撤回消息,日志如下:

撤回私聊消息

1
2
3
4
5
com.tencent.mobileqq.msg.api.impl.MessageFacadeImpl.addMessage(Native Method)
com.tencent.mobileqq.graytip.UniteGrayTipMsgUtil.a(P:70)
com.tencent.imcore.message.BaseMessageManager.a(P:2259)
com.tencent.imcore.message.QQMessageFacade.a(P:1358)
com.tencent.mobileqq.service.message.codec.decoder.msgType0x210.MsgType0x210Decoder.a(P:1017)

撤回群聊消息

1
2
3
4
5
com.tencent.mobileqq.msg.api.impl.MessageFacadeImpl.addMessage(Native Method)
com.tencent.mobileqq.graytip.UniteGrayTipMsgUtil.a(P:70)
com.tencent.imcore.message.BaseMessageManager.a(P:2259)
com.tencent.imcore.message.QQMessageFacade.a(P:1358)
com.tencent.mobileqq.troop.utils.TroopTipsMsgMgr.a(P:462)

注意到无论是撤回私聊还是群聊消息,都是调用com.tencent.mobileqq.msg.api.impl.MessageFacadeImpl.addMessage(com.tencent.mobileqq.data.MessageRecord, java.lang.String, boolean, boolean, boolean, boolean),(参数值为false,true,true,false):

而该方法最终调用的是BaseQQMessage.a(MessageRecord,String,boolean,boolean,boolean,boolean)函数

因此,我们构造完MessageRecord后,依葫芦画瓢,调用该函数即可。

PS:此处是为了与正常流程保持一致,其实根据另一个addMessage函数,调用BaseQQMessage.a(MessageRecord,String)也是可以的。

无限打开闪照

定位到关键类com.tencent.mobileqq.pic.api.impl.PicFlashImpl
有以下两个函数:isFlashPicMsg(判断消息是否为闪照)、isFlashPicMsgReaded(判断闪照是否已读)

1
2
3
4
5
6
7
8
9
@Override  // com.tencent.mobileqq.pic.api.IPicFlash
public boolean isFlashPicMsg(MessageRecord arg1) {
return FlashPicHelper.a(arg1);
}

@Override // com.tencent.mobileqq.pic.api.IPicFlash
public boolean isFlashPicMsgReaded(MessageRecord arg1) {
return FlashPicHelper.b(arg1);
}

hook前者,固定返回false,可实现闪照作为图片打开。
hook后者,固定返回false,可实现无限打开闪照。

考虑到实际使用时,需要判断发送的图片是否为闪照,选择hook后者(FlashPicHelper.b)。