好友
阅读权限 25
听众
最后登录 1970-1-1
L剑仙
发表于 2020-2-3 10:26
本帖最后由 L剑仙 于 2021-5-31 12:13 编辑
严重声明:本文仅供学习交流使用,严禁随意转载或者用于其他用途,所产生的一切后果与本人无关。
菜鸟继https://www.52pojie.cn/thread-1097114-1-1.html 简单分析筛子流程之后,在同大家一起学习一下撤回的一步步实现。如果大家支持,希望学习的人够多,抢红包、转账、甚至整个协议在java和so层的组装和剥离菜鸟都可以写文章共同学习。
搜一搜网上的文章,很多以前的大佬都是通过hook 掉updateWithOnConflict 函数,当type=1000 时,判断出撤回信息的id ,然后重新插入数据库,并修改“xx 撤回一条信息”为“xx 撤回信息失败”。猜想这个过程可能分为三步:
1. 接收到某某发出的一条信息
2. 接收到某某撤回了一条信息,删除或者替换这条信息
3. 插入文字“某某撤回了一条信息通过monitor 方法回溯或者直接hook 函数updateWithOnConflict 作为突破口,因为发送消息肯定要与数据库交互,而处理数据库信息很有可能用到这个函数,手动狗头。
[JavaScript] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var
sql = Java.use(
'com.tencent.wcdb.database.SQLiteDatabase'
);
sql.updateWithOnConflict.implementation=
function
(a1,a2,a3,a4,a5)
{
console.log(
"hook update start"
);
console.log(
"a1:"
+a1);
console.log(
"a2:"
+a2);
console.log(
"a3:"
+a3);
console.log(
"a4:"
+a4);
console.log(
"a5:"
+a5);
console.log(
"rtn:"
+
this
.updateWithOnConflict(a1,a2,a3,a4,a5));
return
this
.updateWithOnConflict(a1,a2,a3,a4,a5)
}
我们先正常发一条消息,查看hook 结果函数原型updateWithOnConflict(Stringtable, ContentValues values, String whereClause, String[] whereArgs,int conflictAlgorithm)
向测试手机发送 haha ,拼接数据库语句为
[Asm] 纯文本查看 复制代码
1
2
3
4
5
6
7
8
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
堆栈信息如下,这个下面分析撤回的堆栈信息时要用 [Asm] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
com.tencent.wcdb.database.SQLiteDatabase.updateWithOnConflict(Native Method)
com.tencent.wcdb.database.SQLiteDatabase.update(SourceFile:1726)
com.tencent.mm.cf.f.update(SourceFile:774)
com.tencent.mm.cf.h.update(SourceFile:601)
com.tencent.mm.storage.
al
.a(SourceFile:1193)
com.tencent.mm.storage.
al
.b(SourceFile:25178)
com.tencent.mm.storage.bj
$
1.fL(SourceFile:326)
com.tencent.mm.sdk.e.l.dQs(SourceFile:158)
com.tencent.mm.sdk.e.l.unlock(SourceFile:49)
com.tencent.mm.storage.bj.Ve(SourceFile:409)
com.tencent.mm.plugin.messenger.foundation.f.cO(SourceFile:141)
com.tencent.mm.plugin.zero.
c
.cO(SourceFile:70)
com.tencent.mm.modelmulti.o
$
a
$
1.onTimerExpired(SourceFile:821)
com.tencent.mm.sdk.platformtools.ap.handleMessage(SourceFile:69)
com.tencent.mm.sdk.platformtools.am.handleMessage(SourceFile:185)
com.tencent.mm.sdk.platformtools.am.dispatchMessage(SourceFile:140)
android.os.Looper.
loop
(Looper.java:164)
android.os.HandlerThread.run(HandlerThread.java:65)
com.tencent.mm.sdk.g.
c
.
c
$
1
$
1.run(SourceFile:39)
下面我们撤回这条信息,发现 updateWithOnConflict 这个函数被调用了 3 次,也就是说一次撤回包含着 3 次数据库更新。 [Asm] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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=10000flag=1579779746000 digestUser= digest="baba" 撤回了一条消息 isSend=0hasTrunc=1 unReadCount=0 conversationTime=1579779746000content="baba" 撤回了一条消息 username=wxid_dajiadebabastatus=3 a22:msgType=1flag=1579779746000 content=haha digestUser= digest=hahalastSeq=707367243 msgCount=33 isSend=0hasTrunc=1 unReadCount=1 conversationTime=1579779746000username=wxid_dajiadebaba status=3
第一句 updatemessage 这个表,还附带 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 堆栈如下:
[Asm] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
com.tencent.wcdb.database.SQLiteDatabase.updateWithOnConflict(Native Method)
com.tencent.wcdb.database.SQLiteDatabase.update(SourceFile:1726)
com.tencent.mm.cf.f.update(SourceFile:774)
com.tencent.mm.cf.h.update(SourceFile:601)
com.tencent.mm.storage.bj.a(SourceFile:2311) mo40526a
com.tencent.mm
.model
.f.a(SourceFile:145)
com.tencent.mm
.model
.f.a(SourceFile:352) mo7343a
com.tencent.mm
.model
.cc.b(SourceFile:258)
com.tencent.mm.r.b.b(SourceFile:40)
com.tencent.mm.plugin.messenger.foundation.
c
.a(SourceFile:165)
com.tencent.mm.plugin.messenger.foundation.
c
.a(SourceFile:1059)
com.tencent.mm.plugin.messenger.foundation.f.a(SourceFile:118)
com.tencent.mm.plugin.zero.
c
.a(SourceFile:57) 处理协议
com.tencent.mm.modelmulti.o
$
a
$
1.onTimerExpired(SourceFile:806)
com.tencent.mm.sdk.platformtools.ap.handleMessage(SourceFile:69)
com.tencent.mm.sdk.platformtools.am.handleMessage(SourceFile:185)
com.tencent.mm.sdk.platformtools.am.dispatchMessage(SourceFile:140)
android.os.Looper.
loop
(Looper.java:164)
android.os.HandlerThread.run(HandlerThread.java:65)
com.tencent.mm.sdk.g.
c
.
c
$
1
$
1.run(SourceFile:39) 这之上应该与撤回功能有关
Full
call
stack:undefined
com.tencent.wcdb.database.SQLiteDatabase.updateWithOnConflict(Native Method)
com.tencent.wcdb.database.SQLiteDatabase.update(SourceFile:1726)
com.tencent.mm.cf.f.update(SourceFile:774)
com.tencent.mm.cf.h.update(SourceFile:601)
com.tencent.mm.storage.
al
.a(SourceFile:1193)
com.tencent.mm.storage.
al
.b(SourceFile:25178)
com.tencent.mm.storage.bj
$
1.fL(SourceFile:326)
com.tencent.mm.sdk.e.l.dQs(SourceFile:158)
com.tencent.mm.sdk.e.l.unlock(SourceFile:49)
com.tencent.mm.storage.bj.Ve(SourceFile:409)
com.tencent.mm.plugin.messenger.foundation.f.cO(SourceFile:141)
com.tencent.mm.plugin.zero.
c
.cO(SourceFile:70)
com.tencent.mm.modelmulti.o
$
a
$
1.onTimerExpired(SourceFile:821)
com.tencent.mm.sdk.platformtools.ap.handleMessage(SourceFile:69)
com.tencent.mm.sdk.platformtools.am.handleMessage(SourceFile:185)
com.tencent.mm.sdk.platformtools.am.dispatchMessage(SourceFile:140)
android.os.Looper.
loop
(Looper.java:164)
android.os.HandlerThread.run(HandlerThread.java:65)
com.tencent.mm.sdk.g.
c
.
c
$
1
$
1.run(SourceFile:39) 这之上是正常接收消息的流程,可以与前面正常发消息调用的堆栈对比
Full
call
stack:undefined
dalvik.system.VMStack.getThreadStackTrace(Native Method)
java.lang.Thread.getStackTrace(Thread.java:1538)
com.tencent.wcdb.database.SQLiteDatabase.updateWithOnConflict(Native Method)
com.tencent.wcdb.database.SQLiteDatabase.update(SourceFile:1726)
com.tencent.mm.cf.f.update(SourceFile:774)
com.tencent.mm.cf.h.update(SourceFile:601)
com.tencent.mm.storage.
al
.avm(SourceFile:42015)
com.tencent.mm.ui.chatting.
c
.
c
.efg(SourceFile:846)
com.tencent.mm.ui.chatting.
c
.aa.axB(SourceFile:479)
com.tencent.mm.ui.chatting.ChattingUIFragment
$
5.run(SourceFile:887)
com.tencent.mm.sdk.platformtools.aq.run(SourceFile:164)
android.os.Handler.handleCallback(Handler.java:790)
android.os.Handler.dispatchMessage(Handler.java:99)
com.tencent.mm.sdk.platformtools.am.dispatchMessage(SourceFile:129)
android.os.Looper.
loop
(Looper.java:164)
android.os.HandlerThread.run(HandlerThread.java:65)
com.tencent.mm.sdk.g.
c
.
c
$
1
$
1.run(SourceFile:39) 这之上处理ui界面相关操作,其storage.
al
.avm函数生成了update的ContentValues的值,值得看一看
这里先点出重点函数吧,后面再具体分析流程,懒得看同学的就看到这里结束吧, 关键函数在 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 反编译的代码粘过来看
[Java] 纯文本查看 复制代码
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
public
final
e$b f.a(String p0,Map p1,e$a p2)
{
e$b v4;
e v4_1;
e v4_2;
String v5;
LinkedList v6;
String v7;
boolean
v8;
c v10;
Object[] v12;
Iterator v8_1;
bi v6_1;
Object[] v11_1;
ak v9_1;
e$b v8_2;
String v8_3;
String v4_3;
cj v8_4;
String v14;
byte
[] v9_2;
long
v6_2;
long
v16;
aq v4_4;
String v6_3;
int
v17;
e v4_5;
bbp v5_1;
Object[] v8_5;
SharedPreferences v10_2;
String[] v11_2;
String[] v8_6;
Integer v4_6;
Object[] v7_1;
byte
[] v6_4;
byte
[] v7_2;
byte
[] v8_7;
PByteArray v10_3;
int
v13;
int
v14_1;
String v4_7;
String v9_3;
Boolean v5_2;
Object[] v6_5;
a v7_3;
Object[] v8_8;
Object[] v9_4;
Object[] v10_4;
boolean
v11_3;
Object[] v0;
in v18;
b v19;
a v20;
String[] v18_1;
ArrayList v19_1;
String v20_1;
String[] v6_6;
boolean
v6_7;
bkn v5_3;
c v6_8;
ak v7_4;
z v6_9;
ac$a v8_9;
Object[] v4_8;
int
v4_9;
Object v5_6;
String v6_10;
Object v7_5;
nx v10_5;
e$b v9_5;
AppMethodBeat.i(
16267
);
cr v15 = p2.foR;
String v11 = aa.a(v15.yCk);
e$b v9 =
null
;
if
(p0 && (v4 =
this
.geB.get(p0))) {
v4 = v4.a(p0, p1, p2);
AppMethodBeat.o(
16267
);
return
v4;
}
else
if
(p0 && p0.equals(
"addcontact"
)){
v15.yCk = aa.wH(p1.get(
".sysmsg.addcontact.content"
));
v15.oXG =
1
;
v4_1 = e$d.bG(Integer.valueOf(
1
));
if
(!v4_1) {
v9 =
0
;
}
else
{
v9_5 = v4_1.b(p2);
}
}
if
(p0 && p0.equals(
"dynacfg"
)) {
g.Vi().a(v11, p1,
false
);
g.Vj();
if
(c.UT() ==
2
) {
h.syT.kvStat(
10879
,
""
);
}
ab.d(
"MicroMsg.BigBallSysCmdMsgConsumer"
,
"Mute_Room_Disable:"
+ Integero.getInt(g.Vi().getValue(
"MuteRoomDisable"
),
0
)));
if
(aa.dOE()) {
f.akg();
}
}
if
(p0 && p0.equals(
"dynacfg_split"
)) {
g.Vi().a(v11, p1,
true
);
if
(aa.dOE()) {
f.akg();
}
}
if
(p0 && p0.equals(
"banner"
)) {
v4_2 = p1.get(
".sysmsg.mainframebanner.$type"
);
v5 = p1.get(
".sysmsg.mainframebanner.showtype"
);
v6 = p1.get(
".sysmsg.mainframebanner.data"
);
if
(v4_2 && (v4_2.length() >
0
)) {
bh.alG().a(
new
bg(bo.getInt(v4_2,
0
), bo.getInt(v5,
0
), v6));
}
v5 = p1.get(
".sysmsg.friendrecommand.touser"
);
if
(p1.get(
".sysmsg.friendrecommand.fromuser"
) && v5) {
az.alz().ajX().a(v5,
true
,
null
);
}
v4_2 = p1.get(
".sysmsg.banner.securitybanner.chatname"
);
v5 = p1.get(
".sysmsg.banner.securitybanner.wording"
);
v6 = p1.get(
".sysmsg.banner.securitybanner.linkname"
);
v7 = p1.get(
".sysmsg.banner.securitybanner.linksrc"
);
v8 = p1.get(
".sysmsg.banner.securitybanner.showtype"
);
if
(!bo.isNullOrNil(v4_2) && !bo.isNullOrNil(v8)) {
v10 =
null
;
v8 = (v8.equals(
"1"
))?
true
: v10;
v12 =
new
String[
3
];
v12[
0
]=v5;
v12[
1
]=v6;
v12[
2
]=v7;
az.alz().ajY().a(v4_2, v8, v12);
}
az.alz().ajZ().o(p1);
}
if
(!bo.isNullOrNil(p0) && p0.equals(
"midinfo"
)) {
v4_2 = p1.get(
".sysmsg.midinfo.json_buffer"
);
v5 = p1.get(
".sysmsg.midinfo.time_interval"
);
v8_1 =
new
Object[
3
];
v8_1[
0
]=v5;
v8_1[
1
]=v4_2;
v8_1[
2
]=v11;
ab.i(
"MicroMsg.BigBallSysCmdMsgConsumer"
,
"QueryMid time[%s] json[%s] [%s] "
, v8_1);
v5 = bo.getInt(v5,
0
);
if
((((
long
)v5-
0x00015180
) >
0
) && (((
long
)v5-
0x000d2f00
) <
0
)) {
az.alz();
c.aaH().set(
0x00051001
, Long.valueOf((bo.azo()+(
long
)v5)));
}
if
(!bo.isNullOrNil(v4_2)) {
d.aaU(v4_2);
}
}
if
(p0 && p0.equals(
"revokemsg"
)) {
ab.i(
"MicroMsg.BigBallSysCmdMsgConsumer"
,
"mm hit MM_DATA_SYSCMD_NEWXML_SUBTYPE_REVOKE"
);
v5 = p1.get(
".sysmsg.revokemsg.newmsgid"
);
v6 = p1.get(
".sysmsg.revokemsg.replacemsg"
);
v9 =
new
Object[
2
];
v9[
0
]=v5;
v9[
1
]=v6;
ab.i(
"MicroMsg.BigBallSysCmdMsgConsumer"
,
"ashutest::[oneliang][xml parse] ,msgId:%s,replaceMsg:%s "
, v9);
az.alz();
f.a(c.ajB().ak(p1.get(
".sysmsg.revokemsg.session"
), bo.getLong(v5,
0
)), p2, v6,
"MicroMsg.BigBallSysCmdMsgConsumer"
);
AppMethodBeat.o(
16267
);
return
null
;
}
else
if
(p0 && p0.equals(
"clouddelmsg"
)){
ab.i(
"MicroMsg.BigBallSysCmdMsgConsumer"
,
"mm hit MM_DATA_SYSCMD_NEWXML_CLOUD_DEL_MSG"
);
v4_2 = p1.get(
".sysmsg.clouddelmsg.delcommand"
);
v5 = p1.get(
".sysmsg.clouddelmsg.msgid"
);
v6 = p1.get(
".sysmsg.clouddelmsg.fromuser"
);
v7 = v11.indexOf(
"<msg>"
);
v8_1 = v11.indexOf(
"</msg>"
);
v7 = (v7 == -
1
|| v8_1 == -
1
)?
""
: be.bp(br.J(v11.substring(v7, (v8_1+
6
)),
"msg"
));
v10_1 =
new
Object[
4
];
v10_1[
0
]=v4_2;
v10_1[
1
]=v5;
v10_1[
2
]=v6;
v10_1[
3
]=v7;
ab.i(
"MicroMsg.BigBallSysCmdMsgConsumer"
,
"[hakon][clouddelmsg], delcommand:%s, msgid:%s, fromuser:%s, sysmsgcontent:%s"
, v10_1);
az.alz();
if
(!(v6 = c.ajB().gf(v6, v5)) || (v6.size() <=
0
)) {
ab.e(
"MicroMsg.BigBallSysCmdMsgConsumer"
,
"get null by getByBizClientMsgId"
);
AppMethodBeat.o(
16267
);
return
null
;
}
else
{
v8_1 = v6.iterator();
while
(v8_1.hasNext()) {
v6_1 = v8_1.next();
if
(!v6_1) {
ab.e(
"MicroMsg.BigBallSysCmdMsgConsumer"
,
"[hakon][clouddelmsg], msgInfo == null"
);
}
else
if
((v6_1.field_msgSvrId <
0
)){
v11_1 =
new
Object[
2
];
v11_1[
0
]=Long.valueOf(v6_1.field_msgId);
v11_1[
1
]=Long.valueOf(v6_1.field_msgSvrId);
ab.e(
"MicroMsg.BigBallSysCmdMsgConsumer"
,
"[hakon][clouddelmsg], invalid msgInfo.msgId = %s, srvId = %s"
, v11_1);
}
else
{
v11_1 =
new
Object[
2
];
v11_1[
0
]=Long.valueOf(v6_1.field_msgId);
v11_1[
1
]=Long.valueOf(v6_1.field_msgSvrId);
ab.i(
"MicroMsg.BigBallSysCmdMsgConsumer"
,
"[hakon][clouddelmsg], msgInfo.msgId = %s, srvId = %s"
, v11_1);
v9 = bo.getInt(v4_2,
0
);
if
(v9 ==
1
) {
az.alz();
c.ajB().au(v6_1.field_talker, v6_1.field_msgSvrId);
}
else
if
(v9 ==
2
&& v6_1.dSS()){
v6_1.setContent(v7);
bi.a(v6_1, p2);
az.alz();
c.ajB().b(v6_1.field_msgSvrId, v6_1);
az.alz();
v9_1 = c.ajE().avk(v6_1.field_talker);
if
(v9_1 && (v9_1.field_unReadCount >
0
)) {
az.alz();
if
(v9_1.field_unReadCount >= c.ajB().Z(v6_1)) {
v9_1.iS((v9_1.field_unReadCount-
1
));
az.alz();
c.ajE().a(v9_1, v9_1.field_username);
}
}
}
v9 =
new
pu();
v9.dlv.cUH = v6_1.field_msgId;
v9.dlv.dlw = v7;
v9.dlv.djW = v6_1;
a.AGO.l(v9);
}
}
AppMethodBeat.o(
16267
);
return
null
;
}
}
else
if
(p0 && p0.equals(
"updatepackage"
)){
v4_2 = e$d.bG(Integer.valueOf(
0x90000011
));
v8_2 = (!v4_2)?
null
: v4_2.b(p2);
}
else
{
v8 = v9;
}
我们一眼就看到了这一句 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 时直接返回就可以屏蔽掉撤回了;粗略一看,这个函数还控制着添加联系人,更新包等功能,大家有兴趣可以具体跟一下。 [JavaScript] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
var
modlef = Java.use(
'com.tencent.mm.model.f'
);
modlef.a.overload(
'java.lang.String'
,
'java.util.Map'
,
'com.tencent.mm.aj.e$a'
).implementation=
function
(a1,a2,a3)
{ console.log(
"hook a start"
);
console.log(
"a1:"
+a1);
console.log(
"a2:"
+ JSON.stringify(a2));
console.log(
"a3:"
+a3);
if
(a1==
"revokemsg"
)
return
null
return
this
.a(a1,a2,a3)
}
下面,我们就来一步一步看一下撤回的实现,从系统调用一直到 updateWithOnConflict ,我们仍然是倒着看 简单观察堆栈,第一步这 4 个 update 是必经之路,最底层是 updateWithOnConflict ,上面都是这个 update 函数的层层封装,这里的关键点就是数据库名,也就是 update 的到底是哪个数据库, [Asm] 纯文本查看 复制代码
1
2
3
4
com.tencent.wcdb.database.SQLiteDatabase.updateWithOnConflict(Native Method)
com.tencent.wcdb.database.SQLiteDatabase.update(SourceFile:1726)
com.tencent.mm.cf.f.update(SourceFile:774)
com.tencent.mm.cf.h.update(SourceFile:601)
2个sqlitedatabase的封装实现我们就不看了,熟悉数据库编程的同学早就用滥了,直接看com.tencent.mm.cf.f.update [Java] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
com.tencent.mm.cf.f.update
public
final
int
update(String str, ContentValues contentValues, String str2, String[] strArr) {
AppMethodBeat.m3378i(
59081
);
SQLiteDatabase sQLiteDatabase =
this
.BnR !=
null
?
this
.BnR :
this
.BnS;
if
(isMainThread()) {
BnX.mo6448a(sQLiteDatabase,
32769
, str);
}
int
update = sQLiteDatabase.update(str, contentValues, str2, strArr);
AppMethodBeat.m3379o(
59081
);
return
update;
}
数据库实例 BnR 赋值语句在 m4070F 函数中: fVar.BnR= SQLiteDatabase.openDatabase(str3, bytes, sQLiteCipherSpec, null ,i, fVar); 数据库实例 Bn S 赋值语句在 m4074cs 函数中: fVar.BnS= SQLiteDatabase.openDatabase(str, null ,i, fVar); 这 2 个函数简单看,对于数据库赋值而言就是一个有加密一个没加密,我们在这里 hook 可以得到 sQLiteCipherSpec 对象实例,分析它的加密流程,有意思的函数还有一个 get Path 是获得路径的,这里贴一下数据库的打开与解密吧,与本文无关,权当延伸,很清晰的打开与解密流程
[Java] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
public
static
C1534f m4070F(String str, String str2,
boolean
z) {
String str3;
byte
[] bytes;
SQLiteCipherSpec sQLiteCipherSpec;
AppMethodBeat.m3378i(
59074
);
try
{
C9958c cVar =
new
C9958c(str +
"-vfslog"
);
C9958c cVar2 =
new
C9958c(str +
"-vfslo1"
);
if
(cVar.exists() && cVar.length() >
256
) {
cVar.delete();
}
if
(cVar2.exists() && cVar2.length() >
256
) {
cVar2.delete();
}
}
catch
(Throwable th) {
C8953ab.printErrStackTrace(
"MicroMsg.MMDataBase"
, th,
""
,
new
Object[
0
]);
}
C1534f fVar =
new
C1534f();
int
i =
268435456
;
if
(C9015bo.isNullOrNil(str)) {
str3 = SQLiteDatabaseConfiguration.MEMORY_DB_PATH;
fVar.BnY =
true
;
}
else
{
str3 = str;
}
if
(C9015bo.isNullOrNil(str2)) {
sQLiteCipherSpec =
null
;
bytes =
null
;
}
else
{
bytes = str2.getBytes();
sQLiteCipherSpec = mnM;
}
if
(z && C1531b.BnM) {
i =
805306368
;
}
else
if
(!C1531b.BnM) {
C9961f.deleteFile(str +
"-shm"
);
}
try
{
fVar.BnR = SQLiteDatabase.openDatabase(str3, bytes, sQLiteCipherSpec,
null
, i, fVar);
fVar.BnR.setTraceCallback(fVar);
if
(dXc()) {
fVar.BnR.setCheckpointCallback(BnW);
C8953ab.m13556i(
"MicroMsg.MMDataBase"
,
"Enable async checkpointer for DB: "
+ fVar.getPath());
}
if
(C9030f.AHl.ass(
"ENABLE_STETHO"
)) {
BnT.put(fVar.getPath(), fVar.BnR);
}
if
(fVar.BnR ==
null
) {
AppMethodBeat.m3379o(
59074
);
return
null
;
}
AppMethodBeat.m3379o(
59074
);
return
fVar;
}
catch
(SQLiteException e) {
C6339e.sxI.mo11124f(
"DBCantOpen"
,
"DB ("
+
new
C9958c(str3).getName() +
") can't open: "
+ C9015bo.m13722k(e),
null
);
AppMethodBeat.m3379o(
59074
);
throw
e;
}
}
下面 上层的com.tencent.mm.cf.h.update [Java] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public
final
int
update(String str, ContentValues contentValues, String str2, String[] strArr) {
int
i;
AppMethodBeat.m3378i(
59123
);
if
(!isOpen()) {
C8953ab.m13553e(
this
.TAG,
"DB IS CLOSED ! {%s}"
, C9015bo.dPZ());
AppMethodBeat.m3379o(
59123
);
return
-
2
;
}
boolean
z = WXHardCoderJNI.hcDBEnable;
int
i2 = WXHardCoderJNI.hcDBDelayWrite;
int
i3 = WXHardCoderJNI.hcDBCPU;
int
i4 = WXHardCoderJNI.hcDBIO;
if
(WXHardCoderJNI.hcDBThr) {
i = C2700g.abc().dPe();
}
else
{
i =
0
;
}
int
startPerformance = WXHardCoderJNI.startPerformance(z, i2, i3, i4, i, WXHardCoderJNI.hcDBTimeout,
501
, WXHardCoderJNI.hcDBActionWrite,
this
.TAG);
C1532c.begin();
try
{
int
update =
this
.BnH.update(str, contentValues, str2, strArr);
C1532c.m4059a(str,
null
,
this
.kLT);
WXHardCoderJNI.stopPerformance(WXHardCoderJNI.hcDBEnable, startPerformance);
AppMethodBeat.m3379o(
59123
);
return
update;
}
catch
(Exception e) {
C6339e.sxI.idkeyStat(
181
,
11
,
1
,
false
);
C8953ab.m13552e(
this
.TAG,
"update Error :"
+ e.getMessage());
C1532c.m4060m(e);
WXHardCoderJNI.stopPerformance(WXHardCoderJNI.hcDBEnable, startPerformance);
AppMethodBeat.m3379o(
59123
);
return
-
1
;
}
catch
(Throwable th) {
WXHardCoderJNI.stopPerformance(WXHardCoderJNI.hcDBEnable, startPerformance);
AppMethodBeat.m3379o(
59123
);
throw
th;
}
}
这个函数封装了上一个 update ,简单看一下他有很多 WXHardCoderJNI 的调用,而这个类里面有很多 native 函数,这应该是鹅厂同行写的一个控制优化相关的类,与本文无关, 它的数据库实例是 this .BnH ,他赋值在 this .BnH= this .Boo.BnH; 而 Boo 属性初始化在 public C1520aBoo = new C1520a(); 于是我们跳转到类 C1520a ( com.tencent.mm.cf.a ) ,这个类里面有很多 sql 语句,其中包含解密初始化等,一些初始化函数中有这样的语句 this .BnH= C1534f.m4070F(str, this .aBL,z); this .BnH= C1534f.m4074cs(str,z); ,这不就是调用了 com.tencent.mm.cf.h 这个类里面的数据库赋值,至此,我们就搞清楚了数据库实例如何获得,贴一个函数大家简单看看:
[Java] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
private
boolean
m4020a(String str,
long
j,
boolean
z, String str2) {
int
i;
AppMethodBeat.m3378i(
59029
);
if
(
this
.BnH !=
null
) {
AssertionError assertionError =
new
AssertionError();
AppMethodBeat.m3379o(
59029
);
throw
assertionError;
}
this
.isNew = !C9961f.m15607dA(str);
boolean
z2 =
false
;
Iterator it = dWT().iterator();
while
(
true
) {
if
(!it.hasNext()) {
break
;
}
String str3 = (String) it.next();
this
.aBL = C1220g.m3432D((str3 + j).getBytes()).substring(
0
,
7
);
try
{
this
.BnH = C1534f.m4070F(str,
this
.aBL, z);
m4019a(
this
.BnH);
if
(!C1615q.m4230cl(
true
).equals(str3)) {
C8953ab.m13556i(
"MicroMsg.DBInit"
,
"IMEI changed detected: "
.concat(String.valueOf(str3)));
C1609l.m4192SN().set(
258
, str3);
C6339e.sxI.idkeyStat(
181
,
5
,
1
,
false
);
}
AppMethodBeat.m3379o(
59029
);
return
true
;
}
catch
(SQLiteException e) {
if
(!(e
instanceof
SQLiteDatabaseCorruptException)) {
z2 =
false
;
break
;
}
z2 =
true
;
}
}
if
(z2) {
if
(!z) {
i =
42
;
}
else
if
(C1534f.dXc()) {
i =
43
;
}
else
{
i =
41
;
}
C6339e.sxI.idkeyStat(
181
, (
long
) i,
1
,
true
);
C1534f.awG(str);
if
(str.endsWith(
"EnMicroMsg.db"
)) {
C1534f.awG(C2700g.aaZ().fxL +
"dbback/EnMicroMsg.db"
);
}
try
{
this
.aBL = C1220g.m3432D((C1615q.m4230cl(
true
) + j).getBytes()).substring(
0
,
7
);
this
.BnH = C1534f.m4070F(str,
this
.aBL, z);
m4019a(
this
.BnH);
this
.isNew =
true
;
C6339e.sxI.idkeyStat(
181
,
6
,
1
,
false
);
AppMethodBeat.m3379o(
59029
);
return
true
;
}
catch
(SQLiteException e2) {
C6339e.sxI.idkeyStat(
181
,
7
,
1
,
false
);
}
}
else
{
if
(str2 !=
null
&& str2.length() >
0
) {
this
.isNew = !C9961f.m15607dA(str2);
try
{
this
.BnH = C1534f.m4070F(str2,
this
.aBL, z);
m4019a(
this
.BnH);
C6339e.sxI.idkeyStat(
181
,
6
,
1
,
false
);
AppMethodBeat.m3379o(
59029
);
return
true
;
}
catch
(SQLiteException e3) {
C6339e.sxI.idkeyStat(
181
,
7
,
1
,
false
);
}
}
if
(
this
.BnH !=
null
) {
this
.BnH.close();
this
.BnH =
null
;
}
this
.aBL =
null
;
AppMethodBeat.m3379o(
59029
);
return
false
;
}
}
上面是 com.tencent.mm.storage.bj.a , storage 这个类肯定和存储有关,这个 a 方法有很多重载,简单 hook 一下判断出 a 方法签名 (JLcom/tencent/mm/storage/bi;)V ,所以函数为 mo10677a [Asm] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
public
final void mo10677a(long j, C9124bi biVar) {//j就是 msgId, biVar存储了contentValues
AppMethodBeat.m3378i(1389);
if
(biVar.dUZ()) {
String avS = avS(biVar.dVU);
if
(C21678w.m34652oO(avS)) {
C8953ab.m13551d(
"MicroMsg.MsgInfoStorage"
,
"msgCluster = %s"
, avS);
biVar.mo6820kh(
"notifymessage"
);
}
}
m46253au(biVar);
if
(
this
.ght.update(m46255ow(j), biVar.convertTo(),
"msgId=?"
, new String[]{String.valueOf(j)}) != 0) {//这一句调用上面的update
doNotify();
mo10682a(new C5973c(biVar.field_talker,
"update"
, biVar));//这一句还取了 talker,猜想功能是替换为xx撤回了一条消息的上层函数,懒得跟了
AppMethodBeat.m3379o(1389);
return;
}
C6339e.sxI.idkeyStat(111, 244, 1, false);
AppMethodBeat.m3379o(1389);
}
在往上就是我开头提到的关键函数 com.tencent.mm.model.f.a(SourceFile:145), 和 com.tencent.mm.model.f.a(SourceFile:352) mo7343a 组合了,不记得的同学翻上去看看
在上面是 com.tencent.mm.model.cc.b(Lcom/tencent/mm/aj/e$a;)Lcom/tencent/mm/aj/e$b; ( mo5953b ),这个函数: [Java] 纯文本查看 复制代码
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
public
final
C1268b mo5953b(C1267a aVar) {
Map map;
String str;
List<C5988p> list;
AppMethodBeat.m3378i(
59939
);
C27985cr crVar = aVar.foR;
switch
(crVar.oXG) {
case
10001
:
m5274a(C2976aa.m5550a(crVar.yCi), aVar,
false
);
C6339e.sxI.kvStat(
10395
, String.valueOf(crVar.rCB));
AppMethodBeat.m3379o(
59939
);
return
null
;
case
10002
:
String a = C2976aa.m5550a(crVar.yCk);
if
(C9015bo.isNullOrNil(a)) {
C8953ab.m13552e(
"MicroMsg.SysCmdMsgExtension"
,
"null msg content"
);
AppMethodBeat.m3379o(
59939
);
return
null
;
}
if
(a.startsWith(
"~SEMI_XML~"
)) {
Map asT = C9008be.asT(a);
if
(asT ==
null
) {
C8953ab.m13553e(
"MicroMsg.SysCmdMsgExtension"
,
"SemiXml values is null, msgContent %s"
, a);
AppMethodBeat.m3379o(
59939
);
return
null
;
}
map = asT;
str =
"brand_service"
;
}
else
{
int
indexOf = a.indexOf(
"<sysmsg"
);
if
(indexOf != -
1
) {
String substring = a.substring(indexOf);
C8953ab.m13551d(
"MicroMsg.SysCmdMsgExtension"
,
"oneliang, msg content:%s,sub content:%s"
, a, substring);
Map J = C9020br.m13742J(substring,
"sysmsg"
);
if
(J ==
null
) {
C8953ab.m13553e(
"MicroMsg.SysCmdMsgExtension"
,
"XmlParser values is null, msgContent %s"
, a);
AppMethodBeat.m3379o(
59939
);
return
null
;
}
map = J;
str = (String) J.get(
".sysmsg.$type"
);
}
else
{
int
indexOf2 = a.indexOf(
"<appmsg"
);
if
(indexOf2 != -
1
) {
C8953ab.m13556i(
"MicroMsg.SysCmdMsgExtension"
,
"msgContent start with <appmsg"
);
String substring2 = a.substring(indexOf2);
C8953ab.m13551d(
"MicroMsg.SysCmdMsgExtension"
,
"oneliang, msg content:%s,sub content:%s"
, a, substring2);
Map J2 = C9020br.m13742J(substring2,
"appmsg"
);
if
(J2 ==
null
) {
C8953ab.m13553e(
"MicroMsg.SysCmdMsgExtension"
,
"XmlParser values is null, msgContent %s"
, a);
AppMethodBeat.m3379o(
59939
);
return
null
;
}
map = J2;
str = (String) J2.get(
".appmsg.title"
);
}
else
{
C8953ab.m13552e(
"MicroMsg.SysCmdMsgExtension"
,
"msgContent not start with <sysmsg or <appmsg"
);
AppMethodBeat.m3379o(
59939
);
return
null
;
}
}
}
C8953ab.m13551d(
"MicroMsg.SysCmdMsgExtension"
,
"recieve a syscmd_newxml %s subType %s"
, a, str);
if
(str !=
null
) {
m5274a(str, aVar,
true
);
synchronized
(
this
.ghU) {
try
{
list = (List)
this
.ghU.get(str);
}
finally
{
while
(
true
) {
AppMethodBeat.m3379o(
59939
);
break
;
}
}
}
if
(list ==
null
|| list.isEmpty()) {
C8953ab.m13560w(
"MicroMsg.SysCmdMsgExtension"
,
"listener list is empty, return now"
);
}
else
{
C8953ab.m13557i(
"MicroMsg.SysCmdMsgExtension"
,
"listener list size is %d"
, Integer.valueOf(list.size()));
synchronized
(list) {
try
{
for
(C5988p onNewXmlReceived : list) {
onNewXmlReceived.onNewXmlReceived(str, map, aVar);
}
}
catch
(Throwable th) {
AppMethodBeat.m3379o(
59939
);
throw
th;
}
}
}
C5987o oVar = (C5987o)
this
.ghV.get(str);
if
(oVar !=
null
) {
return
oVar.mo7343a(str, map, aVar);
}
C8953ab.m13553e(
"MicroMsg.SysCmdMsgExtension"
,
"no NewXmlConsumer to consume cmd %s!!"
, str);
}
AppMethodBeat.m3379o(
59939
);
return
null
;
default
:
C8953ab.m13561w(
"MicroMsg.SysCmdMsgExtension"
,
"cmdAM msgType is %d, ignore, return now"
, Integer.valueOf(crVar.oXG));
AppMethodBeat.m3379o(
59939
);
return
null
;
}
}
容易看出 msg 分为 sysmsg 和 appmsg ,这里我们主要看一下参数 map 的传递, 撤回好像跑的是 appmsg ; C27985crcrVar = aVar.foR; //1 Stringa = C2976aa.m5550a(crVar.yCk); //2 m5550a 应该就是个tostring 类似功能的函数 int indexOf= a.indexOf( "<sysmsg" ); //3 Stringsubstring = a.substring(indexOf); //4 居然是暴力切割字符串 MapJ = C9020br.m13742J(substring, "sysmsg" ); //5 组合一下, MapJ = C9020br.m13742J( C2976aa.m5550a(aVar.foR .yCk).substring(C2976aa.m5550a(aVar.foR .yCk).indexOf( "<sysmsg" )), "sysmsg" ); 哈哈,是不是挺复杂的,其实我就是想说明,通过上层函数参数 aVar 的数据结构完全可以定位下层函数的 map 参数,只不过表达式有点复杂而已。
再往上进入到 plugin.messenger.foundation 这个大类里面,扫一眼发现,上层函数的参数已经和 protocal 相关了。 com.tencent.mm.plugin.messenger.foundation.c.a(Lcom/tencent/mm/aj/e$a;Lcom/tencent/mm/plugin/messenger/foundation/a/v;)Lcom/tencent/mm/aj/e$b;= m38177a
com.tencent.mm.plugin.messenger.foundation.c.a(Lcom/tencent/mm/protocal/protobuf/vg;[BZLcom/tencent/mm/plugin/messenger/foundation/a/v;)V= mo33997a
这个函数没啥好说的直接传参到下一层的 mo5953b ,值得注意的是 aVar.foR.yCi 存储着这个 app 是 newsapp 或 readerapp 或 blogapp ,还有有个有意思的是外层的一个 fucking 判断: thisfucking msg from mac weixin ,someone send msg to newsapp at macweixin ,givp up ,跟一下应该可以找到如何判断 msg 是 mac 系统的,顺便过掉。 [Java] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
public
static
C1268b m38177a(C1267a aVar, C24153v vVar) {
AppMethodBeat.m3378i(TXLiteAVCode.EVT_RTMP_PUSH_PUBLISH_START);
C27985cr crVar = aVar.foR;
if
(
10008
== C2977ae.hiZ && C2977ae.hja !=
0
) {
C8953ab.m13557i(
"MicroMsg.MessageSyncExtension"
,
"dkmsgid set svrmsgid %d -> %d"
, Long.valueOf(crVar.rCB), Integer.valueOf(C2977ae.hja));
crVar.rCB = (
long
) C2977ae.hja;
C2977ae.hja =
0
;
}
if
(((C5985k) C2700g.m5022ac(C5985k.
class
)).ciy().mo10747kF(crVar.rCB)) {
C8953ab.m13556i(
"MicroMsg.MessageSyncExtension"
,
"ignore, because reSync the deleted msg perhaps the IDC has change has swtiched"
);
AppMethodBeat.m3379o(TXLiteAVCode.EVT_RTMP_PUSH_PUBLISH_START);
return
null
;
}
String a = C2976aa.m5550a(crVar.yCi);
String a2 = C2976aa.m5550a(crVar.yCj);
if
(!a.equals(C2837u.akn()) || !a2.equals(
"newsapp"
) || crVar.oXG ==
51
) {
C8953ab.m13557i(
"MicroMsg.MessageSyncExtension"
,
"dkAddMsg from:%s to:%s id:[%d,%d,%d] status:%d type:%d time:[%d %s] diff:%d imgstatus:%d imgbuf:%d src:%d push:%d content:%s"
, a, a2, Long.valueOf(crVar.rCB), Integer.valueOf(crVar.rCz), Integer.valueOf(crVar.yCp), Integer.valueOf(crVar.kVf), Integer.valueOf(crVar.oXG), Integer.valueOf(crVar.CreateTime), C9015bo.m13727nW((
long
) crVar.CreateTime), Long.valueOf(C9015bo.azo() - ((
long
) crVar.CreateTime)), Integer.valueOf(crVar.yCl), Integer.valueOf(C2976aa.m5553a(crVar.yCm,
new
byte
[
0
]).length), Integer.valueOf(C9015bo.nullAsNil(crVar.yCn).length()), Integer.valueOf(C9015bo.nullAsNil(crVar.yCo).length()), C9015bo.atw(C2976aa.m5551a(crVar.yCk,
""
)));
C8953ab.m13557i(
"MicroMsg.MessageSyncExtension"
,
"parseMsgSource has been Deprecated by dk. at 20151218 [%s] %s "
, crVar.yCn,
""
);
C24154w.m38170h(crVar);
if
(a.equals(
"readerapp"
)) {
crVar.yCi = C2976aa.m5556wH(
"newsapp"
);
crVar.oXG =
12399999
;
}
if
((a.equals(
"blogapp"
) || a.equals(
"newsapp"
)) && crVar.oXG !=
10002
) {
crVar.oXG =
12399999
;
}
if
(crVar.oXG ==
52
) {
crVar.oXG =
1000052
;
}
if
(crVar.oXG ==
53
) {
crVar.oXG =
1000053
;
}
C21628bi.m34464c(aVar);
boolean
z =
false
;
C1268b bVar =
null
;
C1264e bG = C1266d.m3510bG(Integer.valueOf(crVar.oXG));
if
(bG ==
null
) {
bG = C1266d.m3510bG(a);
}
if
(bG !=
null
) {
bVar = bG.mo5953b(aVar);
C9124bi biVar = bVar ==
null
?
null
: bVar.cRE;
if
(biVar ==
null
) {
C8953ab.m13561w(
"MicroMsg.MessageSyncExtension"
,
"summerbadcr extension declared but skipped msg, type=%d, svrId=%d, MsgSeq=%d, createTime=%d, addMsgInfo=%s"
, Integer.valueOf(crVar.oXG), Long.valueOf(crVar.rCB), Integer.valueOf(crVar.yCp), Integer.valueOf(crVar.CreateTime), aVar);
}
else
if
(!m38176UL(a)) {
C8953ab.m13550d(
"MicroMsg.MessageSyncExtension"
,
" msg , id ="
+ biVar.field_msgId +
" "
+ vVar);
if
(biVar.field_msgId >
0
&& vVar !=
null
&& bVar.gnh) {
vVar.mo10881a(biVar, crVar);
}
}
z =
true
;
}
C24154w.m38168b(
5
, crVar);
if
(!z) {
C8953ab.m13555f(
"MicroMsg.MessageSyncExtension"
,
"unknown add msg request, type=%d. drop now !!!"
, Integer.valueOf(crVar.oXG));
}
AppMethodBeat.m3379o(TXLiteAVCode.EVT_RTMP_PUSH_PUBLISH_START);
return
bVar;
}
C8953ab.m13561w(
"MicroMsg.MessageSyncExtension"
,
"msgid:%d type:%d this fucking msg from mac weixin ,someone send msg to newsapp at mac weixin ,givp up."
, Long.valueOf(crVar.rCB), Integer.valueOf(crVar.oXG));
AppMethodBeat.m3379o(TXLiteAVCode.EVT_RTMP_PUSH_PUBLISH_START);
return
null
;
}
这里主要还是关注一下参数的传递,关键是C27985cr 这个类,其他没啥好说的 [Java] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public
final
void
mo33997a(C28418vg vgVar,
byte
[] bArr,
boolean
z, C24153v vVar) {
int
i =
0
;
AppMethodBeat.m3378i(TXLiteAVCode.EVT_CAMERA_CLOSE);
switch
(vgVar.zbt) {
case
5
:
C27985cr crVar = (C27985cr)
new
C27985cr().parseFrom(bArr);
if
(crVar !=
null
) {
C1267a aVar =
new
C1267a(crVar,
false
,
false
,
false
);
m38177a(aVar, vVar);
if
(!aVar.gnc) {
C21637bj.m34487a(C2976aa.m5550a(crVar.yCi), crVar.rCB, ((
long
) crVar.CreateTime) *
1000
, crVar.oXG);
}
}
AppMethodBeat.m3379o(TXLiteAVCode.EVT_CAMERA_CLOSE);
return
;
case
8
:
C28483ya yaVar = (C28483ya)
new
C28483ya().parseFrom(bArr);
String a = C2976aa.m5550a(yaVar.zdL);
int
i2 = yaVar.zdO;
Cursor di = ((C5985k) C2700g.m5022ac(C5985k.
class
)).ciy().mo10730di(a, i2);
if
(di.moveToFirst()) {
while
(!di.isAfterLast()) {
C9124bi biVar =
new
C9124bi();
biVar.convertFrom(di);
C21628bi.m34468m(biVar);
di.moveToNext();
}
}
di.close();
((C5985k) C2700g.m5022ac(C5985k.
class
)).ciy().mo10729dh(a, i2);
AppMethodBeat.m3379o(TXLiteAVCode.EVT_CAMERA_CLOSE);
return
;
case
9
:
C28490yh yhVar = (C28490yh)
new
C28490yh().parseFrom(bArr);
LinkedList<Integer> linkedList = yhVar.zdR;
while
(
true
) {
int
i3 = i;
if
(i3 >= linkedList.size()) {
break
;
}
else
{
C21628bi.m34484y(C2976aa.m5550a(yhVar.zdL), (
long
) ((Integer) linkedList.get(i3)).intValue());
i = i3 +
1
;
}
}
}
AppMethodBeat.m3379o(TXLiteAVCode.EVT_CAMERA_CLOSE);
}
在往上是 com.tencent.mm.plugin.messenger.foundation.f.a(Lcom/tencent/mm/protocal/protobuf/vg;[BZ)V= mo36123a ,很简单,自己看看吧
[Java] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
public
final
void
mo36123a(C28418vg vgVar,
byte
[] bArr,
boolean
z) {
AppMethodBeat.m3378i(
1062
);
C24149s By = C24150a.m38158By(vgVar.zbt);
if
(By !=
null
) {
try
{
By.mo33997a(vgVar, bArr, z,
this
.qvv);
AppMethodBeat.m3379o(
1062
);
}
catch
(IOException e) {
C8953ab.m13552e(
"MicroMsg.SyncDoCmdExtensions"
,
"docmd: parse protobuf error, "
+ e.getMessage());
RuntimeException runtimeException =
new
RuntimeException(
"docmd: parse protobuf error"
);
AppMethodBeat.m3379o(
1062
);
throw
runtimeException;
}
}
else
{
C8953ab.m13561w(
"MicroMsg.SyncDoCmdExtensions"
,
"SyncDoCmdExtension for cmd id [%s] is null."
, Integer.valueOf(vgVar.zbt));
AppMethodBeat.m3379o(
1062
);
}
}
。
在上 com.tencent.mm.plugin.zero.c.a(Lcom/tencent/mm/protocal/protobuf/vg;Z)Z=mo40148a ,仍然是注意一下参数传递,其实每次参数传递和变换,就是把外层协议层层剥离的过程,每一次消耗一些参数进行判断或其他动作,大家应该很容易看明白。 [Java] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public
final
boolean
mo40148a(C28418vg vgVar,
boolean
z) {
AppMethodBeat.m3378i(
58774
);
if
(!C2700g.aaU()) {
C8953ab.m13552e(
"MicroMsg.SyncDoCmdDelegate"
,
"account storage disabled, discard all commands"
);
AppMethodBeat.m3379o(
58774
);
return
false
;
}
long
azp = C9015bo.azp();
byte
[] a = C2976aa.m5552a(vgVar.zbu);
C8953ab.m13557i(
"MicroMsg.SyncDoCmdDelegate"
,
"doCmd %d cmdid:%d buf:%d thr:[%d]"
, Long.valueOf(azp), Integer.valueOf(vgVar.zbt), Integer.valueOf(C9015bo.m13690co(a)), Long.valueOf(Thread.currentThread().getId()));
if
(C9015bo.m13689cn(a)) {
C8953ab.m13552e(
"MicroMsg.SyncDoCmdDelegate"
,
"docmd: no protobuf found."
);
AppMethodBeat.m3379o(
58774
);
return
false
;
}
try
{
if
(
this
.xUP !=
null
) {
this
.xUP.mo36123a(vgVar, a, z);
}
C8953ab.m13557i(
"MicroMsg.SyncDoCmdDelegate"
,
"doCmd FIN %d cmdid:%d Time:%d"
, Long.valueOf(azp), Integer.valueOf(vgVar.zbt), Long.valueOf(C9015bo.m13715hn(azp)));
AppMethodBeat.m3379o(
58774
);
return
true
;
}
catch
(Exception e) {
C8953ab.printErrStackTrace(
"MicroMsg.SyncDoCmdDelegate"
, e,
""
,
new
Object[
0
]);
AppMethodBeat.m3379o(
58774
);
return
false
;
}
}
在往上 com.tencent.mm.modelmulti.o$a$1.onTimerExpired ,明显是判断是否超时了,超了怎么处理,在上面就是一些系统调用了,到此为止了 [Asm] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public
final boolean onTimerExpired() {
AppMethodBeat.m3378i(58399);
if
(C2700g.aaU() && !C2586a.
aaa
()) {
C2700g.aba();
if
(C2700g.aaZ() != null) {
C2700g.aba();
if
(C2700g.aaZ().aaH() != null) {
LinkedList<C28418vg> linkedList = C21737a.
this
.gDu.yUs.kUI;
C27641c cVar = new C27641c();
cVar.mo40149cN(C21737a.
this
.gDw);
long azp = C9015bo.azp();
while
(C21737a.
this
.gCe < linkedList.
size
()) {
linkedList.
size
();
if
(!cVar.mo40148a((C28418vg) linkedList.get(C21737a.
this
.gCe), false)) {
C6339e.sxI.idkeyStat(99, 46, 1, false);
}
C21737a.
this
.gCe++;
long hn = C9015bo.m13715hn(azp);
C8953ab.m13557i(
"MicroMsg.SyncService"
,
"processResp %s time:%s size:%s index:%s"
, C21737a.
this
.gDw, Long.valueOf(hn), Integer.valueOf(linkedList.
size
()), Integer.valueOf(C21737a.
this
.gCe - 1));
if
(hn >= 500) {
break;
}
}
cVar.mo40150cO(C21737a.
this
.gDw);
if
(C21737a.
this
.gCe < linkedList.
size
()) {
C8953ab.m13557i(
"MicroMsg.SyncService"
,
"processResp %s time:%s size:%s index:%s Shold Continue."
, C21737a.
this
.gDw, Long.valueOf(C9015bo.m13715hn(azp)), Integer.valueOf(linkedList.
size
()), Integer.valueOf(C21737a.
this
.gCe - 1));
AppMethodBeat.m3379o(58399);
return true;
}
C21733o.m34801a(C21737a.
this
.gDt, C21737a.
this
.gDu, C21737a.
this
.gDw);
C21737a.
this
.gDv.mo34011no(linkedList.
size
());
AppMethodBeat.m3379o(58399);
return false;
}
}
[/
size
][/
size
][/font]
[font=新宋体][
size
=4][
size
=9pt] }
最后,简单总结一下:
1. 从 com.tencent.mm.sdk.g.c.c$1$1.run 开始 ,进入系统调用,开启 loop 消息循环,通过厂商自己重写的 dispatchMessage 和 handleMessage 处理message 。
2. 进入com.tencent.mm.modelmulti.o$a$1.onTimerExpired ,判断是否超时,记得超时了好像撤回的功能就没有了,这个大家自己跟吧
3. 从com.tencent.mm.r.b.b(SourceFile:40) com.tencent.mm.plugin.messenger.foundation.c.a(SourceFile:165)
com.tencent.mm.plugin.messenger.foundation.c.a(SourceFile:1059)
com.tencent.mm.plugin.messenger.foundation.f.a(SourceFile:118)
都是处理上层传来的的protobuf ,有的直接继承参数,有的剥离外层后继承 ,protocal相关函数大家有时间慢慢分析
4. 函数com.tencent.mm.model.f.a(SourceFile:145) 和 com.tencent.mm.model.f.a(SourceFile:352) 通过 上层函数参数 aVar 的数据结构生成下层函数的 map 参数 ,这个参数很关键,保存了 ( ".sysmsg.revokemsg.newmsgid" ) 和( ".sysmsg.revokemsg.replacemsg" ) 两个键值。
5. 进入本文的关键函数com.tencent.mm.model.f.a(SourceFile:145) m34535a 和com.tencent.mm.model.f.a(SourceFile:352)mo7343a ;函数 mo7343a 调用了 m34535a , 直接 hook 这个函数,当 p0=revokemsg 时直接返回就可以屏蔽掉撤回了 。
6. 进入数据库封装的上层com.tencent.mm.cf.f.update(SourceFile:774) 和com.tencent.mm.cf.h.update(SourceFile:601) ,这里主要注意sqlite 实例的获得,分为加密和不加密,中间还有一些优化的功能。
7. 进入最终更新数据库的com.tencent.wcdb.database.SQLiteDatabase.updateWithOnConflict(NativeMethod) com.tencent.wcdb.database.SQLiteDatabase.update(SourceFile:1726) ,更新数据库,完成功能
ps :中间有一些类和函数我没有仔细去看,可能会有些错误,但大致流程应该没啥问题,今天就开始上班了,时间比较急,中间格式整理的有些乱,大家见谅吧,干活去喽!
ps2:大家觉得不错的话,给个热心再走吧,我真的很想升到2级
免费评分
查看全部评分