设备:VIVO X20A 系统:8.1.0 ---- 以下调研针对该设备型号,不同的VIVO手机表现可能会有不同
前言
VIVO系统添加了自启动管理,为了能够跳转到指定页面引导用户开启。
BgStartUpManagerActivity
该Activity如上图页面所示,可通过以下方式进入:
设置→更多设置→权限管理→权限→自启动
通过查看当前Activity可以获得其ComponentName:
com.vivo.permissionmanager/.activity.BgStartUpManagerActivity
然后通过该ComponentName启动该Activity,代码如下:
try {
Intent intent = new Intent();
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
ComponentName componentName = ComponentName.unflattenFromString("com.vivo.permissionmanager/.activity.BgStartUpManagerActivity");
intent.setComponent(componentName);
context.startActivity(intent);
} catch (Exception e) {
LogUtil.e("CZKGO", e.getMessage());
}
启动失败,可以看到如下日志:
CZKGO: Permission Denial: starting Intent { flg=0x10000000 cmp=com.vivo.permissionmanager/.activity.BgStartUpManagerActivity } from ProcessRecord{97cc72b 8345:com.czkgo.test/u0a784} (pid=8345, uid=10784) requires com.vivo.permission.manage.permission.ACCESS
可以看到缺少com.vivo.permission.manage.permission.ACCESS
权限,尝试在Manifest中添加该权限后,依然启动失败并抛出相同错误,说明该权限第三方应用不能获取。
SoftPermissionDetailActivity
自启开关还可以通过SoftPermissionDetailActivity去开启,该Activity如上图页面所示,其进入方式为:
设置→更多设置→权限管理→应用→(在列表中找到当前应用)→单项权限设置
通过查看当前Activity可以获得其ComponentName:
com.vivo.permissionmanager/.activity.SoftPermissionDetailActivity
通过该ComponentName启动该Activity,可以看到该界面一闪而过(屏幕黑一下)但没有错误日志,猜测这是因为没有传入当前应用包名作为标识,毕竟安卓的所有应用共用SoftPermissionDetailActivity来管理权限。
获取PermissionManager.apk
以上两个Activity属于包名com.vivo.permissionmanager的应用中,通过adb shell pm path com.vivo.permissionmanager
获取到该APK地址:
package:/system/app/PermissionManager/PermissionManager.apk
在通过adb pull /system/app/PermissionManager/PermissionManager.apk
获取到PermissionManager.apk
。
使用Jadx反编译该APK,这时候发现,该APK中没有代码,再找到AndroidManifest,关注到如下三条信息:
...
...
...
...
可以看到BgStartUpManagerActivity确实需要com.vivo.permission.manage.permission.ACCESS
权限,并且该权限被标记为signatureOrSystem
,而SoftPermissionDetailActivity不需要权限.
获取odex,vdex
odex文件是虚拟机对解压出来的dex文件做一定程度的优化,文件大小会减少,并且ODEX 文件比 DEX 文件更难反编译,这也在一定程度上提高了安全性,因此一些系统预装或系统级应用大多采用了 ODEX 优化。Android 8.0在odex的基础上又引入了vdex机制,目的是为了降低dex2oat时间。
在/system/app/PermissionManager目录下除了PermissionManager.apk,还可以找到一个oat文件夹,同样通过pull命令获取其中的PermissionManager.odex,PermissionManager.vdex文件:
adb pull /system/app/PermissionManager/oat/arm64/PermissionManager.odex
adb pull /system/app/PermissionManager/oat/arm64/PermissionManager.vdex
转dex
odex,vdex转为dex需要2个工具jar:smali.jar 和 baksmali.jar:
- smali.jar:smali文件转为dex文件
- baksmali.jar:odex文件转为smali文件
这两个工具可以通过https://bitbucket.org/JesusFreke/smali/downloads/网站获取。
首先通过baksmali.java将odex文件转为smali文件:
java -jar baksmali.jar deodex PermissionManager.odex
这时候会报错:
org.jf.dexlib2.analysis.ClassPathResolver$ResolveException: org.jf.dexlib2.analysis.ClassPathResolver$NotFoundException: Could not find classpath entry boot.oat
这是因为系统APK都会依赖framwork层一些公共功能库,需要到framwork层取到对应oat文件即可:
adb pull /system/framework/arm64/boot.oat
结果又报错误了:
org.jf.dexlib2.DexFileFactory$DexFileNotFoundException: Could not locate the embedded dex file /system/framework/com.qualcomm.qti.camera.jar. Is the vdex file missing?
我们需要将同名的vdex文件也获取到:
adb pull /system/framework/arm64/boot.vdex
但是再次执行java -jar baksmali.jar deodex PermissionManager.odex
还是报错,又显示缺失其他oat文件,毕竟PermissionManager依赖很多库,干脆用adb pull /system/framework/arm64
将整个arm64
内的文件全都取到在执行,成功后会生成out目录,目录中均是smali文件。然后通过smali.jar将其转成class.dex
java -jar smali.jar assemble out/ -o class.dex
查看SoftPermissionDetailActivity源码
通过Jadx打开SoftPermissionDetailActivity,其onCreate方法如下:
可以看到,该Activity通过
packagename
传递包名,如果获取到包名为null,执行finish方法,所以在跳转时添加:
intent.putExtra("packagename", context.getPackageName());
这次成功打开了该页面。
PermissionProvider
现在可以成功跳转页面了,但跳转过去后也可能发现已经授予了自启权限,这就需要判断自启权限是否已经授予。在上文中的Manifest文件中搜索provider,找到了PermissionProvider:
可以看出exported="true"
表示该provider对外暴露,writePermission
则限制了其写入,在找到PermissionProvider源码,其中有如下代码:
猜测"bg_start_up_apps"就是控制自启管理的,在PermissionProvider中搜索它,找到:
根据l111l11111l找到bg_start_up_apps的参数(你也许会遇到多个又l和1组成的参数,注意区分不要混淆了):
这里我们留意pkgname和currentstate参数,通过测试可知currentstate在有自启权限时值为1,没有时值为0,从而完成判断权限是否授予方法:
private static boolean isVIVOBgStartPermissionGet(Context context) {
String packageName = context.getPackageName();
Uri uri2 = Uri.parse("content://com.vivo.permissionmanager.provider.permission/bg_start_up_apps");
String selection = "pkgname = ?";
String[] selectionArgs = new String[]{packageName};
try {
Cursor cursor = context
.getContentResolver()
.query(uri2, null, selection, selectionArgs, null);
if (cursor != null) {
if (cursor.moveToFirst()) {
int currentstate = cursor.getInt(cursor.getColumnIndex("currentstate"));
cursor.close();
return currentstate == 1;
} else {
cursor.close();
return false;
}
}
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return false;
}
参考链接:https://juejin.im/post/5d24a23a51882502e5233571