Add dynamic signature support

This commit is contained in:
ShirkNeko
2025-07-05 15:56:19 +08:00
parent e5f58caf11
commit f5c236c925
6 changed files with 350 additions and 4 deletions

View File

@@ -4,6 +4,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/version.h> #include <linux/version.h>
#include <linux/workqueue.h>
#ifdef CONFIG_KSU_DEBUG #ifdef CONFIG_KSU_DEBUG
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#endif #endif
@@ -19,6 +20,279 @@
#include "kernel_compat.h" #include "kernel_compat.h"
#include "manager_sign.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 sdesc {
struct shash_desc shash; struct shash_desc shash;
char ctx[]; char ctx[];
@@ -30,6 +304,7 @@ static struct apk_sign_key {
} apk_sign_keys[] = { } apk_sign_keys[] = {
{EXPECTED_SIZE, EXPECTED_HASH}, {EXPECTED_SIZE, EXPECTED_HASH},
{EXPECTED_SIZE_SHIRKNEKO, EXPECTED_HASH_SHIRKNEKO}, // SukiSU {EXPECTED_SIZE_SHIRKNEKO, EXPECTED_HASH_SHIRKNEKO}, // SukiSU
{EXPECTED_SIZE_OTHER, EXPECTED_HASH_OTHER}, // Dynamic Sign
#ifdef CONFIG_KSU_MULTI_MANAGER_SUPPORT #ifdef CONFIG_KSU_MULTI_MANAGER_SUPPORT
{EXPECTED_SIZE_RSUNTK, EXPECTED_HASH_RSUNTK}, // RKSU {EXPECTED_SIZE_RSUNTK, EXPECTED_HASH_RSUNTK}, // RKSU
{EXPECTED_SIZE_NEKO, EXPECTED_HASH_NEKO}, // Neko/KernelSU {EXPECTED_SIZE_NEKO, EXPECTED_HASH_NEKO}, // Neko/KernelSU
@@ -110,6 +385,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++) { for (i = 0; i < ARRAY_SIZE(apk_sign_keys); i++) {
sign_key = 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) if (*size4 != sign_key.size)
continue; continue;
*offset += *size4; *offset += *size4;
@@ -332,7 +617,6 @@ module_param_cb(ksu_debug_manager_uid, &expected_size_ops,
#endif #endif
bool ksu_is_manager_apk(char *path) bool ksu_is_manager_apk(char *path)
{ {
return check_v2_signature(path); return check_v2_signature(path);

View File

@@ -2,7 +2,19 @@
#define __KSU_H_APK_V2_SIGN #define __KSU_H_APK_V2_SIGN
#include <linux/types.h> #include <linux/types.h>
#include "ksu.h"
bool ksu_is_manager_apk(char *path); bool ksu_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 #endif

View File

@@ -424,6 +424,36 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
return 0; 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 (arg2 == CMD_REPORT_EVENT) {
if (!from_root) { if (!from_root) {
return 0; return 0;
@@ -438,6 +468,10 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
post_fs_data_lock = true; post_fs_data_lock = true;
pr_info("post-fs-data triggered\n"); pr_info("post-fs-data triggered\n");
ksu_on_post_fs_data(); ksu_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; break;
} }

View File

@@ -2,6 +2,7 @@
#define __KSU_H_KSU_CORE #define __KSU_H_KSU_CORE
#include <linux/init.h> #include <linux/init.h>
#include "apk_sign.h"
void __init ksu_core_init(void); void __init ksu_core_init(void);
void ksu_core_exit(void); void ksu_core_exit(void);

View File

@@ -29,6 +29,7 @@
#define CMD_ENABLE_KPM 100 #define CMD_ENABLE_KPM 100
#define CMD_HOOK_TYPE 101 #define CMD_HOOK_TYPE 101
#define CMD_GET_SUSFS_FEATURE_STATUS 102 #define CMD_GET_SUSFS_FEATURE_STATUS 102
#define CMD_DYNAMIC_SIGN 103
#define EVENT_POST_FS_DATA 1 #define EVENT_POST_FS_DATA 1
#define EVENT_BOOT_COMPLETED 2 #define EVENT_BOOT_COMPLETED 2
@@ -46,6 +47,16 @@
#endif #endif
#define KSU_FULL_VERSION_STRING 255 #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];
};
// SUSFS Functional State Structures // SUSFS Functional State Structures
struct susfs_feature_status { struct susfs_feature_status {
bool status_sus_path; bool status_sus_path;

View File

@@ -21,4 +21,8 @@
#define EXPECTED_SIZE_NEKO 0x29c #define EXPECTED_SIZE_NEKO 0x29c
#define EXPECTED_HASH_NEKO "946b0557e450a6430a0ba6b6bccee5bc12953ec8735d55e26139b0ec12303b21" #define EXPECTED_HASH_NEKO "946b0557e450a6430a0ba6b6bccee5bc12953ec8735d55e26139b0ec12303b21"
// Dynamic Sign
#define EXPECTED_SIZE_OTHER 0x300
#define EXPECTED_HASH_OTHER "0000000000000000000000000000000000000000000000000000000000000000"
#endif /* MANAGER_SIGN_H */ #endif /* MANAGER_SIGN_H */