一、权限分级体系 Android 权限分为三个保护级别:Normal (普通权限,安装时自动授予)、Dangerous (危险权限,需运行时动态申请)、Signature (签名权限,仅与系统同签名的应用可获取)。权限声明在 AndroidManifest.xml 中,但声明不等于获得——系统会根据保护级别分派处理逻辑。
从实现原理上看,权限检查分为两层:Framework 层 通过 PackageManagerService 管理权限注册与检查,调用路径为 PackageManager.checkPermission() → ActivityManagerService.checkComponentPermission();Kernel 层 则基于 Linux DAC(Discretionary Access Control,自主访问控制)和 MAC(Mandatory Access Control,强制访问控制,即 SELinux),通过 UID/GID 映射实现进程级隔离。
一.1 权限保护级别的完整定义 除了常见的三类,Android 还有更多保护级别:
Protection Level 完整分类: Normal (0x0) ├── 普通权限,安装时自动授予 ├── 不会弹出确认对话框 └── 示例: INTERNET, ACCESS_NETWORK_STATE, BLUETOOTH, WAKE_LOCK Dangerous (0x1) ├── 危险权限,涉及用户隐私数据 ├── Android 6.0+ 需运行时动态申请 ├── 按权限组(Permission Group)归类 └── 示例: CAMERA, CONTACTS, LOCATION, SMS, STORAGE Signature (0x2) ├── 签名级别,仅与声明该权限的系统应用同签名的 App 可获取 ├── 安装时检查签名匹配,不匹配则静默拒绝 └── 示例: BIND_DEVICE_ADMIN, BIND_NOTIFICATION_LISTENER_SERVICE SignatureOrSystem (0x3) ├── 签名或系统级:同签名 OR 安装在 /system 分区 ├── 已废弃(API 23),被拆分为 Signature|Privileged └── 示例: WRITE_SECURE_SETTINGS, CHANGE_CONFIGURATION Internal (0x4) ├── 仅由系统使用 └── 第三方应用无法获取 Privileged (0x10, 可组合) ├── 特权级别,必须系统应用且位于 /system/priv-app/ ├── Android 8.0+ 引入 └── 示例: INSTALL_PACKAGES, DELETE_PACKAGES Development (0x20, 可组合) ├── 开发者选项启用后可用 └── 不引入安全风险 Appop (0x40, 可组合) ├── 与 AppOps 框架配合使用 └── 更细粒度的权限控制 Instant (0x100, 可组合) ├── Instant App 可被授予 └── Android 8.0+ RetailDemo (0x200, 可组合) ├── 零售演示模式 └── Android 9.0+ 组合示例: <permission android:protectionLevel="signature|privileged" />
一.2 权限检查的双层架构 ┌─────────────────────────────────────────────────────────┐ │ Framework 层 (Java) │ │ │ │ Context.checkPermission(perm, pid, uid) │ │ └→ ActivityManager.checkPermission(perm, pid, uid) │ │ └→ ActivityManagerService.checkPermission(...) │ │ └→ PermissionManagerService.checkPermission() │ │ ├── 检查 uid 是否声明了该权限 │ │ ├── 检查 protectionLevel 匹配 │ │ ├── 检查 AppOps 状态(动态权限) │ │ └── 返回 PERMISSION_GRANTED / DENIED │ │ │ │ 关键源文件: │ │ /frameworks/base/core/java/android/app/ │ │ ApplicationPackageManager.java │ │ /frameworks/base/services/core/java/com/android/server/│ │ pm/permission/PermissionManagerService.java │ │ /frameworks/base/core/java/android/content/ │ │ ContextImpl.java │ └───────────────────────┬─────────────────────────────────┘ │ UID/GID 映射 ┌───────────────────────▼─────────────────────────────────┐ │ Kernel 层 (C) │ │ │ │ Linux DAC (Discretionary Access Control) │ │ └── 基于 UID/GID 的文件/进程访问控制 │ │ ├── 每个 App 分配唯一 UID (Linux 10000+) │ │ ├── 每个权限可能对应一个 GID (如 inet → 3003) │ │ └── 文件系统权限: rwx 针对 owner/group/other │ │ │ │ SELinux MAC (Mandatory Access Control) │ │ └── 基于安全策略的强制访问控制 │ │ ├── 进程运行在 SELinux domain 中 (如 untrusted_app) │ │ ├── 每个资源有 SELinux label (如 app_data_file) │ │ ├── 策略文件: /sepolicy/ 下的 *.te 文件 │ │ └── 即使在 DAC 通过的操作,SELinux 也能拒绝 │ │ │ │ 关键源文件: │ │ /kernel/security/selinux/ │ │ /system/sepolicy/ │ └─────────────────────────────────────────────────────────┘
一.3 UID/GID 分配机制 Android 中每个应用分配唯一的 Linux UID(从 10000 开始递增)。系统权限通过 GID 映射:
static const struct android_id_info android_ids [] = { { "root" , AID_ROOT, }, { "system" , AID_SYSTEM, }, { "radio" , AID_RADIO, }, { "bluetooth" , AID_BLUETOOTH, }, { "graphics" , AID_GRAPHICS, }, { "input" , AID_INPUT, }, { "audio" , AID_AUDIO, }, { "camera" , AID_CAMERA, }, { "log" , AID_LOG, }, { "compass" , AID_COMPASS, }, { "mount" , AID_MOUNT, }, { "wifi" , AID_WIFI, }, { "adb" , AID_ADB, }, { "install" , AID_INSTALL, }, { "media" , AID_MEDIA, }, { "dhcp" , AID_DHCP, }, { "sdcard_rw" , AID_SDCARD_RW, }, { "vpn" , AID_VPN, }, { "keystore" , AID_KEYSTORE, }, { "usb" , AID_USB, }, { "drm" , AID_DRM, }, { "mdnsr" , AID_MDNSR, }, { "gps" , AID_GPS, }, { "inet" , AID_INET, }, { "net_raw" , AID_NET_RAW, }, { "net_admin" , AID_NET_ADMIN, }, { "net_bw_stats" , AID_NET_BW_STATS}, { "net_bw_acct" , AID_NET_BW_ACCT }, { "everybody" , AID_EVERYBODY, }, { "misc" , AID_MISC, }, { "nobody" , AID_NOBODY, }, { "app" , AID_APP, }, };
当一个应用声明了 INTERNET 权限,系统会在创建应用进程时将其加入 inet (GID 3003) 组,从而使该进程有权访问网络 socket。
二、如何分析应用的权限使用 二.1 使用 aapt 分析权限声明 aapt dump permissions app.apk aapt dump badging app.apk | grep "uses-permission" aapt d --values permissions app.apk
二.2 使用 JADX 分析权限的实际使用 grep -r "checkPermission" jadx_output/ grep -r "checkCallingPermission" jadx_output/ grep -r "enforcePermission" jadx_output/ grep -r "requestPermissions" jadx_output/ grep -r "shouldShowRequestPermissionRationale" jadx_output/
二.3 使用 smali 分析权限检查逻辑 权限相关的 smali 代码模式识别:
invoke-virtual {p0}, ..., Landroid/content/Context; ->checkSelfPermission( Ljava/lang/String; )I move-result v0if-eqz v0, :permission_granted invoke-virtual {p0, p1}, ..., Landroid/content/pm/PackageManager; ->checkPermission( Ljava/lang/String; Ljava/lang/String; )I const/4 v0, 0x1new-array v0, v0, [Ljava/lang/String; const-string v1, "android.permission.CAMERA" aput-object v1, v0, 0invoke-virtual {p0, v0, v2}, ..., Landroid/app/Activity; ->requestPermissions([Ljava/lang/String; I)V
二.4 运行时权限分析工具 adb shell dumpsys package com.target.app | grep -A 50 "requested permissions" adb shell ps -A | grep com.target.app adb shell cat /proc/<pid>/status | grep -E "Uid|Gid" adb shell dumpsys activity permissions
二.5 Frida 动态 Hook 权限检查 Java .perform (function ( ) { var ContextImpl = Java .use ("android.app.ContextImpl" ); ContextImpl .checkPermission .implementation = function (perm, pid, uid ) { var result = this .checkPermission (perm, pid, uid); if (result === 0 ) { console .log ("[+] Permission GRANTED: " + perm + " (pid=" + pid + ", uid=" + uid + ")" ); } else { console .log ("[-] Permission DENIED: " + perm + " (pid=" + pid + ", uid=" + uid + ")" ); } return result; }; var Activity = Java .use ("android.app.Activity" ); Activity .requestPermissions .implementation = function (permissions, reqCode ) { console .log ("[*] requestPermissions called:" ); for (var i = 0 ; i < permissions.length ; i++) { console .log (" " + permissions[i]); } return this .requestPermissions (permissions, reqCode); }; });
三、组件导出与权限保护 三.1 四大组件的权限保护机制 Android 四大组件(Activity、Service、BroadcastReceiver、ContentProvider)都可以通过 android:permission 属性进行权限保护:
<activity android:name =".AdminActivity" android:permission ="com.example.app.ADMIN_ACCESS" android:exported ="true" > <intent-filter > <action android:name ="com.example.action.ADMIN" /> </intent-filter > </activity > <service android:name =".DataSyncService" android:permission ="com.example.app.SYNC_CONTROL" android:exported ="true" /> <receiver android:name =".BootReceiver" android:permission ="android.permission.RECEIVE_BOOT_COMPLETED" android:exported ="true" > <intent-filter > <action android:name ="android.intent.action.BOOT_COMPLETED" /> </intent-filter > </receiver > <provider android:name =".DataProvider" android:permission ="com.example.app.DATA_ACCESS" android:exported ="true" android:authorities ="com.example.app.provider" />
三.2 组件权限检查源码分析 int checkComponentPermission (String permission, int uid, int owningUid, boolean exported) { if (uid == Process.SYSTEM_UID || uid == Process.ROOT_UID) { return PackageManager.PERMISSION_GRANTED; } if (UserHandle.isIsolated(uid)) { return PackageManager.PERMISSION_DENIED; } if (UserHandle.isSameApp(uid, owningUid)) { return PackageManager.PERMISSION_GRANTED; } if (!exported) { return PackageManager.PERMISSION_DENIED; } if (permission == null ) { return PackageManager.PERMISSION_GRANTED; } return checkPermission(permission, ...); }
三.3 bypass 导出组件的攻击思路 Intent intent = new Intent ();intent.setClassName("com.victim.app" , "com.victim.app.AdminActivity" ); startActivity(intent); Cursor cursor = getContentResolver().query( Uri.parse("content://com.victim.app.provider/users" ), null , null , null , null ); Intent intent = new Intent ("com.victim.app.SECRET_ACTION" );intent.putExtra("data" , "malicious_payload" ); sendBroadcast(intent);
三.4 使用 drozer 自动化分析组件权限安全 dz> run app.package.attacksurface com.target.app dz> run app.provider.info -a com.target.app dz> run app.provider.query content://com.target.app.provider/users dz> run app.activity.info -a com.target.app dz> run app.broadcast.info -a com.target.app dz> run scanner.provider.injection -a com.target.app dz> run scanner.provider.traversal -a com.target.app
四、SELinux 与 Android 权限 四.1 SELinux 策略如何限制权限 Android 5.0+ 默认启用 SELinux Enforcing 模式。每个进程运行在预定义的 SELinux domain 中:
SELinux 相关核心概念: Type (类型标签) ├── 进程标签:u:r:untrusted_app:s0 ├── 文件标签:u:object_r:app_data_file:s0 └── 套接字标签:u:object_r:netlink_socket:s0 Domain (安全域) ├── untrusted_app:所有第三方应用 ├── platform_app:系统平台应用 ├── system_server:系统服务进程 ├── zygote:Zygote 进程 ├── init:init 进程 └── kernel:内核进程 策略规则(Policy Rule): allow <source_domain> <target_type>:<class> <permissions>; 示例: allow untrusted_app app_data_file:file { read write open }; allow system_server netlink_socket:socket create;
四.2 AOSP 中系统权限定义的源码剖析 <permission android:name ="android.permission.CAMERA" android:permissionGroup ="android.permission-group.CAMERA" android:protectionLevel ="dangerous" android:label ="@string/permlab_camera" android:description ="@string/permdesc_camera" /> <permission android:name ="android.permission.BIND_DEVICE_ADMIN" android:permissionGroup ="android.permission-group.SYSTEM_TOOLS" android:protectionLevel ="signature" android:label ="@string/permlab_bindDeviceAdmin" android:description ="@string/permdesc_bindDeviceAdmin" /> <permission android:name ="android.permission.INSTALL_PACKAGES" android:protectionLevel ="signature|privileged" android:label ="@string/permlab_installPackages" android:description ="@string/permdesc_installPackages" />
四.3 权限检查的完整调用链 应用调用 checkPermission(permission, pid, uid) │ ├─ ContextImpl.checkPermission(permission, pid, uid) │ frameworks/base/core/java/android/app/ContextImpl.java │ ├─ ActivityManager.getService().checkPermission(...) │ Binder IPC 调用到 system_server │ ├─ ActivityManagerService.checkPermission(...) │ frameworks/base/services/core/java/.../ActivityManagerService.java │ ├─ PermissionManagerService.checkPermission(...) │ 或 PermissionManagerService.checkUidPermission(...) │ frameworks/base/services/core/java/.../PermissionManagerService.java │ ├─ 检查步骤: │ 1. 检查 uid 是否为 SYSTEM_UID / ROOT_UID(直接通过) │ 2. 检查 uid 对应的 package 是否声明了该 permission │ 3. 检查 permission 的 protectionLevel │ 4. 对于 dangerous 权限,检查 AppOpsManager 状态 │ 5. 返回 PERMISSION_GRANTED 或 PERMISSION_DENIED │ └─ 可选:AppOpsManager 的额外检查 frameworks/base/core/java/android/app/AppOpsManager.java 更细粒度的权限使用跟踪和限制
四.4 权限提升漏洞案例分析 案例 1:自定义权限 protectionLevel 配置错误
<permission android:name ="com.example.MY_SECRET_ACCESS" android:protectionLevel ="normal" />
案例 2:ContentProvider 权限设置不完整
<provider android:name =".MyProvider" android:readPermission ="com.example.READ" android:authorities ="com.example.provider" android:exported ="true" />
案例 3:Fragment Injection (Pre-Activity)
<activity android:name =".SettingsActivity" android:exported ="true" />
五、最小权限原则在实践中的应用 五.1 权限审计清单 逆向分析时,评估一个应用是否遵循最小权限原则的检查 checklist:
权限审计检查项: [ ] 是否申请了与功能无关的权限? - 一个简单的计算器应用是否申请了 CAMERA 和 CONTACTS? [ ] 是否申请了多个同组的 dangerous 权限? - 申请了 ACCESS_FINE_LOCATION,是否真的需要精确位置? - 是否可以用 ACCESS_COARSE_LOCATION 代替? [ ] 是否有未使用的权限声明? - Manifest 中声明了但代码中从未调用 [ ] 自定义权限的 protectionLevel 是否正确? - 内部通信权限是否设为 signature 而非 normal? [ ] 导出组件是否都有权限保护? - 所有 exported=true 的组件是否有 android:permission? [ ] ContentProvider 的 readPermission 和 writePermission 是否都设置? - 特别注意单边设置的情况 [ ] Intent Filter 是否过于宽泛? - Action 是否过于通用导致被 Intent Spoofing?
五.2 使用代码扫描工具 python androbugs.py -f target.apk -o report/ qark --apk target.apk --report-type html
五.3 AppOps 权限管理框架 Android 4.3 引入了 AppOps(Application Operations)框架,提供比 Permission 更细粒度的权限控制:
adb shell appops get com.target.app adb shell appops set com.target.app CAMERA deny adb shell appops set com.target.app READ_CONTACTS allow
六、AOSP 权限相关关键源码
模块
源码路径
关键内容
系统权限定义
/frameworks/base/core/res/AndroidManifest.xml
所有系统权限的标签、保护级别
ContextImpl
/frameworks/base/core/java/android/app/ContextImpl.java
checkPermission 入口
ActivityManagerService
frameworks/base/services/core/java/.../am/ActivityManagerService.java
checkComponentPermission
PermissionManagerService
frameworks/base/services/core/java/.../pm/permission/PermissionManagerService.java
权限检查核心逻辑
AppOpsManager
/frameworks/base/core/java/android/app/AppOpsManager.java
AppOps 检查框架
AppOpsService
frameworks/base/services/core/java/.../AppOpsService.java
AppOps 服务端实现
PackageParser
frameworks/base/core/java/android/content/pm/PackageParser.java
APK 安装时解析权限
SELinux 策略
/system/sepolicy/
Android SELinux 策略文件
Process
/frameworks/base/core/java/android/os/Process.java
UID/SYSTEM_UID 定义
AID 映射
/system/core/libcutils/fs_config.cpp
用户/组 ID 映射
面试常考问题 Q1: Normal、Dangerous、Signature 权限的区别?Framework 层如何区分处理?
A:
Normal 权限:安装时静默授予,不弹出确认框(如 INTERNET、BLUETOOTH、ACCESS_NETWORK_STATE)。Framework 层在 PackageManagerService 安装应用时检查 protectionLevel,若为 normal 则直接记录为已授予,无需用户交互。
Dangerous 权限:涉及隐私数据,Android 6.0+ 需运行时通过 requestPermissions() 弹窗获取用户授权(如 CAMERA、CONTACTS、LOCATION)。Framework 层维护了一个权限授权状态表(runtime permission grant),每次 checkPermission 调用时需要同时检查 Manifest 声明 + 动态授权状态 + AppOps 状态。若 Manifest 声明了但未动态授权,返回 PERMISSION_DENIED。
Signature 权限:仅在请求应用的签名与声明该权限的系统应用签名相同时授予。Framework 层在应用安装时,PackageManagerService 计算请求者 APK 的签名证书指纹,与声明该权限的包的签名比较。如果签名不匹配,即使写入 Manifest 也不会被授予。
此外还有 Signature|Privileged 级别:需要同签名且位于 /system/priv-app/,用于更敏感的系统级权限(如 INSTALL_PACKAGES)。
AOSP 中对应的处理逻辑分布在:
/frameworks/base/services/core/java/com/android/server/pm/permission/PermissionManagerService.java(grantPermissions 方法)
/frameworks/base/core/java/android/content/pm/PackageParser.java(解析 Manifest 中的 uses-permission 声明)
Q2: 权限检查在 Framework 和 Kernel 层分别如何实现?两者之间的关系是什么?
A:
Framework 层: 在 PackageManager.checkPermission() → ActivityManagerService.checkPermission() 调用链中完成。检查逻辑包括:(1)系统 UID/Root UID 直接放行;(2)调用 PermissionManagerService.checkUidPermission() 遍历 uid 下所有包,逐一检查是否有该权限的授权记录;(3)对于 dangerous 权限,额外检查 AppOpsManager 状态(用户可能通过设置页面临时关闭权限);(4)对于 isolated process 直接拒绝。源码位置:/frameworks/base/services/core/java/com/android/server/pm/permission/PermissionManagerService.java。
Kernel 层: 通过两层机制实现:(1)Linux DAC:基于 UID/GID 的文件系统和进程访问控制。每个应用运行在唯一的 UID 下,声明的权限映射为附加的 GID(如 INTERNET 权限 → inet GID 3003)。进程能否访问网络 socket 取决于其是否在 inet 组中。(2)SELinux MAC:基于安全策略的强制访问控制,即使 DAC 允许的操作也可能被 SELinux 拒绝。SELinux 策略在 /system/sepolicy/ 中定义,allow untrusted_app app_data_file:file { read write } 控制第三方应用的数据文件访问。
两者的关系:
Framework 层是”守门员”,决定用户空间中哪些调用能被放行。
Kernel 层是”地基”,Framework 的权限最终通过 UID/GID 和 SELinux 策略映射为底层能力。
Framework 层的检查可以被 Java 层面的 Hook(如 Xposed)绕过,但 Kernel 层的限制更难绕过(需要修改内核或 SELinux 策略)。
两者配合形成纵深防御:即使 Framework 层被攻破,Kernel 层 SELinux 仍然可以阻止越权操作(如从普通 App 读取系统级文件)。
Q3: 如何检测一个应用是否存在权限提升漏洞?
A:
检测步骤和方法:
(1)检查导出组件权限保护:
使用 aapt dump xmltree app.apk AndroidManifest.xml | grep -A 3 "activity\|service\|receiver\|provider" 列出所有组件,检查 exported 和 permission 属性。
重点检查:exported=true 但没有 permission 的组件;ContentProvider 的 readPermission/writePermission 是否成对设置。
(2)检查自定义权限的 protectionLevel:
在 Manifest 中搜索 <permission> 标签,检查 protectionLevel 是否合理。
如果 protectionLevel 为 normal(甚至未指定,默认为 normal),但用于保护敏感组件 → 存在风险。
如果 protectionLevel 为 signature 但权限名称暗示内部使用,检查是否真的只有同签名应用能获取。
(3)检查 Intent Filter 的精确性:
过于宽泛的 action(如自定义 action 但未做发送者校验)→ 可能被 Intent Spoofing。
BroadcastReceiver 接收到 Intent 后是否有权限校验(很多 BroadcastReceiver 不做验证就处理数据)。
(4)检查 debuggable 标志:
aapt dump badging app.apk | grep debuggable → 如果为 true,攻击者可以通过 adb run-as 访问应用数据。
(5)检查 backup 标志:
aapt dump badging app.apk | grep backup → allowBackup=true 允许通过 adb backup 导出应用数据。
(6)自动化工具辅助:
MobSF、QARK、drozer、AndroBugs Framework 等工具可以自动化检测上述大多数问题。
(7)常见漏洞模式:
恶意应用调用无权限保护的导出 Activity 进行意图劫持
自定义权限 protectionLevel 为 normal 导致任何应用都能获取敏感权限
ContentProvider 只设置了 readPermission 但 writePermission 留空
BroadcastReceiver 无条件处理外部 Intent 未验证调用者身份
自定义权限定义了两个不同的包都声明了(权限保护失效)
Q4: SELinux 在 Android 权限体系中扮演什么角色?为什么即使绕过 Framework 层的权限检查,SELinux 还能保护系统?
A:
SELinux 是 Linux 内核的强制访问控制(MAC)模块,在 Android 5.0+ 默认以 Enforcing 模式运行。它独立于 Framework 层的权限检查,提供最底层的安全约束。
工作机制: (1)每个进程和系统资源(文件、socket、设备等)都被分配了 SELinux 安全上下文(Security Context),格式为 user:role:type:mls。 (2)内核中的 SELinux LSM(Linux Security Module)在每个系统调用时检查策略规则,决定是否放行。 (3)策略规则定义在 /system/sepolicy/ 的 .te 文件中,格式为 allow <source_type> <target_type>:<object_class> <permissions>。
为什么能”兜底”: (1)Framework 权限检查和 SELinux 策略检查是相互独立的两层。Framework 层的 checkPermission 返回 PERMISSION_GRANTED 只是 Java 层的判断,实际执行操作时内核仍然执行 SELinux 检查。
(2)示例:假设通过 Xposed 绕过了 Framework 层的权限检查,应用试图读取 /data/system/packages.xml。虽然 Framework 层被绕过,但 SELinux 策略中规定 allow untrusted_app system_data_file:file read 不被允许,内核仍然会拒绝该操作。
(3)SELinux 的 bypass 难度远高于 Framework 层:需要修改内核、修改 sepolicy 文件(需要 root)、或利用内核漏洞将 SELinux 降为 Permissive 模式。
(4)关键限制:SELinux 只能保护其定义了策略的资源。如果某个操作在策略中被允许(如 allow untrusted_app app_data_file:file read write 允许应用读写自己的数据),SELinux 就不会阻止。
AOSP 源码位置:
/system/sepolicy/ 下的 untrusted_app.te(第三方应用域策略)
/kernel/common/security/selinux/ 中的 LSM 实现
/external/selinux/ 中的用户空间工具
Q5: Android 权限从安装时到运行时的演进(API 23)在技术实现上做了哪些改动?为什么这个改动对逆向分析有影响?
A:
技术实现改动:
(1)安装时变化(PackageManagerService):
API 22 及之前:PMS 在安装时解析 Manifest 中所有 uses-permission,对于 dangerous 权限直接标记为 granted。
API 23 及之后:PMS 在安装时解析权限声明,但对于 dangerous 权限,初始状态标记为 “仅声明,未授权”(granted=false)。只有 normal 和 signature 权限在安装时自动授予。
(2)权限授权状态存储:
新增了 Runtime Permission State 数据结构,存储在 /data/system/users/<user_id>/runtime-permissions.xml 中。
每个应用的每个 dangerous 权限独立跟踪:是否已提示用户、用户选择、是否被 AppOps 覆盖。
(3)新增运行时交互 API:
Activity.requestPermissions(String[] permissions, int requestCode) → 发起权限请求弹窗
Context.checkSelfPermission(String permission) → 检查自身是否有权限
Activity.shouldShowRequestPermissionRationale(String permission) → 判断是否需要显示权限解释
Activity.onRequestPermissionsResult(int, String[], int[]) → 接收用户选择结果
(4)AppOps 集成:
权限检查不再只依赖 PackageManager,还集成 AppOpsManager。
AppOps 记录每个权限操作的历史(允许/拒绝次数、上次时间),用户可以随时在设置中修改。
对逆向分析的影响: (1)静态分析不足:单看 Manifest 中的权限声明无法判断应用是否真的获得该权限,必须运行时验证。 (2)Hook 点增多:除了传统的 checkPermission,还需要 Hook requestPermissions、onRequestPermissionsResult、AppOpsManager.checkOp 才能完整控制权限状态。 (3)权限请求混淆:应用可以在不同时机请求不同权限,难以通过一次静态扫描理解完整的权限使用模式。 (4)测试复杂性:每个权限组合(授予/拒绝/询问中)对应不同的应用行为路径,逆向分析时需要遍历所有情况。 (5)绕过手段增多:由于权限状态现在存储在用户空间 XML 文件中(而非内核),可以通过修改 /data/system/users/<id>/runtime-permissions.xml 或直接 Hook AppOpsManager 的 checkOp 返回值来模拟任意权限拥有状态。