diff --git a/kernel/core_hook.c b/kernel/core_hook.c index 1090be5e..efe30ad3 100644 --- a/kernel/core_hook.c +++ b/kernel/core_hook.c @@ -243,6 +243,16 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3, return 0; } + if (arg2 == CMD_CHECK_SAFEMODE) { + if (ksu_is_safe_mode()) { + pr_warn("safemode enabled!\n"); + if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) { + pr_err("safemode: prctl reply error\n"); + } + } + return 0; + } + // all other cmds are for 'root manager' if (!is_manager()) { pr_info("Only manager can do cmd: %d\n", arg2); diff --git a/kernel/ksu.h b/kernel/ksu.h index f22c3c95..3ace692d 100644 --- a/kernel/ksu.h +++ b/kernel/ksu.h @@ -21,6 +21,7 @@ #define CMD_GET_DENY_LIST 6 #define CMD_REPORT_EVENT 7 #define CMD_SET_SEPOLICY 8 +#define CMD_CHECK_SAFEMODE 9 #define EVENT_POST_FS_DATA 1 #define EVENT_BOOT_COMPLETED 2 diff --git a/kernel/ksud.c b/kernel/ksud.c index d55ed1ad..40a8a3c7 100644 --- a/kernel/ksud.c +++ b/kernel/ksud.c @@ -3,12 +3,15 @@ #include "linux/dcache.h" #include "linux/err.h" #include "linux/fs.h" +#include "linux/input-event-codes.h" #include "linux/kprobes.h" #include "linux/printk.h" #include "linux/types.h" #include "linux/uaccess.h" #include "linux/version.h" #include "linux/workqueue.h" +#include "linux/input.h" +#include "linux/time64.h" #include "allowlist.h" #include "arch.h" @@ -21,32 +24,35 @@ static const char KERNEL_SU_RC[] = "on post-fs-data\n" // We should wait for the post-fs-data finish - " exec u:r:su:s0 root -- "KSUD_PATH" post-fs-data\n" + " exec u:r:su:s0 root -- " KSUD_PATH " post-fs-data\n" "\n" "on nonencrypted\n" - " exec u:r:su:s0 root -- "KSUD_PATH" services\n" + " exec u:r:su:s0 root -- " KSUD_PATH " services\n" "\n" "on property:vold.decrypt=trigger_restart_framework\n" - " exec u:r:su:s0 root -- "KSUD_PATH" services\n" + " exec u:r:su:s0 root -- " KSUD_PATH " services\n" "\n" "on property:sys.boot_completed=1\n" - " exec u:r:su:s0 root -- "KSUD_PATH" boot-completed\n" + " exec u:r:su:s0 root -- " KSUD_PATH " boot-completed\n" "\n" "\n"; static void stop_vfs_read_hook(); static void stop_execve_hook(); +static void stop_input_hook(); #ifdef CONFIG_KPROBES static struct work_struct stop_vfs_read_work; static struct work_struct stop_execve_hook_work; +static struct work_struct stop_input_hook_work; #else static bool vfs_read_hook = true; static bool execveat_hook = true; +static bool input_hook = true; #endif void on_post_fs_data(void) @@ -59,6 +65,8 @@ void on_post_fs_data(void) done = true; pr_info("ksu_load_allow_list"); ksu_load_allow_list(); + // sanity check, this may influence the performance + stop_input_hook(); } int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr, @@ -184,6 +192,85 @@ int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr, return 0; } +static const time64_t UNINITIALIZED = -1; +static time64_t last_vol_down_pressed = UNINITIALIZED; +static time64_t last_vol_down_release = UNINITIALIZED; + +static bool is_time_initialized(time64_t t) +{ + return t != UNINITIALIZED; +} + +int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, + int *value) +{ +#ifndef CONFIG_KPROBES + if (!input_hook) { + return 0; + } +#endif + if (*type == EV_KEY && *code == KEY_VOLUMEDOWN) { + int val = *value; + pr_info("KEY_VOLUMEDOWN val: %d\n", val); + if (val) { + // key pressed + last_vol_down_pressed = ktime_get_seconds(); + } else { + // key released + if (is_time_initialized(last_vol_down_pressed)) { + last_vol_down_release = ktime_get_seconds(); + // when released, stop hook + stop_input_hook(); + } else { + pr_info("KEY_VOLUMEDOWN released, but not pressed yet\n"); + } + } + pr_info("last_vol_down_pressed: %ld, last_vol_down_release: %ld\n", + last_vol_down_pressed, last_vol_down_release); + } + + return 0; +} + +bool ksu_is_safe_mode() { + + static bool safe_mode = false; + if (safe_mode) { + // don't need to check again, userspace may call multiple times + return true; + } + + // stop hook first! + stop_input_hook(); + + pr_info("ksu_is_safe_mode last_vol_down_pressed: %ld, last_vol_down_release: %ld\n", + last_vol_down_pressed, last_vol_down_release); + if (!is_time_initialized(last_vol_down_pressed)) { + // not pressed yet + return false; + } + + // vol down pressed + time64_t vol_down_time; + if (!is_time_initialized(last_vol_down_release)) { + // not released yet, use current time + vol_down_time = ktime_get_seconds(); + } else { + vol_down_time = last_vol_down_release; + } + + pr_info("ksu_is_safe_mode vol_down_time: %ld, last_vol_down_pressed: %ld\n", + vol_down_time, last_vol_down_pressed); + if (vol_down_time - last_vol_down_pressed >= 2) { + // pressed over 2 seconds + pr_info("KEY_VOLUMEDOWN pressed over 2 seconds, safe mode detected!\n"); + safe_mode = true; + return true; + } + + return false; +} + #ifdef CONFIG_KPROBES // https://elixir.bootlin.com/linux/v5.10.158/source/fs/exec.c#L1864 @@ -209,6 +296,15 @@ static int read_handler_pre(struct kprobe *p, struct pt_regs *regs) return ksu_handle_vfs_read(file_ptr, buf_ptr, count_ptr, pos_ptr); } +static int input_handle_event_handler_pre(struct kprobe *p, + struct pt_regs *regs) +{ + unsigned int *type = (unsigned int *)&PT_REGS_PARM2(regs); + unsigned int *code = (unsigned int *)&PT_REGS_PARM3(regs); + int *value = (int *)&PT_REGS_PARM4(regs); + return ksu_handle_input_handle_event(type, code, value); +} + static struct kprobe execve_kp = { #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) .symbol_name = "do_execveat_common", @@ -225,6 +321,11 @@ static struct kprobe vfs_read_kp = { .pre_handler = read_handler_pre, }; +static struct kprobe input_handle_event_kp = { + .symbol_name = "input_handle_event", + .pre_handler = input_handle_event_handler_pre, +}; + static void do_stop_vfs_read_hook(struct work_struct *work) { unregister_kprobe(&vfs_read_kp); @@ -234,6 +335,11 @@ static void do_stop_execve_hook(struct work_struct *work) { unregister_kprobe(&execve_kp); } + +static void do_stop_input_hook(struct work_struct *work) +{ + unregister_kprobe(&input_handle_event_kp); +} #endif static void stop_vfs_read_hook() @@ -256,6 +362,21 @@ static void stop_execve_hook() #endif } +static void stop_input_hook() +{ + static bool input_hook_stopped = false; + if (input_hook_stopped) { + return; + } + input_hook_stopped = true; +#ifdef CONFIG_KPROBES + bool ret = schedule_work(&stop_input_hook_work); + pr_info("unregister input kprobe: %d!\n", ret); +#else + input_hook = false; +#endif +} + // ksud: module support void ksu_enable_ksud() { @@ -268,7 +389,11 @@ void ksu_enable_ksud() ret = register_kprobe(&vfs_read_kp); pr_info("ksud: vfs_read_kp: %d\n", ret); + ret = register_kprobe(&input_handle_event_kp); + pr_info("ksud: input_handle_event_kp: %d\n", ret); + INIT_WORK(&stop_vfs_read_work, do_stop_vfs_read_hook); INIT_WORK(&stop_execve_hook_work, do_stop_execve_hook); + INIT_WORK(&stop_input_hook_work, do_stop_input_hook); #endif } diff --git a/kernel/ksud.h b/kernel/ksud.h index c8821486..d5a35aec 100644 --- a/kernel/ksud.h +++ b/kernel/ksud.h @@ -5,4 +5,6 @@ void on_post_fs_data(void); +bool ksu_is_safe_mode(void); + #endif \ No newline at end of file