Android系统安装APP有如下几种方法:
Android 的包管理功能分布在数个系统组件之中,在安装时,它们之间互相交互,如图所示:
Android 区别系统安装与用户安装的应用程序。系统应用可以在只读的 system 分区中找到,并且在量产设备上不能被修改或卸载。系统应用因此被认为是可信的,并且赋予了更多的权限,还会放松一些签名检查。
为系统和用户安装的应用数据目录创建在 userdata 分区的 /data/data/ 目录下。userdata 分区同样存放一些为用户安装应用优化过的 DEX 文件(data/dalvik-cache/)、系统包数据库(存放在 /data/system/packages.xm/)、系统数据库和设置文件。
PackageInstaller 为包管理提供了一个基本的 GUI,并且当它被传递 VIEW 或 INSTALL_ACTION 动作并附带上 APK 文件的 URI 的 intent 时,它就会处理安装包并在屏幕上弹出一个显示应用所需权限的确认对话框。使用 PackageInstaller 安装应用,只有在用户开启了设备安全设置中的“未知来源”选项后方可进行。
PackageInstaller 获取请求 APK 安装动作的应用程序的 UID 和 包名,并检查它是否属于特权应用(安装在/system/priv-app/目录下)。如果请求的应用程序不具有特权,则认为它是一个未知来源。
如果“未知来源”选项开启,并且用户点击了对话框中的“确认”按钮,PackageInstaller 会调用PackageManagerService 服务,它将会执行真实的安装过程。当应用更新或卸载时,PackageInstaller 的 GUI 同样会做出提示。
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
PackageManagerService 是 Android 包管理基础设施中的核心对象。它负责解析 APK 文件,启动应用安装、更新和卸载包、维护包数据库,并负责管理权限。
PackageManagerService 同样提供了若干 installPackage() 方法,用于不同参数选项下执行包安装过程。最通用的方法是 installPackagewithVerificationAndEncryption(),它允许加密 APK 文件的安装,以及通过验证代理进行包验证的操作。
虽然PackageManagerService是一个有特权的Android 系统服务,但是它运行在系统服务进程内(system UID),缺少 root 权限。然而,因为创建、删除和更改应用目录的所有者,需要超级用户权限,PackageManagerService 将这些操作委托给 installd 守护进程。Installer 类使用 /dev/socket/installd 这个UNIX Domain Socket,向 installd 守护进程发起执行请求代为执行。
installd 守护进程是一个有特权的本地守护进程,它向系统包管理器提供应用和用户目录管理功能。它还用于启动 dexopt 命令,用于为新安装的包生成优化过的DEX文件。
可以通过 installd 本地套接字来访问 installd 守护进程,该套接字只可被 system 下运行的进程所访问。installd 守护进程不以 root 执行,它利用 Linux 的 CAP_DAC_OVERRIDE 和CAP_CHOWN 能力,以便设置应用程序所创建的文件及目录的 UID 与 GID 。可参考此篇:Android 系统内的守护进程 - main类服务(3) : installd
MountService 用于挂载可分离式的外存(SD卡等外设存储),也可用于挂载 OBB 文件(opaque binary blob),OBB 文件常用作应用程序的扩展文件。MountService还用于启动设备加密和更改加密密码。
MountService服务还管理安全容器 (secure container),它用于存放应用的文件,以使得非系统应用不可访问。安全容器是加密的,并且用于实现一种叫作转发锁定 (forwardlocking) 的 DRM 机制。转发锁定主要用于付费应用的安装,以确保它们的APK 文件不能轻易地被复制和再发布。
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
MediaContainerService 服务将 APK 文件复制到它们最终的安装位置,或者到一个加密容器,并且允许 PackageManagerService 在移动存储上访问文件。使用 DownoadManager 服务可远程获取APK文件(或通过应用市场),下载文件可以通过 DownloadManager 服务的 contentprovider 接口访问。PackageManager 赋予MediaContainerService 进程对已下载 APK 的临时权限。如果 APK 文件是加密的,MediaContainerService 进程首先解密如果需要加密容器,MediaContainerService 将加密容器委派给 MountService,然后复制 APK 受保护的部分(包括代码和资产)到一个新创建的容器中。不需要保护的文件,则直接复制到文件系统。
AppDirobserver 是一个用于检测应用目录下 APK 文件改动的组件,然后根据事件类型调用相应的PackageManagerService 方法。当 APK 文件被添加到系统时,AppDirobserver 启动一个包扫描器来安装或更新应用。当 APK 文件被移除时,AppDirobserver 启动卸载进程,从而删除 app 目录和系统包数据库中的 app 条目。
打开一个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 文件没有被替换。
APK解析完毕后,PackageInstallerActivity 会在一个对话框中显示该应用的相关信息和所需权限(此为安装时授权,不过在 Android 6 之后采用运行时授权了)。如果用户确认安装,PackageInstallerActivity 将 APK 文件和其 manifest 摘要转发给 InstallAppProgress,然后由它启动实际的包安装进程。其中manifest 摘要包括安装元数据,诸如引用 URL、安装包名和初始 UID。InstallAppProgress然后将APK的URI和安装元数据,传递给 PackageManagerService 的 installPackagewithVerificationAndEncryption() 方法,从而启动安装进程。接下来它只需等待安装进程的完成,并处理各种错误。
安装方法首先验证调用者是否具备 INSTALL_PACKAGES 权限,该权限是 sigmature 保护级别的,并且系统应用默认拥有该权限。
如果 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 环境也被设置。
下一步通过调用 PackageManagerService 的 scanPackageLI() 方法,来触发包扫描。(如果安装进程在扫描之前停止了,那么它最终将会被监控 /data/app/ 目录的 AppDirobserver 实例重新拣起来,然后再触发一次包扫描。)
对于一个新安装的应用来说,包管理器首先创建一个 PackageSettings 结构,其中包括:包名、代码路径、单独的资源路径(如果包是转发锁定的话)和一个本地库的路径。然后,它给新包赋予一个 UID,并且将其存在配置结构里。一旦新的 app 拥有 UID,则其数据目录就可以创建了。
因为 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/内创建符号链接。
下一步为应用程序代码生成 odex (optimized DEX)文件。这个操作是通过向 installd 守护进程发送 dexopt 命令,同样由 installd 守护进程完成。接下来,installd 守护进程 fork 一个 dexopt 进程,该进程会在 /data/dalvik-cache/目录下创建 odex 文件。如果 Android 设备上使用 ART 虚拟机,installd 守护进程会使用 dex2oat 命令来生成本地代码。
以“闲鱼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
对于各文件的说明:
下一步将包添加到系统包数据库中。一个新安装包的条目看起来如下:
<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>
根元素
属性 | 说明 |
---|---|
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 属性指定 |
在创建 packages.xml 的条目之后,PackageManagerService 扫描所有新应用程序 manifest 清单中定义的 Android 组件,并且将其添加到它内部加载在内存中的组件注册表接下来,任何该 app 声明的权限组和权限均会被扫描,并添加到权限注册表中。最终,修改保存在磁盘上的包数据库(包的条目和任何新的权限),然后 PackageManagerService 发送 ACTION_PACKAGE_ADDED 通知其他组件,新增加了一个应用。
加密的 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
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)进行解密。
当包验证功能被打开时,APK 在安装之前要被验证器扫描,当验证器认为 APK 可能有害时,系统会弹出一个警告框或者中断安装进程。验证在受支持的设备上默认开启,但是首次使用时需要用户的许可。应用程序验证可以通过系统设置页面中的“验证应用”选项来开启或关闭。