吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 6449|回复: 20
收起左侧

[Android 原创] 简单分析骰子流程

  [复制链接]
L剑仙 发表于 2020-2-1 19:49
本帖最后由 L剑仙 于 2020-2-1 19:49 编辑

菜鸟最近闲了下来,简单分析学习下南极公司的骰子和猜拳的实现,记录下笔记
首先.准备工作,jadx反编译wx708另存为as源码
点击骰子表情,通过monitor从onclick开始追踪定位到关键函数com.tencent.mm.sdk.platformtools.bo.ii,jadx反混淆后为函数m13717ii,简单观察函数
[Asm] 纯文本查看 复制代码
1
2
3
4
5
6
7
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对应石头剪刀布
[Asm] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
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
}

打印堆栈如下

[Asm] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
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接下来进入关键流程,我们倒着分析
[Asm] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
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); //这一句调用函数m13717ii通过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,可以简单看一下这个类的数据结构,只要与表情相关绕不开它

简单看一下数据库查询,这里是query函数用法query(table,columns,selection, selectionArgs, groupBy, having, orderBy, limit)很清晰,query通过查询EmojiInfo表的catalog 目录和temp应该是当前索引表情定位到具体表情属性,返回到上层函数通过getCount()获取子表情,如骰子应该是6个
[Asm] 纯文本查看 复制代码
1
2
3
4
5
6
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;
}


[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
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);//这一句调用了上面的方法mo46299n
        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的同学很容易就能看懂,这里我们不在深入跟踪了

[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
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产生随机数


ps:有同学问我,直接hook函数m13717ii就可以控制随机数了,为啥还要分析流程,其实,流程中的每一个函数都可以hook,只不过参数更麻烦一点,熟悉这个流程,熟悉内部机制,我们不但可以模仿实现类似的功能,版本更新后再次逆向也有了思路,还有其他的好处,下一篇简单分析撤回消息的调用流程,我们不见不散。
还有,菜鸟码字不易,大佬们给个热心再走啊


免费评分

参与人数 7吾爱币 +17 热心值 +5 收起 理由
吐槽户 + 2 我很赞同!
qtfreet00 + 12 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
晏宁 + 1 + 1 用心讨论,共获提升!
137029187 + 1 我很赞同!
Phuah2000 + 1 真棒点赞
生有涯知无涯 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
笙若 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

 楼主| L剑仙 发表于 2020-2-1 19:50
卧槽,格式又是乱的,换行很多都没了
StevenK 发表于 2020-2-2 08:39
ciker_li 发表于 2020-2-2 09:23
cptw 发表于 2020-2-2 09:50
谢谢分享,学习了
愈来记挂 发表于 2020-2-2 10:05
期待你的下一篇文章
就是长得帅 发表于 2020-2-2 11:44
厉害了大佬
whofly 发表于 2020-2-2 14:35
这个分析很好啊 鼓励
木下结子日渐尧 发表于 2020-2-2 14:45
板凳学习,谢谢分享
cdevil 发表于 2020-2-2 16:27
tql,向dalao学习
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2025-4-25 00:07

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表