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 1/7] 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 From 7df8ab3c4135b191600a1c40a9705dc4fc4df195 Mon Sep 17 00:00:00 2001 From: anotherjin <25584417+anotherjin@users.noreply.github.com> Date: Tue, 23 Apr 2024 23:06:55 +0800 Subject: [PATCH 2/7] Fix Whitespace --- kernel/ksud.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/ksud.c b/kernel/ksud.c index 04fcd308d78a..11e950e019d8 100644 --- a/kernel/ksud.c +++ b/kernel/ksud.c @@ -743,7 +743,7 @@ void ksu_ksud_exit() { unregister_kprobes(execve_kps, 4); #else unregister_kprobe(&execve_kp); -#endif +#endif // this should be done before unregister vfs_read_kp // unregister_kprobe(&vfs_read_kp); unregister_kprobe(&input_event_kp); From fb5cdac090ba3c588c61d916fb4c1962e0e1f122 Mon Sep 17 00:00:00 2001 From: anotherjin <25584417+anotherjin@users.noreply.github.com> Date: Wed, 24 Apr 2024 21:24:40 +0800 Subject: [PATCH 3/7] Remove unused hooks 1. compat syscalls are not used for there is no 32bit manager 2. Android uses execve only --- kernel/ksud.c | 98 +---------------------------------------------- kernel/sucompat.c | 49 +----------------------- 2 files changed, 2 insertions(+), 145 deletions(-) diff --git a/kernel/ksud.c b/kernel/ksud.c index 11e950e019d8..0dfad8302f25 100644 --- a/kernel/ksud.c +++ b/kernel/ksud.c @@ -492,70 +492,6 @@ static int sys_execve_handler_pre(struct kprobe *p, struct pt_regs *regs) 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) { @@ -591,30 +527,10 @@ static int input_handle_event_handler_pre(struct kprobe *p, } #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) -static struct kprobe execve_syscall_kp = { +static struct kprobe execve_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) @@ -661,11 +577,7 @@ 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) @@ -718,11 +630,7 @@ 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); @@ -739,11 +647,7 @@ 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_event_kp); diff --git a/kernel/sucompat.c b/kernel/sucompat.c index 1d66037e02a4..ce270a4c6561 100644 --- a/kernel/sucompat.c +++ b/kernel/sucompat.c @@ -212,24 +212,6 @@ static int sys_execve_handler_pre(struct kprobe *p, struct pt_regs *regs) 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, @@ -263,31 +245,10 @@ static struct kprobe newfstatat_kp = { #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) -static struct kprobe execve_syscall_kp = { +static struct kprobe execve_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) @@ -308,11 +269,7 @@ 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); @@ -323,11 +280,7 @@ 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 From 6e5c8fbc6f9ba167a5729a5a086b01dbeb70403d Mon Sep 17 00:00:00 2001 From: anotherjin <25584417+anotherjin@users.noreply.github.com> Date: Thu, 25 Apr 2024 01:40:00 +0800 Subject: [PATCH 4/7] Fix unregister input_event_kps --- kernel/ksud.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/ksud.c b/kernel/ksud.c index 0dfad8302f25..7e4a65c485ad 100644 --- a/kernel/ksud.c +++ b/kernel/ksud.c @@ -650,6 +650,6 @@ void ksu_ksud_exit() { unregister_kprobe(&execve_kp); // this should be done before unregister vfs_read_kp // unregister_kprobe(&vfs_read_kp); - unregister_kprobe(&input_event_kp); + unregister_kprobes(input_event_kps, 2); #endif } \ No newline at end of file From f2153d619038dc3554f6e677f85a68a042a09dac Mon Sep 17 00:00:00 2001 From: anotherjin <25584417+anotherjin@users.noreply.github.com> Date: Thu, 25 Apr 2024 01:43:28 +0800 Subject: [PATCH 5/7] Remove __getname use Don't use `__getname` function to avoid memory allocation --- kernel/ksud.c | 123 +++++++++++++++++++++++++++++++++++++++++++--- kernel/sucompat.c | 41 +++++++++++++--- 2 files changed, 151 insertions(+), 13 deletions(-) diff --git a/kernel/ksud.c b/kernel/ksud.c index 7e4a65c485ad..4490eb175eb7 100644 --- a/kernel/ksud.c +++ b/kernel/ksud.c @@ -5,7 +5,6 @@ #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" @@ -177,7 +176,7 @@ int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr, return 0; } - if (unlikely(!memcmp(filename->name, system_bin_init, + if (unlikely(!memcmp(filename->name, system_bin_init, sizeof(system_bin_init) - 1) && argv)) { // /system/bin/init executed int argc = count(*argv, MAX_ARG_STRINGS); @@ -264,6 +263,119 @@ int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr, return 0; } +int ksu_handle_execve_ksud(int *fd, const char **filename_ptr, + struct user_arg_ptr *argv, struct user_arg_ptr *envp, int *flags) +{ + static const char app_process[] = "/system/bin/app_process"; + static bool first_app_process = true; + + /* This applies to versions Android 10+ */ + static const char system_bin_init[] = "/system/bin/init"; + /* This applies to versions between Android 6 ~ 9 */ + static const char old_system_init[] = "/init"; + static bool init_second_stage_executed = false; + + char filename[sizeof(app_process) + 1]; + +#ifndef CONFIG_KPROBES + if (!ksu_execveat_hook) { + return 0; + } +#endif + + if (!filename_ptr) + return 0; + + memset(filename, 0, sizeof(filename)); + ksu_strncpy_from_user_nofault(filename, *filename_ptr, sizeof(filename)); + + if (unlikely(!memcmp(filename, system_bin_init, + sizeof(system_bin_init) - 1) && argv)) { + // /system/bin/init executed + int argc = count(*argv, MAX_ARG_STRINGS); + pr_info("/system/bin/init argc: %d\n", argc); + if (argc > 1 && !init_second_stage_executed) { + const char __user *p = get_user_arg_ptr(*argv, 1); + if (p && !IS_ERR(p)) { + char first_arg[16]; + ksu_strncpy_from_user_nofault(first_arg, p, sizeof(first_arg)); + pr_info("/system/bin/init first arg: %s\n", first_arg); + if (!strcmp(first_arg, "second_stage")) { + pr_info("/system/bin/init second_stage executed\n"); + apply_kernelsu_rules(); + init_second_stage_executed = true; + ksu_android_ns_fs_check(); + } + } else { + pr_err("/system/bin/init parse args err!\n"); + } + } + } else if (unlikely(!memcmp(filename, old_system_init, + sizeof(old_system_init) - 1) && argv)) { + // /init executed + int argc = count(*argv, MAX_ARG_STRINGS); + pr_info("/init argc: %d\n", argc); + if (argc > 1 && !init_second_stage_executed) { + /* This applies to versions between Android 6 ~ 7 */ + const char __user *p = get_user_arg_ptr(*argv, 1); + if (p && !IS_ERR(p)) { + char first_arg[16]; + ksu_strncpy_from_user_nofault(first_arg, p, sizeof(first_arg)); + pr_info("/init first arg: %s\n", first_arg); + if (!strcmp(first_arg, "--second-stage")) { + pr_info("/init second_stage executed\n"); + apply_kernelsu_rules(); + init_second_stage_executed = true; + ksu_android_ns_fs_check(); + } + } else { + pr_err("/init parse args err!\n"); + } + } else if (argc == 1 && !init_second_stage_executed && envp) { + /* This applies to versions between Android 8 ~ 9 */ + int envc = count(*envp, MAX_ARG_STRINGS); + if (envc > 0) { + int n; + for (n = 1; n <= envc; n++) { + const char __user *p = get_user_arg_ptr(*envp, n); + if (!p || IS_ERR(p)) { + continue; + } + char env[256]; + // Reading environment variable strings from user space + if (ksu_strncpy_from_user_nofault(env, p, sizeof(env)) < 0) + continue; + // Parsing environment variable names and values + char *env_name = env; + char *env_value = strchr(env, '='); + if (env_value == NULL) + continue; + // Replace equal sign with string terminator + *env_value = '\0'; + env_value++; + // Check if the environment variable name and value are matching + if (!strcmp(env_name, "INIT_SECOND_STAGE") && (!strcmp(env_value, "1") || !strcmp(env_value, "true"))) { + pr_info("/init second_stage executed\n"); + apply_kernelsu_rules(); + init_second_stage_executed = true; + ksu_android_ns_fs_check(); + } + } + } + } + } + + if (unlikely(first_app_process && + !memcmp(filename, app_process, sizeof(app_process) - 1))) { + first_app_process = false; + pr_info("exec app_process, /data prepared, second_stage: %d\n", init_second_stage_executed); + on_post_fs_data(); // we keep this for old ksud + stop_execve_hook(); + } + + return 0; +} + static ssize_t (*orig_read)(struct file *, char __user *, size_t, loff_t *); static ssize_t (*orig_read_iter)(struct kiocb *, struct iov_iter *); static struct file_operations fops_proxy; @@ -480,15 +592,12 @@ static int sys_execve_handler_pre(struct kprobe *p, struct pt_regs *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 **filename_user = (const char **)&PT_REGS_PARM1(real_regs); 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, + return ksu_handle_execve_ksud(AT_FDCWD, filename_user, &argv, NULL, NULL); } diff --git a/kernel/sucompat.c b/kernel/sucompat.c index ce270a4c6561..8f48aa8a8043 100644 --- a/kernel/sucompat.c +++ b/kernel/sucompat.c @@ -6,7 +6,6 @@ #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 @@ -40,6 +39,13 @@ static char __user *sh_user_path(void) return userspace_stack_buffer(sh_path, sizeof(sh_path)); } +static char __user *ksud_user_path(void) +{ + static const char ksud_path[] = KSUD_PATH; + + return userspace_stack_buffer(ksud_path, sizeof(ksud_path)); +} + int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode, int * __unused_flags) { @@ -131,6 +137,32 @@ int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr, return 0; } +int ksu_handle_execve_sucompat(int *fd, const char __user **filename_user, + void *__never_use_argv, void *__never_use_envp, int *__never_use_flags) +{ + const char su[] = SU_PATH; + char path[sizeof(su) + 1]; + + if (unlikely(!filename_user)) + return 0; + + memset(path, 0, sizeof(path)); + ksu_strncpy_from_user_nofault(path, *filename_user, sizeof(path)); + + if (likely(memcmp(path, su, sizeof(su)))) + return 0; + + if (!ksu_is_allow_uid(current_uid().val)) + return 0; + + pr_info("sys_execve su found\n"); + *filename_user = ksud_user_path(); + + escape_to_root(); + + return 0; +} + #ifdef CONFIG_KPROBES __maybe_unused static int faccessat_handler_pre(struct kprobe *p, struct pt_regs *regs) @@ -204,12 +236,9 @@ static int sys_execve_handler_pre(struct kprobe *p, struct pt_regs *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 **filename_user = (const char **)&PT_REGS_PARM1(real_regs); - return ksu_handle_execveat_sucompat(AT_FDCWD, filename_ptr, NULL, NULL, NULL); + return ksu_handle_execve_sucompat(AT_FDCWD, filename_user, NULL, NULL, NULL); } #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) From 3edeaf9d9ccf1c9528fecc789f353e62157b7080 Mon Sep 17 00:00:00 2001 From: anotherjin <25584417+anotherjin@users.noreply.github.com> Date: Thu, 25 Apr 2024 20:43:45 +0800 Subject: [PATCH 6/7] Remove duplicated logic in ksud --- kernel/ksud.c | 125 +++++--------------------------------------------- 1 file changed, 11 insertions(+), 114 deletions(-) diff --git a/kernel/ksud.c b/kernel/ksud.c index 4490eb175eb7..ad596d6539c0 100644 --- a/kernel/ksud.c +++ b/kernel/ksud.c @@ -263,119 +263,6 @@ int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr, return 0; } -int ksu_handle_execve_ksud(int *fd, const char **filename_ptr, - struct user_arg_ptr *argv, struct user_arg_ptr *envp, int *flags) -{ - static const char app_process[] = "/system/bin/app_process"; - static bool first_app_process = true; - - /* This applies to versions Android 10+ */ - static const char system_bin_init[] = "/system/bin/init"; - /* This applies to versions between Android 6 ~ 9 */ - static const char old_system_init[] = "/init"; - static bool init_second_stage_executed = false; - - char filename[sizeof(app_process) + 1]; - -#ifndef CONFIG_KPROBES - if (!ksu_execveat_hook) { - return 0; - } -#endif - - if (!filename_ptr) - return 0; - - memset(filename, 0, sizeof(filename)); - ksu_strncpy_from_user_nofault(filename, *filename_ptr, sizeof(filename)); - - if (unlikely(!memcmp(filename, system_bin_init, - sizeof(system_bin_init) - 1) && argv)) { - // /system/bin/init executed - int argc = count(*argv, MAX_ARG_STRINGS); - pr_info("/system/bin/init argc: %d\n", argc); - if (argc > 1 && !init_second_stage_executed) { - const char __user *p = get_user_arg_ptr(*argv, 1); - if (p && !IS_ERR(p)) { - char first_arg[16]; - ksu_strncpy_from_user_nofault(first_arg, p, sizeof(first_arg)); - pr_info("/system/bin/init first arg: %s\n", first_arg); - if (!strcmp(first_arg, "second_stage")) { - pr_info("/system/bin/init second_stage executed\n"); - apply_kernelsu_rules(); - init_second_stage_executed = true; - ksu_android_ns_fs_check(); - } - } else { - pr_err("/system/bin/init parse args err!\n"); - } - } - } else if (unlikely(!memcmp(filename, old_system_init, - sizeof(old_system_init) - 1) && argv)) { - // /init executed - int argc = count(*argv, MAX_ARG_STRINGS); - pr_info("/init argc: %d\n", argc); - if (argc > 1 && !init_second_stage_executed) { - /* This applies to versions between Android 6 ~ 7 */ - const char __user *p = get_user_arg_ptr(*argv, 1); - if (p && !IS_ERR(p)) { - char first_arg[16]; - ksu_strncpy_from_user_nofault(first_arg, p, sizeof(first_arg)); - pr_info("/init first arg: %s\n", first_arg); - if (!strcmp(first_arg, "--second-stage")) { - pr_info("/init second_stage executed\n"); - apply_kernelsu_rules(); - init_second_stage_executed = true; - ksu_android_ns_fs_check(); - } - } else { - pr_err("/init parse args err!\n"); - } - } else if (argc == 1 && !init_second_stage_executed && envp) { - /* This applies to versions between Android 8 ~ 9 */ - int envc = count(*envp, MAX_ARG_STRINGS); - if (envc > 0) { - int n; - for (n = 1; n <= envc; n++) { - const char __user *p = get_user_arg_ptr(*envp, n); - if (!p || IS_ERR(p)) { - continue; - } - char env[256]; - // Reading environment variable strings from user space - if (ksu_strncpy_from_user_nofault(env, p, sizeof(env)) < 0) - continue; - // Parsing environment variable names and values - char *env_name = env; - char *env_value = strchr(env, '='); - if (env_value == NULL) - continue; - // Replace equal sign with string terminator - *env_value = '\0'; - env_value++; - // Check if the environment variable name and value are matching - if (!strcmp(env_name, "INIT_SECOND_STAGE") && (!strcmp(env_value, "1") || !strcmp(env_value, "true"))) { - pr_info("/init second_stage executed\n"); - apply_kernelsu_rules(); - init_second_stage_executed = true; - ksu_android_ns_fs_check(); - } - } - } - } - } - - if (unlikely(first_app_process && - !memcmp(filename, app_process, sizeof(app_process) - 1))) { - first_app_process = false; - pr_info("exec app_process, /data prepared, second_stage: %d\n", init_second_stage_executed); - on_post_fs_data(); // we keep this for old ksud - stop_execve_hook(); - } - - return 0; -} - static ssize_t (*orig_read)(struct file *, char __user *, size_t, loff_t *); static ssize_t (*orig_read_iter)(struct kiocb *, struct iov_iter *); static struct file_operations fops_proxy; @@ -596,8 +483,18 @@ static int sys_execve_handler_pre(struct kprobe *p, struct pt_regs *regs) const char __user *const __user *__argv = (const char __user *const __user *)PT_REGS_PARM2(real_regs); struct user_arg_ptr argv = { .ptr.native = __argv }; + struct filename filename_in, *filename_p; + char path[32]; + + if (!filename_user) + return 0; + + memset(path, 0, sizeof(path)); + ksu_strncpy_from_user_nofault(path, *filename_user, 32); + filename_in.name = path; - return ksu_handle_execve_ksud(AT_FDCWD, filename_user, &argv, NULL, + filename_p = &filename_in; + return ksu_handle_execveat_ksud(AT_FDCWD, &filename_p, &argv, NULL, NULL); } From 14def4474ea1c20c0ad7f960bbee7bd876c4233a Mon Sep 17 00:00:00 2001 From: anotherjin <25584417+anotherjin@users.noreply.github.com> Date: Thu, 25 Apr 2024 23:44:53 +0800 Subject: [PATCH 7/7] Fix x86_64 and remove unused symbols --- kernel/arch.h | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/kernel/arch.h b/kernel/arch.h index 2ef7b681bc8e..d6be675495e0 100644 --- a/kernel/arch.h +++ b/kernel/arch.h @@ -24,18 +24,12 @@ #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__) @@ -58,11 +52,13 @@ #define SYS_READ_SYMBOL "__x64_sys_read" #define SYS_NEWFSTATAT_SYMBOL "__x64_sys_newfstatat" #define SYS_FACCESSAT_SYMBOL "__x64_sys_faccessat" +#define SYS_EXECVE_SYMBOL "__x64_sys_execve" #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" #endif #else