kernel: press KEY_VOLUMEDOWN over 2 seconds will enter safemode and disable all modules

Co-authored-by: Ylarod <me@ylarod.cn>
This commit is contained in:
weishu
2023-02-13 21:23:18 +07:00
parent 20ff530962
commit ca950d909b
4 changed files with 142 additions and 4 deletions

View File

@@ -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);

View File

@@ -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

View File

@@ -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
}

View File

@@ -5,4 +5,6 @@
void on_post_fs_data(void);
bool ksu_is_safe_mode(void);
#endif