破解某app主题皮肤

前言

今天在使用一款视频app时,弹出一个对话框提示主题皮肤已经到期,想重新选一个主题时发现不是免费的。想到近来无事可做,不如就分析一下,权当练手学习了。

工具

  • jadx
  • VirtualXposed

思路分析

  1. 我们先来看一下主题选择界面


    主题界面

    对于主题皮肤这种功能,功能肯定是要放到本地的,然后每次打开app时校验一下失效时间。
    因为点击右侧的按钮之后才会有下一步的逻辑,所以就先把突破点放在这。

  2. 下一步就是直接拖入jadx反编译。大厂的app基本上不会做加固处理,除开效率不说,加固的app也是有一定几率崩溃的,所以直接反编译就行。之后在 string.xml 直接搜索文本

    字符串搜索结果

    接着全局搜索theme_action_subscribe_fmt
    字符串引用结果

    需要注意的是,jadx在反编译时会直接把smali中16进制的资源值转化为 R.string.xxx的形式,方便阅读。所以这里直接搜索16进制资源值是搜不到的。


这里的结果比较好,两个结果都源自同一个类,所以直接跟踪进去。


bl.eok$b类部分代码

这里的逻辑实际上还是比较清楚的。就是根据BiliSkin的不同状态确定具体的显示内容。实际上这个类还实现了View.OnClickListener 接口,也说明我们的思路是正确的

onClick函数部分代码


在实际使用过程中,主题列表的前两项,即少女粉夜间模式是免费的。结合这两处代码分析,比较可疑的是BiliSkinmIsFreemIsBought 两个域。


  1. 下一步就是编写Xposed模块,确认一下我们的思路。Xposed模块的编写这里就不多说了,毕竟网上资料很多。这里直接附上代码
public class Main  implements IXposedHookLoadPackage {

    @Override
    public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
        if (! "tv.danmaku.bili".equals(loadPackageParam.packageName)) {
            XposedBridge.log("ignore this package: " + loadPackageParam.packageName);
            return;
        }

        XposedBridge.log("start------------------->");

        XposedHelpers.findAndHookMethod("bl.eok$b", loadPackageParam.classLoader,
                "b", Object.class, new HookBiliSkinInfoMethod());
    }

    private static class HookBiliSkinInfoMethod extends XC_MethodHook {

        private static Class sBiliSkinClass = null;
        private static Field sIsBiliSkinFree = null;

        @Override
        protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
            XposedBridge.log("start with param: " + param.args[0]);

            if (sBiliSkinClass == null) {
                sBiliSkinClass = XposedHelpers.findClass(
                        "tv.danmaku.bili.ui.theme.api.BiliSkin",
                        param.thisObject.getClass().getClassLoader());
            }

            if (sIsBiliSkinFree == null) {
                sIsBiliSkinFree = XposedHelpers.findField(sBiliSkinClass, "mIsFree");
            }

            if (! (sBiliSkinClass.isInstance( param.args[0]))) {
                XposedBridge.log("This param is not the instance of BiliSkin, ignore");
                return;
            }

            sIsBiliSkinFree.set(param.args[0], true);
        }

        @Override
        protected void afterHookedMethod(MethodHookParam param) throws Throwable {
            XposedBridge.log("done");
        }
    }
}


  1. 重启后的效果如下图所示


    Xposed hook后主题界面

    可以看到,现在所有主题已经都可以用了。


  1. 然而关闭app,再打开时问题出现了
    主题过期对话框

    在点击确定按钮之后,app恢复成默认主题。所以下一步我们就想办法除去这个对话框。

  1. 同样的思路,在string.xml中搜索文本
    字符串搜索结果

    然后全局搜索,找到调用处
    字符串搜索结果

代码跟踪进去,很清楚的逻辑,就是创建了个Dialog监听点击事件

MainActivity部分代码


接下来就是hook掉这个方法了


XposedHelpers.findAndHookMethod("tv.danmaku.bili", loadPackageParam.classLoader,
                "d", new HookSkinExpiredMethod());

    private static class HookSkinExpiredMethod extends XC_MethodHook {

        @Override
        protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
            param.setResult(null);
        }

        @Override
        protected void afterHookedMethod(MethodHookParam param) throws Throwable {

        }
    }

总结

  • 上面这些步骤看起来挺侥幸,怎么就这么刚好成功了呢?实际上也经过挺长时间的分析,走错了分析方向。一开始的思路是从点击5硬币/月后的对话框出发,它这里肯定会联网同步数据,然后再更改主题。因此只要把联网请求取消掉就可以了。之后发现服务器的返回结果会构建OrderResult 类,然后再从这个对象修改 BiliSkin 对象的 mStatusMBuyTimemDueTime 等值,再保存到本地。相比之下要复杂很多,读者可自行分析。
  • 为什么不直接修改apk,而是用Xposed呢?首先是很多app虽然没有加固,但做了很严密的签名验证,如果放到native层那修改起来难度较大。再者,由于经常要调整代码,如果每次都回编译、签名、安装,效率是很低的,Xposed就没有这个问题。而传统Xposed模块经常需要重启,也很麻烦。这里借助的是VirtualXposed 这个app,基于多开的虚拟框架,免root使用,几秒内重启,非常方便。想要了解更多的可以去github。

说明

  • 仅为个人学习交流,禁止用于其他用途

你可能感兴趣的:(破解某app主题皮肤)