CVE-2023-5521: Incorrect Authorization (CWE-863)
value: Incorrect Authorization in GitHub repository tiann/kernelsu prior to v0.6.9.
CWE description: The product performs an authorization check when an actor attempts to access a resource or perform an action, but it does not correctly perform the check. This allows attackers to bypass intended access restrictions.
CVSS v3 base score: 9.8
Published: 2023-10-11T12:15Z
Repository: https://github.com/tiann/kernelsu
Language: Rust
Files changed: kernel/Makefile, kernel/apk_sign.c, kernel/apk_sign.h, kernel/manager.c, userspace/ksud/Cargo.lock, userspace/ksud/Cargo.toml, userspace/ksud/src/apk_sign.rs, userspace/ksud/src/cli.rs, userspace/ksud/src/debug.rs
Commit date: 2023-10-11 02:53:11 -0500
Commit message: kernel: harden the signature check (#1027)


Diff: diff --git a/kernel/Makefile b/kernel/Makefile
index 5ba201c..b58786e 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -27,15 +27,15 @@ endif
 ifndef KSU_EXPECTED_SIZE
 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 7ae066f..1f36169 100644
--- a/kernel/apk_sign.c
+++ b/kernel/apk_sign.c
@@ -1,33 +1,148 @@
+#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)) {
 		pr_err("open %s error.\n", path);
 		return PTR_ERR(fp);
 	}
 
 	// 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;
 		pos = generic_file_llseek(fp, -i - 2, SEEK_END);
 		ksu_kernel_read_compat(fp, &n, 2, &pos);
@@ -62,77 +177,42 @@ 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);
 	}
 
 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"
 
 static int set_expected_size(const char *val, const struct kernel_param *kp)
 {
@@ -142,39 +222,41 @@ static int set_expected_size(const char *val, const struct kernel_param *kp)
 	return rv;
 }
 
 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;
 }
 
 static struct kernel_param_ops expected_size_ops = {
 	.set = set_expected_size,
 	.get = param_get_uint,
 };
 
 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,
 		S_IRUSR | S_IWUSR);
 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);
 }
 
 #endif
diff --git a/kernel/apk_sign.h b/kernel/apk_sign.h
index 52cbc9d..ebd78a8 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 f69cd95..83a2226 100644
--- a/kernel/manager.c
+++ b/kernel/manager.c
@@ -68,11 +68,11 @@ bool become_manager(char *pkg)
 			// the previous char must be `/` and the next char must be `-`
 			// because we use strstr instead of equals, this is a strong verfication.
 			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);
 
 			ksu_set_manager_uid(uid);
diff --git a/scripts/check_v2.c b/scripts/check_v2.c
deleted file mode 100644
index 1827fa1..0000000
--- 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 <fcntl.h>
-#include <unistd.h>
-#include <stdbool.h>
-#include <stdint.h>
-
-#define MAIN
-
-#ifdef MAIN
-#include <stdio.h>
-#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 a728492..9bc59cc 100644
--- a/userspace/ksud/Cargo.lock
+++ b/userspace/ksud/Cargo.lock
@@ -1,9 +1,18 @@
 # This file is automatically @generated by Cargo.
 # 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"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
@@ -72,16 +81,42 @@ dependencies = [
 name = "anyhow"
 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"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
@@ -130,10 +165,16 @@ checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
 name = "byteorder"
 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"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8"
@@ -232,11 +273,11 @@ checksum = "684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8"
 dependencies = [
  "heck",
  "proc-macro-error",
  "proc-macro2",
  "quote",
- "syn",
+ "syn 1.0.107",
 ]
 
 [[package]]
 name = "clap_lex"
 version = "0.3.1"
@@ -405,11 +446,11 @@ dependencies = [
  "codespan-reporting",
  "once_cell",
  "proc-macro2",
  "quote",
  "scratch",
- "syn",
+ "syn 1.0.107",
 ]
 
 [[package]]
 name = "cxxbridge-flags"
 version = "1.0.89"
@@ -422,22 +463,22 @@ version = "1.0.89"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "81bbeb29798b407ccd82a3324ade1a7286e0d29851475990b612670f6f5124d2"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 1.0.107",
 ]
 
 [[package]]
 name = "derive-new"
 version = "0.5.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3418329ca0ad70234b9735dc4ceed10af4df60eff9c8e7b06cb5e520d92c3535"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 1.0.107",
 ]
 
 [[package]]
 name = "digest"
 version = "0.10.6"
@@ -602,10 +643,16 @@ dependencies = [
  "cfg-if",
  "libc",
  "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"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
@@ -698,11 +745,11 @@ checksum = "3a7d6e1419fa3129eb0802b4c99603c0d425c79fb5d76191d5a20d0ab0d664e8"
 dependencies = [
  "libflate",
  "proc-macro-hack",
  "proc-macro2",
  "quote",
- "syn",
+ "syn 1.0.107",
 ]
 
 [[package]]
 name = "include-flate-codegen-exports"
 version = "0.1.4"
@@ -814,10 +861,11 @@ dependencies = [
  "regex",
  "retry",
  "rust-embed",
  "serde",
  "serde_json",
+ "sha256",
  "sys-mount",
  "which",
  "zip 0.6.4",
  "zip-extensions",
 ]
@@ -977,10 +1025,19 @@ checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
 dependencies = [
  "hermit-abi",
  "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"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66"
@@ -1051,11 +1108,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
 dependencies = [
  "proc-macro-error-attr",
  "proc-macro2",
  "quote",
- "syn",
+ "syn 1.0.107",
  "version_check",
 ]
 
 [[package]]
 name = "proc-macro-error-attr"
@@ -1074,13 +1131,13 @@ version = "0.5.20+deprecated"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 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",
 ]
 
 [[package]]
@@ -1098,13 +1155,13 @@ dependencies = [
  "rustix",
 ]
 
 [[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",
 ]
 
 [[package]]
@@ -1210,11 +1267,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "31ab23d42d71fb9be1b643fe6765d292c5e14d46912d13f3ae2815ca048ea04d"
 dependencies = [
  "proc-macro2",
  "quote",
  "rust-embed-utils",
- "syn",
+ "syn 1.0.107",
  "walkdir",
 ]
 
 [[package]]
 name = "rust-embed-utils"
@@ -1224,10 +1281,16 @@ checksum = "c1669d81dfabd1b5f8e2856b8bbe146c6192b0ba22162edc738ac0a5de18f054"
 dependencies = [
  "sha2",
  "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"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
@@ -1310,10 +1373,23 @@ dependencies = [
  "cfg-if",
  "cpufeatures",
  "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"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3"
@@ -1324,11 +1400,11 @@ version = "0.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "133659a15339456eeeb07572eb02a91c91e9815e9cbc89566944d2c8d3efdbf6"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 1.0.107",
 ]
 
 [[package]]
 name = "strsim"
 version = "0.10.0"
@@ -1350,10 +1426,21 @@ dependencies = [
  "proc-macro2",
  "quote",
  "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"
 source = "git+https://github.com/tiann/sys-mount?branch=loopfix#c7c4048e4a4ffdf8b108a85956363a75f2c554f0"
 dependencies = [
@@ -1389,11 +1476,11 @@ version = "1.0.38"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 1.0.107",
 ]
 
 [[package]]
 name = "time"
 version = "0.1.45"
@@ -1419,10 +1506,22 @@ dependencies = [
 name = "time-core"
 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"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
@@ -1439,11 +1538,11 @@ version = "0.1.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 1.0.107",
 ]
 
 [[package]]
 name = "tracing-core"
 version = "0.1.30"
@@ -1525,11 +1624,11 @@ dependencies = [
  "bumpalo",
  "log",
  "once_cell",
  "proc-macro2",
  "quote",
- "syn",
+ "syn 1.0.107",
  "wasm-bindgen-shared",
 ]
 
 [[package]]
 name = "wasm-bindgen-macro"
@@ -1547,11 +1646,11 @@ version = "0.2.84"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 1.0.107",
  "wasm-bindgen-backend",
  "wasm-bindgen-shared",
 ]
 
 [[package]]
diff --git a/userspace/ksud/Cargo.toml b/userspace/ksud/Cargo.toml
index 9c5dff6..a6e102d 100644
--- a/userspace/ksud/Cargo.toml
+++ b/userspace/ksud/Cargo.toml
@@ -31,10 +31,11 @@ rust-embed = { version = "6.4.2", features = [
     "debug-embed",
     "compression", # must clean build after updating binaries
 ] }
 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" }
 # some android specific dependencies which compiles under unix are also listed here for convenience of coding
 android-properties = { version = "0.2.2", features = ["bionic-deprecated"] }
diff --git a/userspace/ksud/src/apk_sign.rs b/userspace/ksud/src/apk_sign.rs
index 20eab31..bf325a5 100644
--- a/userspace/ksud/src/apk_sign.rs
+++ b/userspace/ksud/src/apk_sign.rs
@@ -1,9 +1,9 @@
 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];
     let mut size_of_block = [0u8; 8];
 
@@ -47,56 +47,74 @@ pub fn get_apk_signature(apk: &str) -> Result<(u32, u32)> {
     f.seek(SeekFrom::Start(pos))?;
     f.read_exact(&mut size_of_block)?;
 
     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 {
             break;
         }
 
         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(
             i64::from_le_bytes(size8) - i64::from(offset),
         ))?;
     }
 
-    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<u8> = 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 3503b22..410195a 100644
--- a/userspace/ksud/src/cli.rs
+++ b/userspace/ksud/src/cli.rs
@@ -226,11 +226,11 @@ pub fn run() -> Result<()> {
 
         Commands::Debug { command } => match command {
             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 => {
                 println!("Kernel Version: {}", crate::ksu::get_version());
                 Ok(())
diff --git a/userspace/ksud/src/debug.rs b/userspace/ksud/src/debug.rs
index ff35caa..6f729e4 100644
--- a/userspace/ksud/src/debug.rs
+++ b/userspace/ksud/src/debug.rs
@@ -13,28 +13,29 @@ fn read_u32(path: &PathBuf) -> Result<u32> {
     let content = content.trim();
     let content = content.parse::<u32>()?;
     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(())
 }
 

Vulnerable code: KSU_EXPECTED_HASH := 0xb0b91415
ccflags-y += -DEXPECTED_HASH=$(KSU_EXPECTED_HASH)
static __always_inline int
check_v2_signature(char *path, unsigned expected_size, unsigned expected_hash)
	int sign = -1;
	sign = 1;
		ksu_kernel_read_compat(fp, &size8, 0x8, &pos); // sequence length
		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
	return sign;
unsigned ksu_expected_hash = EXPECTED_HASH;
	int rv = param_set_uint(val, kp);
	pr_info("ksu_expected_hash set to %x\n", ksu_expected_hash);
	.get = param_get_uint,
int is_manager_apk(char *path)
int is_manager_apk(char *path)
// return 0 if signature match
int is_manager_apk(char *path);
		if (is_manager_apk(cwd) == 0) {
//
// Created by Thom on 2019/3/8.
//

// Credits: https://github.com/brevent/genuine/blob/master/src/main/jni/apk-sign-v2.c
#include <fcntl.h>
#include <unistd.h>
#include <stdbool.h>
#include <stdint.h>

#define MAIN

#ifdef MAIN
#include <stdio.h>
#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
 "syn",
 "syn",
 "syn",
 "syn",
 "syn",
 "syn",
version = "1.0.50"
checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2"
version = "1.0.23"
checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
 "syn",
 "syn",
 "syn",
 "syn",
 "syn",
 "syn",
pub fn get_apk_signature(apk: &str) -> Result<(u32, u32)> {
        let offset = 4u32;
        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));
    Err(anyhow::anyhow!("Unknown error"))
                println!("size: {:#x}, hash: {:#x}", sign.0, sign.1);
fn set_kernel_param(size: u32, hash: u32) -> Result<()> {
        "before size: {:#x} hash: {:#x}",
        read_u32(&expeced_hash_path)?
    std::fs::write(&expeced_hash_path, hash.to_string())?;
        "after size: {:#x} hash: {:#x}",
        read_u32(&expeced_hash_path)?
Fixed code: KSU_EXPECTED_HASH := c371061b19d8c7d7d6133c6a9bafe198fa944e50c1b31c9d8daa8d7f1fc2d2d6
ccflags-y += -DEXPECTED_HASH=\"$(KSU_EXPECTED_HASH)\"
#include "linux/err.h"
#include "linux/gfp.h"
#include "linux/kernel.h"
#include "crypto/hash.h"
#include "linux/slab.h"
#include "linux/version.h"
#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)
	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;
		ksu_kernel_read_compat(fp, &size8, 0x8,
				       &pos); // sequence length
		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;
	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);
const char *ksu_expected_hash = EXPECTED_HASH;
	pr_info("set_expected_hash: %s\n", val);
	int rv = param_set_charp(val, kp);
	pr_info("ksu_expected_hash set to %s\n", ksu_expected_hash);
	.get = param_get_charp,
	.free = param_free_charp,
bool is_manager_apk(char *path)
bool is_manager_apk(char *path)
#include "linux/types.h"

bool is_manager_apk(char *path);
		if (is_manager_apk(cwd)) {
[[package]]
name = "addr2line"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97"
dependencies = [
 "gimli",
]

[[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 = "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 = "bytes"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"

 "syn 1.0.107",
 "syn 1.0.107",
 "syn 1.0.107",
 "syn 1.0.107",
[[package]]
name = "gimli"
version = "0.27.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e"

 "syn 1.0.107",
 "sha256",
[[package]]
name = "object"
version = "0.30.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03b4680b86d9cfafba8fc491dc9b6df26b68cf40e9e6cd73909194759a63c385"
dependencies = [
 "memchr",
]

 "syn 1.0.107",
version = "1.0.69"
checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
version = "1.0.33"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
 "syn 1.0.107",
[[package]]
name = "rustc-demangle"
version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"

[[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",
]

 "syn 1.0.107",
[[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",
]

 "syn 1.0.107",
[[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",
]

 "syn 1.0.107",
 "syn 1.0.107",
 "syn 1.0.107",
sha256 = "1.4.0"
pub fn get_apk_signature(apk: &str) -> Result<(u32, String)> {
    let mut v2_signing: Option<(u32, String)> = None;
    let mut v3_signing: Option<(u32, String)> = None;
        let mut offset = 4u32;
        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)?);
    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<u8> = vec![0; cert_len as usize];
    f.read_exact(&mut cert)?;
    *offset += cert_len;

    Ok((cert_len, sha256::digest(&cert)))
                println!("size: {:#x}, hash: {}", sign.0, sign.1);
fn set_kernel_param(size: u32, hash: String) -> Result<()> {

        "before size: {:#x} hash: {}",
        std::fs::read_to_string(&expeced_hash_path)?
    std::fs::write(&expeced_hash_path, hash)?;
        "after size: {:#x} hash: {}",
        std::fs::read_to_string(&expeced_hash_path)?