Skip to content

签名安全漏洞

结合 Android 17 验证源码理解 Master Key、FakeID、Janus 等历史攻击面的防御边界。

AndroidPMS签名安全

签名安全漏洞 ​

Android 签名漏洞的共同教训是:验证器、ZIP 解析器、运行时加载器和权限授予者必须对同一份内容形成一致结论。本文不复刻历史漏洞利用代码,而是把 Master Key、FakeID、Janus 等攻击面映射到 Android 17 仍在运行的防御链:APK 签名方案选择、ZIP 条目解析、证书链/签名算法验证、完整性校验和 signer capability。

1. 攻击面分层 ​

攻击面历史问题当前主要防线
ZIP 重复条目Master Key严格 JAR/ZIP 解析、v2+ 全文件签名
DEX 与 ZIP 双重解析Janusv2/v3 全文件保护、最低签名方案
证书链信任错误FakeIDX.509/签名算法验证、v2/v3 signer 验证
signer 身份替换伪造更新SigningDetails.checkCapability、lineage
密钥轮换滥用旧 signer 能力过宽capability flags、回滚/权限分离

2. 当前统一验证入口 ​

源码文件:frameworks/base/core/java/android/util/apk/ApkSignatureVerifier.java

java
public static ParseResult<SigningDetails> verify(
        ParseInput input, String apkPath,
        @SignatureSchemeVersion int minSignatureSchemeVersion) {
    return verifySignatures(
            input, apkPath, minSignatureSchemeVersion,
            true /* verifyFull */);
}

private static ParseResult<SigningDetailsWithDigests>
verifySignaturesInternal(
        ParseInput input, String apkPath,
        @SignatureSchemeVersion int minSignatureSchemeVersion,
        boolean verifyFull) {
    if (minSignatureSchemeVersion
            > SignatureSchemeVersion.SIGNING_BLOCK_V4) {
        return input.error(
                INSTALL_PARSE_FAILED_NO_CERTIFICATES,
                "No signature found in package of version "
                        + minSignatureSchemeVersion + " or newer");
    }
    try {
        return verifyV4Signature(
                input, apkPath, minSignatureSchemeVersion, verifyFull);
    } catch (SignatureNotFoundException e) {
        if (minSignatureSchemeVersion
                >= SignatureSchemeVersion.SIGNING_BLOCK_V4) {
            return input.error(
                    INSTALL_PARSE_FAILED_NO_CERTIFICATES,
                    "No APK Signature Scheme v4 signature in package",
                    e);
        }
    }

当前入口先尝试 v4;只有最低版本允许降级时才继续 v3、v2、v1。防御重点是“最低版本约束不能被降级路径绕过”:调用方要求 v3 时,只有 v2 的 APK 不会被接受。

3. Master Key ​

Master Key 类攻击利用 ZIP 允许重复文件名、而不同读取路径选择不同副本的问题。若签名验证看到合法副本、运行时加载到恶意副本,签名就失去了“代码未被替换”的意义。

Android 17 仍保留严格的 JAR/APK 读取路径;更重要的是 v2/v3 签名将签名保护范围扩展到 APK 的整体结构,攻击者对 ZIP 条目或目录布局的修改会破坏完整性摘要。

java
// frameworks/base/core/java/android/util/jar/StrictJarFile.java
// 通过严格的 APK/JAR 读取路径遍历签名相关条目
StrictJarFile jarFile = new StrictJarFile(
        fileName,
        verify,
        signatureSchemeRollbackProtectionsEnforced);

在 Android 17 中,不能把普通 ZIP 遍历结果当作签名验证结果;安装、解析和 dex 相关路径使用 framework 的严格读取与签名验证组合。排查重复条目时应同时检查签名方案和 APK 实际解析器,而不是只看 META-INF。

4. Janus ​

Janus 利用 v1 签名只覆盖 ZIP 条目摘要、而运行时可能从 APK 前缀解释 DEX 的差异。v2/v3 通过 APK Signing Block 对文件内容提供整体保护,Android 17 的 verifier 也允许调用方指定最低 scheme。

java
private static ParseResult<SigningDetailsWithDigests>
verifyV3AndBelowSignatures(
        ParseInput input, String apkPath,
        int minSignatureSchemeVersion, boolean verifyFull) {
    try {
        return verifyV3Signature(input, apkPath, verifyFull);
    } catch (SignatureNotFoundException e) {
        if (minSignatureSchemeVersion
                >= SignatureSchemeVersion.SIGNING_BLOCK_V3) {
            return input.error(
                    INSTALL_PARSE_FAILED_NO_CERTIFICATES,
                    "No APK Signature Scheme v3 signature in package",
                    e);
        }
    }
    // 只有最低版本允许时才继续 v2/v1
    return verifyV2OrV1(...);
}

当前防线不是“所有 APK 都必须 v3”,而是由 target SDK/安装场景计算最低方案;一旦调用方要求 v2 或更高,v1-only 包会失败。安全审计应先确认最低 scheme 的来源,再判断是否存在降级空间。

5. FakeID ​

FakeID 攻击面在于把伪造的证书链当成可信身份,从而命中特权组件的证书判断。当前 v2/v3 verifier 不只提取 X.509 证书,还验证签名算法、签名值、内容摘要和 signer 结构;v3 的 Proof-of-Rotation 还要求每一级由前一级签名背书。

java
// frameworks/base/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
VerifiedSigner signer = verifySigner(
        signerBlock, signatureInfo.contentDigests);
verifyIntegrity(
        signatureInfo, apk,
        signer.contentDigests);

verifySigner 验证 signer 内部签名与证书,verifyIntegrity 再根据 content digest 校验 APK 内容。证书主体名称或包名字符串不是信任根;PMS 后续使用的是验证成功后生成的 SigningDetails。

6. Lineage 防复用 ​

源码文件:frameworks/base/core/java/android/content/pm/SigningDetails.java

java
public boolean checkCapability(
        @NonNull SigningDetails oldDetails,
        @CertCapabilities int flags) {
    if (this == UNKNOWN || oldDetails == UNKNOWN) {
        return false;
    }
    if (oldDetails.mSignatures.length > 1) {
        return signaturesMatchExactly(oldDetails);
    }
    boolean matchFound = hasCertificate(
            oldDetails.mSignatures[0], flags);
    if (!matchFound) return false;
    if (isV32Hybrid() || oldDetails.isV32Hybrid()) {
        return checkV32HybridCapability(oldDetails, flags);
    }
    return true;
}

历史 signer 只有被授予目标 capability 才能继续使用旧能力;多 signer 不能借单 signer lineage 放行;hybrid signer 还要防止只复用一把 key。这样可以把“证书出现在历史链中”和“证书仍被允许执行某动作”分开。

7. 能力分离 ​

java
if (!parsed.checkCapability(
        old, SigningDetails.CertCapabilities.INSTALLED_DATA)
        && !old.checkCapability(
                parsed, SigningDetails.CertCapabilities.ROLLBACK)) {
    throw new PrepareFailure(
            INSTALL_FAILED_UPDATE_INCOMPATIBLE,
            "New package has a different signature");
}

更新使用 INSTALLED_DATA,回滚使用 ROLLBACK,签名权限使用 PERMISSION,shared UID 使用 SHARED_USER_ID。如果所有动作共享一个“签名相等”判断,密钥轮换就会把数据、权限、shared UID 和回滚信任一起扩大;能力位拆分正是当前防御的核心。

8. 当前防御 ​

  1. 方案门槛:最低签名方案阻止 v1-only APK 在要求 v2/v3 的场景降级。
  2. 整体完整性:v2/v3 content digest 覆盖 APK 内容和结构,减少签名验证/加载器分歧。
  3. 证书数学验证:不以证书文本、subject 或包名作为信任依据。
  4. lineage 防环:历史 signer 重复或非法链会在 PoR/SigningDetails 阶段失败。
  5. 能力隔离:更新、回滚、权限、shared UID 使用不同 capability。
  6. 安装时再次比较:解析出 SigningDetails 后仍要与已安装包、系统包和 shared UID 状态比对。

9. 漏洞排查方法 ​

  1. 先确定攻击面是 ZIP 解析、签名块、证书链、lineage 还是运行时组件权限。
  2. 检查 APK 实际使用的最高签名方案和调用方要求的最低方案。
  3. 比较验证器生成的 SigningDetails,不要只读取 META-INF 文件名。
  4. 对更新攻击检查 INSTALLED_DATA/ROLLBACK,对权限提升检查 PERMISSION。
  5. 对 shared UID 或 platform key 检查 SHARED_USER_ID 和 platform signer 关系。
  6. 若涉及增量安装,另外检查 v4/idsig/fs-verity,不要把它与 APK signer 混为一谈。

10. 源码阅读路线 ​

  1. ApkSignatureVerifier:方案选择与最低版本。
  2. v2/v3 verifier:签名块、内容摘要和证书链验证。
  3. StrictJarFile:v1 ZIP/JAR 解析和历史重复条目防御。
  4. SigningDetails:lineage、capability 和 hybrid signer 约束。
  5. InstallPackageHelper / PackageManagerServiceUtils.verifySignatures:安装更新消费。
  6. AppIdPermissionPolicy:签名权限消费。

历史漏洞留下的关键经验是:签名验证必须保护“实际被解释和执行的内容”,证书链必须有密码学背书,密钥轮换必须按动作拆分能力。Android 17 的多层 verifier 和 capability 判断,正是这些防御原则在源码中的具体落点。