本文主要分析miui主题的授权模块,从源码角度解析它的工作原理。
将会从smali代码和java代码的角度具体的讲解。
原料
授权模块主要设计到了2个东西
一个是thememanager.apk 包名是com.android.thememanager 称之为主题管理引擎
另一个是miuisystem.apk 这个apk以前的包名是miui.drm(v7及以前) 现在的包名是com.miui.system 称之为MIUI DRM模块
另外还需要反编译工具
一个是apkdb
官网idoog.me
另一个是smali转java工具
如果工具不能查看某个类的java源码
那么就需要转smali 然后单个smali文件转java 这样可以查看部分没问题的源码
http://blog.csdn.net/jdsjlzx/article/details/41548391
分解
使用APKDB将安装包反编译成smali代码和java源码(部分代码无法转换成java源码,只能从smali入手)
Java源码
Smali代码 (这玩意我基本是看不懂的)
DRM模块
这个DRM和我们平常看到的视频DRM其实是一样的,就用用来验证数据,防破解反盗版的,也可用用来校验数据的完整性。
现在有源码了,那么怎么才能达到我们的目的呢? 我想到的是从源码入手,搜索关键词,比如主题肯定要验证合法性的,那么全局搜索下“validatetheme”,“validate”,“Authorized”之类的词汇,合法的英文单词是“Legal”,是否合法就是“isLegal”,一般语法约定就是这样的。然后验证失败怎么样?主题要恢复。对!恢复的英文单词是“restore”
通过这些关键词,可以快速的找到关键代码,通过修改这些代码,实现程序执行逻辑的修改而达到我们需要的目的。
通过Notepad++的文件夹搜索功能搜索整个反编译出来的java源码文件夹,关键词是validatetheme和validate,可以找到如下方法
Tips:反编译后的代码虽然转化成了java,但这是代码有点“跳”,比如判断paramString1的if,方法体是空的 但可以推测实际情况是这样的:如果是paramString1不为空 那么会继续下一步验证,如果是空的那么直接返回失败。还有for(;;)这样的代码是死循环的,需要手动跳出,但现实中没人会去写这样的代码。还有比如String的包装类没isDirectory这个方法的吧怎么感觉是File 类型的
通过方法的具体代码可以推测出这个方法的参数1是context,参数二是主题名,参数三是主题的路径
方法会调用DrmManager.isLegal来验证具体的主题的合法性,里面还有个循环,因为路径可能是个目录,那么需要遍历一次。
然后我们找DrmManager.isLegal这个方法,遗憾的是DrmManager这个class反编译失败了
然后只好看smali代码了。不过smali代码太长我就只贴关键部分了
先是方法的定义
.method public static isLegal(Landroid/content/Context;Ljava/lang/String;Ljava/io/File;)Lmiui/drm/DrmManager$DrmResult;
方法的入参类型和个数和之前调用的是匹配的.然后返回值是一个内部类,这个类是可以反编译出来的
这是个枚举类,用于表示主题的验证结果,如果修改这个方法 让返回值一直是DRM_SUCCESS不就OK了么?
然后搜索同名的方法isLegal 这个方法的重载挺多的
比如这样的
.method private static isLegal(Landroid/content/Context;Ljava/lang/String;Lmiui/drm/DrmManager$RightObject;)Lmiui/drm/DrmManager$DrmResult;
反编译miui/drm/DrmManager$RightObject 发现没什么特别的 只是用来存储数据的
然后搜索restore 经过查找找到了恢复主题的方法和调用这个方法的方法
onReceive方法通过创建线程的方法,然后在线程里调用恢复主题的方法,那么我删除这个方法的方法体主题就不会恢复了
再看看谁发出了这个广播(onReceive是用来接受广播的)
应该是这个重载的第三个方法 把这个方法的方法体清空了是不是就不去发出恢复主题的广播 然后就不会执行恢复主题的方法了?
主题模块
这个就是miui桌面上的主题应用了
修改这个模块是为了付费的主题全部变成免费的,能下载并应用
讲道理,付费的主题本地应该会验证下是否已经付费了,如果付费了就直接显示下载按钮,
付费就是购买,购买的英文怎么说?bought! 搜索这个词
找了了一个方法
public boolean isProductBought()
{
return this.productBought;
}
这个方法很多地方都有调用 直接返回个true
再往上找到这些方法
public boolean isProductBought()
{
return this.onlineProperties.isProductBought();
}
public boolean isAuthorizedResource()
{
return this.mResource.isProductBought();
}
.....
搜索下isAuthorizedResource
找到了如下方法
public boolean isPermanentRights()
{
File localFile = new File(this.mResResolver.getRightsPath());
return (isAuthorizedResource()) || (getPrice() == 0) || ((isLocalResource()) && (!localFile.exists()) && (!isTrialable())) || ((localFile.exists()) && (DrmManager.isPermanentRights(localFile)));
}
方法名翻译成中文就是 "是否永久权利" 大概是永久使用权,就是付费?
看看里面的代码 如果已经购买或者免费(0元的价格) 则返回真 后面的部分没看懂啥意思(主要是懒得分析)
总结:isAuthorizedResource返回true 或者价格0都能达到我们要的效果
getPrice方法的源码
public int getPrice()
{
return this.mResource.getProductPrice();
}
public int getProductPrice()
{
return this.productPrice;
}
getPrice方法 直接 return 0就好了
或者把productPrice改成0
最暴力的isPermanentRights返回值改成true
然后应用主题的时候 还是有问题 提示支付
然后这部分代码也需要改造
protected void applyResource()
{
checkCompatibilityAndPerform(2);
}
protected void checkResourceRights()
{
if (isLegal())
{
applyResource();
return;
}
new CheckRightsTask().execute(new Void[0]);
}
应用之前还会校验一次 需要修改isLegal的返回值为true
现在,一起来理一下 下载并应用主题可能会出现的情况。
第一种情况是下载了免费主题 然后应用成功并且不会恢复
第二种情况是下载了付费的主题,然后付费使用并且不会恢复
第三种情况是下载付费的主题 然后试用 开始倒计时 最后被强制还原。
最好的情况是第一种,主题是免费的,那么就随意用了,然后第二种,本地修改所有的付费主题都已经购买了(对于用户来说,所有的主题都是免费的)
最后是我们可以改造第三种情况,试用后不会被还原。