📌 问题背景
在开发定制 Android 设备时,我们选择使用 MicroG 替代 Google 移动服务(GMS),以避免 Google 认证和授权的复杂流程。然而,在设备上遇到了一个严重问题:
核心问题
- YouTube 无法打开:点击后闪退或提示”需要 Google Play 服务”
- 其他 Google 应用异常:Gmail、Google Maps 等应用无法正常工作
- 签名验证失败:应用检测到 MicroG 不是真正的 GMS,拒绝运行
什么是 MicroG?
MicroG 是一个开源的 Google 移动服务替代方案,它:
- 提供与 GMS 兼容的 API 接口
- 体积更小,隐私保护更好
- 无需 Google 授权即可使用
- 但需要签名伪装才能被应用识别为真正的 GMS
🔍 问题分析
1. 签名验证机制
Android 应用通过以下方式验证 GMS 的真实性:
// 应用检查 GMS 签名
PackageInfo gmsInfo = pm.getPackageInfo("com.google.android.gms",
PackageManager.GET_SIGNATURES);
Signature[] signatures = gmsInfo.signatures;
// 验证签名是否匹配 Google 官方签名
if (!isGoogleSignature(signatures)) {
throw new SecurityException("Invalid GMS signature");
}
问题:
- MicroG 使用自己的签名,与 Google 官方签名不同
- 应用检测到签名不匹配,拒绝运行
- 需要在系统层面”伪装”签名,让应用误以为 MicroG 就是真正的 GMS
2. 原有实现的不足
之前的签名伪装实现存在以下问题:
问题 1:权限检查时未使用伪装签名
// 原有代码:直接使用包的真实签名
@Override
public boolean checkSignatures(String packageName1, String packageName2) {
final PackageSetting ps1 = mSettings.getPackageLPr(packageName1);
final PackageSetting ps2 = mSettings.getPackageLPr(packageName2);
// 直接比较真实签名,导致权限检查失败
return ps1.signatures.mSigningDetails.checkCapability(
ps2.signatures.mSigningDetails, PERMISSION);
}
后果:
- MicroG 申请系统权限时被拒绝
- 无法获取必要的特权权限
- 功能受限,应用无法正常工作
问题 2:系统权限验证绕过伪装
// 原有代码:平台签名验证未考虑伪装
boolean isPrivileged(AndroidPackage pkg) {
// 直接使用真实签名与平台签名比较
return pkg.getSigningDetails().hasAncestorOrSelf(
mPlatformPackage.getSigningDetails());
}
问题 3:dumpsys 输出泄露真实签名
# dumpsys package 输出显示真实签名
$ adb shell dumpsys package com.google.android.gms
Package [com.google.android.gms]
signatures=PackageSignatures{abc123 [真实的 MicroG 签名]}
# 应用可以通过 dumpsys 检测到真实签名
💡 完整解决方案
架构设计
┌─────────────────────────────────────────────────┐
│ 应用层 (YouTube, Gmail, etc.) │
│ ↓ 调用 GMS API │
├─────────────────────────────────────────────────┤
│ PackageManager (签名伪装层) │
│ ┌───────────────────────────────────────┐ │
│ │ 1. 白名单检查 │ │
│ │ 2. 读取 fake_signature 资源 │ │
│ │ 3. 返回伪装后的签名 │ │
│ └───────────────────────────────────────┘ │
│ ↓ 实际签名 │
├─────────────────────────────────────────────────┤
│ MicroG (真实签名: MicroG 自签名) │
└─────────────────────────────────────────────────┘
核心实现步骤
1. 配置白名单
在系统资源中定义允许签名伪装的包名:
<!-- android/frameworks/base/core/res/res/values/config.xml -->
<resources>
<!-- 允许签名伪装的包名列表 -->
<string-array name="config_signatureSpoofingAllowedPackages" translatable="false">
<item>com.google.android.gms</item>
<item>com.google.android.gsf</item>
<item>com.android.vending</item>
</string-array>
</resources>
2. 授予伪装权限
为 MicroG 预授权签名伪装权限:
<!-- android/device/rockchip/rk3588/permissions/privapp-permissions-microg.xml -->
<permissions>
<privapp-permissions package="com.google.android.gms">
<!-- 签名伪装权限 -->
<permission name="android.permission.FAKE_PACKAGE_SIGNATURE"/>
<!-- 其他必要的系统权限 -->
<permission name="android.permission.INSTALL_PACKAGES"/>
<permission name="android.permission.DELETE_PACKAGES"/>
<permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
<permission name="android.permission.WRITE_SECURE_SETTINGS"/>
</privapp-permissions>
</permissions>
3. 实现签名伪装核心逻辑
// PackageManagerService.java
/**
* 获取包的签名详情,对白名单中的包返回伪装签名
*/
private SigningDetails getSigningDetails(String packageName) {
synchronized (mLock) {
final PackageSetting ps = mSettings.getPackageLPr(packageName);
if (ps == null || ps.pkg == null) {
return null;
}
// 检查是否在白名单中
if (!isPackageWhitelistedForSpoofing(packageName)) {
return ps.signatures.mSigningDetails;
}
// 检查是否有伪装权限
if (!hasPermission(packageName, "android.permission.FAKE_PACKAGE_SIGNATURE")) {
return ps.signatures.mSigningDetails;
}
// 检查 targetSdkVersion(安全限制)
if (ps.pkg.getTargetSdkVersion() <= Build.VERSION_CODES.LOLLIPOP_MR1) {
return ps.signatures.mSigningDetails;
}
// 从 APK 资源中读取伪装签名
String fakeSignature = readFakeSignatureFromApk(ps.pkg);
if (fakeSignature != null && !fakeSignature.isEmpty()) {
// 构造伪装的签名对象
Signature[] fakeSignatures = new Signature[] {
new Signature(fakeSignature)
};
return new SigningDetails(
fakeSignatures,
SigningDetails.SignatureSchemeVersion.UNKNOWN
);
}
// 如果没有伪装签名,返回真实签名
return ps.signatures.mSigningDetails;
}
}
4. 修复权限检查逻辑
// PackageManagerService.java
@Override
public boolean checkSignatures(String packageName1, String packageName2) {
synchronized (mLock) {
// 使用 getSigningDetails 确保伪装签名生效
SigningDetails sig1 = getSigningDetails(packageName1);
SigningDetails sig2 = getSigningDetails(packageName2);
if (sig1 == null || sig2 == null) {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
// 比较签名(可能是伪装的)
return compareSignatures(sig1, sig2);
}
}
/**
* 检查包是否有平台签名(特权应用)
*/
private boolean isPrivileged(String packageName) {
synchronized (mLock) {
// 使用 getSigningDetails 确保伪装签名生效
SigningDetails pkgSigningDetails = getSigningDetails(packageName);
if (pkgSigningDetails == null) {
return false;
}
// 检查是否有平台签名
return pkgSigningDetails.hasAncestorOrSelf(
mPlatformPackage.getSigningDetails());
}
}
5. 修复 dumpsys 输出泄露
// Settings.java
void dumpPackageLPr(PrintWriter pw, String prefix, PackageSetting ps) {
pw.print(prefix); pw.print(" signatures=");
// 对白名单包使用伪装签名输出
boolean printedFakeSignature = false;
if (ps.pkg != null && shouldUseFakeSignature(ps.pkg.getPackageName())) {
String fakeSignature = readFakeSignatureFromApk(ps.pkg);
if (fakeSignature != null && !fakeSignature.isEmpty()) {
// 输出伪装签名
pw.println("[Fake Signature]");
printedFakeSignature = true;
}
}
// 如果没有打印伪装签名,打印真实签名
if (!printedFakeSignature) {
pw.println(ps.signatures);
}
}
🎯 效果验证
修改前
# YouTube 无法启动
$ adb shell am start -n com.google.android.youtube/.HomeActivity
Error: GMS signature verification failed
# dumpsys 显示真实签名
$ adb shell dumpsys package com.google.android.gms | grep signatures
signatures=PackageSignatures{MicroG真实签名}
修改后
# YouTube 正常启动
$ adb shell am start -n com.google.android.youtube/.HomeActivity
Starting: Intent { cmp=com.google.android.youtube/.HomeActivity }
# dumpsys 显示伪装签名
$ adb shell dumpsys package com.google.android.gms | grep signatures
signatures=PackageSignatures{Google官方签名}
# 权限检查通过
$ adb shell dumpsys package com.google.android.gms | grep permission
android.permission.FAKE_PACKAGE_SIGNATURE: granted=true
📊 技术要点总结
1. 安全机制设计
| 机制 | 作用 | 实现方式 |
|---|---|---|
| 白名单 | 限制可伪装的包 | 系统资源配置 |
| 权限检查 | 防止滥用 | FAKE_PACKAGE_SIGNATURE |
| SDK 版本限制 | 兼容性保护 | targetSdkVersion > 21 |
| 资源读取 | 动态签名 | 从 APK 读取 fake_signature |
2. 关键技术点
签名读取流程
APK 文件
↓
AssetManager.addAssetPath()
↓
Resources.getIdentifier("fake_signature")
↓
Resources.getString(resId)
↓
new Signature(fakeSignatureString)
↓
返回伪装的 SigningDetails
三层防护
- 白名单检查:只有配置的包才能伪装
- 权限验证:必须有 FAKE_PACKAGE_SIGNATURE 权限
- 版本限制:targetSdkVersion 必须 > 21
3. 与其他方案对比
| 方案 | 优点 | 缺点 |
|---|---|---|
| 系统级伪装(本方案) | 完全透明、兼容性好 | 需要修改系统源码 |
| Xposed 模块 | 无需编译系统 | 需要 root、稳定性差 |
| 修改 APK 签名 | 简单直接 | 破坏完整性、无法更新 |
| Lucky Patcher | 用户友好 | 不稳定、容易被检测 |
💻 代码位置
本次优化涉及以下文件:
- 系统配置:
android/frameworks/base/core/res/res/values/config.xml - 权限配置:
android/device/rockchip/rk3588/permissions/privapp-permissions-microg.xml - 包管理服务:
android/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java - 设置管理:
android/frameworks/base/services/core/java/com/android/server/pm/Settings.java - 设备配置:
android/device/rockchip/rk3588/device.mk
🚀 部署步骤
1. 修改系统源码
按照上述代码修改系统文件。
2. 编译系统
cd android
source build/envsetup.sh
lunch rk3588_xxx-userdebug
make -j16
3. 安装 MicroG
将 MicroG APK 预置到 /system/priv-app/ 目录。
4. 验证功能
# 检查权限
adb shell dumpsys package com.google.android.gms | grep FAKE_PACKAGE_SIGNATURE
# 测试 YouTube
adb shell am start -n com.google.android.youtube/.HomeActivity
📝 总结
通过这次系统级签名伪装的完善,我们成功解决了 MicroG 无法运行 YouTube 的问题。
核心成果
- 完整的伪装机制:覆盖签名查询、权限检查、dumpsys 输出
- 安全性保障:白名单 + 权限 + 版本三重防护
- 兼容性优秀:YouTube、Gmail 等应用正常运行
- 代码质量:遵循 Android 框架设计规范
技术启示
- 系统安全:签名验证是 Android 安全的基石
- 框架设计:需要在多个层面保持一致性
- 开源精神:MicroG 为隐私保护提供了可能
- 工程实践:细节决定成败,dumpsys 输出也不能忽视
这个方案不仅解决了当前问题,也为后续支持更多 Google 应用奠定了基础。
🏷️ 标签
#Android系统开发 #签名伪装 #MicroG #GMS #PackageManager #系统安全 #RK3588
作者:[你的名字]
日期:2025-11-22
平台:RK3588 Android 12 系统
提交记录:d8ebd503119
如果这篇文章对你有帮助,欢迎点赞、收藏、关注!有任何问题欢迎在评论区讨论交流。
8036

被折叠的 条评论
为什么被折叠?



