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:
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
133
kernel/ksud.c
133
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
|
||||
}
|
||||
|
||||
@@ -5,4 +5,6 @@
|
||||
|
||||
void on_post_fs_data(void);
|
||||
|
||||
bool ksu_is_safe_mode(void);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user