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;
|
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'
|
// all other cmds are for 'root manager'
|
||||||
if (!is_manager()) {
|
if (!is_manager()) {
|
||||||
pr_info("Only manager can do cmd: %d\n", arg2);
|
pr_info("Only manager can do cmd: %d\n", arg2);
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
#define CMD_GET_DENY_LIST 6
|
#define CMD_GET_DENY_LIST 6
|
||||||
#define CMD_REPORT_EVENT 7
|
#define CMD_REPORT_EVENT 7
|
||||||
#define CMD_SET_SEPOLICY 8
|
#define CMD_SET_SEPOLICY 8
|
||||||
|
#define CMD_CHECK_SAFEMODE 9
|
||||||
|
|
||||||
#define EVENT_POST_FS_DATA 1
|
#define EVENT_POST_FS_DATA 1
|
||||||
#define EVENT_BOOT_COMPLETED 2
|
#define EVENT_BOOT_COMPLETED 2
|
||||||
|
|||||||
133
kernel/ksud.c
133
kernel/ksud.c
@@ -3,12 +3,15 @@
|
|||||||
#include "linux/dcache.h"
|
#include "linux/dcache.h"
|
||||||
#include "linux/err.h"
|
#include "linux/err.h"
|
||||||
#include "linux/fs.h"
|
#include "linux/fs.h"
|
||||||
|
#include "linux/input-event-codes.h"
|
||||||
#include "linux/kprobes.h"
|
#include "linux/kprobes.h"
|
||||||
#include "linux/printk.h"
|
#include "linux/printk.h"
|
||||||
#include "linux/types.h"
|
#include "linux/types.h"
|
||||||
#include "linux/uaccess.h"
|
#include "linux/uaccess.h"
|
||||||
#include "linux/version.h"
|
#include "linux/version.h"
|
||||||
#include "linux/workqueue.h"
|
#include "linux/workqueue.h"
|
||||||
|
#include "linux/input.h"
|
||||||
|
#include "linux/time64.h"
|
||||||
|
|
||||||
#include "allowlist.h"
|
#include "allowlist.h"
|
||||||
#include "arch.h"
|
#include "arch.h"
|
||||||
@@ -21,32 +24,35 @@ static const char KERNEL_SU_RC[] =
|
|||||||
|
|
||||||
"on post-fs-data\n"
|
"on post-fs-data\n"
|
||||||
// We should wait for the post-fs-data finish
|
// 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"
|
"\n"
|
||||||
|
|
||||||
"on nonencrypted\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"
|
"\n"
|
||||||
|
|
||||||
"on property:vold.decrypt=trigger_restart_framework\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"
|
"\n"
|
||||||
|
|
||||||
"on property:sys.boot_completed=1\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"
|
||||||
|
|
||||||
"\n";
|
"\n";
|
||||||
|
|
||||||
static void stop_vfs_read_hook();
|
static void stop_vfs_read_hook();
|
||||||
static void stop_execve_hook();
|
static void stop_execve_hook();
|
||||||
|
static void stop_input_hook();
|
||||||
|
|
||||||
#ifdef CONFIG_KPROBES
|
#ifdef CONFIG_KPROBES
|
||||||
static struct work_struct stop_vfs_read_work;
|
static struct work_struct stop_vfs_read_work;
|
||||||
static struct work_struct stop_execve_hook_work;
|
static struct work_struct stop_execve_hook_work;
|
||||||
|
static struct work_struct stop_input_hook_work;
|
||||||
#else
|
#else
|
||||||
static bool vfs_read_hook = true;
|
static bool vfs_read_hook = true;
|
||||||
static bool execveat_hook = true;
|
static bool execveat_hook = true;
|
||||||
|
static bool input_hook = true;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void on_post_fs_data(void)
|
void on_post_fs_data(void)
|
||||||
@@ -59,6 +65,8 @@ void on_post_fs_data(void)
|
|||||||
done = true;
|
done = true;
|
||||||
pr_info("ksu_load_allow_list");
|
pr_info("ksu_load_allow_list");
|
||||||
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,
|
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;
|
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
|
#ifdef CONFIG_KPROBES
|
||||||
|
|
||||||
// https://elixir.bootlin.com/linux/v5.10.158/source/fs/exec.c#L1864
|
// 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);
|
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 = {
|
static struct kprobe execve_kp = {
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
|
||||||
.symbol_name = "do_execveat_common",
|
.symbol_name = "do_execveat_common",
|
||||||
@@ -225,6 +321,11 @@ static struct kprobe vfs_read_kp = {
|
|||||||
.pre_handler = read_handler_pre,
|
.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)
|
static void do_stop_vfs_read_hook(struct work_struct *work)
|
||||||
{
|
{
|
||||||
unregister_kprobe(&vfs_read_kp);
|
unregister_kprobe(&vfs_read_kp);
|
||||||
@@ -234,6 +335,11 @@ static void do_stop_execve_hook(struct work_struct *work)
|
|||||||
{
|
{
|
||||||
unregister_kprobe(&execve_kp);
|
unregister_kprobe(&execve_kp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void do_stop_input_hook(struct work_struct *work)
|
||||||
|
{
|
||||||
|
unregister_kprobe(&input_handle_event_kp);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void stop_vfs_read_hook()
|
static void stop_vfs_read_hook()
|
||||||
@@ -256,6 +362,21 @@ static void stop_execve_hook()
|
|||||||
#endif
|
#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
|
// ksud: module support
|
||||||
void ksu_enable_ksud()
|
void ksu_enable_ksud()
|
||||||
{
|
{
|
||||||
@@ -268,7 +389,11 @@ void ksu_enable_ksud()
|
|||||||
ret = register_kprobe(&vfs_read_kp);
|
ret = register_kprobe(&vfs_read_kp);
|
||||||
pr_info("ksud: vfs_read_kp: %d\n", ret);
|
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_vfs_read_work, do_stop_vfs_read_hook);
|
||||||
INIT_WORK(&stop_execve_hook_work, do_stop_execve_hook);
|
INIT_WORK(&stop_execve_hook_work, do_stop_execve_hook);
|
||||||
|
INIT_WORK(&stop_input_hook_work, do_stop_input_hook);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,4 +5,6 @@
|
|||||||
|
|
||||||
void on_post_fs_data(void);
|
void on_post_fs_data(void);
|
||||||
|
|
||||||
|
bool ksu_is_safe_mode(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
Reference in New Issue
Block a user