本文主要分析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源码

QQ截图20160625120002

 

Smali代码 (这玩意我基本是看不懂的)

 

QQ截图20160625120049

 

 

 

 

DRM模块

这个DRM和我们平常看到的视频DRM其实是一样的,就用用来验证数据,防破解反盗版的,也可用用来校验数据的完整性。

现在有源码了,那么怎么才能达到我们的目的呢? 我想到的是从源码入手,搜索关键词,比如主题肯定要验证合法性的,那么全局搜索下“validatetheme”,“validate”,“Authorized”之类的词汇,合法的英文单词是“Legal”,是否合法就是“isLegal”,一般语法约定就是这样的。然后验证失败怎么样?主题要恢复。对!恢复的英文单词是“restore”

通过这些关键词,可以快速的找到关键代码,通过修改这些代码,实现程序执行逻辑的修改而达到我们需要的目的。

 

通过Notepad++的文件夹搜索功能搜索整个反编译出来的java源码文件夹,关键词是validatetheme和validate,可以找到如下方法

 

QQ截图20160625121743

 

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;

方法的入参类型和个数和之前调用的是匹配的.然后返回值是一个内部类,这个类是可以反编译出来的

QQ截图20160625124200

 

这是个枚举类,用于表示主题的验证结果,如果修改这个方法 让返回值一直是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 经过查找找到了恢复主题的方法和调用这个方法的方法

222

onReceive方法通过创建线程的方法,然后在线程里调用恢复主题的方法,那么我删除这个方法的方法体主题就不会恢复了

再看看谁发出了这个广播(onReceive是用来接受广播的)

4444

应该是这个重载的第三个方法 把这个方法的方法体清空了是不是就不去发出恢复主题的广播 然后就不会执行恢复主题的方法了?

 

主题模块

这个就是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

 

 

 

现在,一起来理一下 下载并应用主题可能会出现的情况。

第一种情况是下载了免费主题 然后应用成功并且不会恢复

第二种情况是下载了付费的主题,然后付费使用并且不会恢复

第三种情况是下载付费的主题 然后试用 开始倒计时 最后被强制还原。

 

最好的情况是第一种,主题是免费的,那么就随意用了,然后第二种,本地修改所有的付费主题都已经购买了(对于用户来说,所有的主题都是免费的)

最后是我们可以改造第三种情况,试用后不会被还原。

 

 

届ける言葉を今は育ててる
最后更新于 2017-10-13