kernel: refact (#113)

* refact

* sort inlude

* update

* unregister execve kprobe

* update log

* don't unregister if not in kprobe

* opt for no kprobe

* opt for no kprobe

* stop debug

* don't forget to call ksu_uid_observer_exit

* rename core to core_hook

* direct call do_persistent_allow_list

* add prefix

* use getter, add warn

* add wrapper

* run clang-format

clang-format --style="{BasedOnStyle: InheritParentConfig, SortIncludes: true}" -i kernel/**/*.[ch]

* try fix wsa x64 build
This commit is contained in:
Ylarod
2023-01-25 21:53:19 +08:00
committed by GitHub
parent 5fb8316e46
commit 2f970f7ab8
26 changed files with 1002 additions and 850 deletions

View File

@@ -1,3 +1,4 @@
Diagnostics: Diagnostics:
UnusedIncludes: Strict
ClangTidy: ClangTidy:
Remove: bugprone-sizeof-expression Remove: bugprone-sizeof-expression

View File

@@ -5,8 +5,9 @@ obj-y += kernelsu.o
obj-y += module_api.o obj-y += module_api.o
obj-y += sucompat.o obj-y += sucompat.o
obj-y += uid_observer.o obj-y += uid_observer.o
obj-y += lsm_hook.o obj-y += manager.o
obj-y += kprobe_hook.o obj-y += core_hook.o
obj-y += ksud.o
obj-y += selinux/ obj-y += selinux/
@@ -21,4 +22,4 @@ endif
ccflags-y += -DEXPECTED_SIZE=$(EXPECTED_SIZE) ccflags-y += -DEXPECTED_SIZE=$(EXPECTED_SIZE)
ccflags-y += -DEXPECTED_HASH=$(EXPECTED_HASH) ccflags-y += -DEXPECTED_HASH=$(EXPECTED_HASH)
ccflags-y += -Wno-implicit-function-declaration -Wno-strict-prototypes -Wno-int-conversion -Wno-gcc-compat ccflags-y += -Wno-implicit-function-declaration -Wno-strict-prototypes -Wno-int-conversion -Wno-gcc-compat
ccflags-y += -Wno-macro-redefined -Wno-declaration-after-statement ccflags-y += -Wno-macro-redefined -Wno-declaration-after-statement

View File

@@ -1,26 +1,10 @@
#include <linux/list.h> #include "linux/delay.h"
#include <linux/cpu.h> #include "linux/fs.h"
#include <linux/errno.h> #include "linux/kernel.h"
#include <linux/init.h> #include "linux/list.h"
#include <linux/kernel.h> #include "linux/printk.h"
#include <linux/kprobes.h> #include "linux/slab.h"
#include <linux/memory.h>
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/uaccess.h>
#include <linux/uidgid.h>
#include <linux/fdtable.h>
#include <linux/fs.h>
#include <linux/fs_struct.h>
#include <linux/namei.h>
#include <linux/rcupdate.h>
#include <linux/delay.h> // msleep
#include "klog.h"
#include "selinux/selinux.h" #include "selinux/selinux.h"
#define FILE_MAGIC 0x7f4b5355 // ' KSU', u32 #define FILE_MAGIC 0x7f4b5355 // ' KSU', u32
@@ -256,39 +240,37 @@ void ksu_prune_allowlist(bool (*is_uid_exist)(uid_t, void *), void *data)
} }
} }
static int init_work(void)
{
INIT_WORK(&ksu_save_work, do_persistent_allow_list);
INIT_WORK(&ksu_load_work, do_load_allow_list);
return 0;
}
// make sure allow list works cross boot // make sure allow list works cross boot
bool persistent_allow_list(void) bool persistent_allow_list(void)
{ {
ksu_queue_work(&ksu_save_work); return ksu_queue_work(&ksu_save_work);
return true;
} }
bool ksu_load_allow_list(void) bool ksu_load_allow_list(void)
{ {
ksu_queue_work(&ksu_load_work); return ksu_queue_work(&ksu_load_work);
return true;
} }
bool ksu_allowlist_init(void) void ksu_allowlist_init(void)
{ {
INIT_LIST_HEAD(&allow_list); INIT_LIST_HEAD(&allow_list);
init_work(); INIT_WORK(&ksu_save_work, do_persistent_allow_list);
INIT_WORK(&ksu_load_work, do_load_allow_list);
// start load allow list, we load it before app_process exec now, refer: sucompat#execve_handler_pre
// ksu_load_allow_list();
return true;
} }
bool ksu_allowlist_exit(void) void ksu_allowlist_exit(void)
{ {
return true; struct perm_data *np = NULL;
struct perm_data *n = NULL;
do_persistent_allow_list(NULL);
// free allowlist
mutex_lock(&allowlist_mutex);
list_for_each_entry_safe (np, n, &allow_list, list) {
list_del(&np->list);
kfree(np);
}
mutex_unlock(&allowlist_mutex);
} }

View File

@@ -1,11 +1,13 @@
#ifndef __KSU_H_ALLOWLIST #ifndef __KSU_H_ALLOWLIST
#define __KSU_H_ALLOWLIST #define __KSU_H_ALLOWLIST
#include <linux/types.h> #include "linux/types.h"
bool ksu_allowlist_init(); void ksu_allowlist_init(void);
bool ksu_allowlist_exit(); void ksu_allowlist_exit(void);
bool ksu_load_allow_list(void);
bool ksu_is_allow_uid(uid_t uid); bool ksu_is_allow_uid(uid_t uid);
@@ -13,8 +15,6 @@ bool ksu_allow_uid(uid_t uid, bool allow);
bool ksu_get_allow_list(int *array, int *length, bool allow); bool ksu_get_allow_list(int *array, int *length, bool allow);
bool ksu_load_allow_list(void); void ksu_prune_allowlist(bool (*is_uid_exist)(uid_t, void *), void *data);
void ksu_prune_allowlist(bool (*is_uid_exist)(uid_t, void *), void* data);
#endif #endif

View File

@@ -1,11 +1,10 @@
#include <linux/moduleparam.h> #include "linux/fs.h"
#include <linux/fs.h> #include "linux/moduleparam.h"
#include "apk_sign.h" #include "apk_sign.h"
#include "klog.h"
static __always_inline int check_v2_signature(char *path, unsigned expected_size, static __always_inline int
unsigned expected_hash) check_v2_signature(char *path, unsigned expected_size, unsigned expected_hash)
{ {
unsigned char buffer[0x11] = { 0 }; unsigned char buffer[0x11] = { 0 };
u32 size4; u32 size4;
@@ -67,23 +66,23 @@ static __always_inline int check_v2_signature(char *path, unsigned expected_size
offset = 4; offset = 4;
pr_info("id: 0x%08x\n", id); pr_info("id: 0x%08x\n", id);
if ((id ^ 0xdeadbeefu) == 0xafa439f5u || if ((id ^ 0xdeadbeefu) == 0xafa439f5u ||
(id ^ 0xdeadbeefu) == 0x2efed62f) { (id ^ 0xdeadbeefu) == 0x2efed62f) {
kernel_read(fp, &size4, 0x4, kernel_read(fp, &size4, 0x4,
&pos); // signer-sequence length &pos); // signer-sequence length
kernel_read(fp, &size4, 0x4, &pos); // signer length kernel_read(fp, &size4, 0x4, &pos); // signer length
kernel_read(fp, &size4, 0x4, kernel_read(fp, &size4, 0x4,
&pos); // signed data length &pos); // signed data length
offset += 0x4 * 3; offset += 0x4 * 3;
kernel_read(fp, &size4, 0x4, kernel_read(fp, &size4, 0x4,
&pos); // digests-sequence length &pos); // digests-sequence length
pos += size4; pos += size4;
offset += 0x4 + size4; offset += 0x4 + size4;
kernel_read(fp, &size4, 0x4, kernel_read(fp, &size4, 0x4,
&pos); // certificates length &pos); // certificates length
kernel_read(fp, &size4, 0x4, kernel_read(fp, &size4, 0x4,
&pos); // certificate length &pos); // certificate length
offset += 0x4 * 2; offset += 0x4 * 2;
#if 0 #if 0
int hash = 1; int hash = 1;
@@ -104,7 +103,7 @@ static __always_inline int check_v2_signature(char *path, unsigned expected_size
} }
offset += size4; offset += size4;
if ((((unsigned)hash) ^ 0x14131211u) == if ((((unsigned)hash) ^ 0x14131211u) ==
expected_hash) { expected_hash) {
sign = 0; sign = 0;
break; break;
} }
@@ -127,8 +126,38 @@ clean:
unsigned ksu_expected_size = EXPECTED_SIZE; unsigned ksu_expected_size = EXPECTED_SIZE;
unsigned ksu_expected_hash = EXPECTED_HASH; unsigned ksu_expected_hash = EXPECTED_HASH;
module_param(ksu_expected_size, uint, S_IRUSR | S_IWUSR); #include "manager.h"
module_param(ksu_expected_hash, uint, S_IRUSR | S_IWUSR);
static int set_expected_size(const char *val, const struct kernel_param *kp)
{
int rv = param_set_uint(val, kp);
ksu_invalidate_manager_uid();
pr_info("ksu_expected_size set to %x", ksu_expected_size);
return rv;
}
static int set_expected_hash(const char *val, const struct kernel_param *kp)
{
int rv = param_set_uint(val, kp);
ksu_invalidate_manager_uid();
pr_info("ksu_expected_hash set to %x", ksu_expected_hash);
return rv;
}
static struct kernel_param_ops expected_size_ops = {
.set = set_expected_size,
.get = param_get_uint,
};
static struct kernel_param_ops expected_hash_ops = {
.set = set_expected_hash,
.get = param_get_uint,
};
module_param_cb(ksu_expected_size, &expected_size_ops, &ksu_expected_size,
S_IRUSR | S_IWUSR);
module_param_cb(ksu_expected_hash, &expected_hash_ops, &ksu_expected_hash,
S_IRUSR | S_IWUSR);
int is_manager_apk(char *path) int is_manager_apk(char *path)
{ {
@@ -137,7 +166,6 @@ int is_manager_apk(char *path)
#else #else
int is_manager_apk(char *path) int is_manager_apk(char *path)
{ {
return check_v2_signature(path, EXPECTED_SIZE, EXPECTED_HASH); return check_v2_signature(path, EXPECTED_SIZE, EXPECTED_HASH);

View File

@@ -2,6 +2,6 @@
#define __KSU_H_APK_V2_SIGN #define __KSU_H_APK_V2_SIGN
// return 0 if signature match // return 0 if signature match
int is_manager_apk(char* path); int is_manager_apk(char *path);
#endif #endif

View File

@@ -1,7 +1,7 @@
#ifndef __KSU_H_ARCH #ifndef __KSU_H_ARCH
#define __KSU_H_ARCH #define __KSU_H_ARCH
#include <linux/version.h> #include "linux/version.h"
#if defined(__aarch64__) #if defined(__aarch64__)

355
kernel/core_hook.c Normal file
View File

@@ -0,0 +1,355 @@
#include "linux/cred.h"
#include "linux/err.h"
#include "linux/init.h"
#include "linux/kernel.h"
#include "linux/kprobes.h"
#include "linux/lsm_hooks.h"
#include "linux/printk.h"
#include "linux/uaccess.h"
#include "linux/uidgid.h"
#include "linux/version.h"
#include "linux/fs.h"
#include "linux/namei.h"
#include "linux/rcupdate.h"
#include "allowlist.h"
#include "arch.h"
#include "core_hook.h"
#include "ksu.h"
#include "manager.h"
#include "selinux/selinux.h"
#include "uid_observer.h"
static inline bool is_allow_su()
{
if (is_manager()) {
// we are manager, allow!
return true;
}
return ksu_is_allow_uid(current_uid().val);
}
static struct group_info root_groups = { .usage = ATOMIC_INIT(2) };
void escape_to_root(void)
{
struct cred *cred;
cred = (struct cred *)__task_cred(current);
memset(&cred->uid, 0, sizeof(cred->uid));
memset(&cred->gid, 0, sizeof(cred->gid));
memset(&cred->suid, 0, sizeof(cred->suid));
memset(&cred->euid, 0, sizeof(cred->euid));
memset(&cred->egid, 0, sizeof(cred->egid));
memset(&cred->fsuid, 0, sizeof(cred->fsuid));
memset(&cred->fsgid, 0, sizeof(cred->fsgid));
memset(&cred->cap_inheritable, 0xff, sizeof(cred->cap_inheritable));
memset(&cred->cap_permitted, 0xff, sizeof(cred->cap_permitted));
memset(&cred->cap_effective, 0xff, sizeof(cred->cap_effective));
memset(&cred->cap_bset, 0xff, sizeof(cred->cap_bset));
memset(&cred->cap_ambient, 0xff, sizeof(cred->cap_ambient));
// disable seccomp
#if defined(CONFIG_GENERIC_ENTRY) && \
LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
current_thread_info()->syscall_work &= ~SYSCALL_WORK_SECCOMP;
#else
current_thread_info()->flags &= ~(TIF_SECCOMP | _TIF_SECCOMP);
#endif
current->seccomp.mode = 0;
current->seccomp.filter = NULL;
// setgroup to root
if (cred->group_info)
put_group_info(cred->group_info);
cred->group_info = get_group_info(&root_groups);
setup_selinux();
}
int ksu_handle_rename(struct dentry *old_dentry, struct dentry *new_dentry)
{
if (!current->mm) {
// skip kernel threads
return 0;
}
if (current_uid().val != 1000) {
// skip non system uid
return 0;
}
if (!old_dentry || !new_dentry) {
return 0;
}
// /data/system/packages.list.tmp -> /data/system/packages.list
if (strcmp(new_dentry->d_iname, "packages.list")) {
return 0;
}
char path[128];
char *buf = dentry_path_raw(new_dentry, path, sizeof(path));
if (IS_ERR(buf)) {
pr_err("dentry_path_raw failed.\n");
return 0;
}
if (strcmp(buf, "/system/packages.list")) {
return 0;
}
pr_info("renameat: %s -> %s\n, new path: %s", old_dentry->d_iname,
new_dentry->d_iname, buf);
update_uid();
return 0;
}
int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
unsigned long arg4, unsigned long arg5)
{
// if success, we modify the arg5 as result!
u32 *result = (u32 *)arg5;
u32 reply_ok = KERNEL_SU_OPTION;
if (KERNEL_SU_OPTION != option) {
return 0;
}
pr_info("option: 0x%x, cmd: %ld\n", option, arg2);
if (arg2 == CMD_BECOME_MANAGER) {
// quick check
if (is_manager()) {
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
pr_err("become_manager: prctl reply error\n");
}
return 0;
}
if (ksu_is_manager_uid_valid()) {
pr_info("manager already exist: %d\n",
ksu_get_manager_uid());
return 0;
}
// someone wants to be root manager, just check it!
// arg3 should be `/data/data/<manager_package_name>`
char param[128];
const char *prefix = "/data/data/";
if (copy_from_user(param, arg3, sizeof(param))) {
pr_err("become_manager: copy param err\n");
return 0;
}
if (startswith(param, (char *)prefix) != 0) {
pr_info("become_manager: invalid param: %s\n", param);
return 0;
}
// stat the param, app must have permission to do this
// otherwise it may fake the path!
struct path path;
if (kern_path(param, LOOKUP_DIRECTORY, &path)) {
pr_err("become_manager: kern_path err\n");
return 0;
}
if (path.dentry->d_inode->i_uid.val != current_uid().val) {
pr_err("become_manager: path uid != current uid\n");
path_put(&path);
return 0;
}
char *pkg = param + strlen(prefix);
pr_info("become_manager: param pkg: %s\n", pkg);
bool success = become_manager(pkg);
if (success) {
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
pr_err("become_manager: prctl reply error\n");
}
}
path_put(&path);
return 0;
}
if (arg2 == CMD_GRANT_ROOT) {
if (is_allow_su()) {
pr_info("allow root for: %d\n", current_uid());
escape_to_root();
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
pr_err("grant_root: prctl reply error\n");
}
} else {
pr_info("deny root for: %d\n", current_uid());
// add it to deny list!
ksu_allow_uid(current_uid().val, false);
}
return 0;
}
// Both root manager and root processes should be allowed to get version
if (arg2 == CMD_GET_VERSION) {
if (is_manager() || 0 == current_uid().val) {
u32 version = KERNEL_SU_VERSION;
if (copy_to_user(arg3, &version, sizeof(version))) {
pr_err("prctl reply error, cmd: %d\n", arg2);
return 0;
}
}
}
// all other cmds are for 'root manager'
if (!is_manager()) {
pr_info("Only manager can do cmd: %d\n", arg2);
return 0;
}
// we are already manager
if (arg2 == CMD_ALLOW_SU || arg2 == CMD_DENY_SU) {
bool allow = arg2 == CMD_ALLOW_SU;
bool success = false;
uid_t uid = (uid_t)arg3;
success = ksu_allow_uid(uid, allow);
if (success) {
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
pr_err("prctl reply error, cmd: %d\n", arg2);
}
}
} else if (arg2 == CMD_GET_ALLOW_LIST || arg2 == CMD_GET_DENY_LIST) {
u32 array[128];
u32 array_length;
bool success = ksu_get_allow_list(array, &array_length,
arg2 == CMD_GET_ALLOW_LIST);
if (success) {
if (!copy_to_user(arg4, &array_length,
sizeof(array_length)) &&
!copy_to_user(arg3, array,
sizeof(u32) * array_length)) {
if (!copy_to_user(result, &reply_ok,
sizeof(reply_ok))) {
pr_err("prctl reply error, cmd: %d\n",
arg2);
}
} else {
pr_err("prctl copy allowlist error\n");
}
}
}
return 0;
}
// Init functons
static int 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 option = (int)PT_REGS_PARM1(real_regs);
unsigned long arg2 = (unsigned long)PT_REGS_PARM2(real_regs);
unsigned long arg3 = (unsigned long)PT_REGS_PARM3(real_regs);
unsigned long arg4 = (unsigned long)PT_REGS_PARM4(real_regs);
unsigned long arg5 = (unsigned long)PT_REGS_PARM5(real_regs);
return ksu_handle_prctl(option, arg2, arg3, arg4, arg5);
}
static struct kprobe prctl_kp = {
.symbol_name = PRCTL_SYMBOL,
.pre_handler = handler_pre,
};
static int renameat_handler_pre(struct kprobe *p, struct pt_regs *regs)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0)
// https://elixir.bootlin.com/linux/v5.12-rc1/source/include/linux/fs.h
struct renamedata *rd = PT_REGS_PARM1(regs);
struct dentry *old_entry = rd->old_dentry;
struct dentry *new_entry = rd->new_dentry;
#else
struct dentry *old_entry = (struct dentry *)PT_REGS_PARM2(regs);
struct dentry *new_entry = (struct dentry *)PT_REGS_PARM4(regs);
#endif
return ksu_handle_rename(old_entry, new_entry);
}
static struct kprobe renameat_kp = {
.symbol_name = "vfs_rename",
.pre_handler = renameat_handler_pre,
};
__maybe_unused int ksu_kprobe_init(void)
{
int rc = 0;
rc = register_kprobe(&prctl_kp);
if (rc) {
pr_info("prctl kprobe failed: %d.\n", rc);
return rc;
}
rc = register_kprobe(&renameat_kp);
pr_info("renameat kp: %d\n", rc);
return rc;
}
__maybe_unused int ksu_kprobe_exit(void)
{
unregister_kprobe(&prctl_kp);
unregister_kprobe(&renameat_kp);
return 0;
}
static int ksu_task_prctl(int option, unsigned long arg2, unsigned long arg3,
unsigned long arg4, unsigned long arg5)
{
ksu_handle_prctl(option, arg2, arg3, arg4, arg5);
return -ENOSYS;
}
static int ksu_inode_rename(struct inode *old_inode, struct dentry *old_dentry,
struct inode *new_inode, struct dentry *new_dentry)
{
return ksu_handle_rename(old_dentry, new_dentry);
}
static struct security_hook_list ksu_hooks[] = {
LSM_HOOK_INIT(task_prctl, ksu_task_prctl),
LSM_HOOK_INIT(inode_rename, ksu_inode_rename),
};
void __init ksu_lsm_hook_init(void)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
security_add_hooks(ksu_hooks, ARRAY_SIZE(ksu_hooks), "ksu");
#else
// https://elixir.bootlin.com/linux/v4.10.17/source/include/linux/lsm_hooks.h#L1892
security_add_hooks(ksu_hooks, ARRAY_SIZE(ksu_hooks));
#endif
}
void __init ksu_core_init(void)
{
#ifndef MODULE
pr_info("ksu_lsm_hook_init\n");
ksu_lsm_hook_init();
#else
pr_info("ksu_kprobe_init\n");
ksu_kprobe_init();
#endif
}
void ksu_core_exit(void)
{
#ifndef MODULE
pr_info("ksu_kprobe_exit\n");
ksu_kprobe_exit();
#endif
}

9
kernel/core_hook.h Normal file
View File

@@ -0,0 +1,9 @@
#ifndef __KSU_H_KSU_CORE
#define __KSU_H_KSU_CORE
#include "linux/init.h"
void __init ksu_core_init(void);
void ksu_core_exit(void);
#endif

View File

@@ -1,17 +1,24 @@
#ifndef __KSU_H_KSHOOK #ifndef __KSU_H_KSHOOK
#define __KSU_H_KSHOOK #define __KSU_H_KSHOOK
#include <linux/fs.h> #include "linux/fs.h"
#include <linux/types.h> #include "linux/types.h"
// For sucompat
int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode, int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
int *flags); int *flags);
int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags); int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv, // For ksud
void *envp, int *flags);
int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr, int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
size_t *count_ptr, loff_t **pos); size_t *count_ptr, loff_t **pos);
// For ksud and sucompat
int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
void *envp, int *flags);
#endif #endif

View File

@@ -1,70 +0,0 @@
#include <linux/version.h>
#include <linux/kprobes.h>
#include "arch.h"
#include "ksu.h"
#include "klog.h"
static int 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 option = (int)PT_REGS_PARM1(real_regs);
unsigned long arg2 = (unsigned long)PT_REGS_PARM2(real_regs);
unsigned long arg3 = (unsigned long)PT_REGS_PARM3(real_regs);
unsigned long arg4 = (unsigned long)PT_REGS_PARM4(real_regs);
unsigned long arg5 = (unsigned long)PT_REGS_PARM5(real_regs);
return ksu_handle_prctl(option, arg2, arg3, arg4, arg5);
}
static struct kprobe prctl_kp = {
.symbol_name = PRCTL_SYMBOL,
.pre_handler = handler_pre,
};
static int renameat_handler_pre(struct kprobe *p, struct pt_regs *regs)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0)
// https://elixir.bootlin.com/linux/v5.12-rc1/source/include/linux/fs.h
struct renamedata *rd = PT_REGS_PARM1(regs);
struct dentry *old_entry = rd->old_dentry;
struct dentry *new_entry = rd->new_dentry;
#else
struct dentry *old_entry = PT_REGS_PARM2(regs);
struct dentry *new_entry = PT_REGS_PARM4(regs);
#endif
return ksu_handle_rename(old_entry, new_entry);
}
static struct kprobe renameat_kp = {
.symbol_name = "vfs_rename",
.pre_handler = renameat_handler_pre,
};
__maybe_unused int ksu_kprobe_init()
{
int rc = 0;
rc = register_kprobe(&prctl_kp);
if (rc) {
pr_info("prctl kprobe failed: %d.\n", rc);
return rc;
}
rc = register_kprobe(&renameat_kp);
pr_info("renameat kp: %d\n", rc);
return rc;
}
__maybe_unused int ksu_kprobe_exit()
{
unregister_kprobe(&prctl_kp);
unregister_kprobe(&renameat_kp);
return 0;
}

View File

@@ -1,324 +1,51 @@
#include "linux/export.h" #include "linux/module.h"
#include <asm-generic/errno-base.h> #include "linux/workqueue.h"
#include <linux/cpu.h> #include "linux/fs.h"
#include <linux/cred.h>
#include <linux/gfp.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/kprobes.h>
#include <linux/memory.h>
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/uaccess.h>
#include <linux/uidgid.h>
#include <linux/version.h>
#include <linux/workqueue.h>
#include <linux/fdtable.h>
#include <linux/fs.h>
#include <linux/fs_struct.h>
#include <linux/namei.h>
#include <linux/rcupdate.h>
#include <linux/delay.h> // msleep
#include "allowlist.h" #include "allowlist.h"
#include "apk_sign.h"
#include "arch.h" #include "arch.h"
#include "klog.h" #include "core_hook.h"
#include "ksu.h" #include "ksu.h"
#include "selinux/selinux.h"
#include "uid_observer.h" #include "uid_observer.h"
static struct group_info root_groups = { .usage = ATOMIC_INIT(2) };
static struct workqueue_struct *ksu_workqueue; static struct workqueue_struct *ksu_workqueue;
uid_t ksu_manager_uid = INVALID_UID; bool ksu_queue_work(struct work_struct *work)
void ksu_queue_work(struct work_struct *work)
{ {
queue_work(ksu_workqueue, work); return queue_work(ksu_workqueue, work);
} }
void escape_to_root() extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
void *argv, void *envp, int *flags);
extern int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr,
void *argv, void *envp, int *flags);
int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
void *envp, int *flags)
{ {
struct cred *cred; ksu_handle_execveat_ksud(fd, filename_ptr, argv, envp, flags);
return ksu_handle_execveat_sucompat(fd, filename_ptr, argv, envp,
cred = (struct cred *)__task_cred(current); flags);
memset(&cred->uid, 0, sizeof(cred->uid));
memset(&cred->gid, 0, sizeof(cred->gid));
memset(&cred->suid, 0, sizeof(cred->suid));
memset(&cred->euid, 0, sizeof(cred->euid));
memset(&cred->egid, 0, sizeof(cred->egid));
memset(&cred->fsuid, 0, sizeof(cred->fsuid));
memset(&cred->fsgid, 0, sizeof(cred->fsgid));
memset(&cred->cap_inheritable, 0xff, sizeof(cred->cap_inheritable));
memset(&cred->cap_permitted, 0xff, sizeof(cred->cap_permitted));
memset(&cred->cap_effective, 0xff, sizeof(cred->cap_effective));
memset(&cred->cap_bset, 0xff, sizeof(cred->cap_bset));
memset(&cred->cap_ambient, 0xff, sizeof(cred->cap_ambient));
// disable seccomp
#if defined(CONFIG_GENERIC_ENTRY) && \
LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
current_thread_info()->syscall_work &= ~SYSCALL_WORK_SECCOMP;
#else
current_thread_info()->flags &= ~(TIF_SECCOMP | _TIF_SECCOMP);
#endif
current->seccomp.mode = 0;
current->seccomp.filter = NULL;
// setgroup to root
if (cred->group_info)
put_group_info(cred->group_info);
cred->group_info = get_group_info(&root_groups);
setup_selinux();
} }
int startswith(char *s, char *prefix) extern void ksu_enable_sucompat();
{ extern void ksu_enable_ksud();
return strncmp(s, prefix, strlen(prefix));
}
int endswith(const char *s, const char *t)
{
size_t slen = strlen(s);
size_t tlen = strlen(t);
if (tlen > slen)
return 1;
return strcmp(s + slen - tlen, t);
}
static bool is_manager()
{
return ksu_manager_uid == current_uid().val;
}
static bool become_manager(char *pkg)
{
struct fdtable *files_table;
int i = 0;
struct path files_path;
char *cwd;
char *buf;
bool result = false;
// must be zygote's direct child, otherwise any app can fork a new process and
// open manager's apk
if (task_uid(current->real_parent).val != 0) {
pr_info("parent is not zygote!\n");
return false;
}
buf = (char *)kmalloc(PATH_MAX, GFP_ATOMIC);
if (!buf) {
pr_err("kalloc path failed.\n");
return false;
}
files_table = files_fdtable(current->files);
// todo: use iterate_fd
while (files_table->fd[i] != NULL) {
files_path = files_table->fd[i]->f_path;
if (!d_is_reg(files_path.dentry)) {
i++;
continue;
}
cwd = d_path(&files_path, buf, PATH_MAX);
if (startswith(cwd, "/data/app/") == 0 &&
endswith(cwd, "/base.apk") == 0) {
// we have found the apk!
pr_info("found apk: %s", cwd);
if (!strstr(cwd, pkg)) {
pr_info("apk path not match package name!\n");
i++;
continue;
}
if (is_manager_apk(cwd) == 0) {
// check passed
uid_t uid = current_uid().val;
pr_info("manager uid: %d\n", uid);
ksu_set_manager_uid(uid);
result = true;
goto clean;
} else {
pr_info("manager signature invalid!");
}
break;
}
i++;
}
clean:
kfree(buf);
return result;
}
static bool is_allow_su()
{
uid_t uid = current_uid().val;
if (uid == ksu_manager_uid) {
// we are manager, allow!
return true;
}
return ksu_is_allow_uid(uid);
}
extern void enable_sucompat();
int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
unsigned long arg4, unsigned long arg5)
{
// if success, we modify the arg5 as result!
u32 *result = (u32 *)arg5;
u32 reply_ok = KERNEL_SU_OPTION;
if (KERNEL_SU_OPTION != option) {
return 0;
}
pr_info("option: 0x%x, cmd: %ld\n", option, arg2);
if (arg2 == CMD_BECOME_MANAGER) {
// quick check
if (is_manager()) {
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
pr_err("become_manager: prctl reply error\n");
}
return 0;
}
if (ksu_is_manager_uid_valid()) {
pr_info("manager already exist: %d\n", ksu_manager_uid);
return 0;
}
// someone wants to be root manager, just check it!
// arg3 should be `/data/data/<manager_package_name>`
char param[128];
const char *prefix = "/data/data/";
if (copy_from_user(param, arg3, sizeof(param))) {
pr_err("become_manager: copy param err\n");
return 0;
}
if (startswith(param, (char *)prefix) != 0) {
pr_info("become_manager: invalid param: %s\n", param);
return 0;
}
// stat the param, app must have permission to do this
// otherwise it may fake the path!
struct path path;
if (kern_path(param, LOOKUP_DIRECTORY, &path)) {
pr_err("become_manager: kern_path err\n");
return 0;
}
if (path.dentry->d_inode->i_uid.val != current_uid().val) {
pr_err("become_manager: path uid != current uid\n");
path_put(&path);
return 0;
}
char *pkg = param + strlen(prefix);
pr_info("become_manager: param pkg: %s\n", pkg);
bool success = become_manager(pkg);
if (success) {
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
pr_err("become_manager: prctl reply error\n");
}
}
path_put(&path);
return 0;
}
if (arg2 == CMD_GRANT_ROOT) {
if (is_allow_su()) {
pr_info("allow root for: %d\n", current_uid());
escape_to_root();
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
pr_err("grant_root: prctl reply error\n");
}
} else {
pr_info("deny root for: %d\n", current_uid());
// add it to deny list!
ksu_allow_uid(current_uid().val, false);
}
return 0;
}
// Both root manager and root processes should be allowed to get version
if (arg2 == CMD_GET_VERSION) {
if (is_manager() || 0 == current_uid().val) {
u32 version = KERNEL_SU_VERSION;
if (copy_to_user(arg3, &version, sizeof(version))) {
pr_err("prctl reply error, cmd: %d\n", arg2);
return 0;
}
}
}
// all other cmds are for 'root manager'
if (!is_manager()) {
pr_info("Only manager can do cmd: %d\n", arg2);
return 0;
}
// we are already manager
if (arg2 == CMD_ALLOW_SU || arg2 == CMD_DENY_SU) {
bool allow = arg2 == CMD_ALLOW_SU;
bool success = false;
uid_t uid = (uid_t)arg3;
success = ksu_allow_uid(uid, allow);
if (success) {
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
pr_err("prctl reply error, cmd: %d\n", arg2);
}
}
} else if (arg2 == CMD_GET_ALLOW_LIST || arg2 == CMD_GET_DENY_LIST) {
u32 array[128];
u32 array_length;
bool success = ksu_get_allow_list(array, &array_length,
arg2 == CMD_GET_ALLOW_LIST);
if (success) {
if (!copy_to_user(arg4, &array_length,
sizeof(array_length)) &&
!copy_to_user(arg3, array,
sizeof(u32) * array_length)) {
if (!copy_to_user(result, &reply_ok,
sizeof(reply_ok))) {
pr_err("prctl reply error, cmd: %d\n",
arg2);
}
} else {
pr_err("prctl copy allowlist error\n");
}
}
}
return 0;
}
int __init kernelsu_init(void) int __init kernelsu_init(void)
{ {
#ifdef CONFIG_KSU_DEBUG #ifdef CONFIG_KSU_DEBUG
pr_alert("You are running DEBUG version of KernelSU"); pr_alert(
"*************************************************************\n"
"** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **\n"
"** **\n"
"** You are running DEBUG version of KernelSU **\n"
"** **\n"
"** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **\n"
"*************************************************************\n");
#endif #endif
#ifndef MODULE ksu_core_init();
ksu_lsm_hook_init();
#else
ksu_kprobe_init();
#endif
ksu_workqueue = alloc_workqueue("kernelsu_work_queue", 0, 0); ksu_workqueue = alloc_workqueue("kernelsu_work_queue", 0, 0);
@@ -327,7 +54,8 @@ int __init kernelsu_init(void)
ksu_uid_observer_init(); ksu_uid_observer_init();
#ifdef CONFIG_KPROBES #ifdef CONFIG_KPROBES
enable_sucompat(); ksu_enable_sucompat();
ksu_enable_ksud();
#else #else
#warning("KPROBES is disabled, KernelSU may not work, please check https://kernelsu.org/guide/how-to-integrate-for-non-gki.html") #warning("KPROBES is disabled, KernelSU may not work, please check https://kernelsu.org/guide/how-to-integrate-for-non-gki.html")
#endif #endif
@@ -339,7 +67,11 @@ void kernelsu_exit(void)
{ {
ksu_allowlist_exit(); ksu_allowlist_exit();
ksu_uid_observer_exit();
destroy_workqueue(ksu_workqueue); destroy_workqueue(ksu_workqueue);
ksu_core_exit();
} }
module_init(kernelsu_init); module_init(kernelsu_init);
@@ -350,6 +82,5 @@ MODULE_AUTHOR("weishu");
MODULE_DESCRIPTION("Android KernelSU"); MODULE_DESCRIPTION("Android KernelSU");
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0) #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0)
MODULE_IMPORT_NS( MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver);
VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver); // 5+才需要导出命名空间
#endif #endif

View File

@@ -1,9 +1,7 @@
#ifndef __KSU_H_KSU #ifndef __KSU_H_KSU
#define __KSU_H_KSU #define __KSU_H_KSU
#include <linux/dcache.h> #include "linux/workqueue.h"
#include <linux/uidgid.h>
#include <linux/workqueue.h>
#define KERNEL_SU_VERSION 10 #define KERNEL_SU_VERSION 10
@@ -18,45 +16,20 @@
#define CMD_GET_ALLOW_LIST 5 #define CMD_GET_ALLOW_LIST 5
#define CMD_GET_DENY_LIST 6 #define CMD_GET_DENY_LIST 6
#define INVALID_UID -1 bool ksu_queue_work(struct work_struct *work);
extern uid_t ksu_manager_uid; static inline int startswith(char *s, char *prefix)
static inline bool ksu_is_manager_uid_valid()
{ {
#ifndef CONFIG_KSU_DEBUG return strncmp(s, prefix, strlen(prefix));
return ksu_manager_uid != INVALID_UID;
#else
return false; // always allow in debug mode
#endif
} }
static inline uid_t ksu_get_manager_uid() static inline int endswith(const char *s, const char *t)
{ {
return ksu_manager_uid; size_t slen = strlen(s);
size_t tlen = strlen(t);
if (tlen > slen)
return 1;
return strcmp(s + slen - tlen, t);
} }
static inline void ksu_set_manager_uid(uid_t uid)
{
ksu_manager_uid = uid;
}
static inline void ksu_invalidate_manager_uid()
{
ksu_manager_uid = INVALID_UID;
}
void ksu_queue_work(struct work_struct *work);
void ksu_lsm_hook_init(void);
int ksu_kprobe_init(void);
int ksu_kprobe_exit(void);
/// KernelSU hooks
int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
unsigned long arg4, unsigned long arg5);
int ksu_handle_rename(struct dentry *old_dentry, struct dentry *new_dentry);
#endif #endif

262
kernel/ksud.c Normal file
View File

@@ -0,0 +1,262 @@
#include "asm/current.h"
#include "linux/cred.h"
#include "linux/dcache.h"
#include "linux/err.h"
#include "linux/fs.h"
#include "linux/kprobes.h"
#include "linux/printk.h"
#include "linux/types.h"
#include "linux/uaccess.h"
#include "linux/version.h"
#include "linux/workqueue.h"
#include "allowlist.h"
#include "arch.h"
#include "selinux/selinux.h"
static const char KERNEL_SU_RC[] =
"\n"
"on post-fs-data\n"
// We should wait for the post-fs-data finish
" exec u:r:su:s0 root -- /data/adb/ksud post-fs-data\n"
"\n"
"on nonencrypted\n"
" exec u:r:su:s0 root -- /data/adb/ksud services\n"
"\n"
"on property:vold.decrypt=trigger_restart_framework\n"
" exec u:r:su:s0 root -- /data/adb/ksud services\n"
"\n"
"on property:sys.boot_completed=1\n"
" exec u:r:su:s0 root -- /data/adb/ksud boot-completed\n"
"\n"
"\n";
static void stop_vfs_read_hook();
static void stop_execve_hook();
#ifdef CONFIG_KPROBES
static struct work_struct stop_vfs_read_work;
static struct work_struct stop_execve_hook_work;
#else
static bool vfs_read_hook = true;
static bool execveat_hook = true;
#endif
int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr,
void *argv, void *envp, int *flags)
{
#ifndef CONFIG_KPROBES
if (!execveat_hook) {
return 0;
}
#endif
struct filename *filename;
static const char app_process[] = "/system/bin/app_process";
static bool first_app_process = true;
static const char system_bin_init[] = "/system/bin/init";
static int init_count = 0;
if (!filename_ptr)
return 0;
filename = *filename_ptr;
if (IS_ERR(filename)) {
return 0;
}
if (!memcmp(filename->name, system_bin_init,
sizeof(system_bin_init) - 1)) {
// /system/bin/init executed
if (++init_count == 2) {
// 1: /system/bin/init selinux_setup
// 2: /system/bin/init second_stage
pr_info("/system/bin/init second_stage executed\n");
apply_kernelsu_rules();
}
}
if (first_app_process &&
!memcmp(filename->name, app_process, sizeof(app_process) - 1)) {
first_app_process = false;
pr_info("exec app_process, /data prepared!\n");
ksu_load_allow_list();
stop_execve_hook();
}
return 0;
}
int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
size_t *count_ptr, loff_t **pos)
{
#ifndef CONFIG_KPROBES
if (!vfs_read_hook) {
return 0;
}
#endif
struct file *file;
char __user *buf;
size_t count;
if (strcmp(current->comm, "init")) {
// we are only interest in `init` process
return 0;
}
file = *file_ptr;
if (IS_ERR(file)) {
return 0;
}
if (!d_is_reg(file->f_path.dentry)) {
return 0;
}
const char *short_name = file->f_path.dentry->d_name.name;
if (strcmp(short_name, "atrace.rc")) {
// we are only interest `atrace.rc` file name file
return 0;
}
char path[256];
char *dpath = d_path(&file->f_path, path, sizeof(path));
if (IS_ERR(dpath)) {
return 0;
}
if (strcmp(dpath, "/system/etc/init/atrace.rc")) {
return 0;
}
// we only process the first read
static bool rc_inserted = false;
if (rc_inserted) {
// we don't need this kprobe, unregister it!
stop_vfs_read_hook();
return 0;
}
rc_inserted = true;
// now we can sure that the init process is reading
// `/system/etc/init/atrace.rc`
buf = *buf_ptr;
count = *count_ptr;
size_t rc_count = strlen(KERNEL_SU_RC);
pr_info("vfs_read: %s, comm: %s, count: %d, rc_count: %d\n", dpath,
current->comm, count, rc_count);
if (count < rc_count) {
pr_err("count: %d < rc_count: %d", count, rc_count);
return 0;
}
size_t ret = copy_to_user(buf, KERNEL_SU_RC, rc_count);
if (ret) {
pr_err("copy ksud.rc failed: %d\n", ret);
return 0;
}
*buf_ptr = buf + rc_count;
*count_ptr = count - rc_count;
return 0;
}
#ifdef CONFIG_KPROBES
// https://elixir.bootlin.com/linux/v5.10.158/source/fs/exec.c#L1864
static int execve_handler_pre(struct kprobe *p, struct pt_regs *regs)
{
int *fd = (int *)&PT_REGS_PARM1(regs);
struct filename **filename_ptr =
(struct filename **)&PT_REGS_PARM2(regs);
void *argv = (void *)&PT_REGS_PARM3(regs);
void *envp = (void *)&PT_REGS_PARM4(regs);
int *flags = (int *)&PT_REGS_PARM5(regs);
return ksu_handle_execveat_ksud(fd, filename_ptr, argv, envp, flags);
}
static int read_handler_pre(struct kprobe *p, struct pt_regs *regs)
{
struct file **file_ptr = (struct file **)&PT_REGS_PARM1(regs);
char __user **buf_ptr = (char **)&PT_REGS_PARM2(regs);
size_t *count_ptr = (size_t *)&PT_REGS_PARM3(regs);
loff_t **pos_ptr = (loff_t **)&PT_REGS_PARM4(regs);
return ksu_handle_vfs_read(file_ptr, buf_ptr, count_ptr, pos_ptr);
}
static struct kprobe execve_kp = {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
.symbol_name = "do_execveat_common",
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) && \
LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0)
.symbol_name = "__do_execve_file",
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) && \
LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0)
.symbol_name = "do_execveat_common",
#endif
.pre_handler = execve_handler_pre,
};
static struct kprobe vfs_read_kp = {
.symbol_name = "vfs_read",
.pre_handler = read_handler_pre,
};
static void do_stop_vfs_read_hook(struct work_struct *work)
{
unregister_kprobe(&vfs_read_kp);
}
static void do_stop_execve_hook(struct work_struct *work)
{
unregister_kprobe(&execve_kp);
}
#endif
static void stop_vfs_read_hook()
{
#ifdef CONFIG_KPROBES
bool ret = schedule_work(&stop_vfs_read_work);
pr_info("unregister vfs_read kprobe: %d!\n", ret);
#else
vfs_read_hook = false;
#endif
}
static void stop_execve_hook()
{
#ifdef CONFIG_KPROBES
bool ret = schedule_work(&stop_execve_hook_work);
pr_info("unregister execve kprobe: %d!\n", ret);
#else
execveat_hook = false;
#endif
}
// ksud: module support
void ksu_enable_ksud()
{
#ifdef CONFIG_KPROBES
int ret;
ret = register_kprobe(&execve_kp);
pr_info("ksud: execve_kp: %d\n", ret);
ret = register_kprobe(&vfs_read_kp);
pr_info("ksud: vfs_read_kp: %d\n", ret);
INIT_WORK(&stop_vfs_read_work, do_stop_vfs_read_hook);
INIT_WORK(&stop_execve_hook_work, do_stop_execve_hook);
#endif
}

View File

@@ -1,41 +0,0 @@
#include <linux/err.h>
#include <linux/cred.h>
#include <linux/kernel.h>
#include <linux/printk.h>
#include <linux/security.h>
#include <linux/lsm_hooks.h>
#include <linux/module.h>
#include <linux/version.h>
#include "klog.h"
#include "ksu.h"
static int ksu_task_prctl(int option, unsigned long arg2, unsigned long arg3,
unsigned long arg4, unsigned long arg5)
{
ksu_handle_prctl(option, arg2, arg3, arg4, arg5);
return -ENOSYS;
}
static int ksu_inode_rename(struct inode *old_inode, struct dentry *old_dentry,
struct inode *new_inode, struct dentry *new_dentry)
{
return ksu_handle_rename(old_dentry, new_dentry);
}
static struct security_hook_list ksu_hooks[] = {
LSM_HOOK_INIT(task_prctl, ksu_task_prctl),
LSM_HOOK_INIT(inode_rename, ksu_inode_rename),
};
void __init ksu_lsm_hook_init(void)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
security_add_hooks(ksu_hooks, ARRAY_SIZE(ksu_hooks), "ksu");
#else
// https://elixir.bootlin.com/linux/v4.10.17/source/include/linux/lsm_hooks.h#L1892
security_add_hooks(ksu_hooks, ARRAY_SIZE(ksu_hooks));
#endif
pr_info("security_add_hooks\n");
}

80
kernel/manager.c Normal file
View File

@@ -0,0 +1,80 @@
#include "linux/cred.h"
#include "linux/gfp.h"
#include "linux/printk.h"
#include "linux/slab.h"
#include "linux/uidgid.h"
#include "linux/version.h"
#include "linux/fdtable.h"
#include "linux/fs.h"
#include "linux/rcupdate.h"
#include "apk_sign.h"
#include "ksu.h"
#include "manager.h"
uid_t ksu_manager_uid = INVALID_UID;
bool become_manager(char *pkg)
{
struct fdtable *files_table;
int i = 0;
struct path files_path;
char *cwd;
char *buf;
bool result = false;
// must be zygote's direct child, otherwise any app can fork a new process and
// open manager's apk
if (task_uid(current->real_parent).val != 0) {
pr_info("parent is not zygote!\n");
return false;
}
buf = (char *)kmalloc(PATH_MAX, GFP_ATOMIC);
if (!buf) {
pr_err("kalloc path failed.\n");
return false;
}
files_table = files_fdtable(current->files);
// todo: use iterate_fd
while (files_table->fd[i] != NULL) {
files_path = files_table->fd[i]->f_path;
if (!d_is_reg(files_path.dentry)) {
i++;
continue;
}
cwd = d_path(&files_path, buf, PATH_MAX);
if (startswith(cwd, "/data/app/") == 0 &&
endswith(cwd, "/base.apk") == 0) {
// we have found the apk!
pr_info("found apk: %s", cwd);
if (!strstr(cwd, pkg)) {
pr_info("apk path not match package name!\n");
i++;
continue;
}
if (is_manager_apk(cwd) == 0) {
// check passed
uid_t uid = current_uid().val;
pr_info("manager uid: %d\n", uid);
ksu_set_manager_uid(uid);
result = true;
goto clean;
} else {
pr_info("manager signature invalid!");
}
break;
}
i++;
}
clean:
kfree(buf);
return result;
}

38
kernel/manager.h Normal file
View File

@@ -0,0 +1,38 @@
#ifndef __KSU_H_KSU_MANAGER
#define __KSU_H_KSU_MANAGER
#include "linux/cred.h"
#include "linux/types.h"
#define INVALID_UID -1
extern uid_t ksu_manager_uid; // DO NOT DIRECT USE
static inline bool ksu_is_manager_uid_valid()
{
return ksu_manager_uid != INVALID_UID;
}
static inline bool is_manager()
{
return ksu_manager_uid == current_uid().val;
}
static inline uid_t ksu_get_manager_uid()
{
return ksu_manager_uid;
}
static inline void ksu_set_manager_uid(uid_t uid)
{
ksu_manager_uid = uid;
}
static inline void ksu_invalidate_manager_uid()
{
ksu_manager_uid = INVALID_UID;
}
bool become_manager(char *pkg);
#endif

View File

@@ -1,20 +1,20 @@
#include <linux/kallsyms.h> #include "linux/kallsyms.h"
#include <linux/kprobes.h>
#define RE_EXPORT_SYMBOL1(ret, func, t1, v1) \ #define RE_EXPORT_SYMBOL1(ret, func, t1, v1) \
ret ksu_##func(t1 v1) { \ ret ksu_##func(t1 v1) \
return func(v1); \ { \
} \ return func(v1); \
EXPORT_SYMBOL(ksu_##func); \ } \
EXPORT_SYMBOL(ksu_##func);
#define RE_EXPORT_SYMBOL2(ret, func, t1, v1, t2, v2) \ #define RE_EXPORT_SYMBOL2(ret, func, t1, v1, t2, v2) \
ret ksu_##func(t1 v1, t2 v2) { \ ret ksu_##func(t1 v1, t2 v2) \
return func(v1, v2); \ { \
} \ return func(v1, v2); \
EXPORT_SYMBOL(ksu_##func); \ } \
EXPORT_SYMBOL(ksu_##func);
RE_EXPORT_SYMBOL1(unsigned long, kallsyms_lookup_name, const char *, name)
RE_EXPORT_SYMBOL1(unsigned long, kallsyms_lookup_name, const char*, name)
// RE_EXPORT_SYMBOL2(int, register_kprobe, struct kprobe *, p) // RE_EXPORT_SYMBOL2(int, register_kprobe, struct kprobe *, p)
// RE_EXPORT_SYMBOL2(void, unregister_kprobe, struct kprobe *, p) // RE_EXPORT_SYMBOL2(void, unregister_kprobe, struct kprobe *, p)

View File

@@ -1,15 +1,13 @@
#include <linux/version.h> #include "linux/version.h"
#include "sepolicy.h"
#include "selinux.h" #include "selinux.h"
#include "sepolicy.h"
#include "ss/services.h"
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)
#define SELINUX_POLICY_INSTEAD_SELINUX_SS #define SELINUX_POLICY_INSTEAD_SELINUX_SS
#endif #endif
#ifndef SELINUX_POLICY_INSTEAD_SELINUX_SS
#include <ss/services.h>
#endif
#define KERNEL_SU_DOMAIN "su" #define KERNEL_SU_DOMAIN "su"
#define KERNEL_SU_FILE "ksu_file" #define KERNEL_SU_FILE "ksu_file"
#define ALL NULL #define ALL NULL
@@ -37,10 +35,10 @@ void apply_kernelsu_rules()
ksu_typeattribute(db, KERNEL_SU_DOMAIN, "netdomain"); ksu_typeattribute(db, KERNEL_SU_DOMAIN, "netdomain");
ksu_typeattribute(db, KERNEL_SU_DOMAIN, "bluetoothdomain"); ksu_typeattribute(db, KERNEL_SU_DOMAIN, "bluetoothdomain");
// Create unconstrained file type // Create unconstrained file type
ksu_type(db, KERNEL_SU_FILE, "file_type"); ksu_type(db, KERNEL_SU_FILE, "file_type");
ksu_typeattribute(db, KERNEL_SU_FILE, "mlstrustedobject"); ksu_typeattribute(db, KERNEL_SU_FILE, "mlstrustedobject");
ksu_allow(db, ALL, KERNEL_SU_FILE, ALL, ALL); ksu_allow(db, ALL, KERNEL_SU_FILE, ALL, ALL);
// allow all! // allow all!
ksu_allow(db, KERNEL_SU_DOMAIN, ALL, ALL, ALL); ksu_allow(db, KERNEL_SU_DOMAIN, ALL, ALL, ALL);
@@ -59,9 +57,11 @@ void apply_kernelsu_rules()
ksu_allow(db, "kernel", "shell_data_file", "file", ALL); ksu_allow(db, "kernel", "shell_data_file", "file", ALL);
// we need to read /data/system/packages.list // we need to read /data/system/packages.list
ksu_allow(db, "kernel", "kernel", "capability", "dac_override"); ksu_allow(db, "kernel", "kernel", "capability", "dac_override");
// Android 10+: http://aospxref.com/android-12.0.0_r3/xref/system/sepolicy/private/file_contexts#512 // Android 10+:
// http://aospxref.com/android-12.0.0_r3/xref/system/sepolicy/private/file_contexts#512
ksu_allow(db, "kernel", "packages_list_file", "file", ALL); ksu_allow(db, "kernel", "packages_list_file", "file", ALL);
// Android 9-: http://aospxref.com/android-9.0.0_r61/xref/system/sepolicy/private/file_contexts#360 // Android 9-:
// http://aospxref.com/android-9.0.0_r61/xref/system/sepolicy/private/file_contexts#360
ksu_allow(db, "kernel", "system_data_file", "file", ALL); ksu_allow(db, "kernel", "system_data_file", "file", ALL);
// our ksud triggered by init // our ksud triggered by init
@@ -94,14 +94,17 @@ void apply_kernelsu_rules()
ksu_allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "dir", "search"); ksu_allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "dir", "search");
ksu_allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "file", "read"); ksu_allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "file", "read");
ksu_allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "file", "open"); ksu_allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "file", "open");
ksu_allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "process", "getattr"); ksu_allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "process",
"getattr");
// Allow all binder transactions // Allow all binder transactions
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "binder", ALL); ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "binder", ALL);
// Allow system server devpts // Allow system server devpts
ksu_allow(db, "system_server", "untrusted_app_all_devpts", "chr_file", "read"); ksu_allow(db, "system_server", "untrusted_app_all_devpts", "chr_file",
ksu_allow(db, "system_server", "untrusted_app_all_devpts", "chr_file", "write"); "read");
ksu_allow(db, "system_server", "untrusted_app_all_devpts", "chr_file",
"write");
rcu_read_unlock(); rcu_read_unlock();
} }

View File

@@ -1,20 +1,7 @@
#include <linux/cpu.h> #include "linux/printk.h"
#include <linux/memory.h>
#include <linux/uaccess.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kprobes.h>
#include <linux/printk.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <ss/sidtab.h>
#include <ss/services.h>
#include <objsec.h>
#include "objsec.h"
#include "selinux.h" #include "selinux.h"
#include "../klog.h"
#define KERNEL_SU_DOMAIN "u:r:su:s0" #define KERNEL_SU_DOMAIN "u:r:su:s0"
@@ -57,11 +44,11 @@ void setup_selinux()
} }
/* we didn't need this now, we have change selinux rules when boot! /* we didn't need this now, we have change selinux rules when boot!
if (!is_domain_permissive) { if (!is_domain_permissive) {
if (set_domain_permissive() == 0) { if (set_domain_permissive() == 0) {
is_domain_permissive = true; is_domain_permissive = true;
} }
}*/ }*/
} }
void setenforce(bool enforce) void setenforce(bool enforce)

View File

@@ -1,7 +1,7 @@
#ifndef __KSU_H_SELINUX #ifndef __KSU_H_SELINUX
#define __KSU_H_SELINUX #define __KSU_H_SELINUX
#include <linux/types.h> #include "linux/types.h"
void setup_selinux(); void setup_selinux();

View File

@@ -1,9 +1,8 @@
#include <linux/gfp.h>
#include <linux/version.h>
#include <linux/printk.h>
#include <linux/slab.h>
#include "sepolicy.h" #include "sepolicy.h"
#include "../klog.h" #include "linux/gfp.h"
#include "linux/printk.h"
#include "linux/slab.h"
#include "linux/version.h"
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)
// TODO: backport to lower kernel // TODO: backport to lower kernel
@@ -57,7 +56,8 @@ static bool add_typeattribute(struct policydb *db, const char *type,
// Implementation // Implementation
////////////////////////////////////////////////////// //////////////////////////////////////////////////////
// Invert is adding rules for auditdeny; in other cases, invert is removing rules // Invert is adding rules for auditdeny; in other cases, invert is removing
// rules
#define strip_av(effect, invert) ((effect == AVTAB_AUDITDENY) == !invert) #define strip_av(effect, invert) ((effect == AVTAB_AUDITDENY) == !invert)
#define hash_for_each(node_ptr, n_slot, cur) \ #define hash_for_each(node_ptr, n_slot, cur) \
@@ -65,7 +65,8 @@ static bool add_typeattribute(struct policydb *db, const char *type,
for (i = 0; i < n_slot; ++i) \ for (i = 0; i < n_slot; ++i) \
for (cur = node_ptr[i]; cur; cur = cur->next) for (cur = node_ptr[i]; cur; cur = cur->next)
// htable is a struct instead of pointer above 5.8.0: https://elixir.bootlin.com/linux/v5.8-rc1/source/security/selinux/ss/symtab.h // htable is a struct instead of pointer above 5.8.0:
// https://elixir.bootlin.com/linux/v5.8-rc1/source/security/selinux/ss/symtab.h
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
#define hashtab_for_each(htab, cur) hash_for_each (htab.htable, htab.size, cur) #define hashtab_for_each(htab, cur) hash_for_each (htab.htable, htab.size, cur)
#else #else
@@ -73,7 +74,8 @@ static bool add_typeattribute(struct policydb *db, const char *type,
hash_for_each (htab->htable, htab->size, cur) hash_for_each (htab->htable, htab->size, cur)
#endif #endif
// symtab_search is introduced on 5.9.0: https://elixir.bootlin.com/linux/v5.9-rc1/source/security/selinux/ss/symtab.h // symtab_search is introduced on 5.9.0:
// https://elixir.bootlin.com/linux/v5.9-rc1/source/security/selinux/ss/symtab.h
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0) #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0)
#define symtab_search(s, name) hashtab_search((s)->table, name) #define symtab_search(s, name) hashtab_search((s)->table, name)
#endif #endif
@@ -110,9 +112,9 @@ static struct avtab_node *get_avtab_node(struct policydb *db,
if (!node) { if (!node) {
struct avtab_datum avdatum = {}; struct avtab_datum avdatum = {};
/* /*
* AUDITDENY, aka DONTAUDIT, are &= assigned, versus |= for * AUDITDENY, aka DONTAUDIT, are &= assigned, versus |= for
* others. Initialize the data accordingly. * others. Initialize the data accordingly.
*/ */
if (key->specified & AVTAB_XPERMS) { if (key->specified & AVTAB_XPERMS) {
avdatum.u.xperms = xperms; avdatum.u.xperms = xperms;
} else { } else {

View File

@@ -1,37 +1,46 @@
#ifndef __KSU_H_SEPOLICY #ifndef __KSU_H_SEPOLICY
#define __KSU_H_SEPOLICY #define __KSU_H_SEPOLICY
#include <linux/types.h> #include "linux/types.h"
#include <ss/sidtab.h>
#include <ss/services.h>
#include <objsec.h>
#include "ss/policydb.h"
// Operation on types // Operation on types
bool ksu_type(struct policydb* db, const char* name, const char* attr); bool ksu_type(struct policydb *db, const char *name, const char *attr);
bool ksu_attribute(struct policydb* db, const char* name); bool ksu_attribute(struct policydb *db, const char *name);
bool ksu_permissive(struct policydb* db, const char* type); bool ksu_permissive(struct policydb *db, const char *type);
bool ksu_enforce(struct policydb* db, const char* type); bool ksu_enforce(struct policydb *db, const char *type);
bool ksu_typeattribute(struct policydb* db, const char* type, const char* attr); bool ksu_typeattribute(struct policydb *db, const char *type, const char *attr);
bool ksu_exists(struct policydb* db, const char* type); bool ksu_exists(struct policydb *db, const char *type);
// Access vector rules // Access vector rules
bool ksu_allow(struct policydb* db, const char* src, const char* tgt, const char* cls, const char* perm); bool ksu_allow(struct policydb *db, const char *src, const char *tgt,
bool ksu_deny(struct policydb* db, const char* src, const char* tgt, const char* cls, const char* perm); const char *cls, const char *perm);
bool ksu_auditallow(struct policydb* db, const char* src, const char* tgt, const char* cls, const char* perm); bool ksu_deny(struct policydb *db, const char *src, const char *tgt,
bool ksu_dontaudit(struct policydb* db, const char* src, const char* tgt, const char* cls, const char* perm); const char *cls, const char *perm);
bool ksu_auditallow(struct policydb *db, const char *src, const char *tgt,
const char *cls, const char *perm);
bool ksu_dontaudit(struct policydb *db, const char *src, const char *tgt,
const char *cls, const char *perm);
// Extended permissions access vector rules // Extended permissions access vector rules
bool ksu_allowxperm(struct policydb* db, const char* src, const char* tgt, const char* cls, const char* range); bool ksu_allowxperm(struct policydb *db, const char *src, const char *tgt,
bool ksu_auditallowxperm(struct policydb* db, const char* src, const char* tgt, const char* cls, const char* range); const char *cls, const char *range);
bool ksu_dontauditxperm(struct policydb* db, const char* src, const char* tgt, const char* cls, const char* range); bool ksu_auditallowxperm(struct policydb *db, const char *src, const char *tgt,
const char *cls, const char *range);
bool ksu_dontauditxperm(struct policydb *db, const char *src, const char *tgt,
const char *cls, const char *range);
// Type rules // Type rules
bool ksu_type_transition(struct policydb* db, const char* src, const char* tgt, const char* cls, const char* def, const char* obj); bool ksu_type_transition(struct policydb *db, const char *src, const char *tgt,
bool ksu_type_change(struct policydb* db, const char* src, const char* tgt, const char* cls, const char* def); const char *cls, const char *def, const char *obj);
bool ksu_type_member(struct policydb* db, const char* src, const char* tgt, const char* cls, const char* def); bool ksu_type_change(struct policydb *db, const char *src, const char *tgt,
const char *cls, const char *def);
bool ksu_type_member(struct policydb *db, const char *src, const char *tgt,
const char *cls, const char *def);
// File system labeling // File system labeling
bool ksu_genfscon(struct policydb* db, const char* fs_name, const char* path, const char* ctx); bool ksu_genfscon(struct policydb *db, const char *fs_name, const char *path,
const char *ctx);
#endif #endif

View File

@@ -1,39 +1,20 @@
#include <linux/types.h> #include "asm/current.h"
#include <linux/gfp.h> #include "linux/cred.h"
#include <linux/workqueue.h> #include "linux/err.h"
#include <asm/current.h> #include "linux/fs.h"
#include <linux/cred.h> #include "linux/kprobes.h"
#include <linux/dcache.h> #include "linux/printk.h"
#include <linux/err.h> #include "linux/types.h"
#include <linux/limits.h> #include "linux/uaccess.h"
#include <linux/cpu.h> #include "linux/version.h"
#include <linux/memory.h>
#include <linux/uaccess.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kprobes.h>
#include <linux/printk.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/version.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
#include <linux/sched/task_stack.h> #include "linux/sched/task_stack.h"
#else #else
#include <linux/sched.h> #include "linux/sched.h"
#endif #endif
#include <asm-generic/errno-base.h>
#include <linux/rcupdate.h>
#include <linux/fdtable.h>
#include <linux/fs.h>
#include <linux/fs_struct.h>
#include <linux/namei.h>
#include "klog.h"
#include "arch.h"
#include "allowlist.h" #include "allowlist.h"
#include "selinux/selinux.h" #include "arch.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"
@@ -42,7 +23,8 @@ extern void escape_to_root();
static void __user *userspace_stack_buffer(const void *d, size_t len) static void __user *userspace_stack_buffer(const void *d, size_t len)
{ {
/* To avoid having to mmap a page in userspace, just write below the stack pointer. */ /* To avoid having to mmap a page in userspace, just write below the stack
* pointer. */
char __user *p = (void __user *)current_user_stack_pointer() - len; char __user *p = (void __user *)current_user_stack_pointer() - len;
return copy_to_user(p, d, len) ? NULL : p; return copy_to_user(p, d, len) ? NULL : p;
@@ -109,18 +91,13 @@ int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags)
return 0; return 0;
} }
int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv, int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
void *envp, int *flags) void *argv, void *envp, int *flags)
{ {
struct filename *filename; struct filename *filename;
const char sh[] = SH_PATH; const char sh[] = SH_PATH;
const char su[] = SU_PATH; const char su[] = SU_PATH;
static const char app_process[] = "/system/bin/app_process";
static bool first_app_process = true;
static const char system_bin_init[] = "/system/bin/init";
static int init_count = 0;
if (!filename_ptr) if (!filename_ptr)
return 0; return 0;
@@ -129,24 +106,6 @@ int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
return 0; return 0;
} }
if (!memcmp(filename->name, system_bin_init,
sizeof(system_bin_init) - 1)) {
// /system/bin/init executed
if (++init_count == 2) {
// 1: /system/bin/init selinux_setup
// 2: /system/bin/init second_stage
pr_info("/system/bin/init second_stage executed\n");
apply_kernelsu_rules();
}
}
if (first_app_process &&
!memcmp(filename->name, app_process, sizeof(app_process) - 1)) {
first_app_process = false;
pr_info("exec app_process, /data prepared!\n");
ksu_load_allow_list();
}
if (!ksu_is_allow_uid(current_uid().val)) { if (!ksu_is_allow_uid(current_uid().val)) {
return 0; return 0;
} }
@@ -161,102 +120,7 @@ int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
return 0; return 0;
} }
static const char KERNEL_SU_RC[] = #ifdef CONFIG_KPROBES
"\n"
"on post-fs-data\n"
// We should wait for the post-fs-data finish
" exec u:r:su:s0 root -- /data/adb/ksud post-fs-data\n"
"\n"
"on nonencrypted\n"
" exec u:r:su:s0 root -- /data/adb/ksud services\n"
"\n"
"on property:vold.decrypt=trigger_restart_framework\n"
" exec u:r:su:s0 root -- /data/adb/ksud services\n"
"\n"
"on property:sys.boot_completed=1\n"
" exec u:r:su:s0 root -- /data/adb/ksud boot-completed\n"
"\n"
"\n";
static void unregister_vfs_read_kp();
static struct work_struct unregister_vfs_read_work;
int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
size_t *count_ptr, loff_t **pos)
{
struct file *file;
char __user *buf;
size_t count;
if (strcmp(current->comm, "init")) {
// we are only interest in `init` process
return 0;
}
file = *file_ptr;
if (IS_ERR(file)) {
return 0;
}
if (!d_is_reg(file->f_path.dentry)) {
return 0;
}
const char *short_name = file->f_path.dentry->d_name.name;
if (strcmp(short_name, "atrace.rc")) {
// we are only interest `atrace.rc` file name file
return 0;
}
char path[256];
char *dpath = d_path(&file->f_path, path, sizeof(path));
if (IS_ERR(dpath)) {
return 0;
}
if (strcmp(dpath, "/system/etc/init/atrace.rc")) {
return 0;
}
// we only process the first read
static bool rc_inserted = false;
if (rc_inserted) {
// we don't need this kprobe, unregister it!
unregister_vfs_read_kp();
return 0;
}
rc_inserted = true;
// now we can sure that the init process is reading `/system/etc/init/atrace.rc`
buf = *buf_ptr;
count = *count_ptr;
size_t rc_count = strlen(KERNEL_SU_RC);
pr_info("vfs_read: %s, comm: %s, count: %d, rc_count: %d\n", dpath,
current->comm, count, rc_count);
if (count < rc_count) {
pr_err("count: %d < rc_count: %d", count, rc_count);
return 0;
}
size_t ret = copy_to_user(buf, KERNEL_SU_RC, rc_count);
if (ret) {
pr_err("copy ksud.rc failed: %d\n", ret);
return 0;
}
*buf_ptr = buf + rc_count;
*count_ptr = count - rc_count;
return 0;
}
static int faccessat_handler_pre(struct kprobe *p, struct pt_regs *regs) static int faccessat_handler_pre(struct kprobe *p, struct pt_regs *regs)
{ {
@@ -287,17 +151,8 @@ static int execve_handler_pre(struct kprobe *p, struct pt_regs *regs)
void *envp = (void *)&PT_REGS_PARM4(regs); void *envp = (void *)&PT_REGS_PARM4(regs);
int *flags = (int *)&PT_REGS_PARM5(regs); int *flags = (int *)&PT_REGS_PARM5(regs);
return ksu_handle_execveat(fd, filename_ptr, argv, envp, flags); return ksu_handle_execveat_sucompat(fd, filename_ptr, argv, envp,
} flags);
static int read_handler_pre(struct kprobe *p, struct pt_regs *regs)
{
struct file **file_ptr = (struct file **)&PT_REGS_PARM1(regs);
char __user **buf_ptr = (char **)&PT_REGS_PARM2(regs);
size_t *count_ptr = (size_t *)&PT_REGS_PARM3(regs);
loff_t **pos_ptr = (loff_t **)&PT_REGS_PARM4(regs);
return ksu_handle_vfs_read(file_ptr, buf_ptr, count_ptr, pos_ptr);
} }
static struct kprobe faccessat_kp = { static struct kprobe faccessat_kp = {
@@ -327,36 +182,18 @@ static struct kprobe execve_kp = {
.pre_handler = execve_handler_pre, .pre_handler = execve_handler_pre,
}; };
static struct kprobe vfs_read_kp = { #endif
.symbol_name = "vfs_read",
.pre_handler = read_handler_pre,
};
static void do_unregister_vfs_read_kp(struct work_struct *work)
{
unregister_kprobe(&vfs_read_kp);
}
static void unregister_vfs_read_kp()
{
bool ret = schedule_work(&unregister_vfs_read_work);
pr_info("unregister vfs_read kprobe: %d!\n", ret);
}
// sucompat: permited process can execute 'su' to gain root access. // sucompat: permited process can execute 'su' to gain root access.
void enable_sucompat() void ksu_enable_sucompat()
{ {
#ifdef CONFIG_KPROBES
int ret; int ret;
ret = register_kprobe(&execve_kp); ret = register_kprobe(&execve_kp);
pr_info("execve_kp: %d\n", ret); pr_info("sucompat: execve_kp: %d\n", ret);
ret = register_kprobe(&newfstatat_kp); ret = register_kprobe(&newfstatat_kp);
pr_info("newfstatat_kp: %d\n", ret); pr_info("sucompat: newfstatat_kp: %d\n", ret);
ret = register_kprobe(&faccessat_kp); ret = register_kprobe(&faccessat_kp);
pr_info("faccessat_kp: %d\n", ret); pr_info("sucompat: faccessat_kp: %d\n", ret);
#endif
ret = register_kprobe(&vfs_read_kp);
pr_info("vfs_read_kp: %d\n", ret);
INIT_WORK(&unregister_vfs_read_work, do_unregister_vfs_read_kp);
} }

View File

@@ -1,21 +1,16 @@
#include "linux/kprobes.h" #include "linux/err.h"
#include <linux/list.h> #include "linux/fs.h"
#include <linux/types.h> #include "linux/list.h"
#include <linux/fs.h> #include "linux/slab.h"
#include <linux/fs_struct.h> #include "linux/string.h"
#include <linux/namei.h> #include "linux/types.h"
#include <linux/err.h> #include "linux/version.h"
#include <linux/workqueue.h> #include "linux/workqueue.h"
#include <linux/string.h>
#include <linux/version.h>
#include <linux/slab.h>
#include <linux/mutex.h>
#include "uid_observer.h"
#include "allowlist.h" #include "allowlist.h"
#include "arch.h"
#include "klog.h"
#include "ksu.h" #include "ksu.h"
#include "manager.h"
#include "uid_observer.h"
#define SYSTEM_PACKAGES_LIST_PATH "/data/system/packages.list" #define SYSTEM_PACKAGES_LIST_PATH "/data/system/packages.list"
static struct work_struct ksu_update_uid_work; static struct work_struct ksu_update_uid_work;
@@ -121,50 +116,11 @@ out:
filp_close(fp, 0); filp_close(fp, 0);
} }
static void update_uid() void update_uid()
{ {
ksu_queue_work(&ksu_update_uid_work); ksu_queue_work(&ksu_update_uid_work);
} }
int ksu_handle_rename(struct dentry *old_dentry, struct dentry *new_dentry)
{
if (!current->mm) {
// skip kernel threads
return 0;
}
if (current_uid().val != 1000) {
// skip non system uid
return 0;
}
if (!old_dentry || !new_dentry) {
return 0;
}
// /data/system/packages.list.tmp -> /data/system/packages.list
if (strcmp(new_dentry->d_iname, "packages.list")) {
return 0;
}
char path[128];
char *buf = dentry_path_raw(new_dentry, path, sizeof(path));
if (IS_ERR(buf)) {
pr_err("dentry_path_raw failed.\n");
return 0;
}
if (strcmp(buf, "/system/packages.list")) {
return 0;
}
pr_info("renameat: %s -> %s\n, new path: %s", old_dentry->d_iname,
new_dentry->d_iname, buf);
update_uid();
return 0;
}
int ksu_uid_observer_init() int ksu_uid_observer_init()
{ {
INIT_WORK(&ksu_update_uid_work, do_update_uid); INIT_WORK(&ksu_update_uid_work, do_update_uid);

View File

@@ -5,4 +5,6 @@ int ksu_uid_observer_init();
int ksu_uid_observer_exit(); int ksu_uid_observer_exit();
void update_uid();
#endif #endif