通过Xposed隐藏MIUI Launcher图标

本文最后更新于:2019年3月14日 凌晨

最近总看一些看雪的东西,顺便反编译了下MiuiHome,然后隐藏了下桌面的应用,虽然是很小的场景吧,但是想想记录下,这个事情本身应该大家都用不到吧,不过作为一个小案例应该还是不错的。

前言

开始之前呢,首先我是基于MIUI10,并且手机已经完成Root以及Xposed框架的安装了。这块就不多说了,由于我的其中一台测试机是红米Note4X,因此就是基于红米Note4X。这块的大概流程我稍微的提下:

  1. 解锁BL,这个就不多说了,唯一的坑点是,MIUI的解锁程序只有Windows的
  2. 解锁后,进入BL… adb reboot bootloader
  3. 下载红米Note4X的TWRP: 这里下载
  4. 在BL状态下,通过fastboot flash recovery twrp-3.2.3-1-mido.img输入TWPR,然后通过fastboot boot twrp-3.2.3-1-mido.img进入TWRP的Recovery,这里顺便提一句,如果你遇到类似 FAILED (remote: 'dtb not found') 那么很有可能是下载的TWRP存在问题
  5. 这里下载最新版本的SuperSu的Zip包,然后通过adb push SuperSu.zip /sdcard/Download/传入手机后,通过TWRP刷入,刷入后重启手机,此时已经完成Root
  6. 最后下载XDA论坛提供的这个xposedinstaller_3.1.5.apk,安装后给到Root权限,便可快速完成Xposed框架安装

这里顺便说下,以前有写过有关Android Root的浅文,感兴趣的同学也可以关注下。

I. 反编译MiuiHome

首先小米的桌面是MiuiHome,其在/system/priv-app/MiuiHome目录下:

需要注意的是,相关的code是在已经优化后的odex文件,so,dump出来,我们通过以下工具进行逆向成源码:

首先在这里下载最新版本的baksmali-x.x.x.jar以及smali-x.x.x.jar

我们通过以下指令将MiuiHome.odex解成smali:

1
java -jar baksmali-x.x.x.jar d MiuiHome.odex -o MiuiHome

然后我们通过以下指令将MiuiHome这个包含一堆smali的目录合并为dex:

1
java -jar smali-x.x.x.jar ass MiuiHome/ -o classes.dex

接下来就愉快了,我相信大家应该都是直接用apktool d classes.dex解好后,然后用JD-GUI打开后,然后将源码全部保存下来最后通过Studio打开来分析,各有所好吧,我个人感觉jadx解的更好些。

II. 分析源码

Okay, 现在已经有源码了,肿么分析呢,全局搜下: Log看看这些明文的日志内容吧。

诶… 好辣眼睛,怎么这个开发日志都没有去掉,,好吧,,可能是桌面似乎也搞不出太多幺蛾子?废话不多说,看来可以直接通过adb logcat看看运行时的日志输出吧,既然有现成的。

捕捉到一条日志,可以看看这个Launcher做啥的,顺便在这个类上面搜索下addApp之类的看看:

果然有惊喜,看来就看这个addToAppsList线索往下找引用关系应该很快会有思路的,不过话说回来,这代码肿么都没有混淆呢????好吧不管了。继续..

顺着这条线路一直找Launcher#bindItem调用,然后LauncherModel#bindItem中调用了Launcher#bindItem,我们要找到的是最终哪里找到这些应用,然后给添加进来的,继续看这条线索。

LauncherModel#loadShortcut中调用,但是依然不是遍历调用,继续看! 居然看到 AllAppsList#addApp调用,看来这个AllAppsList肯定没跑了,我们反过来搜索对AllAppsList引用的。

我们发现LauncherModel#mAllAppsList,ok,看来都是在这里处理,看看哪里有对这个对象添加数据。发现在LauncherModel#loadAndBindMissingIcons有做mAllAppsList.updatePackage,刚好这里是个遍历。看看数据源。

这里的数据源来自updatedPackages,再追溯这个的赋值会发现来自mInstalledComponents,OK,我们看看mInstalledComponents的数据来源。

很快就搜索到在LauncherModel#loadAndBindWorkspace中通过遍历PortableUtils.launcherApps_getActivityList(this.mContext, null, null);installedApps得到的,八九不离十了。

我们hook下看看这个的输出结果。不用多说了,就是这个。。

III. 编写代码进行Hook

这块就比较简单了,可以直接参看我之前写过的一篇短文: 5分钟发布一个Xposed Module

不过代码相关的咱们还是稍微提下,我稍微抽象了下,请原谅我没有用驼峰,自己玩嘛,我觉得这也挺可读:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
class MiuiHome {
companion object {
val packageName = "com.miui.home"

val className_PortableUtils = "com.miui.launcher.utils.PortableUtils"
val methodName_launcherApps_getActivityList = "launcherApps_getActivityList"
}

}

object Launcher {
private const val TAG = "Launcher"
private val hideList = arrayListOf("com.xiaomi.wallet")

fun hideIcons(lpparam: XC_LoadPackage.LoadPackageParam) {
if (lpparam.packageName == MiuiHome.packageName) {
ALog.d(TAG, "init for hide icon ${lpparam.packageName} ${lpparam.appInfo} ${BuildConfig.BUILD_TIME}")

findAndHookMethod(MiuiHome.className_PortableUtils,
lpparam.classLoader,
MiuiHome.methodName_launcherApps_getActivityList,
"android.content.Context",
"java.lang.String",
"android.os.UserHandle",
object : XC_MethodHook() {
override fun afterHookedMethod(param: MethodHookParam) {
val list = (param.result as ArrayList<*>)
val toHideList = ArrayList<Any>()
list.forEach { info ->
val componentName = info!!::class.java.getDeclaredField("componentName").get(info)
val packageName =
componentName::class.java.getMethod("getPackageName").invoke(componentName) as String
if (hideList.contains(packageName)) {
toHideList.add(info)
ALog.d(TAG, "find $packageName so hide it!")
} else {
ALog.d(TAG, "permitted show: $packageName")
}
}

for (it in toHideList) {
list.remove(it)
}
}
})
}
}
}

最后,Xposed调试确实不是很友好,如果不升级版本,需要生效需要重新勾选一次module,并且软重启有时候不会生效,也没有花时间看具体原因,就加了包的编译时间在日志,如果不是最新的就手动重启设备。后面有时间还是要看看原因,多学习学习,拥抱社区吧。


通过Xposed隐藏MIUI Launcher图标
https://blog.dreamtobe.cn/xposed_miui_launcher/
作者
Jacksgong
发布于
2019年3月14日
更新于
2019年3月14日
许可协议