破解某app主题皮肤
2019-06-07 本文已影响118人
好奇害死猫咪阿
前言
今天在使用一款视频app时,弹出一个对话框提示主题皮肤已经到期,想重新选一个主题时发现不是免费的。想到近来无事可做,不如就分析一下,权当练手学习了。
工具
- jadx
- VirtualXposed
思路分析
-
我们先来看一下主题选择界面
主题界面
对于主题皮肤这种功能,功能肯定是要放到本地的,然后每次打开app时校验一下失效时间。
因为点击右侧的按钮之后才会有下一步的逻辑,所以就先把突破点放在这。 -
下一步就是直接拖入jadx反编译。大厂的app基本上不会做加固处理,除开效率不说,加固的app也是有一定几率崩溃的,所以直接反编译就行。之后在
字符串搜索结果string.xml
直接搜索文本
接着全局搜索theme_action_subscribe_fmt
字符串引用结果
需要注意的是,jadx在反编译时会直接把smali中16进制的资源值转化为R.string.xxx
的形式,方便阅读。所以这里直接搜索16进制资源值是搜不到的。
这里的结果比较好,两个结果都源自同一个类,所以直接跟踪进去。
bl.eok$b类部分代码
这里的逻辑实际上还是比较清楚的。就是根据BiliSkin
的不同状态确定具体的显示内容。实际上这个类还实现了View.OnClickListener
接口,也说明我们的思路是正确的
在实际使用过程中,主题列表的前两项,即少女粉
和 夜间模式
是免费的。结合这两处代码分析,比较可疑的是BiliSkin
的mIsFree
和mIsBought
两个域。
- 下一步就是编写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");
}
}
}
-
重启后的效果如下图所示
Xposed hook后主题界面
可以看到,现在所有主题已经都可以用了。
- 然而关闭app,再打开时问题出现了
主题过期对话框
在点击确定
按钮之后,app恢复成默认主题。所以下一步我们就想办法除去这个对话框。
- 同样的思路,在
string.xml
中搜索文本
字符串搜索结果
然后全局搜索,找到调用处
字符串搜索结果
代码跟踪进去,很清楚的逻辑,就是创建了个Dialog
监听点击事件
接下来就是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
对象的mStatus
,MBuyTime
,mDueTime
等值,再保存到本地。相比之下要复杂很多,读者可自行分析。 - 为什么不直接修改apk,而是用Xposed呢?首先是很多app虽然没有加固,但做了很严密的签名验证,如果放到native层那修改起来难度较大。再者,由于经常要调整代码,如果每次都回编译、签名、安装,效率是很低的,Xposed就没有这个问题。而传统Xposed模块经常需要重启,也很麻烦。这里借助的是
VirtualXposed
这个app,基于多开的虚拟框架,免root使用,几秒内重启,非常方便。想要了解更多的可以去github。
说明
- 仅为个人学习交流,禁止用于其他用途