Android 11 和 MIUI 获取安装来源
近期分析一个恶意软件,需要知道是谁安装的它,本文记录 Android 11 和 MIUI 中获取安装者的方式。
背景知识
Android 10之前,开发者通常使用PackageManager.getInstallerPackage获取安装者的包名,真正调用后,会发现这个API基本毫无用处,只会返回 null、market(应用市场)、vendoring(GooglePlay)、installer(系统安装器。平时我使用浏览器、使用taptap安装游戏,都没有被正确地划分进来,因此这个 API 并没有按照预期工作。
Android 11后,多了一个 API 叫 public InstallSourceInfo getInstallSourceInfo(@NonNull String packageName)
,它是有意义的。
它返回 3 个 String,编写 APP 测试,会发现 getOriginatingPackageName
返回是空,另外两个字段也没什么价值。
- getInitiatingPackageName
- getOriginatingPackageName
- getInstallingPackageName
我的 MIUI 手机,在应用设置界面可以看到 app 的安装信息,显然和上面的 API 不是同一个。
因此,引出本文的第一部分,MIUI设置界面的数据是哪里来的。
MIUI设置界面的数据哪来的
一眼定帧,鉴定为 com.miui.securitycenter/com.miui.appmanager.AMAppInformationActivity
,直接把包拖出来分析。
最后发现是 com.miui.securitycenter.provider.SecurityCenterProvider
提供了数据。
这个 ContentProvider 有权限保护,shell权限无法访问,需要 root 后进行测试。
1 | gauguinpro:/ $ su |
第一部分搞定,确实是存在数据库里的,而且看起来是MIUI专属的数据库,和AOSP无关。
于是提出下一个问题,在没有 root 的情况下,能否获取每个包的实际安装者。
AOSP的数据是哪来的
虽然 pm list packages -i
是错的,但也先看看它的实现吧。
文件位于:platform/frameworks/base/services/core/java/com/android/server/pm/PackageManagerShellCommand.java。它调用了 getInstallerPackageName
,那当然是错的。
1 | /******/ |
继续看 getInstallerPackageName
的实现,位于:platform/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java 。
1 |
|
再追到 platform/frameworks/base/services/core/java/com/android/server/pm/Settings.java ,会发现 3 个文件,位于 /data/system
下。
1 | mSettingsFilename = new File(mSystemDir, "packages.xml"); |
使用root,读这里的文件,这里数据是有的,只是没有显示出来。
1 | <package name="org.cocos2d.Dice" codePath="/data/app/~~iHhdolZdlOaWLmqtjOgq6Q==/org.cocos2d.Dice-drB5TRscmvfPl9HpP0UuQQ==" nativeLibraryPath="/data/app/~~iHhdolZdlOaWLmqtjOgq6Q==/org.cocos2d.Dice-drB5TRscmvfPl9HpP0UuQQ==/lib" primaryCpuAbi="arm64-v8a" publicFlags="940097092" privateFlags="-1409282048" ft="180992bd3e0" it="17fd5f6a55b" ut="180992bdb7c" version="65" userId="10074" installer="com.miui.packageinstaller" installInitiator="com.miui.packageinstaller" installOriginator="com.taptap"> |
Android 11 的新的 API getInstallSourceInfo,根据文档,缺少 INSTALL_PACKAGES 权限时,无法拿到 originatingPackageName。
If the calling application does not hold the INSTALL_PACKAGES permission then the result will always return null from InstallSourceInfo.getOriginatingPackageName().
看看具体实现,位于 platform/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java 。
代码大意为:先取回 InstallSource ,此时数据是完整的;然后 checkCallingOrSelfPermission
INSTALL_PACKAGES ,对于无权限的调用者,PM 会主动将 originatingPackageName
置空,和文档描述一致,普通APP无法读到该字段。
1 |
|
既然需要 INSTALL_PACKAGES 权限的话。。。
众所周知,INSTALL_PACKAGES 是一个 SignatureOrSystem,普通的 APP 是不可能获得这个权限的。但真的已经没有办法了吗?
众所周知啊,adb shell是唯一一个可以安装APP的用户,shell执行的代码开发者完全可控,既然 pm 命令可以直接被执行,说明 shell 是可以访问到 binder 的,计划通。
第一步:让 shell 执行任意的 java 代码
背景知识1:am、pm等命令,都是通过java实现的,细节见:https://www.cnblogs.com/wanghongzhu/p/15067133.html
背景知识2:开发一个shell里可以使用cli调用的apk,细节见:https://testerhome.com/topics/8622
第二步:拿到 binder
追一下 am
的代码,位于 frameworks/base/cmds/am/src/com/android/commands/am/Am.java ,直接开抄!
1 | private IActivityManager mAm; |
第三步:自己写个 app
备注:IPackageManager 在 Android SDK 里没有,可以自己写个假的放在 apk 里用于编译。
1 | public class Main { |
第四步:加载和验证
1 | adb push /Users/leadroyal/Android_code/HelloAndroid/app/build/outputs/apk/debug/app-debug.apk /sdcard/ |
好的,完工!
结论
- 小于等于 Androi 10,无解
- 大于等于 Android 11,使用 shell 调用 getInstallSourceInfo
- MIUI,需要 root 权限,读 provider