From 056359d8980828bc352f8d930733305547580c96 Mon Sep 17 00:00:00 2001 From: yrom Date: Tue, 23 Oct 2018 16:19:49 +0800 Subject: [PATCH] Support writing chanel to apk that signed by android signature scheme v3 New scheme requires an extra dummy ID-value block padding to make size a multiple of 4096 bytes See https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/util/apk/ApkVerityBuilder.java#445 Fixed Meituan-Dianping/walle#256 Meituan-Dianping/walle#255 --- .../com/meituan/android/walle/ApkUtil.java | 9 ++++++ .../android/walle/ApkSigningPayload.java | 10 +++++++ .../meituan/android/walle/PayloadWriter.java | 29 ++++++++++++++++++- 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/payload_reader/src/main/java/com/meituan/android/walle/ApkUtil.java b/payload_reader/src/main/java/com/meituan/android/walle/ApkUtil.java index 1eba71f..e5151da 100644 --- a/payload_reader/src/main/java/com/meituan/android/walle/ApkUtil.java +++ b/payload_reader/src/main/java/com/meituan/android/walle/ApkUtil.java @@ -27,6 +27,15 @@ private ApkUtil() { */ public static final int APK_SIGNATURE_SCHEME_V2_BLOCK_ID = 0x7109871a; + /** + * The padding in APK SIG BLOCK (V3 scheme introduced) + * See https://android.googlesource.com/platform/tools/apksig/+/master/src/main/java/com/android/apksig/internal/apk/ApkSigningBlockUtils.java + */ + public static final int VERITY_PADDING_BLOCK_ID = 0x42726577; + + public static final int ANDROID_COMMON_PAGE_ALIGNMENT_BYTES = 4096; + + // Our Channel Block ID public static final int APK_CHANNEL_BLOCK_ID = 0x71777777; diff --git a/payload_writer/src/main/java/com/meituan/android/walle/ApkSigningPayload.java b/payload_writer/src/main/java/com/meituan/android/walle/ApkSigningPayload.java index 57fef02..4ba72fd 100644 --- a/payload_writer/src/main/java/com/meituan/android/walle/ApkSigningPayload.java +++ b/payload_writer/src/main/java/com/meituan/android/walle/ApkSigningPayload.java @@ -7,6 +7,7 @@ class ApkSigningPayload { private final int id; private final ByteBuffer buffer; + private final int totalSize; ApkSigningPayload(final int id, final ByteBuffer buffer) { super(); @@ -15,6 +16,8 @@ class ApkSigningPayload { throw new IllegalArgumentException("ByteBuffer byte order must be little endian"); } this.buffer = buffer; + // assume buffer is not consumed + this.totalSize = 8 + 4 + buffer.remaining(); // size + id + value } public int getId() { @@ -27,4 +30,11 @@ public byte[] getByteBuffer() { return Arrays.copyOfRange(array, arrayOffset + buffer.position(), arrayOffset + buffer.limit()); } + + /** + * Total bytes of this block + */ + public int getTotalSize() { + return totalSize; + } } diff --git a/payload_writer/src/main/java/com/meituan/android/walle/PayloadWriter.java b/payload_writer/src/main/java/com/meituan/android/walle/PayloadWriter.java index 02e83ea..d34d82d 100644 --- a/payload_writer/src/main/java/com/meituan/android/walle/PayloadWriter.java +++ b/payload_writer/src/main/java/com/meituan/android/walle/PayloadWriter.java @@ -174,8 +174,35 @@ static void handleApkSigningBlock(final File apkFile, final ApkSigningBlockHandl "No APK Signature Scheme v2 block in APK Signing Block"); } - + final boolean needPadding = originIdValues.remove(ApkUtil.VERITY_PADDING_BLOCK_ID) != null; final ApkSigningBlock apkSigningBlock = handler.handle(originIdValues); + // replace VERITY_PADDING_BLOCK with new one + if (needPadding) { + // uint64: size (excluding this field) + // repeated ID-value pairs: + // uint64: size (excluding this field) + // uint32: ID + // (size - 4) bytes: value + // (extra dummy ID-value for padding to make block size a multiple of 4096 bytes) + // uint64: size (same as the one above) + // uint128: magic + + int blocksSize = 0; + for (ApkSigningPayload payload : apkSigningBlock.getPayloads()) { + blocksSize += payload.getTotalSize(); + } + + int resultSize = 8 + blocksSize + 8 + 16; // size(uint64) + pairs size + size(uint64) + magic(uint128) + if (resultSize % ApkUtil.ANDROID_COMMON_PAGE_ALIGNMENT_BYTES != 0) { + int padding = ApkUtil.ANDROID_COMMON_PAGE_ALIGNMENT_BYTES - 12 // size(uint64) + id(uint32) + - (resultSize % ApkUtil.ANDROID_COMMON_PAGE_ALIGNMENT_BYTES); + if (padding < 0) { + padding += ApkUtil.ANDROID_COMMON_PAGE_ALIGNMENT_BYTES; + } + final ByteBuffer dummy = ByteBuffer.allocate(padding).order(ByteOrder.LITTLE_ENDIAN); + apkSigningBlock.addPayload(new ApkSigningPayload(ApkUtil.VERITY_PADDING_BLOCK_ID,dummy)); + } + } if (apkSigningBlockOffset != 0 && centralDirStartOffset != 0) {