META-INF 目录
- APK 包中有个“META_INF”目录,里面存储了一些与 APK 签名有关的信息:
- 执行如下命令,可解压签名信息:
CERT.RSA
- CERT.RSA 文件中存放了 APK 的开发者证书与签名信息。通过该文件可试别开发者的身份,以及判断 APK 是否被篡改。CERT.RSA 文件是由 DER 编码的证书。由于在 DER 内部使用了 ASN1 进行编码,使用任何 ASN1 解码库都能对其进行解码,如 GNU 的 libtasn1 库。这里使用 OpenSSL 提供的解码功能来查看 CERT.RSA 的证书内容
- OpenSSL 是一款跨平台的加解密库管理套件
- 安装:
- Windows:访问 OpenSSL 官网下载完整的可执行文件包,或在 Cygwin 中搜索安装
- Ubuntu:执行
sudo apt-get install openssl
命令 - macOS:执行
brew install openssl
- 安装完成后,执行如下命令可查看 CERT.RSA 中开发者证书的内容:
- 输出的证书信息是 APK 合法和有效的凭证,在对 APK 进行保护时,其中的许多项都是用来鉴别 APK 是否已被修改的有力证据
MANIFEST.MF
- MANIFEST.MF 是签名的清单文件,是一个文本文件,内容:
cat META-INF/MANIFEST.MF | less
- 可看出,打包该文件的工具是 Android Gradle 3.5.3,下面的每组信息都包括 Name 和 SHA1-Digest,表示 APK 中每个文件的路径和它的 SHA-1 散列值的 Base64 值
- 以 res/layout/notification_action.xml 文件为例,执行下述命令,对比它的值:
- “vUd0FW2wrq1c1vqQxRuZryvEKPg=” 与 MANIFEST.MF 文件中的值是一样的。这证明了在 MANIFEST.MF 中存放的是 APK 中所有包含的文件列表的 SHA-1 散列值的 Base64 值,从而保证了在进行 APK 签名验证时 APK 中所有文件均未被修改
CERT.SF
- CERT.SF 是签名信息文件,也是一个文本文件,内容:
cat META-INF/CERT.SF | less
- 上面的每组信息也包括 Name 和 SHA1-Digest,且 Name 和 MANIFEST.MF 中的值相同,而 SHA1-Digest 的值却不同,这是因为此处每一项的 SHA1-Digest 是对 MANIFEST 中的每一项进行了如下计算(仍以 res/layout/notification_action.xml 为例):
- 先将 MANIFEST.MF 中的这一项(注意:末尾要加两个“\r\n”)保存文件,再计算这个文件的 SHA-1 散列值(在保存此文件的目录按 shift + 鼠标右键,运行 powerShell,再以如图所示的命令计算 SHA-1):
Name: res/layout/notification_action.xml
SHA1-Digest: vUd0FW2wrq1c1vqQxRuZryvEKPg=
- SHA-1 散列值为:91a49699d35630198834f54fb4bab4d3313e79fe
- 再计算其 Base64 值:
- 将此值与 CERT.SF 中这一项的 SHA1-Digest 比较,会发现相同
- 另外,CERT.SF 文件的开头部分多了一个 SHA1-Digest-Manifest 的值。其实,SHA1-Digest-Manifest 的值就是对 MANIFEST.MF 文件内容的 SHA-1 散列值进行 Base64 计算得到的结果,执行如下命令即可证实:
- 在 CERT.SF 中,有一个重要字段 X-Android-APK-Signed,它的值为 2,表示使用新版本 APK Signature Scheme v2 进行签名。v2 签名信息不同于以往在 Android 开发中使用 SignApk 得到的结果。AS 在新版本的 SDK 调构建工具中增加了一个签名工具 apksigner,它同时支持旧版本的 v1 签名和在 Android 7.0 中引入的 v2 签名
- 使用低版本的 SignApk 对 Crackme0201.apk 进行签名,生成的 CERT.SF 如下:
- 可看到,每个 Name 所对应的 SHA1-Digest值与之前输出的内容不一样。但 MANIFEST.MF 的内容没变化:
- 这是因为低版本的 SignApk 在对 APK 签名并生成 CERT.SF 时,会对每栏中的 Name 和 SHA1-Digest 的值单独计算签名,然后将每个签名结果写入 CERT.SF 的 SHA1-Digest 字段。这样做的结果是要逐个验证 APK 中的文件,就会导致在验证 APK 这个步骤上花费很长时间