kernel: Rewrite the kernel source code (#554)

* clean unused header

* on_module_mounted in ksud.c

* refact: use app_profile

* unified hook manager

* add zygote to hook target

* move reboot hook to supercall.c

* refactor: kernel_umount setuid_hook

* update mark rules, add init mark tracker

* remove reboot from check_syscall_fastpath

* update setuid_hook, remove uneeded sucompat enable

* log freely

* kernel: Migrate kprobe hook configuration items

* kernel: fix build

* cli: add ksud debug mark

* Fix rustfmt warning

---------

Co-authored-by: Ylarod <me@ylarod.cn>
Co-authored-by: Wang Han <416810799@qq.com>
Co-authored-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
This commit is contained in:
ShirkNeko
2025-11-09 01:14:26 +08:00
parent 678c00cad9
commit af4b502631
27 changed files with 1567 additions and 1374 deletions

View File

@@ -1,18 +1,22 @@
kernelsu-objs := ksu.o kernelsu-objs := ksu.o
kernelsu-objs += allowlist.o kernelsu-objs += allowlist.o
kernelsu-objs += app_profile.o
kernelsu-objs += dynamic_manager.o kernelsu-objs += dynamic_manager.o
kernelsu-objs += apk_sign.o kernelsu-objs += apk_sign.o
kernelsu-objs += sucompat.o kernelsu-objs += sucompat.o
kernelsu-objs += syscall_hook_manager.o
kernelsu-objs += throne_tracker.o
kernelsu-objs += pkg_observer.o kernelsu-objs += pkg_observer.o
kernelsu-objs += core_hook.o kernelsu-objs += umount_manager.o
kernelsu-objs += kprobe_hook_manager.o
kernelsu-objs += setuid_hook.o
kernelsu-objs += kernel_umount.o
kernelsu-objs += supercalls.o kernelsu-objs += supercalls.o
kernelsu-objs += feature.o kernelsu-objs += feature.o
kernelsu-objs += ksud.o kernelsu-objs += ksud.o
kernelsu-objs += embed_ksud.o kernelsu-objs += embed_ksud.o
kernelsu-objs += seccomp_cache.o kernelsu-objs += seccomp_cache.o
kernelsu-objs += kernel_compat.o
kernelsu-objs += file_wrapper.o kernelsu-objs += file_wrapper.o
kernelsu-objs += throne_tracker.o
kernelsu-objs += throne_comm.o kernelsu-objs += throne_comm.o
kernelsu-objs += sulog.o kernelsu-objs += sulog.o

View File

@@ -14,7 +14,6 @@
#include <linux/compiler_types.h> #include <linux/compiler_types.h>
#endif #endif
#include "ksu.h"
#include "klog.h" // IWYU pragma: keep #include "klog.h" // IWYU pragma: keep
#include "selinux/selinux.h" #include "selinux/selinux.h"
#include "allowlist.h" #include "allowlist.h"

View File

@@ -2,7 +2,7 @@
#define __KSU_H_ALLOWLIST #define __KSU_H_ALLOWLIST
#include <linux/types.h> #include <linux/types.h>
#include "ksu.h" #include "app_profile.h"
void ksu_allowlist_init(void); void ksu_allowlist_init(void);

277
kernel/app_profile.c Normal file
View File

@@ -0,0 +1,277 @@
#include <linux/capability.h>
#include <linux/cred.h>
#include <linux/sched.h>
#include <linux/sched/signal.h>
#include <linux/seccomp.h>
#include <linux/thread_info.h>
#include <linux/uidgid.h>
#include <linux/version.h>
#include "allowlist.h"
#include "app_profile.h"
#include "klog.h" // IWYU pragma: keep
#include "selinux/selinux.h"
#include "syscall_hook_manager.h"
#include "sucompat.h"
#include "sulog.h"
#include "kprobe_hook_manager.h"
#if LINUX_VERSION_CODE >= KERNEL_VERSION (6, 7, 0)
static struct group_info root_groups = { .usage = REFCOUNT_INIT(2), };
#else
static struct group_info root_groups = { .usage = ATOMIC_INIT(2) };
#endif
static void setup_groups(struct root_profile *profile, struct cred *cred)
{
if (profile->groups_count > KSU_MAX_GROUPS) {
pr_warn("Failed to setgroups, too large group: %d!\n",
profile->uid);
return;
}
if (profile->groups_count == 1 && profile->groups[0] == 0) {
// setgroup to root and return early.
if (cred->group_info)
put_group_info(cred->group_info);
cred->group_info = get_group_info(&root_groups);
return;
}
u32 ngroups = profile->groups_count;
struct group_info *group_info = groups_alloc(ngroups);
if (!group_info) {
pr_warn("Failed to setgroups, ENOMEM for: %d\n", profile->uid);
return;
}
int i;
for (i = 0; i < ngroups; i++) {
gid_t gid = profile->groups[i];
kgid_t kgid = make_kgid(current_user_ns(), gid);
if (!gid_valid(kgid)) {
pr_warn("Failed to setgroups, invalid gid: %d\n", gid);
put_group_info(group_info);
return;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0)
group_info->gid[i] = kgid;
#else
GROUP_AT(group_info, i) = kgid;
#endif
}
groups_sort(group_info);
set_groups(cred, group_info);
put_group_info(group_info);
}
static void disable_seccomp()
{
assert_spin_locked(&current->sighand->siglock);
// disable seccomp
#if defined(CONFIG_GENERIC_ENTRY) && \
LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
clear_syscall_work(SECCOMP);
#else
clear_thread_flag(TIF_SECCOMP);
#endif
#ifdef CONFIG_SECCOMP
current->seccomp.mode = 0;
current->seccomp.filter = NULL;
#else
#endif
}
void escape_with_root_profile(void)
{
struct cred *cred;
struct task_struct *p = current;
struct task_struct *t;
cred = prepare_creds();
if (!cred) {
pr_warn("prepare_creds failed!\n");
return;
}
if (cred->euid.val == 0) {
pr_warn("Already root, don't escape!\n");
#if __SULOG_GATE
ksu_sulog_report_su_grant(current_euid().val, NULL, "escape_to_root_failed");
#endif
abort_creds(cred);
return;
}
struct root_profile *profile = ksu_get_root_profile(cred->uid.val);
cred->uid.val = profile->uid;
cred->suid.val = profile->uid;
cred->euid.val = profile->uid;
cred->fsuid.val = profile->uid;
cred->gid.val = profile->gid;
cred->fsgid.val = profile->gid;
cred->sgid.val = profile->gid;
cred->egid.val = profile->gid;
cred->securebits = 0;
BUILD_BUG_ON(sizeof(profile->capabilities.effective) !=
sizeof(kernel_cap_t));
// setup capabilities
// we need CAP_DAC_READ_SEARCH becuase `/data/adb/ksud` is not accessible for non root process
// we add it here but don't add it to cap_inhertiable, it would be dropped automaticly after exec!
u64 cap_for_ksud =
profile->capabilities.effective | CAP_DAC_READ_SEARCH;
memcpy(&cred->cap_effective, &cap_for_ksud,
sizeof(cred->cap_effective));
memcpy(&cred->cap_permitted, &profile->capabilities.effective,
sizeof(cred->cap_permitted));
memcpy(&cred->cap_bset, &profile->capabilities.effective,
sizeof(cred->cap_bset));
setup_groups(profile, cred);
commit_creds(cred);
// Refer to kernel/seccomp.c: seccomp_set_mode_strict
// When disabling Seccomp, ensure that current->sighand->siglock is held during the operation.
spin_lock_irq(&current->sighand->siglock);
disable_seccomp();
spin_unlock_irq(&current->sighand->siglock);
setup_selinux(profile->selinux_domain);
#if __SULOG_GATE
ksu_sulog_report_su_grant(current_euid().val, NULL, "escape_to_root");
#endif
for_each_thread (p, t) {
ksu_set_task_tracepoint_flag(t);
}
}
#ifdef CONFIG_KSU_MANUAL_SU
static void disable_seccomp_for_task(struct task_struct *tsk)
{
if (!tsk->seccomp.filter && tsk->seccomp.mode == SECCOMP_MODE_DISABLED)
return;
if (WARN_ON(!spin_is_locked(&tsk->sighand->siglock)))
return;
#ifdef CONFIG_SECCOMP
tsk->seccomp.mode = 0;
if (tsk->seccomp.filter) {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)
seccomp_filter_release(tsk);
atomic_set(&tsk->seccomp.filter_count, 0);
#else
// for 6.11+ kernel support?
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0)
put_seccomp_filter(tsk);
#endif
tsk->seccomp.filter = NULL;
#endif
}
#endif
}
void escape_to_root_for_cmd_su(uid_t target_uid, pid_t target_pid)
{
struct cred *newcreds;
struct task_struct *target_task;
struct task_struct *p = current;
struct task_struct *t;
pr_info("cmd_su: escape_to_root_for_cmd_su called for UID: %d, PID: %d\n", target_uid, target_pid);
// Find target task by PID
rcu_read_lock();
target_task = pid_task(find_vpid(target_pid), PIDTYPE_PID);
if (!target_task) {
rcu_read_unlock();
pr_err("cmd_su: target task not found for PID: %d\n", target_pid);
#if __SULOG_GATE
ksu_sulog_report_su_grant(target_uid, "cmd_su", "target_not_found");
#endif
return;
}
get_task_struct(target_task);
rcu_read_unlock();
if (task_uid(target_task).val == 0) {
pr_warn("cmd_su: target task is already root, PID: %d\n", target_pid);
put_task_struct(target_task);
return;
}
newcreds = prepare_kernel_cred(target_task);
if (newcreds == NULL) {
pr_err("cmd_su: failed to allocate new cred for PID: %d\n", target_pid);
#if __SULOG_GATE
ksu_sulog_report_su_grant(target_uid, "cmd_su", "cred_alloc_failed");
#endif
put_task_struct(target_task);
return;
}
struct root_profile *profile = ksu_get_root_profile(target_uid);
newcreds->uid.val = profile->uid;
newcreds->suid.val = profile->uid;
newcreds->euid.val = profile->uid;
newcreds->fsuid.val = profile->uid;
newcreds->gid.val = profile->gid;
newcreds->fsgid.val = profile->gid;
newcreds->sgid.val = profile->gid;
newcreds->egid.val = profile->gid;
newcreds->securebits = 0;
u64 cap_for_cmd_su = profile->capabilities.effective | CAP_DAC_READ_SEARCH | CAP_SETUID | CAP_SETGID;
memcpy(&newcreds->cap_effective, &cap_for_cmd_su, sizeof(newcreds->cap_effective));
memcpy(&newcreds->cap_permitted, &profile->capabilities.effective, sizeof(newcreds->cap_permitted));
memcpy(&newcreds->cap_bset, &profile->capabilities.effective, sizeof(newcreds->cap_bset));
setup_groups(profile, newcreds);
task_lock(target_task);
const struct cred *old_creds = get_task_cred(target_task);
rcu_assign_pointer(target_task->real_cred, newcreds);
rcu_assign_pointer(target_task->cred, get_cred(newcreds));
task_unlock(target_task);
if (target_task->sighand) {
spin_lock_irq(&target_task->sighand->siglock);
disable_seccomp_for_task(target_task);
spin_unlock_irq(&target_task->sighand->siglock);
}
setup_selinux(profile->selinux_domain);
put_cred(old_creds);
wake_up_process(target_task);
if (target_task->signal->tty) {
struct inode *inode = target_task->signal->tty->driver_data;
if (inode && inode->i_sb->s_magic == DEVPTS_SUPER_MAGIC) {
__ksu_handle_devpts(inode);
}
}
put_task_struct(target_task);
#if __SULOG_GATE
ksu_sulog_report_su_grant(target_uid, "cmd_su", "manual_escalation");
#endif
for_each_thread (p, t) {
ksu_set_task_tracepoint_flag(t);
}
pr_info("cmd_su: privilege escalation completed for UID: %d, PID: %d\n", target_uid, target_pid);
}
#endif

68
kernel/app_profile.h Normal file
View File

@@ -0,0 +1,68 @@
#ifndef __KSU_H_APP_PROFILE
#define __KSU_H_APP_PROFILE
#include <linux/types.h>
// Forward declarations
struct cred;
#define KSU_APP_PROFILE_VER 2
#define KSU_MAX_PACKAGE_NAME 256
// NGROUPS_MAX for Linux is 65535 generally, but we only supports 32 groups.
#define KSU_MAX_GROUPS 32
#define KSU_SELINUX_DOMAIN 64
struct root_profile {
int32_t uid;
int32_t gid;
int32_t groups_count;
int32_t groups[KSU_MAX_GROUPS];
// kernel_cap_t is u32[2] for capabilities v3
struct {
u64 effective;
u64 permitted;
u64 inheritable;
} capabilities;
char selinux_domain[KSU_SELINUX_DOMAIN];
int32_t namespaces;
};
struct non_root_profile {
bool umount_modules;
};
struct app_profile {
// It may be utilized for backward compatibility, although we have never explicitly made any promises regarding this.
u32 version;
// this is usually the package of the app, but can be other value for special apps
char key[KSU_MAX_PACKAGE_NAME];
int32_t current_uid;
bool allow_su;
union {
struct {
bool use_default;
char template_name[KSU_MAX_PACKAGE_NAME];
struct root_profile profile;
} rp_config;
struct {
bool use_default;
struct non_root_profile profile;
} nrp_config;
};
};
// Escalate current process to root with the appropriate profile
void escape_with_root_profile(void);
void escape_to_root_for_cmd_su(uid_t target_uid, pid_t target_pid);
#endif

View File

@@ -22,9 +22,6 @@
#define PRCTL_SYMBOL "__arm64_sys_prctl" #define PRCTL_SYMBOL "__arm64_sys_prctl"
#define REBOOT_SYMBOL "__arm64_sys_reboot" #define REBOOT_SYMBOL "__arm64_sys_reboot"
#define SYS_READ_SYMBOL "__arm64_sys_read" #define SYS_READ_SYMBOL "__arm64_sys_read"
#define SYS_NEWFSTATAT_SYMBOL "__arm64_sys_newfstatat"
#define SYS_FSTATAT64_SYMBOL "__arm64_sys_fstatat64"
#define SYS_FACCESSAT_SYMBOL "__arm64_sys_faccessat"
#define SYS_EXECVE_SYMBOL "__arm64_sys_execve" #define SYS_EXECVE_SYMBOL "__arm64_sys_execve"
#define SYS_EXECVE_COMPAT_SYMBOL "__arm64_compat_sys_execve" #define SYS_EXECVE_COMPAT_SYMBOL "__arm64_compat_sys_execve"
#else #else
@@ -62,9 +59,6 @@
#define PRCTL_SYMBOL "__x64_sys_prctl" #define PRCTL_SYMBOL "__x64_sys_prctl"
#define REBOOT_SYMBOL "__x64_sys_reboot" #define REBOOT_SYMBOL "__x64_sys_reboot"
#define SYS_READ_SYMBOL "__x64_sys_read" #define SYS_READ_SYMBOL "__x64_sys_read"
#define SYS_NEWFSTATAT_SYMBOL "__x64_sys_newfstatat"
#define SYS_FSTATAT64_SYMBOL "__x64_sys_fstatat64"
#define SYS_FACCESSAT_SYMBOL "__x64_sys_faccessat"
#define SYS_EXECVE_SYMBOL "__x64_sys_execve" #define SYS_EXECVE_SYMBOL "__x64_sys_execve"
#define SYS_EXECVE_COMPAT_SYMBOL "__x64_compat_sys_execve" #define SYS_EXECVE_COMPAT_SYMBOL "__x64_compat_sys_execve"
#else #else

View File

@@ -1,16 +0,0 @@
#ifndef __KSU_H_KSU_CORE
#define __KSU_H_KSU_CORE
#include <linux/init.h>
#include "apk_sign.h"
void __init ksu_core_init(void);
void ksu_core_exit(void);
void escape_to_root(void);
void nuke_ext4_sysfs(void);
extern bool ksu_module_mounted;
#endif

View File

@@ -10,14 +10,8 @@
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/version.h> #include <linux/version.h>
#include "allowlist.h"
#include "klog.h" // IWYU pragma: keep #include "klog.h" // IWYU pragma: keep
#include "ksu.h"
#include "ksud.h"
#include "manager.h"
#include "selinux/selinux.h" #include "selinux/selinux.h"
#include "core_hook.h"
#include "objsec.h"
#include "file_wrapper.h" #include "file_wrapper.h"

265
kernel/kernel_umount.c Normal file
View File

@@ -0,0 +1,265 @@
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/task_work.h>
#include <linux/cred.h>
#include <linux/fs.h>
#include <linux/mount.h>
#include <linux/namei.h>
#include <linux/nsproxy.h>
#include <linux/path.h>
#include <linux/printk.h>
#include <linux/types.h>
#include "kernel_umount.h"
#include "klog.h" // IWYU pragma: keep
#include "allowlist.h"
#include "selinux/selinux.h"
#include "feature.h"
#include "ksud.h"
static bool ksu_kernel_umount_enabled = true;
static int kernel_umount_feature_get(u64 *value)
{
*value = ksu_kernel_umount_enabled ? 1 : 0;
return 0;
}
static int kernel_umount_feature_set(u64 value)
{
bool enable = value != 0;
ksu_kernel_umount_enabled = enable;
pr_info("kernel_umount: set to %d\n", enable);
return 0;
}
static const struct ksu_feature_handler kernel_umount_handler = {
.feature_id = KSU_FEATURE_KERNEL_UMOUNT,
.name = "kernel_umount",
.get_handler = kernel_umount_feature_get,
.set_handler = kernel_umount_feature_set,
};
static bool should_umount(struct path *path)
{
if (!path) {
return false;
}
if (current->nsproxy->mnt_ns == init_nsproxy.mnt_ns) {
pr_info("ignore global mnt namespace process: %d\n", current_uid().val);
return false;
}
if (path->mnt && path->mnt->mnt_sb && path->mnt->mnt_sb->s_type) {
const char *fstype = path->mnt->mnt_sb->s_type->name;
return strcmp(fstype, "overlay") == 0;
}
return false;
}
extern int path_umount(struct path *path, int flags);
static void ksu_umount_mnt(struct path *path, int flags)
{
int err = path_umount(path, flags);
if (err) {
pr_info("umount %s failed: %d\n", path->dentry->d_iname, err);
}
}
static void try_umount(const char *mnt, bool check_mnt, int flags)
{
struct path path;
int err = kern_path(mnt, 0, &path);
if (err) {
return;
}
if (path.dentry != path.mnt->mnt_root) {
// it is not root mountpoint, maybe umounted by others already.
path_put(&path);
return;
}
// we are only interest in some specific mounts
if (check_mnt && !should_umount(&path)) {
path_put(&path);
return;
}
ksu_umount_mnt(&path, flags);
}
struct umount_tw {
struct callback_head cb;
const struct cred *old_cred;
};
#ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT
static void umount_tw_func(struct callback_head *cb)
{
struct umount_tw *tw = container_of(cb, struct umount_tw, cb);
const struct cred *saved = NULL;
if (tw->old_cred) {
saved = override_creds(tw->old_cred);
}
uid_t uid = current_uid().val;
// fixme: use `collect_mounts` and `iterate_mount` to iterate all mountpoint and
// filter the mountpoint whose target is `/data/adb`
try_umount("/odm", true, 0, uid);
try_umount("/system", true, 0, uid);
try_umount("/vendor", true, 0, uid);
try_umount("/product", true, 0, uid);
try_umount("/system_ext", true, 0, uid);
try_umount("/data/adb/modules", false, MNT_DETACH, uid);
try_umount("/data/adb/kpm", false, MNT_DETACH, uid);
// try umount ksu temp path
try_umount("/debug_ramdisk", false, MNT_DETACH, uid);
try_umount("/sbin", false, MNT_DETACH, uid);
// try umount lsposed dex2oat bins
try_umount("/system/etc/hosts", false, MNT_DETACH, uid);
// try umount lsposed dex2oat bins
try_umount("/apex/com.android.art/bin/dex2oat64", false, MNT_DETACH, uid);
try_umount("/apex/com.android.art/bin/dex2oat32", false, MNT_DETACH, uid);
if (saved)
revert_creds(saved);
if (tw->old_cred)
put_cred(tw->old_cred);
kfree(tw);
}
#else
static void umount_tw_func(struct callback_head *cb)
{
struct umount_tw *tw = container_of(cb, struct umount_tw, cb);
const struct cred *saved = NULL;
if (tw->old_cred) {
saved = override_creds(tw->old_cred);
}
// fixme: use `collect_mounts` and `iterate_mount` to iterate all mountpoint and
// filter the mountpoint whose target is `/data/adb`
try_umount("/odm", true, 0);
try_umount("/system", true, 0);
try_umount("/vendor", true, 0);
try_umount("/product", true, 0);
try_umount("/system_ext", true, 0);
try_umount("/data/adb/modules", false, MNT_DETACH);
try_umount("/data/adb/kpm", false, MNT_DETACH);
// try umount ksu temp path
try_umount("/debug_ramdisk", false, MNT_DETACH);
try_umount("/sbin", false, MNT_DETACH);
try_umount("/system/etc/hosts", false, MNT_DETACH);
// try umount lsposed dex2oat bins
try_umount("/apex/com.android.art/bin/dex2oat64", false, MNT_DETACH);
try_umount("/apex/com.android.art/bin/dex2oat32", false, MNT_DETACH);
if (saved)
revert_creds(saved);
if (tw->old_cred)
put_cred(tw->old_cred);
kfree(tw);
}
static inline bool is_appuid(uid_t uid)
{
#define PER_USER_RANGE 100000
#define FIRST_APPLICATION_UID 10000
#define LAST_APPLICATION_UID 19999
uid_t appid = uid % PER_USER_RANGE;
return appid >= FIRST_APPLICATION_UID && appid <= LAST_APPLICATION_UID;
}
static inline bool is_unsupported_uid(uid_t uid)
{
#define LAST_APPLICATION_UID 19999
uid_t appid = uid % 100000;
return appid > LAST_APPLICATION_UID;
}
int ksu_handle_umount(uid_t old_uid, uid_t new_uid)
{
struct umount_tw *tw;
// this hook is used for umounting overlayfs for some uid, if there isn't any module mounted, just ignore it!
if (!ksu_module_mounted) {
return 0;
}
if (!ksu_kernel_umount_enabled) {
return 0;
}
if (!is_appuid(new_uid) || is_unsupported_uid(new_uid)) {
pr_info("handle setuid ignore non application or isolated uid: %d\n", new_uid);
return 0;
}
if (!ksu_uid_should_umount(new_uid)) {
return 0;
} else {
pr_info("uid: %d should not umount!\n", current_uid().val);
}
// check old process's selinux context, if it is not zygote, ignore it!
// because some su apps may setuid to untrusted_app but they are in global mount namespace
// when we umount for such process, that is a disaster!
bool is_zygote_child = is_zygote(get_current_cred());
if (!is_zygote_child) {
pr_info("handle umount ignore non zygote child: %d\n", current->pid);
return 0;
}
// umount the target mnt
pr_info("handle umount for uid: %d, pid: %d\n", new_uid, current->pid);
tw = kmalloc(sizeof(*tw), GFP_ATOMIC);
if (!tw)
return 0;
tw->old_cred = get_current_cred();
tw->cb.func = umount_tw_func;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
int err = task_work_add(current, &tw->cb, TWA_RESUME);
#else
int err = task_work_add(current, &tw->cb, true);
#endif
if (err) {
if (tw->old_cred) {
put_cred(tw->old_cred);
}
kfree(tw);
pr_warn("unmount add task_work failed\n");
}
return 0;
}
void ksu_kernel_umount_init(void)
{
int rc = 0;
rc = ksu_umount_manager_init();
if (rc) {
pr_err("Failed to initialize umount manager: %d\n", rc);
}
if (ksu_register_feature_handler(&kernel_umount_handler)) {
pr_err("Failed to register kernel_umount feature handler\n");
}
}
void ksu_kernel_umount_exit(void)
{
ksu_unregister_feature_handler(KSU_FEATURE_KERNEL_UMOUNT);
}

12
kernel/kernel_umount.h Normal file
View File

@@ -0,0 +1,12 @@
#ifndef __KSU_H_KERNEL_UMOUNT
#define __KSU_H_KERNEL_UMOUNT
#include <linux/types.h>
void ksu_kernel_umount_init(void);
void ksu_kernel_umount_exit(void);
// Handler function to be called from setresuid hook
int ksu_handle_umount(uid_t old_uid, uid_t new_uid);
#endif

View File

@@ -0,0 +1,163 @@
#include "kprobe_hook_manager.h"
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/kprobes.h>
#include <linux/sched.h>
#include <linux/uidgid.h>
#include "arch.h"
#include "klog.h"
#include "ksud.h"
#ifdef CONFIG_KSU_MANUAL_SU
#include "manual_su.h"
#endif
#ifdef CONFIG_COMPAT
bool ksu_is_compat __read_mostly = false;
#endif
#ifdef CONFIG_KSU_MANUAL_SU
static void ksu_try_escalate_for_uid(uid_t uid)
{
if (!is_pending_root(uid))
return;
pr_info("pending_root: UID=%d temporarily allowed\n", uid);
remove_pending_root(uid);
}
#endif
// inode_permission hook for handling devpts
static int ksu_inode_permission_handler_pre(struct kprobe *p, struct pt_regs *regs)
{
struct inode *inode = (struct inode *)PT_REGS_PARM1(regs);
if (inode && inode->i_sb && unlikely(inode->i_sb->s_magic == DEVPTS_SUPER_MAGIC)) {
// pr_info("%s: handling devpts for: %s \n", __func__, current->comm);
__ksu_handle_devpts(inode);
}
return 0;
}
static struct kprobe ksu_inode_permission_kp = {
.symbol_name = "security_inode_permission",
.pre_handler = ksu_inode_permission_handler_pre,
};
// bprm_check_security hook for handling ksud compatibility
static int ksu_bprm_check_handler_pre(struct kprobe *p, struct pt_regs *regs)
{
struct linux_binprm *bprm = (struct linux_binprm *)PT_REGS_PARM1(regs);
char *filename = (char *)bprm->filename;
if (likely(!ksu_execveat_hook))
return 0;
#ifdef CONFIG_COMPAT
static bool compat_check_done __read_mostly = false;
if (unlikely(!compat_check_done) && unlikely(!strcmp(filename, "/data/adb/ksud"))
&& !memcmp(bprm->buf, "\x7f\x45\x4c\x46", 4)) {
if (bprm->buf[4] == 0x01)
ksu_is_compat = true;
pr_info("%s: %s ELF magic found! ksu_is_compat: %d \n", __func__, filename, ksu_is_compat);
compat_check_done = true;
}
#endif
ksu_handle_pre_ksud(filename);
#ifdef CONFIG_KSU_MANUAL_SU
ksu_try_escalate_for_uid(current_uid().val);
#endif
return 0;
}
static struct kprobe ksu_bprm_check_kp = {
.symbol_name = "security_bprm_check",
.pre_handler = ksu_bprm_check_handler_pre,
};
#ifdef CONFIG_KSU_MANUAL_SU
// task_alloc hook for handling manual su escalation
static int ksu_task_alloc_handler_pre(struct kprobe *p, struct pt_regs *regs)
{
struct task_struct *task = (struct task_struct *)PT_REGS_PARM1(regs);
ksu_try_escalate_for_uid(task_uid(task).val);
return 0;
}
static struct kprobe ksu_task_alloc_kp = {
.symbol_name = "security_task_alloc",
.pre_handler = ksu_task_alloc_handler_pre,
};
#endif
__maybe_unused int ksu_kprobe_init(void)
{
int rc = 0;
// Register inode_permission kprobe
rc = register_kprobe(&ksu_inode_permission_kp);
if (rc) {
pr_err("inode_permission kprobe failed: %d\n", rc);
} else {
pr_info("inode_permission kprobe registered successfully\n");
}
// Register bprm_check_security kprobe
rc = register_kprobe(&ksu_bprm_check_kp);
if (rc) {
pr_err("bprm_check_security kprobe failed: %d\n", rc);
} else {
pr_info("bprm_check_security kprobe registered successfully\n");
}
#ifdef CONFIG_KSU_MANUAL_SU
// Register task_alloc kprobe
rc = register_kprobe(&ksu_task_alloc_kp);
if (rc) {
pr_err("task_alloc kprobe failed: %d\n", rc);
} else {
pr_info("task_alloc kprobe registered successfully\n");
}
#endif
return 0;
}
__maybe_unused int ksu_kprobe_exit(void)
{
unregister_kprobe(&ksu_inode_permission_kp);
unregister_kprobe(&ksu_bprm_check_kp);
#ifdef CONFIG_KSU_MANUAL_SU
unregister_kprobe(&ksu_task_alloc_kp);
#endif
return 0;
}
void ksu_kprobe_hook_init(void)
{
int rc = 0;
#ifdef CONFIG_KPROBES
rc = ksu_kprobe_init();
if (rc) {
pr_err("ksu_kprobe_init failed: %d\n", rc);
}
#endif
}
void ksu_kprobe_hook_exit(void)
{
#ifdef CONFIG_KPROBES
pr_info("ksu_core_exit\n");
ksu_kprobe_exit();
#endif
}

View File

@@ -0,0 +1,23 @@
#ifndef __KSU_H_KSU_KBROBES_HOOK_MANAGER
#define __KSU_H_KSU_KBROBES_HOOK_MANAGER
#include <linux/init.h>
#include <linux/version.h>
#include <linux/binfmts.h>
#include <linux/tty.h>
#include <linux/fs.h>
#include "selinux/selinux.h"
#include "objsec.h"
#ifndef DEVPTS_SUPER_MAGIC
#define DEVPTS_SUPER_MAGIC 0x1cd1
#endif
extern bool ksu_is_compat __read_mostly;
extern int __ksu_handle_devpts(struct inode *inode); // sucompat.c
void ksu_kprobe_hook_init(void);
void ksu_kprobe_hook_exit(void);
#endif

View File

@@ -6,16 +6,18 @@
#include <linux/version.h> #include <linux/version.h>
#include "allowlist.h" #include "allowlist.h"
#include "arch.h"
#include "core_hook.h"
#include "feature.h" #include "feature.h"
#include "klog.h" // IWYU pragma: keep #include "klog.h" // IWYU pragma: keep
#include "ksu.h"
#include "throne_tracker.h" #include "throne_tracker.h"
#include "sucompat.h" #include "syscall_hook_manager.h"
#include "ksud.h" #include "ksud.h"
#include "supercalls.h" #include "supercalls.h"
#include "sulog.h"
#include "throne_comm.h"
#include "dynamic_manager.h"
#include "kprobe_hook_manager.h"
#ifdef CONFIG_KSU_SUSFS #ifdef CONFIG_KSU_SUSFS
#include <linux/susfs.h> #include <linux/susfs.h>
#endif #endif
@@ -27,68 +29,90 @@ bool ksu_queue_work(struct work_struct *work)
return queue_work(ksu_workqueue, work); return queue_work(ksu_workqueue, work);
} }
void sukisu_custom_config_init(void)
{
ksu_kprobe_hook_init();
}
void sukisu_custom_config_exit(void)
{
ksu_kprobe_hook_exit();
ksu_uid_exit();
ksu_throne_comm_exit();
ksu_dynamic_manager_exit();
#if __SULOG_GATE
ksu_sulog_exit();
#endif
}
int __init kernelsu_init(void) int __init kernelsu_init(void)
{ {
#ifdef CONFIG_KSU_DEBUG #ifdef CONFIG_KSU_DEBUG
pr_alert("*************************************************************"); pr_alert("*************************************************************");
pr_alert("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **"); pr_alert("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **");
pr_alert("** **"); pr_alert("** **");
pr_alert("** You are running KernelSU in DEBUG mode **"); pr_alert("** You are running KernelSU in DEBUG mode **");
pr_alert("** **"); pr_alert("** **");
pr_alert("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **"); pr_alert("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **");
pr_alert("*************************************************************"); pr_alert("*************************************************************");
#endif #endif
#ifdef CONFIG_KSU_SUSFS #ifdef CONFIG_KSU_SUSFS
susfs_init(); susfs_init();
#endif #endif
ksu_feature_init(); ksu_feature_init();
ksu_supercalls_init(); ksu_supercalls_init();
ksu_core_init(); sukisu_custom_config_init();
ksu_workqueue = alloc_ordered_workqueue("kernelsu_work_queue", 0); ksu_syscall_hook_manager_init();
ksu_allowlist_init(); ksu_workqueue = alloc_ordered_workqueue("kernelsu_work_queue", 0);
ksu_throne_tracker_init(); ksu_allowlist_init();
ksu_sucompat_init(); ksu_throne_tracker_init();
#ifdef KSU_KPROBES_HOOK #ifdef KSU_KPROBES_HOOK
ksu_ksud_init(); ksu_ksud_init();
#else #else
pr_debug("init ksu driver\n"); pr_alert("KPROBES is disabled, KernelSU may not work, please check https://kernelsu.org/guide/how-to-integrate-for-non-gki.html");
#endif #endif
#ifdef MODULE #ifdef MODULE
#ifndef CONFIG_KSU_DEBUG #ifndef CONFIG_KSU_DEBUG
kobject_del(&THIS_MODULE->mkobj.kobj); kobject_del(&THIS_MODULE->mkobj.kobj);
#endif #endif
#endif #endif
return 0; return 0;
} }
extern void ksu_observer_exit(void);
void kernelsu_exit(void) void kernelsu_exit(void)
{ {
ksu_allowlist_exit(); ksu_allowlist_exit();
ksu_throne_tracker_exit(); ksu_observer_exit();
ksu_observer_exit(); ksu_throne_tracker_exit();
destroy_workqueue(ksu_workqueue);
destroy_workqueue(ksu_workqueue);
ksu_sucompat_exit();
#ifdef KSU_KPROBES_HOOK #ifdef KSU_KPROBES_HOOK
ksu_ksud_exit(); ksu_ksud_exit();
#endif #endif
ksu_core_exit(); ksu_syscall_hook_manager_exit();
ksu_feature_exit();
sukisu_custom_config_exit();
ksu_supercalls_exit();
ksu_feature_exit();
} }
module_init(kernelsu_init); module_init(kernelsu_init);

View File

@@ -13,12 +13,6 @@ extern bool ksu_uid_scanner_enabled;
#define EVENT_BOOT_COMPLETED 2 #define EVENT_BOOT_COMPLETED 2
#define EVENT_MODULE_MOUNTED 3 #define EVENT_MODULE_MOUNTED 3
#define KSU_APP_PROFILE_VER 2
#define KSU_MAX_PACKAGE_NAME 256
// NGROUPS_MAX for Linux is 65535 generally, but we only supports 32 groups.
#define KSU_MAX_GROUPS 32
#define KSU_SELINUX_DOMAIN 64
// SukiSU Ultra kernel su version full strings // SukiSU Ultra kernel su version full strings
#ifndef KSU_VERSION_FULL #ifndef KSU_VERSION_FULL
#define KSU_VERSION_FULL "v3.x-00000000@unknown" #define KSU_VERSION_FULL "v3.x-00000000@unknown"
@@ -47,54 +41,6 @@ struct manager_list_info {
} managers[2]; } managers[2];
}; };
struct root_profile {
int32_t uid;
int32_t gid;
int32_t groups_count;
int32_t groups[KSU_MAX_GROUPS];
// kernel_cap_t is u32[2] for capabilities v3
struct {
u64 effective;
u64 permitted;
u64 inheritable;
} capabilities;
char selinux_domain[KSU_SELINUX_DOMAIN];
int32_t namespaces;
};
struct non_root_profile {
bool umount_modules;
};
struct app_profile {
// It may be utilized for backward compatibility, although we have never explicitly made any promises regarding this.
u32 version;
// this is usually the package of the app, but can be other value for special apps
char key[KSU_MAX_PACKAGE_NAME];
int32_t current_uid;
bool allow_su;
union {
struct {
bool use_default;
char template_name[KSU_MAX_PACKAGE_NAME];
struct root_profile profile;
} rp_config;
struct {
bool use_default;
struct non_root_profile profile;
} nrp_config;
};
};
bool ksu_queue_work(struct work_struct *work); bool ksu_queue_work(struct work_struct *work);
static inline int startswith(char *s, char *prefix) static inline int startswith(char *s, char *prefix)

View File

@@ -2,7 +2,6 @@
#include <linux/rcupdate.h> #include <linux/rcupdate.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/task_work.h> #include <linux/task_work.h>
#include "manager.h"
#include <asm/current.h> #include <asm/current.h>
#include <linux/compat.h> #include <linux/compat.h>
#include <linux/cred.h> #include <linux/cred.h>
@@ -23,8 +22,10 @@
#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/namei.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include "manager.h"
#include "allowlist.h" #include "allowlist.h"
#include "arch.h" #include "arch.h"
#include "klog.h" // IWYU pragma: keep #include "klog.h" // IWYU pragma: keep
@@ -32,7 +33,10 @@
#include "kernel_compat.h" #include "kernel_compat.h"
#include "selinux/selinux.h" #include "selinux/selinux.h"
#include "sucompat.h" #include "syscall_hook_manager.h"
bool ksu_module_mounted __read_mostly = false;
bool ksu_boot_completed __read_mostly = false;
static const char KERNEL_SU_RC[] = static const char KERNEL_SU_RC[] =
"\n" "\n"
@@ -103,17 +107,44 @@ void on_post_fs_data(void)
pr_info("ksu_file sid: %d\n", ksu_file_sid); pr_info("ksu_file sid: %d\n", ksu_file_sid);
} }
struct user_arg_ptr { extern void ext4_unregister_sysfs(struct super_block *sb);
#ifdef CONFIG_COMPAT static void nuke_ext4_sysfs(void)
bool is_compat; {
#ifdef CONFIG_EXT4_FS
struct path path;
int err = kern_path("/data/adb/modules", 0, &path);
if (err) {
pr_err("nuke path err: %d\n", err);
return;
}
struct super_block *sb = path.dentry->d_inode->i_sb;
const char *name = sb->s_type->name;
if (strcmp(name, "ext4") != 0) {
pr_info("nuke but module aren't mounted\n");
path_put(&path);
return;
}
ext4_unregister_sysfs(sb);
path_put(&path);
#endif #endif
union { }
const char __user *const __user *native;
#ifdef CONFIG_COMPAT void on_module_mounted(void){
const compat_uptr_t __user *compat; pr_info("on_module_mounted!\n");
#endif ksu_module_mounted = true;
} ptr; nuke_ext4_sysfs();
}; }
void on_boot_completed(void){
ksu_boot_completed = true;
pr_info("on_boot_completed!\n");
// remark process, we don't want to mark other init
// forked process excepte zygote and adbd
ksu_unmark_all_process();
ksu_mark_running_process();
}
static void on_post_fs_data_cbfun(struct callback_head *cb) static void on_post_fs_data_cbfun(struct callback_head *cb)
{ {

View File

@@ -9,10 +9,14 @@ void ksu_ksud_init();
void ksu_ksud_exit(); void ksu_ksud_exit();
void on_post_fs_data(void); void on_post_fs_data(void);
void on_module_mounted(void);
void on_boot_completed(void);
bool ksu_is_safe_mode(void); bool ksu_is_safe_mode(void);
extern u32 ksu_file_sid; extern u32 ksu_file_sid;
extern bool ksu_module_mounted;
extern bool ksu_boot_completed;
extern bool ksu_execveat_hook __read_mostly; extern bool ksu_execveat_hook __read_mostly;
extern int ksu_handle_pre_ksud(const char *filename); extern int ksu_handle_pre_ksud(const char *filename);

View File

@@ -13,8 +13,8 @@
#include "allowlist.h" #include "allowlist.h"
#include "manager.h" #include "manager.h"
#include "allowlist.h" #include "allowlist.h"
#include "app_profile.h"
extern void escape_to_root_for_cmd_su(uid_t, pid_t);
static bool current_verified = false; static bool current_verified = false;
static void ksu_cleanup_expired_tokens(void); static void ksu_cleanup_expired_tokens(void);
static bool is_current_verified(void); static bool is_current_verified(void);

View File

@@ -6,7 +6,7 @@
#include "selinux.h" #include "selinux.h"
#include "sepolicy.h" #include "sepolicy.h"
#include "ss/services.h" #include "ss/services.h"
#include "linux/lsm_audit.h" #include "linux/lsm_audit.h" // IWYU pragma: keep
#include "xfrm.h" #include "xfrm.h"
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)

File diff suppressed because it is too large Load Diff

14
kernel/setuid_hook.h Normal file
View File

@@ -0,0 +1,14 @@
#ifndef __KSU_H_KSU_CORE
#define __KSU_H_KSU_CORE
#include <linux/init.h>
#include <linux/types.h>
#include "apk_sign.h"
#include <linux/thread_info.h>
void ksu_setuid_hook_init(void);
void ksu_setuid_hook_exit(void);
int ksu_handle_setresuid(uid_t ruid, uid_t euid, uid_t suid);
#endif

View File

@@ -1,14 +1,8 @@
#include "linux/compiler.h" #include "linux/compiler.h"
#include "linux/printk.h" #include "linux/printk.h"
#include "selinux/selinux.h"
#include <linux/dcache.h>
#include <linux/security.h>
#include <asm/current.h> #include <asm/current.h>
#include <linux/cred.h> #include <linux/cred.h>
#include <linux/err.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/kprobes.h>
#include <linux/tracepoint.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>
@@ -18,74 +12,23 @@
#else #else
#include <linux/sched.h> #include <linux/sched.h>
#endif #endif
#include <linux/spinlock.h>
#include <asm/syscall.h>
#include <trace/events/syscalls.h>
#ifdef CONFIG_KSU_SUSFS_SUS_SU #ifdef CONFIG_KSU_SUSFS_SUS_SU
#include <linux/susfs_def.h> #include <linux/susfs_def.h>
#endif #endif
#include "objsec.h"
#include "allowlist.h" #include "allowlist.h"
#include "arch.h"
#include "feature.h" #include "feature.h"
#include "klog.h" // IWYU pragma: keep #include "klog.h" // IWYU pragma: keep
#include "ksud.h" #include "ksud.h"
#include "sucompat.h" #include "sucompat.h"
#include "core_hook.h" #include "app_profile.h"
#include "syscall_hook_manager.h"
#include "sulog.h" #include "sulog.h"
#include "kernel_compat.h" #include "kernel_compat.h"
#define SU_PATH "/system/bin/su" #define SU_PATH "/system/bin/su"
#define SH_PATH "/system/bin/sh" #define SH_PATH "/system/bin/sh"
extern void escape_to_root();
void ksu_sucompat_enable();
void ksu_sucompat_disable();
void ksu_mark_running_process(void)
{
struct task_struct *p, *t;
read_lock(&tasklist_lock);
for_each_process_thread (p, t) {
if (!t->mm) { // only user processes
continue;
}
int uid = task_uid(t).val;
bool ksu_root_process =
uid == 0 && is_task_ksu_domain(get_task_cred(t));
if (ksu_root_process || ksu_is_allow_uid(uid)) {
ksu_set_task_tracepoint_flag(t);
pr_info("sucompat: mark process: pid:%d, uid: %d, comm:%s\n",
t->pid, uid, t->comm);
}
}
read_unlock(&tasklist_lock);
}
static void handle_process_mark(bool mark)
{
struct task_struct *p, *t;
read_lock(&tasklist_lock);
for_each_process_thread(p, t) {
if (mark)
ksu_set_task_tracepoint_flag(t);
else
ksu_clear_task_tracepoint_flag(t);
}
read_unlock(&tasklist_lock);
}
static void mark_all_process(void)
{
handle_process_mark(true);
pr_info("sucompat: mark all user process done!\n");
}
static void unmark_all_process(void)
{
handle_process_mark(false);
pr_info("sucompat: unmark all user process done!\n");
}
bool ksu_su_compat_enabled __read_mostly = true; bool ksu_su_compat_enabled __read_mostly = true;
@@ -99,17 +42,6 @@ static int su_compat_feature_set(u64 value)
{ {
bool enable = value != 0; bool enable = value != 0;
if (enable == ksu_su_compat_enabled) {
pr_info("su_compat: no need to change\n");
return 0;
}
if (enable) {
ksu_sucompat_enable();
} else {
ksu_sucompat_disable();
}
ksu_su_compat_enabled = enable; ksu_su_compat_enabled = enable;
pr_info("su_compat: set to %d\n", enable); pr_info("su_compat: set to %d\n", enable);
@@ -153,7 +85,7 @@ int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
{ {
#ifndef KSU_HAVE_SYSCALL_TRACEPOINTS_HOOK #ifndef KSU_HAVE_SYSCALL_TRACEPOINTS_HOOK
if (!ksu_sucompat_hook_state) { if (!ksu_su_compat_enabled) {
return 0; return 0;
} }
#endif #endif
@@ -209,7 +141,7 @@ int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags)
{ {
#ifndef KSU_HAVE_SYSCALL_TRACEPOINTS_HOOK #ifndef KSU_HAVE_SYSCALL_TRACEPOINTS_HOOK
if (!ksu_sucompat_hook_state) { if (!ksu_su_compat_enabled) {
return 0; return 0;
} }
#endif #endif
@@ -266,7 +198,7 @@ int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
struct filename *filename; struct filename *filename;
#ifndef KSU_HAVE_SYSCALL_TRACEPOINTS_HOOK #ifndef KSU_HAVE_SYSCALL_TRACEPOINTS_HOOK
if (!ksu_sucompat_hook_state) { if (!ksu_su_compat_enabled) {
return 0; return 0;
} }
#endif #endif
@@ -306,7 +238,7 @@ int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
pr_info("do_execveat_common su found\n"); pr_info("do_execveat_common su found\n");
memcpy((void *)filename->name, ksud_path, sizeof(ksud_path)); memcpy((void *)filename->name, ksud_path, sizeof(ksud_path));
escape_to_root(); escape_with_root_profile();
return 0; return 0;
} }
@@ -329,10 +261,9 @@ int ksu_handle_execve_sucompat(int *fd, const char __user **filename_user,
#endif #endif
#ifndef KSU_HAVE_SYSCALL_TRACEPOINTS_HOOK #ifndef KSU_HAVE_SYSCALL_TRACEPOINTS_HOOK
if (!ksu_sucompat_hook_state) { if (!ksu_su_compat_enabled) {
return 0; return 0;
} }
#endif
if (unlikely(!filename_user)) if (unlikely(!filename_user))
return 0; return 0;
@@ -371,7 +302,7 @@ int ksu_handle_execve_sucompat(int *fd, const char __user **filename_user,
pr_info("sys_execve su found\n"); pr_info("sys_execve su found\n");
*filename_user = ksud_user_path(); *filename_user = ksud_user_path();
escape_to_root(); escape_with_root_profile();
return 0; return 0;
} }
@@ -380,7 +311,7 @@ int __ksu_handle_devpts(struct inode *inode)
{ {
#ifndef KSU_HAVE_SYSCALL_TRACEPOINTS_HOOK #ifndef KSU_HAVE_SYSCALL_TRACEPOINTS_HOOK
if (!ksu_sucompat_hook_state) if (!ksu_su_compat_enabled)
return 0; return 0;
#endif #endif
@@ -409,186 +340,16 @@ int __ksu_handle_devpts(struct inode *inode)
return 0; return 0;
} }
#ifdef KSU_HAVE_SYSCALL_TRACEPOINTS_HOOK
// Tracepoint probe for sys_enter
static void sucompat_sys_enter_handler(void *data, struct pt_regs *regs,
long id)
{
// Handle newfstatat
if (unlikely(id == __NR_newfstatat)) {
int *dfd = (int *)&PT_REGS_PARM1(regs);
const char __user **filename_user =
(const char __user **)&PT_REGS_PARM2(regs);
int *flags = (int *)&PT_REGS_SYSCALL_PARM4(regs);
ksu_handle_stat(dfd, filename_user, flags);
return;
}
// Handle faccessat
if (unlikely(id == __NR_faccessat)) {
int *dfd = (int *)&PT_REGS_PARM1(regs);
const char __user **filename_user =
(const char __user **)&PT_REGS_PARM2(regs);
int *mode = (int *)&PT_REGS_PARM3(regs);
ksu_handle_faccessat(dfd, filename_user, mode, NULL);
return;
}
// Handle execve
if (unlikely(id == __NR_execve)) {
const char __user **filename_user =
(const char __user **)&PT_REGS_PARM1(regs);
ksu_handle_execve_sucompat(AT_FDCWD, filename_user, NULL, NULL, NULL);
return;
}
}
#endif // KSU_HAVE_SYSCALL_TRACEPOINTS_HOOK
#ifdef CONFIG_KRETPROBES
static struct kretprobe *init_kretprobe(const char *name,
kretprobe_handler_t handler)
{
struct kretprobe *rp = kzalloc(sizeof(struct kretprobe), GFP_KERNEL);
if (!rp)
return NULL;
rp->kp.symbol_name = name;
rp->handler = handler;
rp->data_size = 0;
rp->maxactive = 0;
int ret = register_kretprobe(rp);
pr_info("sucompat: register_%s kretprobe: %d\n", name, ret);
if (ret) {
kfree(rp);
return NULL;
}
return rp;
}
static void destroy_kretprobe(struct kretprobe **rp_ptr)
{
struct kretprobe *rp = *rp_ptr;
if (!rp)
return;
unregister_kretprobe(rp);
synchronize_rcu();
kfree(rp);
*rp_ptr = NULL;
}
#endif
#ifdef CONFIG_KRETPROBES
static int tracepoint_reg_count = 0;
static DEFINE_SPINLOCK(tracepoint_reg_lock);
static int syscall_regfunc_handler(struct kretprobe_instance *ri, struct pt_regs *regs)
{
unsigned long flags;
spin_lock_irqsave(&tracepoint_reg_lock, flags);
if (tracepoint_reg_count < 1) {
// while install our tracepoint, mark our processes
unmark_all_process();
ksu_mark_running_process();
} else {
// while installing other tracepoint, mark all processes
mark_all_process();
}
tracepoint_reg_count++;
spin_unlock_irqrestore(&tracepoint_reg_lock, flags);
return 0;
}
static int syscall_unregfunc_handler(struct kretprobe_instance *ri, struct pt_regs *regs)
{
unsigned long flags;
spin_lock_irqsave(&tracepoint_reg_lock, flags);
if (tracepoint_reg_count <= 1) {
// while uninstall our tracepoint, unmark all processes
unmark_all_process();
} else {
// while uninstalling other tracepoint, mark our processes
unmark_all_process();
ksu_mark_running_process();
}
tracepoint_reg_count--;
spin_unlock_irqrestore(&tracepoint_reg_lock, flags);
return 0;
}
static struct kretprobe *syscall_regfunc_rp = NULL;
static struct kretprobe *syscall_unregfunc_rp = NULL;
#endif
void ksu_sucompat_enable()
{
int ret;
pr_info("sucompat: ksu_sucompat_enable called\n");
#ifdef CONFIG_KRETPROBES
// Register kretprobe for syscall_regfunc
syscall_regfunc_rp = init_kretprobe("syscall_regfunc", syscall_regfunc_handler);
// Register kretprobe for syscall_unregfunc
syscall_unregfunc_rp = init_kretprobe("syscall_unregfunc", syscall_unregfunc_handler);
#endif
#ifdef KSU_HAVE_SYSCALL_TRACEPOINTS_HOOK
ret = register_trace_sys_enter(sucompat_sys_enter_handler, NULL);
#ifndef CONFIG_KRETPROBES
unmark_all_process();
ksu_mark_running_process();
#endif
if (ret) {
pr_err("sucompat: failed to register sys_enter tracepoint: %d\n", ret);
} else {
pr_info("sucompat: sys_enter tracepoint registered\n");
}
#else
ksu_sucompat_hook_state = true;
pr_info("ksu_sucompat_init: hooks enabled: execve/execveat_su, faccessat, stat\n");
#endif
}
void ksu_sucompat_disable()
{
pr_info("sucompat: ksu_sucompat_disable called\n");
#ifdef KSU_HAVE_SYSCALL_TRACEPOINTS_HOOK
unregister_trace_sys_enter(sucompat_sys_enter_handler, NULL);
tracepoint_synchronize_unregister();
pr_info("sucompat: sys_enter tracepoint unregistered\n");
#else
ksu_sucompat_hook_state = false;
pr_info("ksu_sucompat_exit: hooks disabled: execve/execveat_su, faccessat, stat\n");
#endif
#ifdef CONFIG_KRETPROBES
destroy_kretprobe(&syscall_regfunc_rp);
destroy_kretprobe(&syscall_unregfunc_rp);
#endif
}
// sucompat: permited process can execute 'su' to gain root access. // sucompat: permited process can execute 'su' to gain root access.
void ksu_sucompat_init() void ksu_sucompat_init()
{ {
if (ksu_register_feature_handler(&su_compat_handler)) { if (ksu_register_feature_handler(&su_compat_handler)) {
pr_err("Failed to register su_compat feature handler\n"); pr_err("Failed to register su_compat feature handler\n");
} }
if (ksu_su_compat_enabled) {
ksu_sucompat_enable();
}
} }
void ksu_sucompat_exit() void ksu_sucompat_exit()
{ {
if (ksu_su_compat_enabled) {
ksu_sucompat_disable();
}
ksu_unregister_feature_handler(KSU_FEATURE_SU_COMPAT); ksu_unregister_feature_handler(KSU_FEATURE_SU_COMPAT);
} }

View File

@@ -1,34 +1,18 @@
#ifndef __KSU_H_SUCOMPAT #ifndef __KSU_H_SUCOMPAT
#define __KSU_H_SUCOMPAT #define __KSU_H_SUCOMPAT
#include <linux/sched.h> #include <linux/types.h>
#include <linux/thread_info.h>
#include <linux/version.h>
extern bool ksu_su_compat_enabled; extern bool ksu_su_compat_enabled;
void ksu_sucompat_init(void); void ksu_sucompat_init(void);
void ksu_sucompat_exit(void); void ksu_sucompat_exit(void);
void ksu_sucompat_enable(void); // Handler functions exported for hook_manager
void ksu_sucompat_disable(void); int ksu_handle_faccessat(int *dfd, const char __user **filename_user,
int *mode, int *__unused_flags);
int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
int ksu_handle_execve_sucompat(int *fd, const char __user **filename_user,
void *__never_use_argv, void *__never_use_envp,
int *__never_use_flags);
void ksu_mark_running_process(void);
static inline void ksu_set_task_tracepoint_flag(struct task_struct *t)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
set_task_syscall_work(t, SYSCALL_TRACEPOINT);
#else
set_tsk_thread_flag(t, TIF_SYSCALL_TRACEPOINT);
#endif
}
static inline void ksu_clear_task_tracepoint_flag(struct task_struct *t)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
clear_task_syscall_work(t, SYSCALL_TRACEPOINT);
#else
clear_tsk_thread_flag(t, TIF_SYSCALL_TRACEPOINT);
#endif
}
#endif #endif

View File

@@ -10,6 +10,7 @@
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/version.h> #include <linux/version.h>
#include "arch.h"
#include "allowlist.h" #include "allowlist.h"
#include "feature.h" #include "feature.h"
#include "klog.h" // IWYU pragma: keep #include "klog.h" // IWYU pragma: keep
@@ -17,9 +18,9 @@
#include "manager.h" #include "manager.h"
#include "sulog.h" #include "sulog.h"
#include "selinux/selinux.h" #include "selinux/selinux.h"
#include "core_hook.h"
#include "objsec.h" #include "objsec.h"
#include "file_wrapper.h" #include "file_wrapper.h"
#include "syscall_hook_manager.h"
#include "throne_comm.h" #include "throne_comm.h"
#include "dynamic_manager.h" #include "dynamic_manager.h"
@@ -77,7 +78,7 @@ static int do_grant_root(void __user *arg)
// we already check uid above on allowed_for_su() // we already check uid above on allowed_for_su()
pr_info("allow root for: %d\n", current_uid().val); pr_info("allow root for: %d\n", current_uid().val);
escape_to_root(); escape_with_root_profile();
return 0; return 0;
} }
@@ -130,13 +131,13 @@ static int do_report_event(void __user *arg)
if (!boot_complete_lock) { if (!boot_complete_lock) {
boot_complete_lock = true; boot_complete_lock = true;
pr_info("boot_complete triggered\n"); pr_info("boot_complete triggered\n");
on_boot_completed();
} }
break; break;
} }
case EVENT_MODULE_MOUNTED: { case EVENT_MODULE_MOUNTED: {
ksu_module_mounted = true;
pr_info("module mounted!\n"); pr_info("module mounted!\n");
nuke_ext4_sysfs(); on_module_mounted();
break; break;
} }
default: default:
@@ -408,6 +409,71 @@ put_orig_file:
return ret; return ret;
} }
static int do_manage_mark(void __user *arg)
{
struct ksu_manage_mark_cmd cmd;
int ret = 0;
if (copy_from_user(&cmd, arg, sizeof(cmd))) {
pr_err("manage_mark: copy_from_user failed\n");
return -EFAULT;
}
switch (cmd.operation) {
case KSU_MARK_GET: {
// Get task mark status
ret = ksu_get_task_mark(cmd.pid);
if (ret < 0) {
pr_err("manage_mark: get failed for pid %d: %d\n", cmd.pid, ret);
return ret;
}
cmd.result = (u32)ret;
break;
}
case KSU_MARK_MARK: {
if (cmd.pid == 0) {
ksu_mark_all_process();
} else {
ret = ksu_set_task_mark(cmd.pid, true);
if (ret < 0) {
pr_err("manage_mark: set_mark failed for pid %d: %d\n", cmd.pid,
ret);
return ret;
}
}
break;
}
case KSU_MARK_UNMARK: {
if (cmd.pid == 0) {
ksu_unmark_all_process();
} else {
ret = ksu_set_task_mark(cmd.pid, false);
if (ret < 0) {
pr_err("manage_mark: set_unmark failed for pid %d: %d\n",
cmd.pid, ret);
return ret;
}
}
break;
}
case KSU_MARK_REFRESH: {
ksu_mark_running_process();
pr_info("manage_mark: refreshed running processes\n");
break;
}
default: {
pr_err("manage_mark: invalid operation %u\n", cmd.operation);
return -EINVAL;
}
}
if (copy_to_user(arg, &cmd, sizeof(cmd))) {
pr_err("manage_mark: copy_to_user failed\n");
return -EFAULT;
}
return 0;
}
// 100. GET_FULL_VERSION - Get full version string // 100. GET_FULL_VERSION - Get full version string
static int do_get_full_version(void __user *arg) static int do_get_full_version(void __user *arg)
{ {
@@ -626,6 +692,7 @@ static const struct ksu_ioctl_cmd_map ksu_ioctl_handlers[] = {
{ .cmd = KSU_IOCTL_GET_FEATURE, .name = "GET_FEATURE", .handler = do_get_feature, .perm_check = manager_or_root }, { .cmd = KSU_IOCTL_GET_FEATURE, .name = "GET_FEATURE", .handler = do_get_feature, .perm_check = manager_or_root },
{ .cmd = KSU_IOCTL_SET_FEATURE, .name = "SET_FEATURE", .handler = do_set_feature, .perm_check = manager_or_root }, { .cmd = KSU_IOCTL_SET_FEATURE, .name = "SET_FEATURE", .handler = do_set_feature, .perm_check = manager_or_root },
{ .cmd = KSU_IOCTL_GET_WRAPPER_FD, .name = "GET_WRAPPER_FD", .handler = do_get_wrapper_fd, .perm_check = manager_or_root }, { .cmd = KSU_IOCTL_GET_WRAPPER_FD, .name = "GET_WRAPPER_FD", .handler = do_get_wrapper_fd, .perm_check = manager_or_root },
{ .cmd = KSU_IOCTL_MANAGE_MARK, .name = "MANAGE_MARK", .handler = do_manage_mark, .perm_check = manager_or_root },
{ .cmd = KSU_IOCTL_GET_FULL_VERSION,.name = "GET_FULL_VERSION", .handler = do_get_full_version, .perm_check = always_allow}, { .cmd = KSU_IOCTL_GET_FULL_VERSION,.name = "GET_FULL_VERSION", .handler = do_get_full_version, .perm_check = always_allow},
{ .cmd = KSU_IOCTL_HOOK_TYPE,.name = "GET_HOOK_TYPE", .handler = do_get_hook_type, .perm_check = manager_or_root}, { .cmd = KSU_IOCTL_HOOK_TYPE,.name = "GET_HOOK_TYPE", .handler = do_get_hook_type, .perm_check = manager_or_root},
{ .cmd = KSU_IOCTL_ENABLE_KPM, .name = "GET_ENABLE_KPM", .handler = do_enable_kpm, .perm_check = manager_or_root}, { .cmd = KSU_IOCTL_ENABLE_KPM, .name = "GET_ENABLE_KPM", .handler = do_enable_kpm, .perm_check = manager_or_root},
@@ -641,6 +708,52 @@ static const struct ksu_ioctl_cmd_map ksu_ioctl_handlers[] = {
{ .cmd = 0, .name = NULL, .handler = NULL, .perm_check = NULL} // Sentine { .cmd = 0, .name = NULL, .handler = NULL, .perm_check = NULL} // Sentine
}; };
// downstream: make sure to pass arg as reference, this can allow us to extend things.
int ksu_handle_sys_reboot(int magic1, int magic2, unsigned int cmd, void __user **arg)
{
if (magic1 != KSU_INSTALL_MAGIC1)
return 0;
#ifdef CONFIG_KSU_DEBUG
pr_info("sys_reboot: intercepted call! magic: 0x%x id: %d\n", magic1, magic2);
#endif
// Check if this is a request to install KSU fd
if (magic2 == KSU_INSTALL_MAGIC2) {
int fd = ksu_install_fd();
pr_info("[%d] install ksu fd: %d\n", current->pid, fd);
// downstream: dereference all arg usage!
if (copy_to_user((void __user *)*arg, &fd, sizeof(fd))) {
pr_err("install ksu fd reply err\n");
}
return 0;
}
// extensions
return 0;
}
// Reboot hook for installing fd
static int reboot_handler_pre(struct kprobe *p, struct pt_regs *regs)
{
struct pt_regs *real_regs = PT_REAL_REGS(regs);
int magic1 = (int)PT_REGS_PARM1(real_regs);
int magic2 = (int)PT_REGS_PARM2(real_regs);
int cmd = (int)PT_REGS_PARM3(real_regs);
void __user **arg = (void __user **)&PT_REGS_SYSCALL_PARM4(real_regs);
return ksu_handle_sys_reboot(magic1, magic2, cmd, arg);
}
static struct kprobe reboot_kp = {
.symbol_name = REBOOT_SYMBOL,
.pre_handler = reboot_handler_pre,
};
void ksu_supercalls_init(void) void ksu_supercalls_init(void)
{ {
int i; int i;
@@ -649,6 +762,17 @@ void ksu_supercalls_init(void)
for (i = 0; ksu_ioctl_handlers[i].handler; i++) { for (i = 0; ksu_ioctl_handlers[i].handler; i++) {
pr_info(" %-18s = 0x%08x\n", ksu_ioctl_handlers[i].name, ksu_ioctl_handlers[i].cmd); pr_info(" %-18s = 0x%08x\n", ksu_ioctl_handlers[i].name, ksu_ioctl_handlers[i].cmd);
} }
int rc = register_kprobe(&reboot_kp);
if (rc) {
pr_err("reboot kprobe failed: %d\n", rc);
} else {
pr_info("reboot kprobe registered successfully\n");
}
}
void ksu_supercalls_exit(void){
unregister_kprobe(&reboot_kp);
} }
static inline void ksu_ioctl_audit(unsigned int cmd, const char *cmd_name, uid_t uid, int ret) static inline void ksu_ioctl_audit(unsigned int cmd, const char *cmd_name, uid_t uid, int ret)

View File

@@ -4,6 +4,7 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/ioctl.h> #include <linux/ioctl.h>
#include "ksu.h" #include "ksu.h"
#include "app_profile.h"
#ifdef CONFIG_KPM #ifdef CONFIG_KPM
#include "kpm/kpm.h" #include "kpm/kpm.h"
@@ -82,6 +83,17 @@ struct ksu_get_wrapper_fd_cmd {
__u32 flags; // Input: flags of userspace fd __u32 flags; // Input: flags of userspace fd
}; };
struct ksu_manage_mark_cmd {
__u32 operation; // Input: KSU_MARK_*
__s32 pid; // Input: target pid (0 for all processes)
__u32 result; // Output: for get operation - mark status or reg_count
};
#define KSU_MARK_GET 1
#define KSU_MARK_MARK 2
#define KSU_MARK_UNMARK 3
#define KSU_MARK_REFRESH 4
// Other command structures // Other command structures
struct ksu_get_full_version_cmd { struct ksu_get_full_version_cmd {
char version_full[KSU_FULL_VERSION_STRING]; // Output: full version string char version_full[KSU_FULL_VERSION_STRING]; // Output: full version string
@@ -134,6 +146,7 @@ struct ksu_manual_su_cmd {
#define KSU_IOCTL_GET_FEATURE _IOC(_IOC_READ|_IOC_WRITE, 'K', 13, 0) #define KSU_IOCTL_GET_FEATURE _IOC(_IOC_READ|_IOC_WRITE, 'K', 13, 0)
#define KSU_IOCTL_SET_FEATURE _IOC(_IOC_WRITE, 'K', 14, 0) #define KSU_IOCTL_SET_FEATURE _IOC(_IOC_WRITE, 'K', 14, 0)
#define KSU_IOCTL_GET_WRAPPER_FD _IOC(_IOC_WRITE, 'K', 15, 0) #define KSU_IOCTL_GET_WRAPPER_FD _IOC(_IOC_WRITE, 'K', 15, 0)
#define KSU_IOCTL_MANAGE_MARK _IOC(_IOC_READ|_IOC_WRITE, 'K', 16, 0)
// Other IOCTL command definitions // Other IOCTL command definitions
#define KSU_IOCTL_GET_FULL_VERSION _IOC(_IOC_READ, 'K', 100, 0) #define KSU_IOCTL_GET_FULL_VERSION _IOC(_IOC_READ, 'K', 100, 0)
#define KSU_IOCTL_HOOK_TYPE _IOC(_IOC_READ, 'K', 101, 0) #define KSU_IOCTL_HOOK_TYPE _IOC(_IOC_READ, 'K', 101, 0)
@@ -160,6 +173,7 @@ struct ksu_ioctl_cmd_map {
// Install KSU fd to current process // Install KSU fd to current process
int ksu_install_fd(void); int ksu_install_fd(void);
void ksu_supercalls_init(); void ksu_supercalls_init(void);
void ksu_supercalls_exit(void);
#endif // __KSU_H_SUPERCALLS #endif // __KSU_H_SUPERCALLS

View File

@@ -0,0 +1,333 @@
#include "linux/compiler.h"
#include "linux/cred.h"
#include "linux/printk.h"
#include "selinux/selinux.h"
#include <linux/spinlock.h>
#include <linux/kprobes.h>
#include <linux/tracepoint.h>
#include <asm/syscall.h>
#include <linux/slab.h>
#include <linux/ptrace.h>
#include <trace/events/syscalls.h>
#include "allowlist.h"
#include "arch.h"
#include "klog.h" // IWYU pragma: keep
#include "syscall_hook_manager.h"
#include "sucompat.h"
#include "setuid_hook.h"
#include "selinux/selinux.h"
// Tracepoint registration count management
static int tracepoint_reg_count = 0;
static DEFINE_SPINLOCK(tracepoint_reg_lock);
// Process marking management
static void handle_process_mark(bool mark)
{
struct task_struct *p, *t;
read_lock(&tasklist_lock);
for_each_process_thread(p, t) {
if (mark)
ksu_set_task_tracepoint_flag(t);
else
ksu_clear_task_tracepoint_flag(t);
}
read_unlock(&tasklist_lock);
}
void ksu_mark_all_process(void)
{
handle_process_mark(true);
pr_info("hook_manager: mark all user process done!\n");
}
void ksu_unmark_all_process(void)
{
handle_process_mark(false);
pr_info("hook_manager: unmark all user process done!\n");
}
void ksu_mark_running_process(void)
{
struct task_struct *p, *t;
read_lock(&tasklist_lock);
for_each_process_thread (p, t) {
if (!t->mm) { // only user processes
continue;
}
int uid = task_uid(t).val;
const struct cred *cred = get_task_cred(t);
bool ksu_root_process =
uid == 0 && is_task_ksu_domain(cred);
bool is_zygote_process = is_zygote(cred);
bool is_shell = uid == 2000;
// before boot completed, we shall mark init for marking zygote
bool is_init = t->pid == 1;
if (ksu_root_process || is_zygote_process || is_shell || is_init
|| ksu_is_allow_uid(uid)) {
ksu_set_task_tracepoint_flag(t);
pr_info("hook_manager: mark process: pid:%d, uid: %d, comm:%s\n",
t->pid, uid, t->comm);
}
put_cred(cred);
}
read_unlock(&tasklist_lock);
}
// Get task mark status
// Returns: 1 if marked, 0 if not marked, -ESRCH if task not found
int ksu_get_task_mark(pid_t pid)
{
struct task_struct *task;
int marked = -ESRCH;
rcu_read_lock();
task = find_task_by_vpid(pid);
if (task) {
get_task_struct(task);
rcu_read_unlock();
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
marked = test_task_syscall_work(task, SYSCALL_TRACEPOINT) ? 1 : 0;
#else
marked = test_tsk_thread_flag(task, TIF_SYSCALL_TRACEPOINT) ? 1 : 0;
#endif
put_task_struct(task);
} else {
rcu_read_unlock();
}
return marked;
}
// Set task mark status
// Returns: 0 on success, -ESRCH if task not found
int ksu_set_task_mark(pid_t pid, bool mark)
{
struct task_struct *task;
int ret = -ESRCH;
rcu_read_lock();
task = find_task_by_vpid(pid);
if (task) {
get_task_struct(task);
rcu_read_unlock();
if (mark) {
ksu_set_task_tracepoint_flag(task);
pr_info("hook_manager: marked task pid=%d comm=%s\n", pid, task->comm);
} else {
ksu_clear_task_tracepoint_flag(task);
pr_info("hook_manager: unmarked task pid=%d comm=%s\n", pid, task->comm);
}
put_task_struct(task);
ret = 0;
} else {
rcu_read_unlock();
}
return ret;
}
#ifdef CONFIG_KRETPROBES
static struct kretprobe *init_kretprobe(const char *name,
kretprobe_handler_t handler)
{
struct kretprobe *rp = kzalloc(sizeof(struct kretprobe), GFP_KERNEL);
if (!rp)
return NULL;
rp->kp.symbol_name = name;
rp->handler = handler;
rp->data_size = 0;
rp->maxactive = 0;
int ret = register_kretprobe(rp);
pr_info("hook_manager: register_%s kretprobe: %d\n", name, ret);
if (ret) {
kfree(rp);
return NULL;
}
return rp;
}
static void destroy_kretprobe(struct kretprobe **rp_ptr)
{
struct kretprobe *rp = *rp_ptr;
if (!rp)
return;
unregister_kretprobe(rp);
synchronize_rcu();
kfree(rp);
*rp_ptr = NULL;
}
static int syscall_regfunc_handler(struct kretprobe_instance *ri, struct pt_regs *regs)
{
unsigned long flags;
spin_lock_irqsave(&tracepoint_reg_lock, flags);
if (tracepoint_reg_count < 1) {
// while install our tracepoint, mark our processes
ksu_unmark_all_process();
ksu_mark_running_process();
} else {
// while installing other tracepoint, mark all processes
ksu_mark_all_process();
}
tracepoint_reg_count++;
spin_unlock_irqrestore(&tracepoint_reg_lock, flags);
return 0;
}
static int syscall_unregfunc_handler(struct kretprobe_instance *ri, struct pt_regs *regs)
{
unsigned long flags;
spin_lock_irqsave(&tracepoint_reg_lock, flags);
if (tracepoint_reg_count <= 1) {
// while uninstall our tracepoint, unmark all processes
ksu_unmark_all_process();
} else {
// while uninstalling other tracepoint, mark our processes
ksu_unmark_all_process();
ksu_mark_running_process();
}
tracepoint_reg_count--;
spin_unlock_irqrestore(&tracepoint_reg_lock, flags);
return 0;
}
static struct kretprobe *syscall_regfunc_rp = NULL;
static struct kretprobe *syscall_unregfunc_rp = NULL;
#endif
static inline bool check_syscall_fastpath(int nr)
{
switch (nr) {
case __NR_newfstatat:
case __NR_faccessat:
case __NR_execve:
case __NR_setresuid:
return true;
default:
return false;
}
}
int ksu_handle_init_mark_tracker(int *fd, const char __user **filename_user,
void *__never_use_argv, void *__never_use_envp,
int *__never_use_flags)
{
char path[64];
if (unlikely(!filename_user))
return 0;
memset(path, 0, sizeof(path));
strncpy_from_user_nofault(path, *filename_user, sizeof(path));
if (likely(strstr(path, "adbd") == NULL)){
ksu_clear_task_tracepoint_flag(current);
}
return 0;
}
#ifdef KSU_HAVE_SYSCALL_TRACEPOINTS_HOOK
// Generic sys_enter handler that dispatches to specific handlers
static void ksu_sys_enter_handler(void *data, struct pt_regs *regs, long id)
{
if (unlikely(check_syscall_fastpath(id))) {
if (ksu_su_compat_enabled) {
// Handle newfstatat
if (id == __NR_newfstatat) {
int *dfd = (int *)&PT_REGS_PARM1(regs);
const char __user **filename_user =
(const char __user **)&PT_REGS_PARM2(regs);
int *flags = (int *)&PT_REGS_SYSCALL_PARM4(regs);
ksu_handle_stat(dfd, filename_user, flags);
return;
}
// Handle faccessat
if (id == __NR_faccessat) {
int *dfd = (int *)&PT_REGS_PARM1(regs);
const char __user **filename_user =
(const char __user **)&PT_REGS_PARM2(regs);
int *mode = (int *)&PT_REGS_PARM3(regs);
ksu_handle_faccessat(dfd, filename_user, mode, NULL);
return;
}
// Handle execve
if (id == __NR_execve) {
const char __user **filename_user =
(const char __user **)&PT_REGS_PARM1(regs);
if (current->pid == 1) {
ksu_handle_init_mark_tracker(AT_FDCWD, filename_user,
NULL, NULL, NULL);
} else {
ksu_handle_execve_sucompat(AT_FDCWD, filename_user, NULL,
NULL, NULL);
}
return;
}
}
// Handle setresuid
if (id == __NR_setresuid) {
uid_t ruid = (uid_t)PT_REGS_PARM1(regs);
uid_t euid = (uid_t)PT_REGS_PARM2(regs);
uid_t suid = (uid_t)PT_REGS_PARM3(regs);
ksu_handle_setresuid(ruid, euid, suid);
return;
}
}
}
#endif
void ksu_syscall_hook_manager_init(void)
{
int ret;
pr_info("hook_manager: ksu_hook_manager_init called\n");
#ifdef CONFIG_KRETPROBES
// Register kretprobe for syscall_regfunc
syscall_regfunc_rp = init_kretprobe("syscall_regfunc", syscall_regfunc_handler);
// Register kretprobe for syscall_unregfunc
syscall_unregfunc_rp = init_kretprobe("syscall_unregfunc", syscall_unregfunc_handler);
#endif
#ifdef KSU_HAVE_SYSCALL_TRACEPOINTS_HOOK
ret = register_trace_sys_enter(ksu_sys_enter_handler, NULL);
#ifndef CONFIG_KRETPROBES
unmark_all_process();
ksu_mark_running_process();
#endif
if (ret) {
pr_err("hook_manager: failed to register sys_enter tracepoint: %d\n", ret);
} else {
pr_info("hook_manager: sys_enter tracepoint registered\n");
}
#endif
ksu_setuid_hook_init();
ksu_sucompat_init();
}
void ksu_syscall_hook_manager_exit(void)
{
pr_info("hook_manager: ksu_hook_manager_exit called\n");
#ifdef KSU_HAVE_SYSCALL_TRACEPOINTS_HOOK
unregister_trace_sys_enter(ksu_sys_enter_handler, NULL);
tracepoint_synchronize_unregister();
pr_info("hook_manager: sys_enter tracepoint unregistered\n");
#endif
#ifdef CONFIG_KRETPROBES
destroy_kretprobe(&syscall_regfunc_rp);
destroy_kretprobe(&syscall_unregfunc_rp);
#endif
ksu_sucompat_exit();
ksu_setuid_hook_exit();
}

View File

@@ -0,0 +1,40 @@
#ifndef __KSU_H_HOOK_MANAGER
#define __KSU_H_HOOK_MANAGER
#include <linux/version.h>
#include <linux/sched.h>
#include <linux/thread_info.h>
// Hook manager initialization and cleanup
void ksu_syscall_hook_manager_init(void);
void ksu_syscall_hook_manager_exit(void);
// Process marking for tracepoint
void ksu_mark_all_process(void);
void ksu_unmark_all_process(void);
void ksu_mark_running_process(void);
// Per-task mark operations
int ksu_get_task_mark(pid_t pid);
int ksu_set_task_mark(pid_t pid, bool mark);
static inline void ksu_set_task_tracepoint_flag(struct task_struct *t)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
set_task_syscall_work(t, SYSCALL_TRACEPOINT);
#else
set_tsk_thread_flag(t, TIF_SYSCALL_TRACEPOINT);
#endif
}
static inline void ksu_clear_task_tracepoint_flag(struct task_struct *t)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
clear_task_syscall_work(t, SYSCALL_TRACEPOINT);
#else
clear_tsk_thread_flag(t, TIF_SYSCALL_TRACEPOINT);
#endif
}
#endif

View File

@@ -10,7 +10,6 @@
#include "allowlist.h" #include "allowlist.h"
#include "klog.h" // IWYU pragma: keep #include "klog.h" // IWYU pragma: keep
#include "ksu.h"
#include "manager.h" #include "manager.h"
#include "throne_tracker.h" #include "throne_tracker.h"
#include "apk_sign.h" #include "apk_sign.h"