微信骰子隨機數流程
- 2020 年 3 月 26 日
- 筆記
首先.準備工作,jadx反編譯wx708另存為as源碼 點擊骰子表情,通過monitor從onclick開始追蹤定位到關鍵函數com.tencent.mm.sdk.platformtools.bo.ii,jadx反混淆後為函數m13717ii,簡單觀察函數
準備工作,jadx反編譯wx708另存為as源碼
首先,通過monitor方法回溯從 performClick往下追蹤,尋找到關鍵函數com.tencent.mm.sdk.platformtools.bo.ii,在jadx反混淆後為函數m13717ii
public static int m13717ii(int i, int i2) { AppMethodBeat.m3378i(52299); Assert.assertTrue(i > i2); int nextInt = new Random(System.currentTimeMillis()).nextInt((i – i2) + 1) + i2; AppMethodBeat.m3379o(52299); return nextInt; }
hook此函數,修改返回值,容易判斷:
玩骰子時i=5,i2=0,返回0-5對應1-6點
玩石頭剪刀布時i=2,i2=0,返回0-2對應石頭剪刀布
var bo = Java.use('com.tencent.mm.sdk.platformtools.bo');
bo.ii.overload('int','int').implementation=function(a1,a2)
{ console.log("hook ii start");
console.log("a1:"+a1);
console.log("a2:"+a2);
var rtn= this.ii(5,0);
console.log("rtn:"+rtn);
var threadef = Java.use('java.lang.Thread');
var threadinstance = threadef.$new();
var stack = threadinstance.currentThread().getStackTrace();
function Where(stack){
for(var i = 0; i < stack.length; ++i){
console.log(stack[i].toString());
}
}
console.log("Full call stack:" + Where(stack));
return rtn
}
列印堆棧結果如下
com.tencent.mm.sdk.platformtools.bo.ii(Native Method) m13717ii
com.tencent.mm.plugin.emoji.e.f.n(SourceFile:93) mo46299n
com.tencent.mm.bz.a.n(SourceFile:316) mo46299n
com.tencent.mm.emoji.panel.a.d.a(SourceFile:55) mo46720a
com.tencent.mm.emoji.panel.a.q$1.onClick(SourceFile:27)
android.view.View.performClick(View.java:6294)
android.view.View$PerformClick.run(View.java:24770)
android.os.Handler.handleCallback(Handler.java:790)
android.os.Handler.dispatchMessage(Handler.java:99)
android.os.Looper.loop(Looper.java:164)
android.app.ActivityThread.main(ActivityThread.java:6494)
java.lang.reflect.Method.invoke(Native Method)
com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:440)
com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
首先,通過拋異常然後調用Exception的run方法的方式 反射調用main方法
然後,開啟loop循環消息隊列處理message
進入 PerformClick觸發方法com.tencent.mm.emoji.panel.a.q$1.onClick,後面就進入到關鍵的邏輯了
我們倒著看
public final EmojiInfo mo46299n(EmojiInfo emojiInfo) { AppMethodBeat.m3378i(52890); if (emojiInfo.field_catalog == EmojiGroupInfo.BmT && emojiInfo.field_type == EmojiInfo.Bur && emojiInfo.getContent().length() > 0 && EmojiInfo.m66104Qp(C9015bo.getInt(emojiInfo.getContent(), 0))) { Cursor Qu = C33825j.getEmojiStorageMgr().BkN.mo55784Qu(C9015bo.getInt(emojiInfo.getContent(), 0));
這個 Cursor Qu的getCount()得到的數值就是最大點數,如果是骰子則為6 if (Qu != null && Qu.getCount() > 1) { int ii = C9015bo.m13717ii(Qu.getCount() – 1, 0); 這一句調用random產生骰子或划拳結果 emojiInfo = new EmojiInfo(); Qu.moveToPosition(ii); 更新變動表情為確定點數表情 emojiInfo.convertFrom(Qu); } if (Qu != null) { Qu.close(); } } AppMethodBeat.m3379o(52890); return emojiInfo; }
傳入參數是 EmojiInfo類,表情類Lcom/tencent/mm/storage/emotion/EmojiInfo,可以簡單看一下這個類的數據結構
public final Cursor mo55784Qu(int i) { AppMethodBeat.m3378i(62821); Cursor query = this.f15356db.query("EmojiInfo", null, "catalog=? and temp=?", new String[]{String.valueOf(i), AppEventsConstants.EVENT_PARAM_VALUE_NO}, null, null, null); AppMethodBeat.m3379o(62821); return query; }
這裡是query 函數用法query(table,columns, selection, selectionArgs, groupBy, having, orderBy, limit)
很清晰,query通過查詢 EmojiInfo表的 catalog 目錄和temp應該是當前索引表情定位到具體表情屬性,返回到上層函數通過getCount()獲取子表情,如骰子應該是6個
com.tencent.mm.bz.a.n(SourceFile:316)
public final EmojiInfo mo46299n(EmojiInfo emojiInfo) { boolean z; AppMethodBeat.m3378i(62615); if (((C2666h) C2700g.aaW().aaw()).abR()) { EmojiInfo n = ((C33772d) C2700g.m5024ae(C33772d.class)).getEmojiMgr().mo46299n(emojiInfo); AppMethodBeat.m3379o(62615); return n; } Bundle bundle = new Bundle(EmojiInfo.class.getClassLoader()); bundle.putParcelable("emoji", emojiInfo); Bundle call = C8960ah.getContext().getContentResolver().call(Uri.parse("content://com.tencent.mm.storage.provider.emotion/"), "getRandomEmoji", null, bundle); if (call == null) { C8953ab.m13552e("MicroMsg.EmotionStorageResolver", "[getRandomEmoji] bunndle is null! "); AppMethodBeat.m3379o(62615); return null; } call.setClassLoader(EmojiInfo.class.getClassLoader()); if (call.containsKey("data")) { EmojiInfo emojiInfo2 = (EmojiInfo) call.getParcelable("data"); AppMethodBeat.m3379o(62615); return emojiInfo2; } String str = "MicroMsg.EmotionStorageResolver"; String str2 = "[getRandomEmoji] bundle is null?"; Object[] objArr = new Object[1]; if (call == null) { z = true; } else { z = false; } objArr[0] = Boolean.valueOf(z); C8953ab.m13553e(str, str2, objArr); AppMethodBeat.m3379o(62615); return null; }
我們正常情況下直接走第一個if通過query查詢資料庫得到 emojiInfo對象
而下面通過bundle傳遞emojiInfo對象,通過ContentProvider的 getRandomEmoji也獲得了一個emojiInfo對象返回,應該是另一個實現random篩子的機制,熟悉四大組件ContentProvider的同學很容易就能看懂,這裡我們不在深入跟蹤了
public final void mo46720a(Context context, int i, C32002y yVar) { EmojiInfo emojiInfo; String string; AppMethodBeat.m3378i(178705); C0748k.m2427m(context, "context"); C8953ab.m13556i(C32114f.TAG, "onClick: " + i + ", " + yVar); if (yVar == null) { AppMethodBeat.m3379o(178705); return; } switch (yVar.type) { case 0: C31981h hVar = (C31981h) yVar; EmojiInfo emojiInfo2 = hVar.fja; EmojiInfo emojiInfo3 = hVar.fja; if (emojiInfo3.getGroup() == EmojiGroupInfo.BmT) { C2658a ae = C2700g.m5024ae(C33772d.class); C0748k.m2426l(ae, "MMKernel.plugin(IPluginEmoji::class.java)"); EmojiInfo n = ((C33772d) ae).getProvider().mo46299n(emojiInfo3); C0748k.m2426l(n, "MMKernel.plugin(IPluginE…er.getRandomEmoji(toSend)"); emojiInfo = n; } else { emojiInfo = emojiInfo3; } C32015d.m54248Yj().mo46658b(i, emojiInfo2.field_md5, "", emojiInfo2.field_designerID, emojiInfo2.field_activityid); C38218j jVar = this.fmq; if (jVar != null) { jVar.mo36093y(emojiInfo); AppMethodBeat.m3379o(178705); return; } AppMethodBeat.m3379o(178705); return; //後面的case與表情無關,省略 }
這個函數我們直接進入yVar.type =0的case0執行,看他的字元串也很容易看出來 這裡可以簡單看一下C31981h
(com.tencent.mm.emoji.a.a.h)類的數據結構,
通過hVar.fja屬性取出EmojiInfo賦值給emojiInfo2和emojiInfo3
如果emojiInfo3.getGroup() == EmojiGroupInfo.BmT,猜測是判斷是否屬於篩子或者猜拳表情組
如果屬於,調用mo46299n()獲取emojiInfo,如果不屬於,直接賦值
猜測C32015d.m54248Yj().mo46658b(i, emojiInfo2.field_md5, "", emojiInfo2.field_designerID, emojiInfo2.field_activityid)這一句功能是在介面上顯示出EmojiInfo影像,如果是篩子,就是變動的篩子畫面
下一句jVar.mo36093y(emojiInfo)顯示出固定點數的EmojiInfo影像,然後返回
再往上就是onclick方法了
結尾,我們順序梳理一下流程 1.系統函數調用,略過 2.點擊觸發com.tencent.mm.emoji.panel.a.q$1.onClick方法,內部調用mo46720a(com.tencent.mm.emoji.panel.a.d.a)方法 3.進入mo46720a,yVar.type=0直接進入case0,判斷表情屬於EmojiGroupInfo.BmT組後,調用mo46299n(com.tencent.mm.bz.a.n)方法4.進入mo46299n(com.tencent.mm.bz.a.n)方法,進入第一個if分支,調用mo46299n(com.tencent.mm.plugin.emoji.e.f.n)方法 5.進入mo46299n(com.tencent.mm.plugin.emoji.e.f.n)方法,通過query查詢資料庫EmojiInfo表的catalog 目錄和temp當前索引表情定位到具體表情屬性,存儲在Qu里,調用com.tencent.mm.sdk.platformtools.bo.ii 6.進入m13717ii(com.tencent.mm.sdk.platformtools.bo.ii)方法,以當前時間戳為種子,通過random產生隨機數