From b3536f8e6f62389b5938dab286814bc7e6ddd08e Mon Sep 17 00:00:00 2001 From: anotherjin <25584417+anotherjin@users.noreply.github.com> Date: Tue, 23 Apr 2024 21:19:21 +0800 Subject: [PATCH] Hook syscalls and stable symbols 1. Replace `do_execveat_common` with `sys_execve` and `sys_execveat` 2. Replace `input_handle_event` with `input_event` and `input_inject_event` --- kernel/arch.h | 8 +++ kernel/ksud.c | 142 ++++++++++++++++++++++++++++++++++++++++++++-- kernel/sucompat.c | 70 +++++++++++++++++++++++ 3 files changed, 215 insertions(+), 5 deletions(-) diff --git a/kernel/arch.h b/kernel/arch.h index 1de7e7b7d03b..2ef7b681bc8e 100644 --- a/kernel/arch.h +++ b/kernel/arch.h @@ -23,11 +23,19 @@ #define SYS_READ_SYMBOL "__arm64_sys_read" #define SYS_NEWFSTATAT_SYMBOL "__arm64_sys_newfstatat" #define SYS_FACCESSAT_SYMBOL "__arm64_sys_faccessat" +#define SYS_EXECVE_SYMBOL "__arm64_sys_execve" +#define SYS_EXECVEAT_SYMBOL "__arm64_sys_execveat" +#define COMPAT_SYS_EXECVE_SYMBOL "__arm64_compat_sys_execve" +#define COMPAT_SYS_EXECVEAT_SYMBOL "__arm64_compat_sys_execveat" #else #define PRCTL_SYMBOL "sys_prctl" #define SYS_READ_SYMBOL "sys_read" #define SYS_NEWFSTATAT_SYMBOL "sys_newfstatat" #define SYS_FACCESSAT_SYMBOL "sys_faccessat" +#define SYS_EXECVE_SYMBOL "sys_execve" +#define SYS_EXECVEAT_SYMBOL "sys_execveat" +#define COMPAT_SYS_EXECVE_SYMBOL "compat_sys_execve" +#define COMPAT_SYS_EXECVEAT_SYMBOL "compat_sys_execveat" #endif #elif defined(__x86_64__) diff --git a/kernel/ksud.c b/kernel/ksud.c index f1ba3e2e26cf..04fcd308d78a 100644 --- a/kernel/ksud.c +++ b/kernel/ksud.c @@ -5,6 +5,7 @@ #include "linux/err.h" #include "linux/file.h" #include "linux/fs.h" +#include "linux/namei.h" #include "linux/version.h" #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0) #include "linux/input-event-codes.h" @@ -472,6 +473,89 @@ static int execve_handler_pre(struct kprobe *p, struct pt_regs *regs) return ksu_handle_execveat_ksud(fd, filename_ptr, &argv, NULL, NULL); } +static int sys_execve_handler_pre(struct kprobe *p, struct pt_regs *regs) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0) + struct pt_regs *real_regs = (struct pt_regs *)PT_REGS_PARM1(regs); +#else + struct pt_regs *real_regs = regs; +#endif + const char __user *filename_user = + (const char *)PT_REGS_PARM1(real_regs); + struct filename *filename_mid = getname(filename_user); + struct filename **filename_ptr = (struct filename **)&filename_mid; + const char __user *const __user *__argv = + (const char __user *const __user *)PT_REGS_PARM2(real_regs); + struct user_arg_ptr argv = { .ptr.native = __argv }; + + return ksu_handle_execveat_ksud(AT_FDCWD, filename_ptr, &argv, NULL, + NULL); +} + +static int sys_execveat_handler_pre(struct kprobe *p, struct pt_regs *regs) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0) + struct pt_regs *real_regs = (struct pt_regs *)PT_REGS_PARM1(regs); +#else + struct pt_regs *real_regs = regs; +#endif + int *fd = (int *)&PT_REGS_PARM1(real_regs); + const char __user *filename_user = + (const char *)PT_REGS_PARM2(real_regs); + int flags = PT_REGS_PARM5(real_regs); + int lookup_flags = (flags & AT_EMPTY_PATH) ? LOOKUP_EMPTY : 0; + struct filename *filename_mid = + getname_flags(filename_user, lookup_flags, NULL); + struct filename **filename_ptr = (struct filename **)&filename_mid; + const char __user *const __user *__argv = + (const char __user *const __user *)PT_REGS_PARM3(real_regs); + struct user_arg_ptr argv = { .ptr.native = __argv }; + + return ksu_handle_execveat_ksud(fd, filename_ptr, &argv, NULL, NULL); +} + +static int compat_sys_execve_handler_pre(struct kprobe *p, struct pt_regs *regs) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0) + struct pt_regs *real_regs = (struct pt_regs *)PT_REGS_PARM1(regs); +#else + struct pt_regs *real_regs = regs; +#endif + const char __user *filename_user = + (const char *)PT_REGS_PARM1(real_regs); + struct filename *filename_mid = getname(filename_user); + struct filename **filename_ptr = (struct filename **)&filename_mid; + const compat_uptr_t __user *__argv = + (const compat_uptr_t *)PT_REGS_PARM2(real_regs); + struct user_arg_ptr argv = { .ptr.compat = __argv, .is_compat = true }; + + return ksu_handle_execveat_ksud(AT_FDCWD, filename_ptr, &argv, NULL, + NULL); +} + +static int compat_sys_execveat_handler_pre(struct kprobe *p, + struct pt_regs *regs) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0) + struct pt_regs *real_regs = (struct pt_regs *)PT_REGS_PARM1(regs); +#else + struct pt_regs *real_regs = regs; +#endif + int *fd = (int *)&PT_REGS_PARM1(real_regs); + const char __user *filename_user = + (const char *)PT_REGS_PARM2(real_regs); + int flags = PT_REGS_PARM5(real_regs); + int lookup_flags = (flags & AT_EMPTY_PATH) ? LOOKUP_EMPTY : 0; + struct filename *filename_mid = + getname_flags(filename_user, lookup_flags, NULL); + struct filename **filename_ptr = (struct filename **)&filename_mid; + const compat_uptr_t __user *__argv = + (const compat_uptr_t *)PT_REGS_PARM3(real_regs); + struct user_arg_ptr argv = { .ptr.compat = __argv, .is_compat = true }; + + return ksu_handle_execveat_ksud(fd, filename_ptr, &argv, NULL, NULL); +} + // remove this later! __maybe_unused static int vfs_read_handler_pre(struct kprobe *p, struct pt_regs *regs) { @@ -506,6 +590,32 @@ static int input_handle_event_handler_pre(struct kprobe *p, return ksu_handle_input_handle_event(type, code, value); } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) +static struct kprobe execve_syscall_kp = { + .symbol_name = SYS_EXECVE_SYMBOL, + .pre_handler = sys_execve_handler_pre, +}; + +static struct kprobe execveat_syscall_kp = { + .symbol_name = SYS_EXECVEAT_SYMBOL, + .pre_handler = sys_execveat_handler_pre, +}; + +static struct kprobe compat_execve_syscall_kp = { + .symbol_name = COMPAT_SYS_EXECVE_SYMBOL, + .pre_handler = compat_sys_execve_handler_pre, +}; + +static struct kprobe compat_execveat_syscall_kp = { + .symbol_name = COMPAT_SYS_EXECVEAT_SYMBOL, + .pre_handler = compat_sys_execveat_handler_pre, +}; + +static struct kprobe *execve_kps[] = { + &execve_syscall_kp, &compat_execve_syscall_kp, + &execveat_syscall_kp, &compat_execveat_syscall_kp +}; +#else static struct kprobe execve_kp = { #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) .symbol_name = "do_execveat_common", @@ -516,6 +626,7 @@ static struct kprobe execve_kp = { #endif .pre_handler = execve_handler_pre, }; +#endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) static struct kprobe vfs_read_kp = { @@ -529,11 +640,20 @@ static struct kprobe vfs_read_kp = { }; #endif -static struct kprobe input_handle_event_kp = { - .symbol_name = "input_handle_event", +static struct kprobe input_event_kp = { + .symbol_name = "input_event", + .pre_handler = input_handle_event_handler_pre, +}; + +static struct kprobe input_inject_event_kp = { + .symbol_name = "input_inject_event", .pre_handler = input_handle_event_handler_pre, }; +static struct kprobe *input_event_kps[] = { + &input_event_kp, &input_inject_event_kp +}; + static void do_stop_vfs_read_hook(struct work_struct *work) { unregister_kprobe(&vfs_read_kp); @@ -541,12 +661,16 @@ static void do_stop_vfs_read_hook(struct work_struct *work) static void do_stop_execve_hook(struct work_struct *work) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) + unregister_kprobes(execve_kps, 4); +#else unregister_kprobe(&execve_kp); +#endif } static void do_stop_input_hook(struct work_struct *work) { - unregister_kprobe(&input_handle_event_kp); + unregister_kprobes(input_event_kps, 2); } #endif @@ -594,13 +718,17 @@ void ksu_ksud_init() #ifdef CONFIG_KPROBES int ret; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) + ret = register_kprobes(execve_kps, 4); +#else ret = register_kprobe(&execve_kp); +#endif pr_info("ksud: execve_kp: %d\n", ret); ret = register_kprobe(&vfs_read_kp); pr_info("ksud: vfs_read_kp: %d\n", ret); - ret = register_kprobe(&input_handle_event_kp); + ret = register_kprobes(input_event_kps, 2); pr_info("ksud: input_handle_event_kp: %d\n", ret); INIT_WORK(&stop_vfs_read_work, do_stop_vfs_read_hook); @@ -611,9 +739,13 @@ void ksu_ksud_init() void ksu_ksud_exit() { #ifdef CONFIG_KPROBES +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) + unregister_kprobes(execve_kps, 4); +#else unregister_kprobe(&execve_kp); +#endif // this should be done before unregister vfs_read_kp // unregister_kprobe(&vfs_read_kp); - unregister_kprobe(&input_handle_event_kp); + unregister_kprobe(&input_event_kp); #endif } \ No newline at end of file diff --git a/kernel/sucompat.c b/kernel/sucompat.c index f19b2212bec3..1d66037e02a4 100644 --- a/kernel/sucompat.c +++ b/kernel/sucompat.c @@ -6,6 +6,7 @@ #include "linux/types.h" #include "linux/uaccess.h" #include "linux/version.h" +#include "linux/namei.h" #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) #include "linux/sched/task_stack.h" #else @@ -196,6 +197,39 @@ static int execve_handler_pre(struct kprobe *p, struct pt_regs *regs) return ksu_handle_execveat_sucompat(fd, filename_ptr, NULL, NULL, NULL); } +static int sys_execve_handler_pre(struct kprobe *p, struct pt_regs *regs) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0) + struct pt_regs *real_regs = (struct pt_regs *)PT_REGS_PARM1(regs); +#else + struct pt_regs *real_regs = regs; +#endif + const char __user *filename_user = + (const char *)PT_REGS_PARM1(real_regs); + struct filename *filename_mid = getname(filename_user); + struct filename **filename_ptr = (struct filename **)&filename_mid; + + return ksu_handle_execveat_sucompat(AT_FDCWD, filename_ptr, NULL, NULL, NULL); +} + +static int sys_execveat_handler_pre(struct kprobe *p, struct pt_regs *regs) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0) + struct pt_regs *real_regs = (struct pt_regs *)PT_REGS_PARM1(regs); +#else + struct pt_regs *real_regs = regs; +#endif + int *fd = (int *)&PT_REGS_PARM1(real_regs); + const char __user *filename_user = + (const char *)PT_REGS_PARM2(real_regs); + int flags = PT_REGS_PARM5(real_regs); + int lookup_flags = (flags & AT_EMPTY_PATH) ? LOOKUP_EMPTY : 0; + struct filename *filename_mid = getname_flags(filename_user, lookup_flags, NULL); + struct filename **filename_ptr = (struct filename **)&filename_mid; + + return ksu_handle_execveat_sucompat(fd, filename_ptr, NULL, NULL, NULL); +} + #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) static struct kprobe faccessat_kp = { .symbol_name = SYS_FACCESSAT_SYMBOL, @@ -228,6 +262,33 @@ static struct kprobe newfstatat_kp = { }; #endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) +static struct kprobe execve_syscall_kp = { + .symbol_name = SYS_EXECVE_SYMBOL, + .pre_handler = sys_execve_handler_pre, +}; + +static struct kprobe execveat_syscall_kp = { + .symbol_name = SYS_EXECVEAT_SYMBOL, + .pre_handler = sys_execveat_handler_pre, +}; + +static struct kprobe compat_execve_syscall_kp = { + .symbol_name = COMPAT_SYS_EXECVE_SYMBOL, + .pre_handler = sys_execveat_handler_pre, +}; + +static struct kprobe compat_execveat_syscall_kp = { + .symbol_name = COMPAT_SYS_EXECVEAT_SYMBOL, + .pre_handler = sys_execveat_handler_pre, +}; + +static struct kprobe *execve_kps[] = { + &execve_syscall_kp, &compat_execve_syscall_kp, + &execveat_syscall_kp, &compat_execveat_syscall_kp +}; + +#else static struct kprobe execve_kp = { #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) .symbol_name = "do_execveat_common", @@ -238,6 +299,7 @@ static struct kprobe execve_kp = { #endif .pre_handler = execve_handler_pre, }; +#endif #endif @@ -246,7 +308,11 @@ void ksu_sucompat_init() { #ifdef CONFIG_KPROBES int ret; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) + ret = register_kprobes(execve_kps, 4); +#else ret = register_kprobe(&execve_kp); +#endif pr_info("sucompat: execve_kp: %d\n", ret); ret = register_kprobe(&newfstatat_kp); pr_info("sucompat: newfstatat_kp: %d\n", ret); @@ -257,7 +323,11 @@ void ksu_sucompat_init() void ksu_sucompat_exit() { #ifdef CONFIG_KPROBES +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) + unregister_kprobes(execve_kps, 4); +#else unregister_kprobe(&execve_kp); +#endif unregister_kprobe(&newfstatat_kp); unregister_kprobe(&faccessat_kp); #endif