Android系统视角下对APK的分析(2)- APK安装过程的定性分析

声明

  • 以Android手机用户角度来看,安装各式各样的APP,基本就是从应用市场上 “搜索->下载->安装” 三连。而对Android系统来说,这就是个大工程了,因为对Android系统来说APK是“外来户”,如何安装它、有限制地支持它的运行、如何防着它干坏事等问题就来了。
  • 写此专栏的起因是为了给客户在定制的Android系统中实现 大型APK快速安装的功能
  • 本专栏就来从Android系统的角度来分析下APK的整个生命周期(安装-运行-卸载),包含对APK的静态分析、PackageManagerService、pm命令、PackageInstaller、Installd
  • 此篇代码基于LineageOS 14.1(Android 7.1.1),参考一些博客和书籍,不方便逐一列出,仅供学习、知识分享。

1 概述

Android系统安装APP有如下几种方法:

  • 通过应用商店客户端安装;
  • 在设备上直接打开已下载的 apk 文件;
  • 通过Type-C连接到PC,使用 adb install 命令安装;

Android 的包管理功能分布在数个系统组件之中,在安装时,它们之间互相交互,如图所示:
Android系统视角下对APK的分析(2)- APK安装过程的定性分析_第1张图片

2 Android系统中APK包存放位置和数据的位置

Android 区别系统安装与用户安装的应用程序。系统应用可以在只读的 system 分区中找到,并且在量产设备上不能被修改或卸载。系统应用因此被认为是可信的,并且赋予了更多的权限,还会放松一些签名检查。

  • /system/app/ 目录下找到大部分系统应用;
  • /system/priv-app/ 目录下保存有特权的应用,它们可被授权 signatureOrSystem 保护级别的权限;
  • /vendor/app/ 目录下是厂商专有的应用;
  • /data/app/目录下用户安装的应用,可以被任意卸载或替换;

为系统和用户安装的应用数据目录创建在 userdata 分区的 /data/data/ 目录下。userdata 分区同样存放一些为用户安装应用优化过的 DEX 文件(data/dalvik-cache/)、系统包数据库(存放在 /data/system/packages.xm/)、系统数据库和设置文件。

3 APK安装过程中涉及的Android系统活动组件(Active Component)

3.1 PackageInstaller 系统应用

  PackageInstaller 为包管理提供了一个基本的 GUI,并且当它被传递 VIEW 或 INSTALL_ACTION 动作并附带上 APK 文件的 URI 的 intent 时,它就会处理安装包并在屏幕上弹出一个显示应用所需权限的确认对话框。使用 PackageInstaller 安装应用,只有在用户开启了设备安全设置中的“未知来源”选项后方可进行。
  PackageInstaller 获取请求 APK 安装动作的应用程序的 UID 和 包名,并检查它是否属于特权应用(安装在/system/priv-app/目录下)。如果请求的应用程序不具有特权,则认为它是一个未知来源。
  如果“未知来源”选项开启,并且用户点击了对话框中的“确认”按钮,PackageInstaller 会调用PackageManagerService 服务,它将会执行真实的安装过程。当应用更新或卸载时,PackageInstaller 的 GUI 同样会做出提示。

Android系统视角下对APK的分析(2)- APK安装过程的定性分析_第2张图片Android系统视角下对APK的分析(2)- APK安装过程的定性分析_第3张图片Android系统视角下对APK的分析(2)- APK安装过程的定性分析_第4张图片

3.2 pm命令

  pm 命令可以完成一些系统包管理器的功能。pm install 或 pm uninstall 命令可以用于安装或卸载包。pm install 不依赖未知来源系统选项,并且不会弹出一个 GUI 界面,它提供了多种有用的选项用于测试包的安装,这是 PackageInstaller 不具备的。

hammerhead:/system/framework # pm
usage: pm path [--user USER_ID] PACKAGE
       pm dump PACKAGE
       pm install [-lrtsfd] [-i PACKAGE] [--user USER_ID] [PATH]
       pm install-create [-lrtsfdp] [-i PACKAGE] [-S BYTES]
               [--install-location 0/1/2]
               [--force-uuid internal|UUID]
       pm install-write [-S BYTES] SESSION_ID SPLIT_NAME [PATH]
       pm install-commit SESSION_ID
       pm install-abandon SESSION_ID
       pm uninstall [-k] [--user USER_ID] PACKAGE
       pm set-installer PACKAGE INSTALLER
       pm move-package PACKAGE [internal|UUID]
       pm move-primary-storage [internal|UUID]
       pm clear [--user USER_ID] PACKAGE
       pm enable [--user USER_ID] PACKAGE_OR_COMPONENT
       pm disable [--user USER_ID] PACKAGE_OR_COMPONENT
       pm disable-user [--user USER_ID] PACKAGE_OR_COMPONENT
       pm disable-until-used [--user USER_ID] PACKAGE_OR_COMPONENT
       pm default-state [--user USER_ID] PACKAGE_OR_COMPONENT
       pm hide [--user USER_ID] PACKAGE_OR_COMPONENT
       pm unhide [--user USER_ID] PACKAGE_OR_COMPONENT
       pm grant [--user USER_ID] PACKAGE PERMISSION
       pm revoke [--user USER_ID] PACKAGE PERMISSION
       pm reset-permissions
       pm set-app-link [--user USER_ID] PACKAGE {always|ask|never|undefined}
       pm get-app-link [--user USER_ID] PACKAGE
       pm set-install-location [0/auto] [1/internal] [2/external]
       pm get-install-location
       pm set-permission-enforced PERMISSION [true|false]
       pm trim-caches DESIRED_FREE_SPACE [internal|UUID]
       pm create-user [--profileOf USER_ID] [--managed] [--restricted] [--ephemeral] [--guest] USER_NAME
       pm remove-user USER_ID
       pm get-max-users

NOTE: 'pm list' commands have moved! Run 'adb shell cmd package'
  to display the new commands.

pm path: print the path to the .apk of the given PACKAGE.

pm dump: print system state associated with the given PACKAGE.

pm install: install a single legacy package
pm install-create: create an install session
    -l: forward lock application
    -r: replace existing application
    -t: allow test packages
    -i: specify the installer package name
    -s: install application on sdcard
    -f: install application on internal flash
    -d: allow version code downgrade (debuggable packages only)
    -p: partial application install
    -g: grant all runtime permissions
    -S: size in bytes of entire session

pm install-write: write a package into existing session; path may
  be '-' to read from stdin
    -S: size in bytes of package, required for stdin

pm install-commit: perform install of fully staged session
pm install-abandon: abandon session

pm set-installer: set installer package name

pm uninstall: removes a package from the system. Options:
    -k: keep the data and cache directories around after package removal.

pm clear: deletes all data associated with a package.

pm enable, disable, disable-user, disable-until-used, default-state:
  these commands change the enabled state of a given package or
  component (written as "package/class").

pm grant, revoke: these commands either grant or revoke permissions
    to apps. The permissions must be declared as used in the app's
    manifest, be runtime permissions (protection level dangerous),
    and the app targeting SDK greater than Lollipop MR1.

pm reset-permissions: revert all runtime permissions to their default state.

pm get-install-location: returns the current install location.
    0 [auto]: Let system decide the best location
    1 [internal]: Install on internal device storage
    2 [external]: Install on external media

pm set-install-location: changes the default install location.
  NOTE: this is only intended for debugging; using this can cause
  applications to break and other undersireable behavior.
    0 [auto]: Let system decide the best location
    1 [internal]: Install on internal device storage
    2 [external]: Install on external media

pm trim-caches: trim cache files to reach the given free space.

pm create-user: create a new user with the given USER_NAME,
  printing the new user identifier of the user.

pm remove-user: remove the user with the given USER_IDENTIFIER,
  deleting all data associated with that user

3.3 PackageManager/PackageManagerService

  PackageManagerService 是 Android 包管理基础设施中的核心对象。它负责解析 APK 文件,启动应用安装、更新和卸载包、维护包数据库,并负责管理权限。
PackageManagerService 同样提供了若干 installPackage() 方法,用于不同参数选项下执行包安装过程。最通用的方法是 installPackagewithVerificationAndEncryption(),它允许加密 APK 文件的安装,以及通过验证代理进行包验证的操作。

3.4 Installer类

  虽然PackageManagerService是一个有特权的Android 系统服务,但是它运行在系统服务进程内(system UID),缺少 root 权限。然而,因为创建、删除和更改应用目录的所有者,需要超级用户权限,PackageManagerService 将这些操作委托给 installd 守护进程。Installer 类使用 /dev/socket/installd 这个UNIX Domain Socket,向 installd 守护进程发起执行请求代为执行。

3.5 installd守护进程

  installd 守护进程是一个有特权的本地守护进程,它向系统包管理器提供应用和用户目录管理功能。它还用于启动 dexopt 命令,用于为新安装的包生成优化过的DEX文件。
  可以通过 installd 本地套接字来访问 installd 守护进程,该套接字只可被 system 下运行的进程所访问。installd 守护进程不以 root 执行,它利用 Linux 的 CAP_DAC_OVERRIDE 和CAP_CHOWN 能力,以便设置应用程序所创建的文件及目录的 UID 与 GID 。可参考此篇:Android 系统内的守护进程 - main类服务(3) : installd

3.6 MountService

  MountService 用于挂载可分离式的外存(SD卡等外设存储),也可用于挂载 OBB 文件(opaque binary blob),OBB 文件常用作应用程序的扩展文件。MountService还用于启动设备加密和更改加密密码。
  MountService服务还管理安全容器 (secure container),它用于存放应用的文件,以使得非系统应用不可访问。安全容器是加密的,并且用于实现一种叫作转发锁定 (forwardlocking) 的 DRM 机制。转发锁定主要用于付费应用的安装,以确保它们的APK 文件不能轻易地被复制和再发布。

3.7 vold守护进程

  vold 是 Android 存储管理守护进程。虽然 MountService 包含大多数的存储管理的 API,但因为它以 system 用户运行,缺少挂载/卸载磁盘的权限。这些特权操作由 vold 守护进程实现,它以 root 用户运行。
vold通过 UNIX Domain Socket /dev/socket/vold 暴露一个本地套接字,该 socket 只能被 root 和 mount 组成员访问。因为 system_server 进程(MountService 服务为其子线程)的补充GID包含 mount(GID 1009),MountService 是允许访问 vold 命令套接字的。除了挂载和卸载存储,vold 还可以创建和格式化文件系统,管理安全容器。可参考此篇:Android 系统内的守护进程 - core类中的服务 (4) : vold

3.8 MediaContainerService

  MediaContainerService 服务将 APK 文件复制到它们最终的安装位置,或者到一个加密容器,并且允许 PackageManagerService 在移动存储上访问文件。使用 DownoadManager 服务可远程获取APK文件(或通过应用市场),下载文件可以通过 DownloadManager 服务的 contentprovider 接口访问。PackageManager 赋予MediaContainerService 进程对已下载 APK 的临时权限。如果 APK 文件是加密的,MediaContainerService 进程首先解密如果需要加密容器,MediaContainerService 将加密容器委派给 MountService,然后复制 APK 受保护的部分(包括代码和资产)到一个新创建的容器中。不需要保护的文件,则直接复制到文件系统。

3.9 AppDirObserver

  AppDirobserver 是一个用于检测应用目录下 APK 文件改动的组件,然后根据事件类型调用相应的PackageManagerService 方法。当 APK 文件被添加到系统时,AppDirobserver 启动一个包扫描器来安装或更新应用。当 APK 文件被移除时,AppDirobserver 启动卸载进程,从而删除 app 目录和系统包数据库中的 app 条目。

  • 在svstem分区上监视的目录有/system/framework/(持有框架资源包famework-resapk)、/system/app/和/system/priv-app(系统包);
  • 在vendor分区上监视的目录有/vendor/app;
  • 在userdata分区上监控的目录有/data/app/和/data/app-private/,其中 /data/app-private 目录中包含着APK解密过程中产生的临时文件;

4 定性分析一个APK的安装过程

4.1 解析和验证包

  打开一个APK文件会触发 “application/vnd.android.package-archive” 处理程序的启动,这处理程序通常是 PackageInstaller 系统应用中的 PackageInstallerActivity 组件。

        <activity android:name=".PackageInstallerActivity"
                android:configChanges="orientation|keyboardHidden|screenSize"
                android:excludeFromRecents="true">
            <intent-filter android:priority="1">
                <action android:name="android.intent.action.VIEW" />
                <action android:name="android.intent.action.INSTALL_PACKAGE" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:scheme="file" />
                <data android:scheme="content" />
                <data android:mimeType="application/vnd.android.package-archive" />
            intent-filter>
            <intent-filter android:priority="1">
                <action android:name="android.intent.action.INSTALL_PACKAGE" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:scheme="file" />
                <data android:scheme="package" />
                <data android:scheme="content" />
            intent-filter>
            <intent-filter android:priority="1">
                <action android:name="android.content.pm.action.CONFIRM_PERMISSIONS" />
                <category android:name="android.intent.category.DEFAULT" />
            intent-filter>
        activity>

  PackageInstallerActivity 组件首先检查应用是否受信任(不考虑“未知来源”的应用)。如果不受信任,并且 Settings.Global.INSTALL_NON_MARKET_APPS 被设置为 false (“未知来源”复选框被勾选时,为 true),那么将会弹出一个警告框,并终止安装进程。
  如果允许安装,PackageInstallerActivity 解析 APK 文件,并且从 AndroidManifest.xml 文件提取信息和包签名。当使用 java.util.jar.JarFile 和相关类对每个条目提取签名证书时,APK 文件的完整性会被自动验证。  系统应用是被隐式信任的,并且在解析APK 文件时,只验证了 AndroidManifest.xml 文件的完整性。而非系统镜像的应用,其所有条目均需要被验证,比如说用户安装应用或系统应用的更新包。
  AndroidMamifest.xml 文件的哈希值计算同样也是 APK 解析过程的一部分,并且将会传给后续的安装步骤,该值将被用于验证在用户点击 OK 和 APK 复制进程启动之间,APK 文件没有被替换。

4.2 接受权限和启动安装进程

  APK解析完毕后,PackageInstallerActivity 会在一个对话框中显示该应用的相关信息和所需权限(此为安装时授权,不过在 Android 6 之后采用运行时授权了)。如果用户确认安装,PackageInstallerActivity 将 APK 文件和其 manifest 摘要转发给 InstallAppProgress,然后由它启动实际的包安装进程。其中manifest 摘要包括安装元数据,诸如引用 URL、安装包名和初始 UID。InstallAppProgress然后将APK的URI和安装元数据,传递给 PackageManagerService 的 installPackagewithVerificationAndEncryption() 方法,从而启动安装进程。接下来它只需等待安装进程的完成,并处理各种错误。
  安装方法首先验证调用者是否具备 INSTALL_PACKAGES 权限,该权限是 sigmature 保护级别的,并且系统应用默认拥有该权限。

4.3 复制到应用程序目录

  如果 APK 文件没有加密,那么就不需要验证操作,下一步就是将它复制到应用程序的目录中(/data/app)。为复制文件,PackageManagerService 首先在应用程序目录中创建一个临时文件(具有 vmdl 前缀和tmp 扩展名),然后委派 MediaContainerService 进行复制。文件通常不是直接复制的,因为它可能需要解密,或者因为需要转发锁定,为其创建加密容器。因为 MediaContainerServices 封装了这些任务,PackageManagerService 也就不需要了解其内部实现。
  当APK 文件成功复制后,它包含的任何本地库都会被提取到一个指定的 app 目录,该目录在系统本地库目录下 (/data/app-lib)。接下来,临时的 APK 文件和库目录均会被重命名为它们最终的基于包名称的名字,例如:

hammerhead:/data/app # ls
com.jay.example.gpsdemo-1
hammerhead:/data/app # cd com.jay.example.gpsdemo-1/
hammerhead:/data/app/com.jay.example.gpsdemo-1 # ls
base.apk  lib  oat

最终,APK 文件的权限设为 0644,并且其 SELinux 环境也被设置。

4.4 包扫描

  下一步通过调用 PackageManagerService 的 scanPackageLI() 方法,来触发包扫描。(如果安装进程在扫描之前停止了,那么它最终将会被监控 /data/app/ 目录的 AppDirobserver 实例重新拣起来,然后再触发一次包扫描。)
  对于一个新安装的应用来说,包管理器首先创建一个 PackageSettings 结构,其中包括:包名、代码路径、单独的资源路径(如果包是转发锁定的话)和一个本地库的路径。然后,它给新包赋予一个 UID,并且将其存在配置结构里。一旦新的 app 拥有 UID,则其数据目录就可以创建了。

4.5 创建数据目录

  因为 PackageManagerService 没有足够的权限来创建和设置 app 目录的所有权,它通过向 installd守护进程发送一个 install 命令,该命令包括:包名称、UID、GID和seinfo标签(用于SELinux)等参数,从而将目录创建的动作委托给 installd 守护进程。然后,installd 守护进程创建包的数据目录(例如,安装 com.jay.example.gpsdemo 包时,其数据目录为/data/dala/com.jay.example.gpsdemo/)、共享原生库目录(/data/app-lib/com.jay.example.gpsdemo/)、本地库录(/data/data/com.jay.example.gpsdemo/lib/)。然后,它设置包目录的权限到 0751,并且在本地库目录中,创建 app 本地库的符号链接。最后,设置包目录的 SELinux 环境,并切换其所有者到 app 被赋予的 UID 和 GID。
  当所有必要的目录均创建完毕后,控制权还给 PackageManagerService,然后由它提取所有原生库到应用的原生库目录,并在/data/data/com.jay.example.gpsdemo/lib/内创建符号链接。

4.6 生成odex

  下一步为应用程序代码生成 odex (optimized DEX)文件。这个操作是通过向 installd 守护进程发送 dexopt 命令,同样由 installd 守护进程完成。接下来,installd 守护进程 fork 一个 dexopt 进程,该进程会在 /data/dalvik-cache/目录下创建 odex 文件。如果 Android 设备上使用 ART 虚拟机,installd 守护进程会使用 dex2oat 命令来生成本地代码。

4.7 文件和目录结构

以“闲鱼app”为例,安装进LineageOS 14.1 Nexus 5手机后 /data/data/com.taobao.idlefish 目录内容:

hammerhead:/data/app/com.taobao.idlefish-1 # ls -al
total 118768
drwxr-xr-x 4 system system       4096 2023-06-12 12:28 .
drwxrwx--x 5 system system       4096 2023-06-12 12:29 ..
-rw-r--r-- 1 system system  121598101 2023-06-12 12:28 base.apk
drwxr-xr-x 3 system system       4096 2023-06-12 12:28 lib
drwxrwx--x 3 system install      4096 2023-06-12 12:28 oat

hammerhead:/data/app/com.taobao.idlefish-1/oat/arm # ls -al
total 71956
drwxrwx--x 2 system install       4096 2023-06-12 12:28 .
drwxrwx--x 3 system install       4096 2023-06-12 12:28 ..
-rw-r--r-- 1 system u0_a29999 73666984 2023-06-12 12:28 base.odex

hammerhead:/data/data/com.taobao.idlefish # ls -al
total 92
drwx------  22 u0_a68 u0_a68 4096 2023-06-12 12:30 .
drwxrwx--x 101 system system 4096 2023-06-12 12:29 ..
drwxrwx--x   6 u0_a68 u0_a68 4096 2023-06-12 12:29 app_SGLib
drwxrwx--x   2 u0_a68 u0_a68 4096 2023-06-12 12:29 app_accs
drwxrwx--x   2 u0_a68 u0_a68 4096 2023-06-12 12:29 app_cyclone
drwxrwx--x   3 u0_a68 u0_a68 4096 2023-06-12 12:29 app_efs
drwxrwx--x   2 u0_a68 u0_a68 4096 2023-06-12 12:29 app_flutter
drwxrwx--x   3 u0_a68 u0_a68 4096 2023-06-12 12:29 app_local_libs
drwxrwx--x   2 u0_a68 u0_a68 4096 2023-06-12 12:29 app_patrons
drwxrwx--x   2 u0_a68 u0_a68 4096 2023-06-12 12:29 app_sslcache
drwxrwx--x   2 u0_a68 u0_a68 4096 2023-06-12 12:29 app_textures
drwxrwx--x   5 u0_a68 u0_a68 4096 2023-06-12 12:29 app_tombstone
drwx------   2 u0_a68 u0_a68 4096 2023-06-12 12:30 app_u4_webview
drwxrwx--x   5 u0_a68 u0_a68 4096 2023-06-12 12:30 app_ucmsdk
drwx------   3 u0_a68 u0_a68 4096 2023-06-12 12:29 app_webview
drwxrwx--x   5 u0_a68 u0_a68 4096 2023-06-12 12:30 app_wpksdk
drwxrwx--x   7 u0_a68 u0_a68 4096 2023-06-12 12:29 app_zcache
drwxrwx--x   4 u0_a68 u0_a68 4096 2023-06-12 12:30 cache
drwxrwx--x   3 u0_a68 u0_a68 4096 2023-06-12 12:29 code_cache
drwxrwx--x   2 u0_a68 u0_a68 4096 2023-06-12 12:29 databases
drwxrwx--x  27 u0_a68 u0_a68 4096 2023-06-12 12:29 files
lrwxrwxrwx   1 root   root     39 2023-06-12 12:29 lib -> /data/app/com.taobao.idlefish-1/lib/arm
drwxrwx--x   2 u0_a68 u0_a68 4096 2023-06-12 12:30 shared_prefs

对于各文件的说明:

  • base.apk 是 APK 文件;
  • lib 目录是解压出来的原生库文件,这些均属于 system 用户,并且是全局可读的;
  • oat 目录中是 odex 文件,属主被设置为 system,并且所有组被设置为u0_a29999,该组包括所有安装该应用的设备用户。这样允许所有用户共享相同的 odex 文件,因此避了为每个用户分别创建一份该文件,从而在多用户支持设备中减少了磁盘占用率。
  • 应用的数据目录 /data/data/com.taobao.idlefish 和其子目录均属于特定的 Linux 用户,该用户由安装应用的设备用户ID (u0,如果是单用户设备) 和 appID (a68)组合而成,即为u0_a68。

4.8 将新安装包添加到packages.xml

下一步将包添加到系统包数据库中。一个新安装包的条目看起来如下:

    <package name="com.taobao.idlefish" codePath="/data/app/com.taobao.idlefish-1" nativeLibraryPath="/data/app/com.taobao.idlefish-1/lib" primaryCpuAbi="armeabi-v7a" publicFlags="945307204" privateFlags="0" ft="188addc6838" it="188addd6079" ut="188addd6079" version="330" userId="10068" installer="com.android.packageinstaller">
        <sigs count="1">
            <cert index="6" key="3082023b308201a4a003020102020456e7ce5f300d06092a864886f70d01010505003061310e300c060355040613056368696e61310b3009060355040813027a6a310b300906035504071302687a3110300e060355040a1307616c69626162613110300e060355040b1307616c69626162613111300f0603550403130869646c65666973683020170d3136303331353038353730335a180f32313136303232303038353730335a3061310e300c060355040613056368696e61310b3009060355040813027a6a310b300906035504071302687a3110300e060355040a1307616c69626162613110300e060355040b1307616c69626162613111300f0603550403130869646c656669736830819f300d06092a864886f70d010101050003818d003081890281810089253f9181dae9888669b5dfc0fefb91f9630c575f93cc70c8c20be4af7b1714d3f5d5870c59b73d78481ac29e49fffc49a4a2b441fca387ebeeea2bcb2c71590429d901412d633d5eb445529cb57077a78a2b0d46fbf8c12b3c79aa22952c6ab22b80737c43404a5949d83f18720760ee4d191adb93ee9003ed2da9b5bb1a930203010001300d06092a864886f70d0101050500038181000ea193fcb0de60ed6d4a76d957a6f2bf00f5103ab31714149745b9583924cd1b84afa35e36c2a283327448565ebce36c8b4c1daa31fdf7b46ee2c099195e971730eeb0a42f531e80714ac99dcf3aec31406bd18fd445158f8b638dc82787dc14a8840a9134878947ad4ae7eaec1925dbd06c98cc11ceaf60557f7cd088f373ef" />
        sigs>
        <perms>
            <item name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" granted="true" flags="0" />
            <item name="com.taobao.idlefish.push.permission.MESSAGE" granted="true" flags="0" />
            <item name="android.permission.USE_CREDENTIALS" granted="true" flags="0" />
            <item name="android.permission.MODIFY_AUDIO_SETTINGS" granted="true" flags="0" />
            <item name="android.permission.MANAGE_ACCOUNTS" granted="true" flags="0" />
            <item name="android.permission.NFC" granted="true" flags="0" />
            <item name="android.permission.CHANGE_NETWORK_STATE" granted="true" flags="0" />
            <item name="android.permission.RECEIVE_BOOT_COMPLETED" granted="true" flags="0" />
            <item name="android.permission.BLUETOOTH" granted="true" flags="0" />
            <item name="android.permission.CHANGE_WIFI_MULTICAST_STATE" granted="true" flags="0" />
            <item name="android.permission.GET_TASKS" granted="true" flags="0" />
            <item name="android.permission.AUTHENTICATE_ACCOUNTS" granted="true" flags="0" />
            <item name="android.permission.INTERNET" granted="true" flags="0" />
            <item name="android.permission.REORDER_TASKS" granted="true" flags="0" />
            <item name="com.android.browser.permission.READ_HISTORY_BOOKMARKS" granted="true" flags="0" />
            <item name="android.permission.BLUETOOTH_ADMIN" granted="true" flags="0" />
            <item name="com.taobao.idlefish.permission.C2D_MESSAGE" granted="true" flags="0" />
            <item name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" granted="true" flags="0" />
            <item name="android.permission.BROADCAST_STICKY" granted="true" flags="0" />
            <item name="android.permission.CHANGE_WIFI_STATE" granted="true" flags="0" />
            <item name="android.permission.FLASHLIGHT" granted="true" flags="0" />
            <item name="android.permission.ACCESS_NETWORK_STATE" granted="true" flags="0" />
            <item name="android.permission.USE_FINGERPRINT" granted="true" flags="0" />
            <item name="com.taobao.idlefish.permission.MIPUSH_RECEIVE" granted="true" flags="0" />
            <item name="com.taobao.idlefish.permission.PUSH_PROVIDER" granted="true" flags="0" />
            <item name="com.taobao.idlefish.permission.PROCESS_PUSH_MSG" granted="true" flags="0" />
            <item name="android.permission.VIBRATE" granted="true" flags="0" />
            <item name="android.permission.ACCESS_WIFI_STATE" granted="true" flags="0" />
            <item name="android.permission.REQUEST_INSTALL_PACKAGES" granted="true" flags="0" />
            <item name="com.android.launcher.permission.INSTALL_SHORTCUT" granted="true" flags="0" />
            <item name="android.permission.WAKE_LOCK" granted="true" flags="0" />
        perms>
        <proper-signing-keyset identifier="7" />
    package>
  • 元素含有DER编码十六进制字符串形式的包签名证书(通常只有一个),或者在多 app 被同一证书签名的情况下,是一个指向证书首次出现位置的引用;
  • 元素包含的是赋予该应用的权限;
  • 是持有应用程序签名密钥集合的一个引用,其包括所有在 APK 内部签名文件的公开密 (不是证书)。

4.9 包属性

  根元素 含有每个包的核心属性,如:安装位置和版本号等。主要的包属性如下表所示。每个包条目中的信息,可以通过 android.content.pm.PackageManager SDK 类的 getPackageInfo(String packageName, int flags) 方法获取,该方法会返回一个 PackageInfo 的实例,其封装了 packages.xml 中每个条目的有效属性。同时也包含组件、权限和 manifest 文件中定义的特性等信息。

属性 说明
name 包名
codePath 包所在位置的完整路径
resourcePath 包的公开部分的完整路径(主要资源包和manifest文件)。针对转发锁定app
nativeLibraryPath 存储的原生库所在目录的完整路径
flags 应用程序相关的标志
ft APK文件时间戳(UNIX毫秒时间,System.currentTimeMillis())
it 应用首次安装的时间(UNIX 毫秒时间)
ut 应用最后更新的时间(UNIX毫秒时间)
version 包的版本号,由应用的manifest 文件中的versionCode 属性指定
userId 该应用程序的内核UID
installer 安装该app的应用程序名称
sharedUserId 包的共享用户ID,由manifest文件中的sharedUserId 属性指定

4.10 更新组件和权限

  在创建 packages.xml 的条目之后,PackageManagerService 扫描所有新应用程序 manifest 清单中定义的 Android 组件,并且将其添加到它内部加载在内存中的组件注册表接下来,任何该 app 声明的权限组和权限均会被扫描,并添加到权限注册表中。最终,修改保存在磁盘上的包数据库(包的条目和任何新的权限),然后 PackageManagerService 发送 ACTION_PACKAGE_ADDED 通知其他组件,新增加了一个应用。

5 安装加密的APK

5.1 生成加密的APK

  加密的 APK 文件可以使用 enc OpenSSL 命令进行加密。如下所示。这里使用 CBC 模式的 AES 加密算法,密钥长度为128-bit,初始向量 IV 与密钥相同。

openssl enc -aes-128-cbc -K 000102030405060708090A0BOCODOEOF -iv 000102030405060708090A0BOCOD0E0F -in test-app.apk -out test-app-enc.apk

5.2 安装加密的APK

adb install [-l] [-r] [-s] [--algo <algorithm name> --key <hex-encoded key> --iv <hex-encoded iv>] <file>

  该命令中的 --algo、–key 和 --iv 参数,允许分别指定加密算法、密钥和初始向量(IV)。

通过将加密算法、密钥和 IV 传递给 adb install 命令,来安装加密APK:

adb instal1 --algo 'AES/CBC/PKCS5Padding' --key 0001020340596070809ABBCODEEEF --iv 000102030405060708090AOBOCODOEOF test-app-enc.apk

  如输出的 Success,APK 安装过程没有遇到任何错误。实际的 APK 文件被复制到 /data/app 目录下,并且将其哈希值与加密 APK 比较的话,会发现它们实际上是不同的文件。其哈希值恰好与未加密的 APK 哈希相同,所以 APK 在安装时,使用提供的加密参数(算法、密钥和IV)进行解密。

6 包验证

  当包验证功能被打开时,APK 在安装之前要被验证器扫描,当验证器认为 APK 可能有害时,系统会弹出一个警告框或者中断安装进程。验证在受支持的设备上默认开启,但是首次使用时需要用户的许可。应用程序验证可以通过系统设置页面中的“验证应用”选项来开启或关闭。

你可能感兴趣的:(#,android,APK)