Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hook syscalls and stable symbols #1657

Merged
merged 7 commits into from
Apr 26, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions kernel/arch.h
Original file line number Diff line number Diff line change
Expand Up @@ -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__)
Expand Down
157 changes: 151 additions & 6 deletions kernel/ksud.c
Original file line number Diff line number Diff line change
Expand Up @@ -176,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);
Expand Down Expand Up @@ -263,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,
anotherjin marked this conversation as resolved.
Show resolved Hide resolved
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;
Expand Down Expand Up @@ -472,6 +585,22 @@ 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);
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_execve_ksud(AT_FDCWD, filename_user, &argv, NULL,
NULL);
}

// remove this later!
__maybe_unused static int vfs_read_handler_pre(struct kprobe *p, struct pt_regs *regs)
{
Expand Down Expand Up @@ -506,6 +635,12 @@ 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_kp = {
.symbol_name = SYS_EXECVE_SYMBOL,
.pre_handler = sys_execve_handler_pre,
};
#else
static struct kprobe execve_kp = {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
.symbol_name = "do_execveat_common",
Expand All @@ -516,6 +651,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 = {
Expand All @@ -529,11 +665,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);
Expand All @@ -546,7 +691,7 @@ static void do_stop_execve_hook(struct work_struct *work)

static void do_stop_input_hook(struct work_struct *work)
{
unregister_kprobe(&input_handle_event_kp);
unregister_kprobes(input_event_kps, 2);
}
#endif

Expand Down Expand Up @@ -600,7 +745,7 @@ void ksu_ksud_init()
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);
Expand All @@ -614,6 +759,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_handle_event_kp);
unregister_kprobes(input_event_kps, 2);
#endif
}
52 changes: 52 additions & 0 deletions kernel/sucompat.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,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)
{
Expand Down Expand Up @@ -130,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)
Expand Down Expand Up @@ -196,6 +229,18 @@ 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);

return ksu_handle_execve_sucompat(AT_FDCWD, filename_user, NULL, NULL, NULL);
}

#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)
static struct kprobe faccessat_kp = {
.symbol_name = SYS_FACCESSAT_SYMBOL,
Expand Down Expand Up @@ -228,6 +273,12 @@ static struct kprobe newfstatat_kp = {
};
#endif

#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)
static struct kprobe execve_kp = {
.symbol_name = SYS_EXECVE_SYMBOL,
.pre_handler = sys_execve_handler_pre,
};
#else
static struct kprobe execve_kp = {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
.symbol_name = "do_execveat_common",
Expand All @@ -238,6 +289,7 @@ static struct kprobe execve_kp = {
#endif
.pre_handler = execve_handler_pre,
};
#endif

#endif

Expand Down