吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 6180|回复: 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] 纯文本查看 复制代码
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] 纯文本查看 复制代码
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] 纯文本查看 复制代码
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] 纯文本查看 复制代码
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] 纯文本查看 复制代码
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] 纯文本查看 复制代码
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] 纯文本查看 复制代码
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, 2024-11-15 14:56

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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