diff --git a/kernel/apk_sign.c b/kernel/apk_sign.c index fcb2aedc..5ee4329e 100644 --- a/kernel/apk_sign.c +++ b/kernel/apk_sign.c @@ -4,6 +4,7 @@ #include #include #include +#include #ifdef CONFIG_KSU_DEBUG #include #endif @@ -19,6 +20,279 @@ #include "kernel_compat.h" #include "manager_sign.h" +// Expected sizes and hashes for various APK signatures +#define DYNAMIC_SIGN_FILE_MAGIC 0x7f445347 // 'DSG', u32 +#define DYNAMIC_SIGN_FILE_VERSION 1 // u32 +#define KERNEL_SU_DYNAMIC_SIGN "/data/adb/ksu/.dynamic_sign" + +static struct dynamic_sign_config dynamic_sign = { + .size = 0x300, + .hash = "0000000000000000000000000000000000000000000000000000000000000000", + .is_set = 0 +}; + +static DEFINE_SPINLOCK(dynamic_sign_lock); +static struct work_struct ksu_save_dynamic_sign_work; +static struct work_struct ksu_load_dynamic_sign_work; +static struct work_struct ksu_clear_dynamic_sign_work; + +static void do_save_dynamic_sign(struct work_struct *work) +{ + u32 magic = DYNAMIC_SIGN_FILE_MAGIC; + u32 version = DYNAMIC_SIGN_FILE_VERSION; + struct dynamic_sign_config config_to_save; + loff_t off = 0; + unsigned long flags; + struct file *fp; + + spin_lock_irqsave(&dynamic_sign_lock, flags); + config_to_save = dynamic_sign; + spin_unlock_irqrestore(&dynamic_sign_lock, flags); + + if (!config_to_save.is_set) { + pr_info("Dynamic sign config not set, skipping save\n"); + return; + } + + fp = ksu_filp_open_compat(KERNEL_SU_DYNAMIC_SIGN, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (IS_ERR(fp)) { + pr_err("save_dynamic_sign create file failed: %ld\n", PTR_ERR(fp)); + return; + } + + if (ksu_kernel_write_compat(fp, &magic, sizeof(magic), &off) != sizeof(magic)) { + pr_err("save_dynamic_sign write magic failed.\n"); + goto exit; + } + + if (ksu_kernel_write_compat(fp, &version, sizeof(version), &off) != sizeof(version)) { + pr_err("save_dynamic_sign write version failed.\n"); + goto exit; + } + + if (ksu_kernel_write_compat(fp, &config_to_save, sizeof(config_to_save), &off) != sizeof(config_to_save)) { + pr_err("save_dynamic_sign write config failed.\n"); + goto exit; + } + + pr_info("Dynamic sign config saved successfully\n"); + +exit: + filp_close(fp, 0); +} + +// Loading dynamic signatures from persistent storage +static void do_load_dynamic_sign(struct work_struct *work) +{ + loff_t off = 0; + ssize_t ret = 0; + struct file *fp = NULL; + u32 magic; + u32 version; + struct dynamic_sign_config loaded_config; + unsigned long flags; + int i; + + fp = ksu_filp_open_compat(KERNEL_SU_DYNAMIC_SIGN, O_RDONLY, 0); + if (IS_ERR(fp)) { + if (PTR_ERR(fp) == -ENOENT) { + pr_info("No saved dynamic sign config found\n"); + } else { + pr_err("load_dynamic_sign open file failed: %ld\n", PTR_ERR(fp)); + } + return; + } + + if (ksu_kernel_read_compat(fp, &magic, sizeof(magic), &off) != sizeof(magic) || + magic != DYNAMIC_SIGN_FILE_MAGIC) { + pr_err("dynamic sign file invalid magic: %x!\n", magic); + goto exit; + } + + if (ksu_kernel_read_compat(fp, &version, sizeof(version), &off) != sizeof(version)) { + pr_err("dynamic sign read version failed\n"); + goto exit; + } + + pr_info("dynamic sign file version: %d\n", version); + + ret = ksu_kernel_read_compat(fp, &loaded_config, sizeof(loaded_config), &off); + if (ret <= 0) { + pr_info("load_dynamic_sign read err: %zd\n", ret); + goto exit; + } + + if (ret != sizeof(loaded_config)) { + pr_err("load_dynamic_sign read incomplete config: %zd/%zu\n", ret, sizeof(loaded_config)); + goto exit; + } + + if (loaded_config.size < 0x100 || loaded_config.size > 0x1000) { + pr_err("Invalid saved config size: 0x%x\n", loaded_config.size); + goto exit; + } + + if (strlen(loaded_config.hash) != 64) { + pr_err("Invalid saved config hash length: %zu\n", strlen(loaded_config.hash)); + goto exit; + } + + for (i = 0; i < 64; i++) { + char c = loaded_config.hash[i]; + if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f'))) { + pr_err("Invalid saved config hash character at position %d: %c\n", i, c); + goto exit; + } + } + + spin_lock_irqsave(&dynamic_sign_lock, flags); + dynamic_sign = loaded_config; + spin_unlock_irqrestore(&dynamic_sign_lock, flags); + + pr_info("Dynamic sign config loaded: size=0x%x, hash=%.16s...\n", + loaded_config.size, loaded_config.hash); + +exit: + filp_close(fp, 0); +} + +static bool persistent_dynamic_sign(void) +{ + return ksu_queue_work(&ksu_save_dynamic_sign_work); +} + +// Clear dynamic sign config file using the same method as do_save_dynamic_sign +static void do_clear_dynamic_sign_file(struct work_struct *work) +{ + loff_t off = 0; + struct file *fp; + char zero_buffer[512]; + + memset(zero_buffer, 0, sizeof(zero_buffer)); + + fp = ksu_filp_open_compat(KERNEL_SU_DYNAMIC_SIGN, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (IS_ERR(fp)) { + pr_err("clear_dynamic_sign create file failed: %ld\n", PTR_ERR(fp)); + return; + } + + // Write null bytes to overwrite the file content + if (ksu_kernel_write_compat(fp, zero_buffer, sizeof(zero_buffer), &off) != sizeof(zero_buffer)) { + pr_err("clear_dynamic_sign write null bytes failed.\n"); + } else { + pr_info("Dynamic sign config file cleared successfully\n"); + } + + filp_close(fp, 0); +} + +static bool clear_dynamic_sign_file(void) +{ + return ksu_queue_work(&ksu_clear_dynamic_sign_work); +} + +int ksu_handle_dynamic_sign(struct dynamic_sign_user_config *config) +{ + unsigned long flags; + int ret = 0; + int i; + + if (!config) { + return -EINVAL; + } + + switch (config->operation) { + case DYNAMIC_SIGN_OP_SET: + if (config->size < 0x100 || config->size > 0x1000) { + pr_err("invalid size: 0x%x\n", config->size); + return -EINVAL; + } + + if (strlen(config->hash) != 64) { + pr_err("invalid hash length: %zu\n", strlen(config->hash)); + return -EINVAL; + } + + for (i = 0; i < 64; i++) { + char c = config->hash[i]; + if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f'))) { + pr_err("invalid hash character at position %d: %c\n", i, c); + return -EINVAL; + } + } + + spin_lock_irqsave(&dynamic_sign_lock, flags); + dynamic_sign.size = config->size; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0) + strscpy(dynamic_sign.hash, config->hash, sizeof(dynamic_sign.hash)); +#else + strlcpy(dynamic_sign.hash, config->hash, sizeof(dynamic_sign.hash)); +#endif + dynamic_sign.is_set = 1; + spin_unlock_irqrestore(&dynamic_sign_lock, flags); + + persistent_dynamic_sign(); + pr_info("dynamic sign updated: size=0x%x, hash=%.16s...\n", config->size, config->hash); + break; + + case DYNAMIC_SIGN_OP_GET: + // Getting Dynamic Signatures + spin_lock_irqsave(&dynamic_sign_lock, flags); + if (dynamic_sign.is_set) { + config->size = dynamic_sign.size; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0) + strscpy(config->hash, dynamic_sign.hash, sizeof(config->hash)); +#else + strlcpy(config->hash, dynamic_sign.hash, sizeof(config->hash)); +#endif + ret = 0; + } else { + ret = -ENODATA; + } + spin_unlock_irqrestore(&dynamic_sign_lock, flags); + break; + + case DYNAMIC_SIGN_OP_CLEAR: + // Clearing dynamic signatures + spin_lock_irqsave(&dynamic_sign_lock, flags); + dynamic_sign.size = 0x300; + strcpy(dynamic_sign.hash, "0000000000000000000000000000000000000000000000000000000000000000"); + dynamic_sign.is_set = 0; + spin_unlock_irqrestore(&dynamic_sign_lock, flags); + + // Clear file using the same method as save + clear_dynamic_sign_file(); + + pr_info("Dynamic sign config cleared\n"); + break; + + default: + pr_err("Invalid dynamic sign operation: %d\n", config->operation); + return -EINVAL; + } + + return ret; +} + +bool ksu_load_dynamic_sign(void) +{ + return ksu_queue_work(&ksu_load_dynamic_sign_work); +} + +void ksu_dynamic_sign_init(void) +{ + INIT_WORK(&ksu_save_dynamic_sign_work, do_save_dynamic_sign); + INIT_WORK(&ksu_load_dynamic_sign_work, do_load_dynamic_sign); + INIT_WORK(&ksu_clear_dynamic_sign_work, do_clear_dynamic_sign_file); + pr_info("Dynamic sign initialized with persistent storage\n"); +} + +void ksu_dynamic_sign_exit(void) +{ + do_save_dynamic_sign(NULL); + pr_info("Dynamic sign exited with persistent storage\n"); +} + struct sdesc { struct shash_desc shash; char ctx[]; @@ -30,9 +304,7 @@ static struct apk_sign_key { } apk_sign_keys[] = { {EXPECTED_SIZE, EXPECTED_HASH}, {EXPECTED_SIZE_SHIRKNEKO, EXPECTED_HASH_SHIRKNEKO}, // SukiSU - {EXPECTED_SIZE_ZAKO, EXPECTED_HASH_ZAKO}, // ZakoSU - {EXPECTED_SIZE_RSUNTK, EXPECTED_HASH_RSUNTK}, // RKSU - {EXPECTED_SIZE_NEKO, EXPECTED_HASH_NEKO}, // Neko/KernelSU + {EXPECTED_SIZE_OTHER, EXPECTED_HASH_OTHER}, // Dynamic Sign }; static struct sdesc *init_sdesc(struct crypto_shash *alg) @@ -105,6 +377,16 @@ static bool check_block(struct file *fp, u32 *size4, loff_t *pos, u32 *offset) for (i = 0; i < ARRAY_SIZE(apk_sign_keys); i++) { sign_key = apk_sign_keys[i]; + if (i == 2) { + unsigned long flags; + spin_lock_irqsave(&dynamic_sign_lock, flags); + if (dynamic_sign.is_set) { + sign_key.size = dynamic_sign.size; + sign_key.sha256 = dynamic_sign.hash; + } + spin_unlock_irqrestore(&dynamic_sign_lock, flags); + } + if (*size4 != sign_key.size) continue; *offset += *size4; diff --git a/kernel/apk_sign.h b/kernel/apk_sign.h index 786d17bc..42d525c6 100644 --- a/kernel/apk_sign.h +++ b/kernel/apk_sign.h @@ -2,7 +2,19 @@ #define __KSU_H_APK_V2_SIGN #include +#include "ksu.h" bool is_manager_apk(char *path); +struct dynamic_sign_config { + unsigned int size; + char hash[65]; + int is_set; +}; + +int ksu_handle_dynamic_sign(struct dynamic_sign_user_config *config); +void ksu_dynamic_sign_init(void); +void ksu_dynamic_sign_exit(void); +bool ksu_load_dynamic_sign(void); + #endif \ No newline at end of file diff --git a/kernel/core_hook.c b/kernel/core_hook.c index 6601478c..1afebff4 100644 --- a/kernel/core_hook.c +++ b/kernel/core_hook.c @@ -342,6 +342,36 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3, return 0; } + // Allow the root manager to configure dynamic signatures + if (arg2 == CMD_DYNAMIC_SIGN) { + if (!from_root && !from_manager) { + return 0; + } + + struct dynamic_sign_user_config config; + + if (copy_from_user(&config, (void __user *)arg3, sizeof(config))) { + pr_err("copy dynamic sign config failed\n"); + return 0; + } + + int ret = ksu_handle_dynamic_sign(&config); + + if (ret == 0 && config.operation == DYNAMIC_SIGN_OP_GET) { + if (copy_to_user((void __user *)arg3, &config, sizeof(config))) { + pr_err("copy dynamic sign config back failed\n"); + return 0; + } + } + + if (ret == 0) { + if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) { + pr_err("dynamic_sign: prctl reply error\n"); + } + } + return 0; + } + if (arg2 == CMD_REPORT_EVENT) { if (!from_root) { return 0; @@ -353,6 +383,10 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3, post_fs_data_lock = true; pr_info("post-fs-data triggered\n"); on_post_fs_data(); + // Initializing Dynamic Signatures + ksu_dynamic_sign_init(); + ksu_load_dynamic_sign(); + pr_info("Dynamic sign config loaded during post-fs-data\n"); } break; } diff --git a/kernel/core_hook.h b/kernel/core_hook.h index 616951e8..6ed328a0 100644 --- a/kernel/core_hook.h +++ b/kernel/core_hook.h @@ -2,6 +2,7 @@ #define __KSU_H_KSU_CORE #include +#include "apk_sign.h" void __init ksu_core_init(void); void ksu_core_exit(void); diff --git a/kernel/ksu.h b/kernel/ksu.h index 341c436f..2f234c1d 100644 --- a/kernel/ksu.h +++ b/kernel/ksu.h @@ -27,6 +27,7 @@ #define CMD_GET_FULL_VERSION 0xC0FFEE1A #define CMD_ENABLE_KPM 100 +#define CMD_DYNAMIC_SIGN 103 #define EVENT_POST_FS_DATA 1 #define EVENT_BOOT_COMPLETED 2 @@ -44,6 +45,16 @@ #endif #define KSU_FULL_VERSION_STRING 255 +#define DYNAMIC_SIGN_OP_SET 0 +#define DYNAMIC_SIGN_OP_GET 1 +#define DYNAMIC_SIGN_OP_CLEAR 2 + +struct dynamic_sign_user_config { + unsigned int operation; + unsigned int size; + char hash[65]; +}; + struct root_profile { int32_t uid; int32_t gid; diff --git a/kernel/manager_sign.h b/kernel/manager_sign.h index dfff19f5..4377ba8e 100644 --- a/kernel/manager_sign.h +++ b/kernel/manager_sign.h @@ -5,24 +5,8 @@ #define EXPECTED_SIZE_SHIRKNEKO 0x35c #define EXPECTED_HASH_SHIRKNEKO "947ae944f3de4ed4c21a7e4f7953ecf351bfa2b36239da37a34111ad29993eef" -// weishu/KernelSU -#define EXPECTED_SIZE_WEISHU 0x033b -#define EXPECTED_HASH_WEISHU "c371061b19d8c7d7d6133c6a9bafe198fa944e50c1b31c9d8daa8d7f1fc2d2d6" - -// 5ec1cff/KernelSU -#define EXPECTED_SIZE_5EC1CFF 384 -#define EXPECTED_HASH_5EC1CFF "7e0c6d7278a3bb8e364e0fcba95afaf3666cf5ff3c245a3b63c8833bd0445cc4" - -// rsuntk/KernelSU -#define EXPECTED_SIZE_RSUNTK 0x396 -#define EXPECTED_HASH_RSUNTK "f415f4ed9435427e1fdf7f1fccd4dbc07b3d6b8751e4dbcec6f19671f427870b" - -// Neko/KernelSU -#define EXPECTED_SIZE_NEKO 0x29c -#define EXPECTED_HASH_NEKO "946b0557e450a6430a0ba6b6bccee5bc12953ec8735d55e26139b0ec12303b21" - -// ZAKO/ZAKOSU -#define EXPECTED_SIZE_ZAKO 0x34e -#define EXPECTED_HASH_ZAKO "a96ec51db032011dffb1184fa6513e421bd9073b3f392b04ecd2e7fdd4798065" +// Dynamic Sign +#define EXPECTED_SIZE_OTHER 0x300 +#define EXPECTED_HASH_OTHER "0000000000000000000000000000000000000000000000000000000000000000" #endif /* MANAGER_SIGN_H */