diff --git a/native/jni/su/cert.cpp b/native/jni/su/cert.cpp index 7c01db632..a3c2fd187 100644 --- a/native/jni/su/cert.cpp +++ b/native/jni/su/cert.cpp @@ -2,9 +2,13 @@ using namespace std; +#define APK_SIGNING_BLOCK_MAGIC "APK Sig Block 42" +#define SIGNATURE_SCHEME_V2_MAGIC 0x7109871a +#define EOCD_MAGIC 0x6054b50 + // Top-level block container struct signing_block { - uint64_t len; + uint64_t block_sz; struct id_value_pair { uint64_t len; @@ -14,7 +18,7 @@ struct signing_block { }; } id_value_pair_sequence[0]; - uint64_t block_len; // *MUST* be same as len + uint64_t block_sz_; // *MUST* be same as block_sz char magic[16]; // "APK Sig Block 42" }; @@ -59,78 +63,124 @@ struct v2_signature { } signer_sequence[0]; }; -// The structures above are just for documentation purpose -// The real parsing logic is the following +// End of central directory record +struct EOCD { + uint32_t magic; // 0x6054b50 + uint8_t pad[8]; // 8 bytes of irrelevant data + uint32_t central_dir_sz; // size of central directory + uint32_t central_dir_off; // offset of central directory + uint16_t comment_sz; // size of comment + char comment[0]; +} __attribute__((packed)); +/* + * A v2/v3 signed APK has the format as following + * + * +---------------+ + * | zip content | + * +---------------+ + * | signing block | + * +---------------+ + * | central dir | + * +---------------+ + * | EOCD | + * +---------------+ + * + * Scan from end of file to find EOCD, and figure our way back to the + * offset of the signing block. Next, directly extract the certificate + * from the v2 signature block. + * + * All structures above are mostly just for documentation purpose. + * + * This method extracts the first certificate of the first signer + * within the APK v2 signature block. + */ string read_certificate(int fd) { - string certificate; uint32_t size4; - uint64_t size8, size_of_block; + uint64_t size8; + // Find EOCD for (int i = 0;; i++) { - unsigned short n; - lseek(fd, -i - 2, SEEK_END); - read(fd, &n, 2); - if (n == i) { - lseek(fd, -22, SEEK_CUR); - read(fd, &size4, 4); - if (size4 == 0x6054b50u) { // central directory end magic + // i is the absolute offset to end of file + uint16_t comment_sz = 0; + lseek(fd, -((off_t) sizeof(comment_sz)) - i, SEEK_END); + read(fd, &comment_sz, sizeof(comment_sz)); + if (comment_sz == i) { + // Double check if we actually found the structure + lseek(fd, -((off_t) sizeof(EOCD)), SEEK_CUR); + uint32_t magic = 0; + read(fd, &magic, sizeof(magic)); + if (magic == EOCD_MAGIC) { break; } } if (i == 0xffff) { - return certificate; + // Comments cannot be longer than 0xffff (overflow), abort + return {}; } } - lseek(fd, 12, SEEK_CUR); + // We are now at EOCD + sizeof(magic) + // Seek and read central_dir_off to find start of central directory + uint32_t central_dir_off = 0; + { + constexpr off_t off = offsetof(EOCD, central_dir_off) - sizeof(EOCD::magic); + lseek(fd, off, SEEK_CUR); + } + read(fd, ¢ral_dir_off, sizeof(central_dir_off)); - read(fd, &size4, 0x4); - lseek(fd, (off_t) (size4 - 0x18), SEEK_SET); - - read(fd, &size8, 0x8); - char magic[0x10] = {0}; + // Next, find the start of the APK signing block + { + constexpr int off = sizeof(signing_block::block_sz_) + sizeof(signing_block::magic); + lseek(fd, (off_t) (central_dir_off - off), SEEK_SET); + } + read(fd, &size8, sizeof(size8)); // size8 = block_sz_ + char magic[sizeof(signing_block::magic)] = {0}; read(fd, magic, sizeof(magic)); - if (memcmp(magic, "APK Sig Block 42", sizeof(magic)) != 0) { - return certificate; + if (memcmp(magic, APK_SIGNING_BLOCK_MAGIC, sizeof(magic)) != 0) { + // Invalid signing block magic, abort + return {}; + } + uint64_t signing_blk_sz = 0; + lseek(fd, (off_t) (central_dir_off - size8 - sizeof(signing_blk_sz)), SEEK_SET); + read(fd, &signing_blk_sz, sizeof(signing_blk_sz)); + if (signing_blk_sz != size8) { + // block_sz != block_sz_, invalid signing block format, abort + return {}; } - lseek(fd, (off_t) (size4 - (size8 + 0x8)), SEEK_SET); - read(fd, &size_of_block, 0x8); - if (size_of_block != size8) { - return certificate; - } + // Finally, we are now at the beginning of the id-value pair sequence for (;;) { - uint32_t id; - uint32_t offset; - read(fd, &size8, 0x8); // sequence length - if (size8 == size_of_block) { + read(fd, &size8, sizeof(size8)); // id-value pair length + if (size8 == signing_blk_sz) { + // Outside of the id-value pair sequence; actually reading block_sz_ break; } - read(fd, &id, 0x4); // id - offset = 4; - if (id == 0x7109871au) { - read(fd, &size4, 0x4); // signer-sequence length - read(fd, &size4, 0x4); // signer length - read(fd, &size4, 0x4); // signed data length - offset += 0x4 * 3; + uint32_t id; + read(fd, &id, sizeof(id)); + if (id == SIGNATURE_SCHEME_V2_MAGIC) { + read(fd, &size4, sizeof(size4)); // signer sequence length - read(fd, &size4, 0x4); // digests-sequence length - lseek(fd, (off_t) (size4), SEEK_CUR);// skip digests - offset += 0x4 + size4; + read(fd, &size4, sizeof(size4)); // signer length + read(fd, &size4, sizeof(size4)); // signed data length - read(fd, &size4, 0x4); // certificates length - read(fd, &size4, 0x4); // certificate length - offset += 0x4 * 2; + read(fd, &size4, sizeof(size4)); // digest sequence length + lseek(fd, (off_t) (size4), SEEK_CUR); // skip all digests - certificate.resize(size4); - read(fd, certificate.data(), size4); + read(fd, &size4, sizeof(size4)); // cert sequence length + read(fd, &size4, sizeof(size4)); // cert length - offset += size4; + string cert; + cert.resize(size4); + read(fd, cert.data(), size4); + + return cert; + } else { + // Skip this id-value pair + lseek(fd, (off_t) (size8 - sizeof(id)), SEEK_CUR); } - lseek(fd, (off_t) (size8 - offset), SEEK_CUR); } - return certificate; + return {}; }