-
通过函数重载,最终的verify()接受三个参数:apk的二进制文件数据对象,一个SignatureInfo对象,是否检查完整性的bool。并返回一个VerifiedSigner的对象
-
其中传递的SignatureInfo对象,是由ApkSigningBlockUtils类中的findSignature()获得
-
可见plsCertsNoVerifyOnlyCerts()与verify()的区别是完整性校验的bool值不同,最终调用的函数殊途同归,这个值最终会判断是否跳过位于ApkSigningBlockUtils类中的verifyIntegrity()方法的校验
所以为了继续分析这个verify()我们先要知道SignatureInfo这个对象是什么
ApkSigningBlockUtils.findSignature()
frameworks/base/core/java/android/util/apk/ApkSigningBlockUtils.java
static SignatureInfo findSignature(RandomAccessFile apk, int blockId)
throws IOException, SignatureNotFoundException {
// Find the ZIP End of Central Directory (EoCD) record.
Pair<ByteBuffer, Long> eocdAndOffsetInFile = getEocd(apk);
ByteBuffer eocd = eocdAndOffsetInFile.first;
long eocdOffset = eocdAndOffsetInFile.second;
if (ZipUtils.isZip64EndOfCentralDirectoryLocatorPresent(apk, eocdOffset)) {
throw new SignatureNotFoundException(“ZIP64 APK not supported”);
}
// Find the APK Signing Block. The block immediately precedes the Central Directory.
long centralDirOffset = getCentralDirOffset(eocd, eocdOffset);
Pair<ByteBuffer, Long> apkSigningBlockAndOffsetInFile =
findApkSigningBlock(apk, centralDirOffset);
ByteBuffer apkSigningBlock = apkSigningBlockAndOffsetInFile.first;
long apkSigningBlockOffset = apkSigningBlockAndOffsetInFile.second;
// Find the APK Signature Scheme Block inside the APK Signing Block.
ByteBuffer apkSignatureSchemeBlock = findApkSignatureSchemeBlock(apkSigningBlock,
blockId);
return new SignatureInfo(
apkSignatureSchemeBlock,
apkSigningBlockOffset,
centralDirOffset,
eocdOffset,
eocd);
}
到这里就已经真的开始对整个apk文件进行检查了,通过获得apk尾部的EOCD块中获得中央目录的偏移,由中央目录开始处往上找24个字节,获取8个字节的小端长整型,这个值即为签名块的长度减8。不过这个长度的值是从签名块开始到中央目录开始,所以这里要从中央目录开始处往前跳转找到签名块的偏移。这里主要看到findApkSigningBlock(),findApkSignatureSchemeBlock()这两个函数
ApkSigningBlockUtils.findApkSigningBlock()
frameworks/base/core/java/android/util/apk/ApkSigningBlockUtils.java
private static final long APK_SIG_BLOCK_MAGIC_HI = 0x3234206b636f6c42L;
private static final long APK_SIG_BLOCK_MAGIC_LO = 0x20676953204b5041L;
private static final int APK_SIG_BLOCK_MIN_SIZE = 32;
static Pair<ByteBuffer, Long> findApkSigningBlock(
RandomAccessFile apk, long centralDirOffset)
throws IOException, SignatureNotFoundException {
// FORMAT:
// OFFSET DATA TYPE DESCRIPTION
// * @+0 bytes uint64: size in bytes (excluding this field)
// * @+8 bytes payload
// * @-24 bytes uint64: size in bytes (same as the one above)
// * @-16 bytes uint128: magic
if (centralDirOffset < APK_SIG_BLOCK_MIN_SIZE) {
throw new SignatureNotFoundException(
"APK too small for APK Signing Block. ZIP Central Directory offset: "
- centralDirOffset);
}
// Read the magic and offset in file from the footer section of the block:
// * uint64: size of block
// * 16 bytes: magic
ByteBuffer footer = ByteBuffer.allocate(24);
footer.order(ByteOrder.LITTLE_ENDIAN);
apk.seek(centralDirOffset - footer.capacity());
apk.readFully(footer.array(), footer.arrayOffset(), footer.capacity());
if ((footer.getLong(8) != APK_SIG_BLOCK_MAGIC_LO)
|| (footer.getLong(16) != APK_SIG_BLOCK_MAGIC_HI)) {
throw new SignatureNotFoundException(getLengthPrefixedSlice
“No APK Signing Block before ZIP Central Directory”);
}
// Read and compare size fields
long apkSigBlockSizeInFooter = footer.getLong(0);
if ((apkSigBlockSizeInFooter < footer.capacity())
|| (apkSigBlockSizeInFooter > Integer.MAX_VALUE - 8)) {
throw new SignatureNotFoundException(
"APK Signing Block size out of range: " + apkSigBlockSizeInFooter);
}
int totalSize = (int) (apkSigBlockSizeInFooter + 8);
long apkSigBlockOffset = centralDirOffset - totalSize;
if (apkSigBlockOffset < 0) {
throw new SignatureNotFoundException(
"APK Signing Block offset out of range: " + apkSigBlockOffset);
}
ByteBuffer apkSigBlock = ByteBuffer.allocate(totalSize);
apkSigBlock.order(ByteOrder.LITTLE_ENDIAN);
apk.seek(apkSigBlockOffset);
apk.readFully(apkSigBlock.array(), apkSigBlock.arrayOffset(), apkSigBlock.capacity());
long apkSigBlockSizeInHeader = apkSigBlock.getLong(0);
if (apkSigBlockSizeInHeader != apkSigBlockSizeInFooter) {
throw new SignatureNotFoundException(
"APK Signing Block sizes in header and footer do not match: "
- apkSigBlockSizeInHeader + " vs " + apkSigBlockSizeInFooter);
}
return Pair.create(apkSigBlock, apkSigBlockOffset);
}
-
通过中心目录偏移往上16字节找到apk签名的魔术字APK Sig Block 42
-
在往上8字节找到签名块的长度,拿到的长度加8字节为整个签名块的长度
-
从中心目录往上找整个签名块的长度,即为签名块的开始位置
-
函数返回整个签名块的数据,以及签名块开始的偏移
整个签名块就是从apkSigBlockSizeInHeader到APK_SIG_BLOCK_MAGIC_HI的所有数据
ApkSigningBlockUtils.findApkSignatureSchemeBlock()
frameworks/base/core/java/android/util/apk/ApkSigningBlockUtils.java
static ByteBuffer findApkSignatureSchemeBlock(ByteBuffer apkSigningBlock, int blockId)
throws SignatureNotFoundException {
checkByteOrderLittleEndian(apkSigningBlock);
// FORMAT:
// OFFSET DATA TYPE DESCRIPTION
// * @+0 bytes uint64: size in bytes (excluding this field)
// * @+8 bytes pairs
// * @-24 bytes uint64: size in bytes (same as the one above)
// * @-16 bytes uint128: magic
ByteBuffer pairs = sliceFromTo(apkSigningBlock, 8, apkSigningBlock.capacity() - 24);
int entryCount = 0;
while (pairs.hasRemaining()) {
entryCount++;
if (pairs.remaining() < 8) {
throw new SignatureNotFoundException(
“Insufficient data to read size of APK Signing Block entry #” + entryCount);
}
long lenLong = pairs.getLong();
if ((lenLong < 4) || (lenLong > Integer.MAX_VALUE)) {
throw new SignatureNotFoundException(
“APK Signing Block entry #” + entryCount
- " size out of range: " + lenLong);
}
int len = (int) lenLong;
int nextEntryPos = pairs.position() + len;
if (len > pairs.remaining()) {
throw new SignatureNotFoundException(
“APK Signing Block entry #” + entryCount + " size out of range: " + len
- ", available: " + pairs.remaining());
}
int id = pairs.getInt();
if (id == blockId) {
return getByteBuffer(pairs, len - 4);
}
pairs.position(nextEntryPos);
}
throw new SignatureNotFoundException(
“No block with ID " + blockId + " in APK Signing Block.”);
}
-
这个函数接受之前解析出的签名部分的数据,通过sliceFromTo剪掉前8个字节和后24个字节
-
利用剩下的部分再次获取八个字节的长度数据,代表着第一个签名部分的长度,一般也只有一个
-
继续获取一个四个字节的id,与blockID进行比较,这个参数来源于ApkSignatureSchemeV3Verifier.verify()函数中传入的常量:APK_SIGNATURE_SCHEME_V3_BLOCK_ID = 0xf05368c0 (这个签名块的标记v3v2不同)
-
最终返回的是剪掉签名长度和签名标记数据块