From a22959beae1aad96b1f72710a5daadf529c41bda Mon Sep 17 00:00:00 2001 From: weishu Date: Wed, 11 Oct 2023 15:53:11 +0800 Subject: [PATCH] kernel: harden the signature check (#1027) --- kernel/Makefile | 4 +- kernel/apk_sign.c | 198 +++++++++++++++++++++++---------- kernel/apk_sign.h | 5 +- kernel/manager.c | 2 +- scripts/check_v2.c | 174 ----------------------------- userspace/ksud/Cargo.lock | 131 +++++++++++++++++++--- userspace/ksud/Cargo.toml | 1 + userspace/ksud/src/apk_sign.rs | 84 ++++++++------ userspace/ksud/src/cli.rs | 2 +- userspace/ksud/src/debug.rs | 13 ++- 10 files changed, 321 insertions(+), 293 deletions(-) delete mode 100644 scripts/check_v2.c diff --git a/kernel/Makefile b/kernel/Makefile index 5ba201cf..b58786e0 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -29,13 +29,13 @@ KSU_EXPECTED_SIZE := 0x033b endif ifndef KSU_EXPECTED_HASH -KSU_EXPECTED_HASH := 0xb0b91415 +KSU_EXPECTED_HASH := c371061b19d8c7d7d6133c6a9bafe198fa944e50c1b31c9d8daa8d7f1fc2d2d6 endif $(info -- KernelSU Manager signature size: $(KSU_EXPECTED_SIZE)) $(info -- KernelSU Manager signature hash: $(KSU_EXPECTED_HASH)) ccflags-y += -DEXPECTED_SIZE=$(KSU_EXPECTED_SIZE) -ccflags-y += -DEXPECTED_HASH=$(KSU_EXPECTED_HASH) +ccflags-y += -DEXPECTED_HASH=\"$(KSU_EXPECTED_HASH)\" ccflags-y += -Wno-implicit-function-declaration -Wno-strict-prototypes -Wno-int-conversion -Wno-gcc-compat ccflags-y += -Wno-declaration-after-statement diff --git a/kernel/apk_sign.c b/kernel/apk_sign.c index 7ae066f7..1f361695 100644 --- a/kernel/apk_sign.c +++ b/kernel/apk_sign.c @@ -1,20 +1,136 @@ +#include "linux/err.h" #include "linux/fs.h" +#include "linux/gfp.h" +#include "linux/kernel.h" #include "linux/moduleparam.h" #include "apk_sign.h" #include "klog.h" // IWYU pragma: keep #include "kernel_compat.h" +#include "crypto/hash.h" +#include "linux/slab.h" +#include "linux/version.h" -static __always_inline int -check_v2_signature(char *path, unsigned expected_size, unsigned expected_hash) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0) +#include "crypto/sha2.h" +#else +#include "crypto/sha.h" +#endif + +struct sdesc { + struct shash_desc shash; + char ctx[]; +}; + +static struct sdesc *init_sdesc(struct crypto_shash *alg) +{ + struct sdesc *sdesc; + int size; + + size = sizeof(struct shash_desc) + crypto_shash_descsize(alg); + sdesc = kmalloc(size, GFP_KERNEL); + if (!sdesc) + return ERR_PTR(-ENOMEM); + sdesc->shash.tfm = alg; + return sdesc; +} + +static int calc_hash(struct crypto_shash *alg, const unsigned char *data, + unsigned int datalen, unsigned char *digest) +{ + struct sdesc *sdesc; + int ret; + + sdesc = init_sdesc(alg); + if (IS_ERR(sdesc)) { + pr_info("can't alloc sdesc\n"); + return PTR_ERR(sdesc); + } + + ret = crypto_shash_digest(&sdesc->shash, data, datalen, digest); + kfree(sdesc); + return ret; +} + +static int ksu_sha256(const unsigned char *data, unsigned int datalen, + unsigned char *digest) +{ + struct crypto_shash *alg; + char *hash_alg_name = "sha256"; + int ret; + + alg = crypto_alloc_shash(hash_alg_name, 0, 0); + if (IS_ERR(alg)) { + pr_info("can't alloc alg %s\n", hash_alg_name); + return PTR_ERR(alg); + } + ret = calc_hash(alg, data, datalen, digest); + crypto_free_shash(alg); + return ret; +} + +static bool check_block(struct file *fp, u32 *size4, loff_t *pos, u32 *offset, + unsigned expected_size, const char* expected_sha256) +{ + ksu_kernel_read_compat(fp, size4, 0x4, pos); // signer-sequence length + ksu_kernel_read_compat(fp, size4, 0x4, pos); // signer length + ksu_kernel_read_compat(fp, size4, 0x4, pos); // signed data length + + *offset += 0x4 * 3; + + ksu_kernel_read_compat(fp, size4, 0x4, pos); // digests-sequence length + + *pos += *size4; + *offset += 0x4 + *size4; + + ksu_kernel_read_compat(fp, size4, 0x4, pos); // certificates length + ksu_kernel_read_compat(fp, size4, 0x4, pos); // certificate length + *offset += 0x4 * 2; + + if (*size4 == expected_size) { + *offset += *size4; + + #define CERT_MAX_LENGTH 1024 + char cert[CERT_MAX_LENGTH]; + if (*size4 > CERT_MAX_LENGTH) { + pr_info("cert length overlimit\n"); + return false; + } + ksu_kernel_read_compat(fp, cert, *size4, pos); + unsigned char digest[SHA256_DIGEST_SIZE]; + if (IS_ERR(ksu_sha256(cert, *size4, digest))) { + pr_info("sha256 error\n"); + return false; + } + + char hash_str[SHA256_DIGEST_SIZE * 2 + 1]; + hash_str[SHA256_DIGEST_SIZE * 2] = '\0'; + + bin2hex(hash_str, digest, SHA256_DIGEST_SIZE); + pr_info("sha256: %s, expected: %s\n", hash_str, expected_sha256); + if (strcmp(expected_sha256, hash_str) == 0) { + return true; + } + } + return false; +} + +static __always_inline bool +check_v2_signature(char *path, unsigned expected_size, const char *expected_sha256) { unsigned char buffer[0x11] = { 0 }; u32 size4; u64 size8, size_of_block; loff_t pos; + bool block_valid; + + const int NOT_EXIST = 0; + const int INVALID = 1; + const int VALID = 2; + int v2_signing_status = NOT_EXIST; + int v3_signing_status = NOT_EXIST; - int sign = -1; int i; struct file *fp = ksu_filp_open_compat(path, O_RDONLY, 0); if (IS_ERR(fp)) { @@ -25,7 +141,6 @@ check_v2_signature(char *path, unsigned expected_size, unsigned expected_hash) // disable inotify for this file fp->f_mode |= FMODE_NONOTIFY; - sign = 1; // https://en.wikipedia.org/wiki/Zip_(file_format)#End_of_central_directory_record_(EOCD) for (i = 0;; ++i) { unsigned short n; @@ -64,59 +179,22 @@ check_v2_signature(char *path, unsigned expected_size, unsigned expected_hash) for (;;) { uint32_t id; uint32_t offset; - ksu_kernel_read_compat(fp, &size8, 0x8, &pos); // sequence length + ksu_kernel_read_compat(fp, &size8, 0x8, + &pos); // sequence length if (size8 == size_of_block) { break; } ksu_kernel_read_compat(fp, &id, 0x4, &pos); // id offset = 4; pr_info("id: 0x%08x\n", id); - if ((id ^ 0xdeadbeefu) == 0xafa439f5u || - (id ^ 0xdeadbeefu) == 0x2efed62f) { - ksu_kernel_read_compat(fp, &size4, 0x4, - &pos); // signer-sequence length - ksu_kernel_read_compat(fp, &size4, 0x4, &pos); // signer length - ksu_kernel_read_compat(fp, &size4, 0x4, - &pos); // signed data length - offset += 0x4 * 3; - - ksu_kernel_read_compat(fp, &size4, 0x4, - &pos); // digests-sequence length - pos += size4; - offset += 0x4 + size4; - - ksu_kernel_read_compat(fp, &size4, 0x4, - &pos); // certificates length - ksu_kernel_read_compat(fp, &size4, 0x4, - &pos); // certificate length - offset += 0x4 * 2; -#if 0 - int hash = 1; - signed char c; - for (i = 0; i < size4; ++i) { - ksu_kernel_read_compat(fp, &c, 0x1, &pos); - hash = 31 * hash + c; - } - offset += size4; - pr_info(" size: 0x%04x, hash: 0x%08x\n", size4, ((unsigned) hash) ^ 0x14131211u); -#else - if (size4 == expected_size) { - int hash = 1; - signed char c; - for (i = 0; i < size4; ++i) { - ksu_kernel_read_compat(fp, &c, 0x1, &pos); - hash = 31 * hash + c; - } - offset += size4; - if ((((unsigned)hash) ^ 0x14131211u) == - expected_hash) { - sign = 0; - break; - } - } - // don't try again. - break; -#endif + if (id == 0x7109871au) { + block_valid = check_block(fp, &size4, &pos, &offset, + expected_size, expected_sha256); + v2_signing_status = block_valid ? VALID : INVALID; + } else if (id == 0xf05368c0u) { + block_valid = check_block(fp, &size4, &pos, &offset, + expected_size, expected_sha256); + v3_signing_status = block_valid ? VALID : INVALID; } pos += (size8 - offset); } @@ -124,13 +202,15 @@ check_v2_signature(char *path, unsigned expected_size, unsigned expected_hash) clean: filp_close(fp, 0); - return sign; + return (v2_signing_status == NOT_EXIST && v3_signing_status == VALID) || + (v2_signing_status == VALID && v3_signing_status == NOT_EXIST) || + (v2_signing_status == VALID && v3_signing_status == VALID); } #ifdef CONFIG_KSU_DEBUG unsigned ksu_expected_size = EXPECTED_SIZE; -unsigned ksu_expected_hash = EXPECTED_HASH; +const char *ksu_expected_hash = EXPECTED_HASH; #include "manager.h" @@ -144,9 +224,10 @@ static int set_expected_size(const char *val, const struct kernel_param *kp) static int set_expected_hash(const char *val, const struct kernel_param *kp) { - int rv = param_set_uint(val, kp); + pr_info("set_expected_hash: %s\n", val); + int rv = param_set_charp(val, kp); ksu_invalidate_manager_uid(); - pr_info("ksu_expected_hash set to %x\n", ksu_expected_hash); + pr_info("ksu_expected_hash set to %s\n", ksu_expected_hash); return rv; } @@ -157,7 +238,8 @@ static struct kernel_param_ops expected_size_ops = { static struct kernel_param_ops expected_hash_ops = { .set = set_expected_hash, - .get = param_get_uint, + .get = param_get_charp, + .free = param_free_charp, }; module_param_cb(ksu_expected_size, &expected_size_ops, &ksu_expected_size, @@ -165,14 +247,14 @@ module_param_cb(ksu_expected_size, &expected_size_ops, &ksu_expected_size, module_param_cb(ksu_expected_hash, &expected_hash_ops, &ksu_expected_hash, S_IRUSR | S_IWUSR); -int is_manager_apk(char *path) +bool is_manager_apk(char *path) { return check_v2_signature(path, ksu_expected_size, ksu_expected_hash); } #else -int is_manager_apk(char *path) +bool is_manager_apk(char *path) { return check_v2_signature(path, EXPECTED_SIZE, EXPECTED_HASH); } diff --git a/kernel/apk_sign.h b/kernel/apk_sign.h index 52cbc9d0..ebd78a8b 100644 --- a/kernel/apk_sign.h +++ b/kernel/apk_sign.h @@ -1,7 +1,8 @@ #ifndef __KSU_H_APK_V2_SIGN #define __KSU_H_APK_V2_SIGN -// return 0 if signature match -int is_manager_apk(char *path); +#include "linux/types.h" + +bool is_manager_apk(char *path); #endif diff --git a/kernel/manager.c b/kernel/manager.c index f69cd957..83a2226e 100644 --- a/kernel/manager.c +++ b/kernel/manager.c @@ -70,7 +70,7 @@ bool become_manager(char *pkg) pr_info("invalid pkg: %s\n", pkg); continue; } - if (is_manager_apk(cwd) == 0) { + if (is_manager_apk(cwd)) { // check passed uid_t uid = current_uid().val; pr_info("manager uid: %d\n", uid); diff --git a/scripts/check_v2.c b/scripts/check_v2.c deleted file mode 100644 index 1827fa10..00000000 --- a/scripts/check_v2.c +++ /dev/null @@ -1,174 +0,0 @@ -// -// Created by Thom on 2019/3/8. -// - -// Credits: https://github.com/brevent/genuine/blob/master/src/main/jni/apk-sign-v2.c -#include -#include -#include -#include - -#define MAIN - -#ifdef MAIN -#include -#else - -#include "common.h" -#include "openat.h" - -#endif - -static bool isApkSigBlock42(const char *buffer) { - // APK Sig Block 42 - return *buffer == 'A' - && *++buffer == 'P' - && *++buffer == 'K' - && *++buffer == ' ' - && *++buffer == 'S' - && *++buffer == 'i' - && *++buffer == 'g' - && *++buffer == ' ' - && *++buffer == 'B' - && *++buffer == 'l' - && *++buffer == 'o' - && *++buffer == 'c' - && *++buffer == 'k' - && *++buffer == ' ' - && *++buffer == '4' - && *++buffer == '2'; -} - -int checkSignature(const char *path) { - unsigned char buffer[0x11] = {0}; - uint32_t size4; - uint64_t size8, size_of_block; - -#ifdef DEBUG - LOGI("check signature for %s", path); -#endif - - int sign = -1; - int fd = (int) openat(AT_FDCWD, path, O_RDONLY); -#ifdef DEBUG_OPENAT - LOGI("openat %s returns %d", path, fd); -#endif - if (fd < 0) { - return sign; - } - - sign = 1; - // https://en.wikipedia.org/wiki/Zip_(file_format)#End_of_central_directory_record_(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 ^ 0xcafebabeu) == 0xccfbf1eeu) { -#ifdef MAIN - if (i > 0) { - printf("warning: comment length is %d\n", i); - } -#endif - break; - } - } - if (i == 0xffff) { -#ifdef MAIN - printf("error: cannot find eocd\n"); -#endif - goto clean; - } - } - - lseek(fd, 12, SEEK_CUR); - // offset - read(fd, &size4, 0x4); - lseek(fd, (off_t) (size4 - 0x18), SEEK_SET); - - read(fd, &size8, 0x8); - read(fd, buffer, 0x10); - if (!isApkSigBlock42((char *) buffer)) { - goto clean; - } - - lseek(fd, (off_t) (size4 - (size8 + 0x8)), SEEK_SET); - read(fd, &size_of_block, 0x8); - if (size_of_block != size8) { - goto clean; - } - - for (;;) { - uint32_t id; - uint32_t offset; - read(fd, &size8, 0x8); // sequence length - if (size8 == size_of_block) { - break; - } - read(fd, &id, 0x4); // id - offset = 4; -#ifdef MAIN - // printf("id: 0x%08x\n", id); -#endif - if ((id ^ 0xdeadbeefu) == 0xafa439f5u || (id ^ 0xdeadbeefu) == 0x2efed62f) { - read(fd, &size4, 0x4); // signer-sequence length - read(fd, &size4, 0x4); // signer length - read(fd, &size4, 0x4); // signed data length - offset += 0x4 * 3; - - read(fd, &size4, 0x4); // digests-sequence length - lseek(fd, (off_t) (size4), SEEK_CUR);// skip digests - offset += 0x4 + size4; - - read(fd, &size4, 0x4); // certificates length - read(fd, &size4, 0x4); // certificate length - offset += 0x4 * 2; -#ifdef MAIN - int hash = 1; - signed char c; - for (unsigned i = 0; i < size4; ++i) { - read(fd, &c, 0x1); - hash = 31 * hash + c; - } - offset += size4; - // printf(" size: 0x%04x, hash: 0x%08x\n", size4, ((unsigned) hash) ^ 0x14131211u); - printf("0x%04x 0x%08x\n", size4, ((unsigned) hash) ^ 0x14131211u); -#else -#if defined(GENUINE_SIZE) && defined(GENUINE_HASH) - if (size4 == GENUINE_SIZE) { - int hash = 1; - signed char c; - for (unsigned i = 0; i < size4; ++i) { - read(fd, &c, 0x1); - hash = 31 * hash + c; - } - offset += size4; - if ((((unsigned) hash) ^ 0x14131211u) == GENUINE_HASH) { - sign = 0; - break; - } - } -#else - sign = 0; - break; -#endif -#endif - } - lseek(fd, (off_t) (size8 - offset), SEEK_CUR); - } - -clean: - close(fd); - - return sign; -} - -#ifdef MAIN -int main(int argc, char **argv) { - if (argc > 1) { - checkSignature(argv[1]); - } -} -#endif diff --git a/userspace/ksud/Cargo.lock b/userspace/ksud/Cargo.lock index a7284927..9bc59ccc 100644 --- a/userspace/ksud/Cargo.lock +++ b/userspace/ksud/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" +dependencies = [ + "gimli", +] + [[package]] name = "adler" version = "1.0.2" @@ -74,12 +83,38 @@ version = "1.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61" +[[package]] +name = "async-trait" +version = "0.1.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] + [[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "backtrace" +version = "0.3.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "base64ct" version = "1.6.0" @@ -132,6 +167,12 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + [[package]] name = "bzip2" version = "0.4.4" @@ -234,7 +275,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -407,7 +448,7 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn", + "syn 1.0.107", ] [[package]] @@ -424,7 +465,7 @@ checksum = "81bbeb29798b407ccd82a3324ade1a7286e0d29851475990b612670f6f5124d2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -435,7 +476,7 @@ checksum = "3418329ca0ad70234b9735dc4ceed10af4df60eff9c8e7b06cb5e520d92c3535" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -604,6 +645,12 @@ dependencies = [ "wasi 0.11.0+wasi-snapshot-preview1", ] +[[package]] +name = "gimli" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" + [[package]] name = "glob" version = "0.3.1" @@ -700,7 +747,7 @@ dependencies = [ "proc-macro-hack", "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -816,6 +863,7 @@ dependencies = [ "rust-embed", "serde", "serde_json", + "sha256", "sys-mount", "which", "zip 0.6.4", @@ -979,6 +1027,15 @@ dependencies = [ "libc", ] +[[package]] +name = "object" +version = "0.30.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03b4680b86d9cfafba8fc491dc9b6df26b68cf40e9e6cd73909194759a63c385" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.17.0" @@ -1053,7 +1110,7 @@ dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", - "syn", + "syn 1.0.107", "version_check", ] @@ -1076,9 +1133,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.50" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] @@ -1100,9 +1157,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.23" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] @@ -1212,7 +1269,7 @@ dependencies = [ "proc-macro2", "quote", "rust-embed-utils", - "syn", + "syn 1.0.107", "walkdir", ] @@ -1226,6 +1283,12 @@ dependencies = [ "walkdir", ] +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + [[package]] name = "rustc-hash" version = "1.1.0" @@ -1312,6 +1375,19 @@ dependencies = [ "digest", ] +[[package]] +name = "sha256" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7895c8ae88588ccead14ff438b939b0c569cd619116f14b4d13fdff7b8333386" +dependencies = [ + "async-trait", + "bytes", + "hex", + "sha2", + "tokio", +] + [[package]] name = "shlex" version = "1.1.0" @@ -1326,7 +1402,7 @@ checksum = "133659a15339456eeeb07572eb02a91c91e9815e9cbc89566944d2c8d3efdbf6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -1352,6 +1428,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "2.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "sys-mount" version = "2.0.2" @@ -1391,7 +1478,7 @@ checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -1421,6 +1508,18 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" +[[package]] +name = "tokio" +version = "1.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" +dependencies = [ + "autocfg", + "backtrace", + "bytes", + "pin-project-lite", +] + [[package]] name = "tracing" version = "0.1.37" @@ -1441,7 +1540,7 @@ checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -1527,7 +1626,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 1.0.107", "wasm-bindgen-shared", ] @@ -1549,7 +1648,7 @@ checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", "wasm-bindgen-backend", "wasm-bindgen-shared", ] diff --git a/userspace/ksud/Cargo.toml b/userspace/ksud/Cargo.toml index 9c5dff68..a6e102d9 100644 --- a/userspace/ksud/Cargo.toml +++ b/userspace/ksud/Cargo.toml @@ -33,6 +33,7 @@ rust-embed = { version = "6.4.2", features = [ ] } which = "4.2.2" getopts = "0.2.21" +sha256 = "1.4.0" [target.'cfg(any(target_os = "android", target_os = "linux"))'.dependencies] sys-mount = { git = "https://github.com/tiann/sys-mount", branch = "loopfix" } diff --git a/userspace/ksud/src/apk_sign.rs b/userspace/ksud/src/apk_sign.rs index 20eab312..bf325a56 100644 --- a/userspace/ksud/src/apk_sign.rs +++ b/userspace/ksud/src/apk_sign.rs @@ -1,7 +1,7 @@ use anyhow::{ensure, Result}; use std::io::{Read, Seek, SeekFrom}; -pub fn get_apk_signature(apk: &str) -> Result<(u32, u32)> { +pub fn get_apk_signature(apk: &str) -> Result<(u32, String)> { let mut buffer = [0u8; 0x10]; let mut size4 = [0u8; 4]; let mut size8 = [0u8; 8]; @@ -49,9 +49,11 @@ pub fn get_apk_signature(apk: &str) -> Result<(u32, u32)> { ensure!(size_of_block == size8, "not a signed apk"); + let mut v2_signing: Option<(u32, String)> = None; + let mut v3_signing: Option<(u32, String)> = None; loop { let mut id = [0u8; 4]; - let offset = 4u32; + let mut offset = 4u32; f.read_exact(&mut size8)?; // sequence length if size8 == size_of_block { @@ -61,36 +63,10 @@ pub fn get_apk_signature(apk: &str) -> Result<(u32, u32)> { f.read_exact(&mut id)?; // id let id = u32::from_le_bytes(id); - if (id ^ 0xdead_beef_u32) == 0xafa4_39f5_u32 || (id ^ 0xdead_beef_u32) == 0x2efe_d62f_u32 { - f.read_exact(&mut size4)?; // signer-sequence length - f.read_exact(&mut size4)?; // signer length - f.read_exact(&mut size4)?; // signed data length - // offset += 0x4 * 3; - - f.read_exact(&mut size4)?; // digests-sequcence length - let pos = u32::from_le_bytes(size4); - f.seek(SeekFrom::Current(i64::from(pos)))?; - // offset += 0x4 + pos; - - f.read_exact(&mut size4)?; // certificates length - f.read_exact(&mut size4)?; // certificate length - // offset += 0x4 * 2; - - let mut hash = 1i32; - let mut c = [0u8; 1]; - - let j = u32::from_le_bytes(size4); - for _ in 0..j { - f.read_exact(&mut c)?; - hash = hash.wrapping_mul(31).wrapping_add(i32::from(c[0] as i8)); - } - - // offset += j; - - let out_size = j; - let out_hash = (hash as u32) ^ 0x1413_1211_u32; - - return Ok((out_size, out_hash)); + if id == 0x7109_871a_u32 { + v2_signing = Some(calc_cert_sha256(&mut f, &mut size4, &mut offset)?); + } else if id == 0xf053_68c0_u32 { + v3_signing = Some(calc_cert_sha256(&mut f, &mut size4, &mut offset)?); } f.seek(SeekFrom::Current( @@ -98,5 +74,47 @@ pub fn get_apk_signature(apk: &str) -> Result<(u32, u32)> { ))?; } - Err(anyhow::anyhow!("Unknown error")) + match (v2_signing, v3_signing) { + (None, Some(s)) => Ok(s), + (Some(s), None) => Ok(s), + (Some(s1), Some(s2)) => { + if s1 == s2 { + Ok(s1) + } else { + Err(anyhow::anyhow!( + "Inconsisent signature, v2: {}, v3: {}!", + s1.1, + s2.1 + )) + } + } + _ => Err(anyhow::anyhow!("Unknown signature!")), + } +} + +fn calc_cert_sha256( + f: &mut std::fs::File, + size4: &mut [u8; 4], + offset: &mut u32, +) -> Result<(u32, String)> { + f.read_exact(size4)?; // signer-sequence length + f.read_exact(size4)?; // signer length + f.read_exact(size4)?; // signed data length + *offset += 0x4 * 3; + + f.read_exact(size4)?; // digests-sequence length + let pos = u32::from_le_bytes(*size4); // skip digests + f.seek(SeekFrom::Current(i64::from(pos)))?; + *offset += 0x4 + pos; + + f.read_exact(size4)?; // certificates length + f.read_exact(size4)?; // certificate length + *offset += 0x4 * 2; + + let cert_len = u32::from_le_bytes(*size4); + let mut cert: Vec = vec![0; cert_len as usize]; + f.read_exact(&mut cert)?; + *offset += cert_len; + + Ok((cert_len, sha256::digest(&cert))) } diff --git a/userspace/ksud/src/cli.rs b/userspace/ksud/src/cli.rs index 3503b226..410195a9 100644 --- a/userspace/ksud/src/cli.rs +++ b/userspace/ksud/src/cli.rs @@ -228,7 +228,7 @@ pub fn run() -> Result<()> { Debug::SetManager { apk } => debug::set_manager(&apk), Debug::GetSign { apk } => { let sign = apk_sign::get_apk_signature(&apk)?; - println!("size: {:#x}, hash: {:#x}", sign.0, sign.1); + println!("size: {:#x}, hash: {}", sign.0, sign.1); Ok(()) } Debug::Version => { diff --git a/userspace/ksud/src/debug.rs b/userspace/ksud/src/debug.rs index ff35caaf..6f729e41 100644 --- a/userspace/ksud/src/debug.rs +++ b/userspace/ksud/src/debug.rs @@ -15,24 +15,25 @@ fn read_u32(path: &PathBuf) -> Result { Ok(content) } -fn set_kernel_param(size: u32, hash: u32) -> Result<()> { +fn set_kernel_param(size: u32, hash: String) -> Result<()> { let kernel_param_path = Path::new(KERNEL_PARAM_PATH).join("parameters"); + let expeced_size_path = kernel_param_path.join("ksu_expected_size"); let expeced_hash_path = kernel_param_path.join("ksu_expected_hash"); println!( - "before size: {:#x} hash: {:#x}", + "before size: {:#x} hash: {}", read_u32(&expeced_size_path)?, - read_u32(&expeced_hash_path)? + std::fs::read_to_string(&expeced_hash_path)? ); std::fs::write(&expeced_size_path, size.to_string())?; - std::fs::write(&expeced_hash_path, hash.to_string())?; + std::fs::write(&expeced_hash_path, hash)?; println!( - "after size: {:#x} hash: {:#x}", + "after size: {:#x} hash: {}", read_u32(&expeced_size_path)?, - read_u32(&expeced_hash_path)? + std::fs::read_to_string(&expeced_hash_path)? ); Ok(())