Android P v3签名新特性,银行软件开发面试题

  • 通过函数重载,最终的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不同)

  • 最终返回的是剪掉签名长度和签名标记数据块

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值