一次VIVO自启动调研

设备:VIVO X20A 系统:8.1.0 ---- 以下调研针对该设备型号,不同的VIVO手机表现可能会有不同

前言

VIVO系统添加了自启动管理,为了能够跳转到指定页面引导用户开启。

BgStartUpManagerActivity

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

自启开关还可以通过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:

  1. smali.jar:smali文件转为dex文件
  2. 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

你可能感兴趣的:(一次VIVO自启动调研)