From 3c75f474c6ff88e8057a476aa47d046b723c8c07 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Sun, 19 Jun 2022 00:43:27 -0700 Subject: [PATCH] Embed version info in prop format --- buildSrc/src/main/java/Setup.kt | 2 +- native/jni/base/files.cpp | 18 ++++++-- native/jni/base/files.hpp | 14 ++---- native/jni/base/xwrap.cpp | 8 ++++ native/jni/base/xwrap.hpp | 1 + native/jni/core/cert.cpp | 81 ++++++++++++++++++--------------- 6 files changed, 73 insertions(+), 51 deletions(-) diff --git a/buildSrc/src/main/java/Setup.kt b/buildSrc/src/main/java/Setup.kt index f1b11f23c..6a8076622 100644 --- a/buildSrc/src/main/java/Setup.kt +++ b/buildSrc/src/main/java/Setup.kt @@ -138,7 +138,7 @@ private fun Project.setupAppCommon() { val apkDir = if (properties["android.injected.invoked.from.ide"] == "true") "intermediates" else "outputs" val apk = File(buildDir, "${apkDir}/apk/${variant}/$projectName-${variant}.apk") - val comment = "${Config.versionCode}" + val comment = "version=${Config.version}\nversionCode=${Config.versionCode}" addComment(apk, signingConfig, android.defaultConfig.minSdk!!, comment) } } diff --git a/native/jni/base/files.cpp b/native/jni/base/files.cpp index e98280bd1..0a7233ce0 100644 --- a/native/jni/base/files.cpp +++ b/native/jni/base/files.cpp @@ -338,12 +338,19 @@ void file_readline(bool trim, FILE *fp, const function &fn) { if (!fn(start)) break; } - fclose(fp); free(buf); } -void parse_prop_file(const char *file, const function &fn) { - file_readline(true, file, [&](string_view line_view) -> bool { +void file_readline(bool trim, const char *file, const function &fn) { + if (auto fp = open_file(file, "re")) + file_readline(trim, fp.get(), fn); +} +void file_readline(const char *file, const function &fn) { + file_readline(false, file, fn); +} + +void parse_prop_file(FILE *fp, const function &fn) { + file_readline(true, fp, [&](string_view line_view) -> bool { char *line = (char *) line_view.data(); if (line[0] == '#') return true; @@ -355,6 +362,11 @@ void parse_prop_file(const char *file, const function &fn) { + if (auto fp = open_file(file, "re")) + parse_prop_file(fp.get(), fn); +} + // Original source: https://android.googlesource.com/platform/bionic/+/master/libc/bionic/mntent.cpp // License: AOSP, full copyright notice please check original source static struct mntent *compat_getmntent_r(FILE *fp, struct mntent *e, char *buf, int buf_len) { diff --git a/native/jni/base/files.hpp b/native/jni/base/files.hpp index 2783a360d..cb62f8fd6 100644 --- a/native/jni/base/files.hpp +++ b/native/jni/base/files.hpp @@ -81,17 +81,9 @@ std::string full_read(int fd); std::string full_read(const char *filename); void write_zero(int fd, size_t size); void file_readline(bool trim, FILE *fp, const std::function &fn); -static inline void file_readline( - bool trim, const char *file, const std::function &fn) { - FILE *fp = xfopen(file, "re"); - if (fp == nullptr) - return; - file_readline(trim, fp, fn); -} -static inline void file_readline(const char *file, - const std::function &fn) { - file_readline(false, file, fn); -} +void file_readline(bool trim, const char *file, const std::function &fn); +void file_readline(const char *file, const std::function &fn); +void parse_prop_file(FILE *fp, const std::function &fn); void parse_prop_file(const char *file, const std::function &fn); void frm_rf(int dirfd); diff --git a/native/jni/base/xwrap.cpp b/native/jni/base/xwrap.cpp index c54d083dd..d9cdfe8c3 100644 --- a/native/jni/base/xwrap.cpp +++ b/native/jni/base/xwrap.cpp @@ -111,6 +111,14 @@ ssize_t xxread(int fd, void *buf, size_t count) { return read_sz; } +off_t xlseek(int fd, off_t offset, int whence) { + off_t ret = lseek(fd, offset, whence); + if (ret < 0) { + PLOGE("lseek"); + } + return ret; +} + int xpipe2(int pipefd[2], int flags) { int ret = pipe2(pipefd, flags); if (ret < 0) { diff --git a/native/jni/base/xwrap.hpp b/native/jni/base/xwrap.hpp index 8f5d9de9d..7c899c2a4 100644 --- a/native/jni/base/xwrap.hpp +++ b/native/jni/base/xwrap.hpp @@ -14,6 +14,7 @@ int xopenat(int dirfd, const char *pathname, int flags, mode_t mode); ssize_t xwrite(int fd, const void *buf, size_t count); ssize_t xread(int fd, void *buf, size_t count); ssize_t xxread(int fd, void *buf, size_t count); +off_t xlseek(int fd, off_t offset, int whence); int xpipe2(int pipefd[2], int flags); int xsetns(int fd, int nstype); int xunshare(int flags); diff --git a/native/jni/core/cert.cpp b/native/jni/core/cert.cpp index cee7db5a5..d0c388bf4 100644 --- a/native/jni/core/cert.cpp +++ b/native/jni/core/cert.cpp @@ -96,26 +96,27 @@ struct EOCD { * within the APK v2 signature block. */ string read_certificate(int fd, int version) { - uint32_t size4; - uint64_t size8; + uint32_t u32; + uint64_t u64; // Find EOCD for (int i = 0;; i++) { // 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)); + xlseek(fd, -static_cast(sizeof(comment_sz)) - i, SEEK_END); + xxread(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); + xlseek(fd, -static_cast(sizeof(EOCD)), SEEK_CUR); uint32_t magic = 0; - read(fd, &magic, sizeof(magic)); + xxread(fd, &magic, sizeof(magic)); if (magic == EOCD_MAGIC) { break; } } if (i == 0xffff) { // Comments cannot be longer than 0xffff (overflow), abort + LOGE("cert: invalid APK format\n"); return {}; } } @@ -125,19 +126,25 @@ string read_certificate(int fd, int version) { uint32_t central_dir_off = 0; { constexpr off_t off = offsetof(EOCD, central_dir_off) - sizeof(EOCD::magic); - lseek(fd, off, SEEK_CUR); + xlseek(fd, off, SEEK_CUR); } - read(fd, ¢ral_dir_off, sizeof(central_dir_off)); + xxread(fd, ¢ral_dir_off, sizeof(central_dir_off)); - // Read comment + // Parse APK comment to get version code if (version >= 0) { - uint16_t comment_sz = 0; - read(fd, &comment_sz, sizeof(comment_sz)); - string comment; - comment.resize(comment_sz); - read(fd, comment.data(), comment_sz); - if (version > parse_int(comment)) { - // Older version of magisk app is not supported + xlseek(fd, sizeof(EOCD::comment_sz), SEEK_CUR); + FILE *fp = fdopen(fd, "r"); // DO NOT close this file pointer + int apk_ver = -1; + parse_prop_file(fp, [&](string_view key, string_view value) -> bool { + if (key == "versionCode") { + apk_ver = parse_int(value); + return false; + } + return true; + }); + if (version > apk_ver) { + // Enforce the magisk app to always be newer than magiskd + LOGE("cert: APK version too low\n"); return {}; } } @@ -145,55 +152,57 @@ string read_certificate(int fd, int version) { // 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); + xlseek(fd, (off_t) (central_dir_off - off), SEEK_SET); } - read(fd, &size8, sizeof(size8)); // size8 = block_sz_ + xxread(fd, &u64, sizeof(u64)); // u64 = block_sz_ char magic[sizeof(signing_block::magic)] = {0}; - read(fd, magic, sizeof(magic)); + xxread(fd, magic, sizeof(magic)); if (memcmp(magic, APK_SIGNING_BLOCK_MAGIC, sizeof(magic)) != 0) { // Invalid signing block magic, abort + LOGE("cert: invalid signing block magic\n"); 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) { + xlseek(fd, -static_cast(u64 + sizeof(signing_blk_sz)), SEEK_CUR); + xxread(fd, &signing_blk_sz, sizeof(signing_blk_sz)); + if (signing_blk_sz != u64) { // block_sz != block_sz_, invalid signing block format, abort + LOGE("cert: invalid signing block format\n"); return {}; } // Finally, we are now at the beginning of the id-value pair sequence for (;;) { - read(fd, &size8, sizeof(size8)); // id-value pair length - if (size8 == signing_blk_sz) { + xxread(fd, &u64, sizeof(u64)); // id-value pair length + if (u64 == signing_blk_sz) { // Outside of the id-value pair sequence; actually reading block_sz_ break; } uint32_t id; - read(fd, &id, sizeof(id)); + xxread(fd, &id, sizeof(id)); if (id == SIGNATURE_SCHEME_V2_MAGIC) { - read(fd, &size4, sizeof(size4)); // signer sequence length + // Skip [signer sequence length] + [1st signer length] + [signed data length] + xlseek(fd, sizeof(uint32_t) * 3, SEEK_CUR); - read(fd, &size4, sizeof(size4)); // signer length - read(fd, &size4, sizeof(size4)); // signed data length + xxread(fd, &u32, sizeof(u32)); // digest sequence length + xlseek(fd, u32, SEEK_CUR); // skip all digests - read(fd, &size4, sizeof(size4)); // digest sequence length - lseek(fd, (off_t) (size4), SEEK_CUR); // skip all digests - - read(fd, &size4, sizeof(size4)); // cert sequence length - read(fd, &size4, sizeof(size4)); // cert length + xlseek(fd, sizeof(uint32_t), SEEK_CUR); // cert sequence length + xxread(fd, &u32, sizeof(u32)); // 1st cert length string cert; - cert.resize(size4); - read(fd, cert.data(), size4); + cert.resize(u32); + xxread(fd, cert.data(), u32); return cert; } else { // Skip this id-value pair - lseek(fd, (off_t) (size8 - sizeof(id)), SEEK_CUR); + xlseek(fd, u64 - sizeof(id), SEEK_CUR); } } + + LOGE("cert: cannot find certificate\n"); return {}; }