Init
This commit is contained in:
5
kernel/Kconfig
Normal file
5
kernel/Kconfig
Normal file
@@ -0,0 +1,5 @@
|
||||
config KSU
|
||||
tristate "KernelSU module"
|
||||
default y
|
||||
help
|
||||
This is the KSU privilege driver for android system.
|
||||
7
kernel/Makefile
Normal file
7
kernel/Makefile
Normal file
@@ -0,0 +1,7 @@
|
||||
obj-y += ksu.o
|
||||
obj-y += allowlist.o
|
||||
obj-y += apk_sign.o
|
||||
obj-y += module_api.o
|
||||
obj-y += selinux/
|
||||
|
||||
ccflags-y += -Wno-implicit-function-declaration -Wno-strict-prototypes -Wno-int-conversion -Wno-gcc-compat
|
||||
211
kernel/allowlist.c
Normal file
211
kernel/allowlist.c
Normal file
@@ -0,0 +1,211 @@
|
||||
#include "linux/uidgid.h"
|
||||
#include <linux/cpu.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 <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 <linux/delay.h> // msleep
|
||||
|
||||
#include "klog.h"
|
||||
|
||||
struct perm_data {
|
||||
struct list_head list;
|
||||
uid_t uid;
|
||||
bool allow;
|
||||
};
|
||||
|
||||
static struct list_head allow_list;
|
||||
|
||||
#define KERNEL_SU_DIR "/data/adb/kernelsu"
|
||||
|
||||
static struct workqueue_struct *ksu_workqueue;
|
||||
static struct work_struct ksu_save_work;
|
||||
static struct work_struct ksu_load_work;
|
||||
|
||||
bool persistent_allow_list();
|
||||
|
||||
bool ksu_allow_uid(uid_t uid, bool allow) {
|
||||
|
||||
// find the node first!
|
||||
struct perm_data* p;
|
||||
struct list_head* pos;
|
||||
bool result;
|
||||
list_for_each(pos, &allow_list) {
|
||||
p = list_entry(pos, struct perm_data, list);
|
||||
pr_info("ksu_allow_uid :%d, allow: %d\n", p->uid, p->allow);
|
||||
if (uid == p->uid) {
|
||||
p->allow = allow;
|
||||
result = true;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
// not found, alloc a new node!
|
||||
p = (struct perm_data*) kmalloc(sizeof(struct perm_data), GFP_KERNEL);
|
||||
if (!p) {
|
||||
pr_err("alloc allow node failed.\n");
|
||||
return false;
|
||||
}
|
||||
p->uid = uid;
|
||||
p->allow = allow;
|
||||
|
||||
list_add_tail(&p->list, &allow_list);
|
||||
result = true;
|
||||
|
||||
exit:
|
||||
|
||||
persistent_allow_list();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ksu_is_allow_uid(uid_t uid) {
|
||||
struct perm_data* p;
|
||||
struct list_head* pos;
|
||||
list_for_each(pos, &allow_list) {
|
||||
p = list_entry(pos, struct perm_data, list);
|
||||
pr_info("uid :%d, allow: %d\n", p->uid, p->allow);
|
||||
if (uid == p->uid) {
|
||||
return p->allow;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ksu_get_allow_list(int* array, int* length, bool allow) {
|
||||
struct perm_data* p;
|
||||
struct list_head* pos;
|
||||
int i = 0;
|
||||
list_for_each(pos, &allow_list) {
|
||||
p = list_entry(pos, struct perm_data, list);
|
||||
pr_info("uid: %d allow: %d\n", p->uid, p->allow);
|
||||
if (p->allow == allow) {
|
||||
array[i++] = p->uid;
|
||||
}
|
||||
}
|
||||
*length = i;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void do_persistent_allow_list(struct work_struct *work)
|
||||
{
|
||||
struct perm_data* p;
|
||||
struct list_head* pos;
|
||||
loff_t off;
|
||||
|
||||
struct file* fp = filp_open("/data/adb/ksu_list", O_WRONLY|O_CREAT, 0644);
|
||||
|
||||
if (IS_ERR(fp)) {
|
||||
pr_err("work creat file failed: %d\n", PTR_ERR(fp));
|
||||
return;
|
||||
}
|
||||
pr_info("work create file success!\n");
|
||||
|
||||
list_for_each(pos, &allow_list) {
|
||||
p = list_entry(pos, struct perm_data, list);
|
||||
pr_info("uid :%d, allow: %d\n", p->uid, p->allow);
|
||||
kernel_write(fp, &p->uid, sizeof(p->uid), &off);
|
||||
kernel_write(fp, &p->allow, sizeof(p->allow), &off);
|
||||
}
|
||||
|
||||
filp_close(fp, 0);
|
||||
}
|
||||
|
||||
void do_load_allow_list(struct work_struct *work) {
|
||||
|
||||
loff_t off;
|
||||
ssize_t ret;
|
||||
struct file* fp;
|
||||
|
||||
fp = filp_open("/data/adb/", O_RDONLY, 0);
|
||||
if (IS_ERR(fp)) {
|
||||
pr_err("work open '/data/adb' failed: %d\n", PTR_ERR(fp));
|
||||
mdelay(2000);
|
||||
|
||||
queue_work(ksu_workqueue, &ksu_load_work);
|
||||
return;
|
||||
}
|
||||
filp_close(fp, 0);
|
||||
|
||||
// load allowlist now!
|
||||
fp = filp_open("/data/adb/ksu_list", O_RDONLY, 0);
|
||||
|
||||
if (IS_ERR(fp)) {
|
||||
pr_err("work open file failed: %d\n", PTR_ERR(fp));
|
||||
return;
|
||||
}
|
||||
pr_info("work open file success!\n");
|
||||
|
||||
while (true) {
|
||||
u32 uid;
|
||||
bool allow;
|
||||
ret = kernel_read(fp, &uid, sizeof(uid), &off);
|
||||
pr_info("kernel read ret: %d, off: %ld\n", ret, off);
|
||||
if (ret <= 0) {
|
||||
pr_info("read err: %d\n", ret);
|
||||
break;
|
||||
}
|
||||
ret = kernel_read(fp, &allow, sizeof(allow), &off);
|
||||
|
||||
pr_info("load_allow_uid: %d, allow: %d\n", uid, allow);
|
||||
|
||||
ksu_allow_uid(uid, allow);
|
||||
}
|
||||
|
||||
filp_close(fp, 0);
|
||||
}
|
||||
|
||||
static int init_work(void) {
|
||||
ksu_workqueue = alloc_workqueue("kernelsu_work", 0, 0);
|
||||
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
|
||||
bool persistent_allow_list() {
|
||||
queue_work(ksu_workqueue, &ksu_save_work);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool load_allow_list() {
|
||||
queue_work(ksu_workqueue, &ksu_load_work);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ksu_allowlist_init() {
|
||||
|
||||
INIT_LIST_HEAD(&allow_list);
|
||||
|
||||
init_work();
|
||||
|
||||
// load_allow_list();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ksu_allowlist_exit() {
|
||||
|
||||
destroy_workqueue(ksu_workqueue);
|
||||
|
||||
return true;
|
||||
}
|
||||
14
kernel/allowlist.h
Normal file
14
kernel/allowlist.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#ifndef __KSU_H_ALLOWLIST
|
||||
#define __KSU_H_ALLOWLIST
|
||||
|
||||
bool ksu_allowlist_init();
|
||||
|
||||
bool ksu_allowlist_exit();
|
||||
|
||||
bool ksu_is_allow_uid(uid_t uid);
|
||||
|
||||
bool ksu_allow_uid(uid_t uid, bool allow);
|
||||
|
||||
bool ksu_get_allow_list(int* array, int* length, bool allow);
|
||||
|
||||
#endif
|
||||
120
kernel/apk_sign.c
Normal file
120
kernel/apk_sign.c
Normal file
@@ -0,0 +1,120 @@
|
||||
#include <linux/fs.h>
|
||||
|
||||
#include "apk_sign.h"
|
||||
#include "klog.h"
|
||||
|
||||
static int check_v2_signature(char* path, unsigned expected_size, unsigned expected_hash) {
|
||||
unsigned char buffer[0x11] = {0};
|
||||
u32 size4;
|
||||
u64 size8, size_of_block;
|
||||
|
||||
loff_t pos;
|
||||
|
||||
int sign = -1;
|
||||
struct file* fp = filp_open(path, O_RDONLY, 0);
|
||||
if (IS_ERR(fp)) {
|
||||
pr_err("open %s error.", path);
|
||||
return PTR_ERR(fp);
|
||||
}
|
||||
|
||||
sign = 1;
|
||||
// https://en.wikipedia.org/wiki/Zip_(file_format)#End_of_central_directory_record_(EOCD)
|
||||
for (int i = 0;; ++i) {
|
||||
unsigned short n;
|
||||
pos = generic_file_llseek(fp, -i - 2, SEEK_END);
|
||||
kernel_read(fp, &n, 2, &pos);
|
||||
if (n == i) {
|
||||
pos -= 22;
|
||||
kernel_read(fp, &size4, 4, &pos);
|
||||
if ((size4 ^ 0xcafebabeu) == 0xccfbf1eeu) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == 0xffff) {
|
||||
pr_info("error: cannot find eocd\n");
|
||||
goto clean;
|
||||
}
|
||||
}
|
||||
|
||||
pos += 12;
|
||||
// offset
|
||||
kernel_read(fp, &size4, 0x4, &pos);
|
||||
pos = size4 - 0x18;
|
||||
|
||||
kernel_read(fp, &size8, 0x8, &pos);
|
||||
kernel_read(fp, buffer, 0x10, &pos);
|
||||
if (strcmp((char *) buffer, "APK Sig Block 42")) {
|
||||
goto clean;
|
||||
}
|
||||
|
||||
pos = size4 - (size8 + 0x8);
|
||||
kernel_read(fp, &size_of_block, 0x8, &pos);
|
||||
if (size_of_block != size8) {
|
||||
goto clean;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
uint32_t id;
|
||||
uint32_t offset;
|
||||
kernel_read(fp, &size8, 0x8, &pos); // sequence length
|
||||
if (size8 == size_of_block) {
|
||||
break;
|
||||
}
|
||||
kernel_read(fp, &id, 0x4, &pos); // id
|
||||
offset = 4;
|
||||
pr_info("id: 0x%08x\n", id);
|
||||
if ((id ^ 0xdeadbeefu) == 0xafa439f5u || (id ^ 0xdeadbeefu) == 0x2efed62f) {
|
||||
kernel_read(fp, &size4, 0x4, &pos); // signer-sequence length
|
||||
kernel_read(fp, &size4, 0x4, &pos); // signer length
|
||||
kernel_read(fp, &size4, 0x4, &pos); // signed data length
|
||||
offset += 0x4 * 3;
|
||||
|
||||
kernel_read(fp, &size4, 0x4, &pos); // digests-sequence length
|
||||
pos += size4;
|
||||
offset += 0x4 + size4;
|
||||
|
||||
kernel_read(fp, &size4, 0x4, &pos); // certificates length
|
||||
kernel_read(fp, &size4, 0x4, &pos); // certificate length
|
||||
offset += 0x4 * 2;
|
||||
#if 0
|
||||
int hash = 1;
|
||||
signed char c;
|
||||
for (unsigned i = 0; i < size4; ++i) {
|
||||
kernel_read(fp, &c, 0x1, &pos);
|
||||
hash = 31 * hash + c;
|
||||
}
|
||||
offset += size4;
|
||||
pr_info(" size: 0x%04x, hash: 0x%08x\n", size4, ((unsigned) hash) ^ 0x14131211u);
|
||||
#else
|
||||
if (size4 == expected_size) {
|
||||
int hash = 1;
|
||||
signed char c;
|
||||
for (unsigned i = 0; i < size4; ++i) {
|
||||
kernel_read(fp, &c, 0x1, &pos);
|
||||
hash = 31 * hash + c;
|
||||
}
|
||||
offset += size4;
|
||||
if ((((unsigned) hash) ^ 0x14131211u) == expected_hash) {
|
||||
sign = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// don't try again.
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
pos += (size8 - offset);
|
||||
}
|
||||
|
||||
clean:
|
||||
filp_close(fp, 0);
|
||||
|
||||
return sign;
|
||||
}
|
||||
|
||||
#define EXPECTED_SIZE 0x023f
|
||||
#define EXPECTED_HASH 0x9eb9c1ea
|
||||
|
||||
int is_manager_apk(char* path) {
|
||||
return check_v2_signature(path, EXPECTED_SIZE, EXPECTED_HASH);
|
||||
}
|
||||
7
kernel/apk_sign.h
Normal file
7
kernel/apk_sign.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#ifndef __KSU_H_APK_V2_SIGN
|
||||
#define __KSU_H_APK_V2_SIGN
|
||||
|
||||
// return 0 if signature match
|
||||
int is_manager_apk(char* path);
|
||||
|
||||
#endif
|
||||
9
kernel/klog.h
Normal file
9
kernel/klog.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#ifndef __KSU_H_KLOG
|
||||
#define __KSU_H_KLOG
|
||||
|
||||
#ifdef pr_fmt
|
||||
#undef pr_fmt
|
||||
#define pr_fmt(fmt) "KernelSU: " fmt
|
||||
#endif
|
||||
|
||||
#endif
|
||||
250
kernel/ksu.c
Normal file
250
kernel/ksu.c
Normal file
@@ -0,0 +1,250 @@
|
||||
#include "linux/uidgid.h"
|
||||
#include <linux/cpu.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 <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 <linux/delay.h> // mslepp
|
||||
|
||||
#include "selinux/selinux.h"
|
||||
#include "klog.h"
|
||||
#include "apk_sign.h"
|
||||
#include "allowlist.h"
|
||||
|
||||
#define KERNEL_SU_VERSION 3
|
||||
|
||||
#define KERNEL_SU_OPTION 0xDEADBEEF
|
||||
|
||||
#define CMD_GRANT_ROOT 0
|
||||
|
||||
#define CMD_BECOME_MANAGER 1
|
||||
#define CMD_GET_VERSION 2
|
||||
#define CMD_ALLOW_SU 3
|
||||
#define CMD_DENY_SU 4
|
||||
#define CMD_GET_ALLOW_LIST 5
|
||||
#define CMD_GET_DENY_LIST 6
|
||||
|
||||
static 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
|
||||
current_thread_info()->flags = 0;
|
||||
current->seccomp.mode = 0;
|
||||
current->seccomp.filter = NULL;
|
||||
|
||||
setup_selinux();
|
||||
}
|
||||
|
||||
int startswith(char* s, char* prefix) {
|
||||
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 uid_t __manager_uid;
|
||||
|
||||
static bool is_manager() {
|
||||
return __manager_uid == current_uid().val;
|
||||
}
|
||||
|
||||
static bool become_manager() {
|
||||
if (__manager_uid != 0) {
|
||||
pr_info("manager already exist: %d\n", __manager_uid);
|
||||
return true;
|
||||
}
|
||||
// list current process's files
|
||||
struct files_struct *current_files;
|
||||
struct fdtable *files_table;
|
||||
int i = 0;
|
||||
struct path files_path;
|
||||
char *cwd;
|
||||
char *buf = (char *)kmalloc(GFP_KERNEL, PATH_MAX);
|
||||
bool result = false;
|
||||
|
||||
current_files = current->files;
|
||||
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 (is_manager_apk(cwd) == 0) {
|
||||
// check passed
|
||||
uid_t uid = current_uid().val;
|
||||
pr_info("manager uid: %d\n", uid);
|
||||
|
||||
__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 == __manager_uid) {
|
||||
// we are manager, allow!
|
||||
return true;
|
||||
}
|
||||
|
||||
if (uid == 0) {
|
||||
// we are already root, allow!
|
||||
return true;
|
||||
}
|
||||
|
||||
return ksu_is_allow_uid(uid);
|
||||
}
|
||||
|
||||
static int handler_pre(struct kprobe *p, struct pt_regs *regs) {
|
||||
|
||||
struct pt_regs* real_regs = (struct pt_regs*) regs->regs[0];
|
||||
int option = (int) real_regs->regs[0];
|
||||
unsigned long arg2 = (unsigned long) real_regs->regs[1];
|
||||
unsigned long arg3 = (unsigned long) real_regs->regs[2];
|
||||
unsigned long arg4 = (unsigned long) real_regs->regs[3];
|
||||
unsigned long arg5 = (unsigned long) real_regs->regs[4];
|
||||
|
||||
// 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) {
|
||||
// someone wants to be root manager, just check it!
|
||||
bool success = become_manager();
|
||||
if (success) {
|
||||
copy_to_user(result, &reply_ok, sizeof(reply_ok));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (arg2 == CMD_GRANT_ROOT) {
|
||||
if (is_allow_su()) {
|
||||
pr_info("allow root for: %d\n", current_uid());
|
||||
escape_to_root();
|
||||
} else {
|
||||
pr_info("deny root for: %d\n", current_uid());
|
||||
// add it to deny list!
|
||||
ksu_allow_uid(current_uid().val, false);
|
||||
}
|
||||
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) {
|
||||
copy_to_user(result, &reply_ok, sizeof(reply_ok));
|
||||
}
|
||||
} 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) {
|
||||
copy_to_user(arg4, &array_length, sizeof(array_length));
|
||||
copy_to_user(arg3, array, sizeof(u32) * array_length);
|
||||
|
||||
copy_to_user(result, &reply_ok, sizeof(reply_ok));
|
||||
}
|
||||
} else if (arg2 == CMD_GET_VERSION) {
|
||||
u32 version = KERNEL_SU_VERSION;
|
||||
copy_to_user(arg3, &version, sizeof(version));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct kprobe kp = {
|
||||
.symbol_name = "__arm64_sys_prctl",
|
||||
.pre_handler = handler_pre,
|
||||
};
|
||||
|
||||
int kernelsu_init(void){
|
||||
int rc = 0;
|
||||
|
||||
ksu_allowlist_init();
|
||||
|
||||
rc = register_kprobe(&kp);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
void kernelsu_exit(void){
|
||||
// should never happen...
|
||||
unregister_kprobe(&kp);
|
||||
|
||||
ksu_allowlist_exit();
|
||||
}
|
||||
|
||||
module_init(kernelsu_init);
|
||||
module_exit(kernelsu_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("weishu");
|
||||
MODULE_DESCRIPTION("Android GKI KernelSU");
|
||||
33
kernel/module_api.c
Normal file
33
kernel/module_api.c
Normal file
@@ -0,0 +1,33 @@
|
||||
#include <linux/kallsyms.h>
|
||||
#include <linux/kprobes.h>
|
||||
|
||||
#define RE_EXPORT_SYMBOL1(ret, func, t1, v1) \
|
||||
ret ksu_##func(t1 v1) { \
|
||||
return func(v1); \
|
||||
} \
|
||||
EXPORT_SYMBOL(ksu_##func); \
|
||||
|
||||
#define RE_EXPORT_SYMBOL2(ret, func, t1, v1, t2, v2) \
|
||||
ret ksu_##func(t1 v1, t2 v2) { \
|
||||
return func(v1, v2); \
|
||||
} \
|
||||
EXPORT_SYMBOL(ksu_##func); \
|
||||
|
||||
|
||||
RE_EXPORT_SYMBOL1(unsigned long, kallsyms_lookup_name, const char*, name)
|
||||
|
||||
// RE_EXPORT_SYMBOL2(int, register_kprobe, struct kprobe *, p)
|
||||
// RE_EXPORT_SYMBOL2(void, unregister_kprobe, struct kprobe *, p)
|
||||
|
||||
// RE_EXPORT_SYMBOL2(int, register_kprobe, struct kprobe *, p)
|
||||
// RE_EXPORT_SYMBOL2(void, unregister_kprobe, struct kprobe *, p)
|
||||
|
||||
// int ksu_register_kprobe(struct kprobe *p);
|
||||
// void ksu_unregister_kprobe(struct kprobe *p);
|
||||
// int ksu_register_kprobes(struct kprobe **kps, int num);
|
||||
// void ksu_unregister_kprobes(struct kprobe **kps, int num);
|
||||
|
||||
// int ksu_register_kretprobe(struct kretprobe *rp);
|
||||
// void unregister_kretprobe(struct kretprobe *rp);
|
||||
// int register_kretprobes(struct kretprobe **rps, int num);
|
||||
// void unregister_kretprobes(struct kretprobe **rps, int num);
|
||||
3
kernel/selinux/Makefile
Normal file
3
kernel/selinux/Makefile
Normal file
@@ -0,0 +1,3 @@
|
||||
obj-y += selinux.o
|
||||
|
||||
ccflags-y += -Wno-implicit-function-declaration -Wno-strict-prototypes -Wno-int-conversion
|
||||
1
kernel/selinux/av_permissions.h
Normal file
1
kernel/selinux/av_permissions.h
Normal file
@@ -0,0 +1 @@
|
||||
#include "../../../security/selinux/av_permissions.h"
|
||||
1
kernel/selinux/flask.h
Normal file
1
kernel/selinux/flask.h
Normal file
@@ -0,0 +1 @@
|
||||
#include "../../../security/selinux/flask.h"
|
||||
1
kernel/selinux/security.h
Normal file
1
kernel/selinux/security.h
Normal file
@@ -0,0 +1 @@
|
||||
#include "../../../security/selinux/include/security.h"
|
||||
86
kernel/selinux/selinux.c
Normal file
86
kernel/selinux/selinux.c
Normal file
@@ -0,0 +1,86 @@
|
||||
#include <linux/cpu.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 "../../../security/selinux/ss/sidtab.h"
|
||||
#include "../../../security/selinux/ss/services.h"
|
||||
#include "../../../security/selinux/include/objsec.h"
|
||||
|
||||
#include "selinux.h"
|
||||
#include "../klog.h"
|
||||
|
||||
#define KERNEL_SU_DOMAIN "u:r:su:s0"
|
||||
|
||||
static int transive_to_domain(const char* domain) {
|
||||
struct cred* cred;
|
||||
struct task_security_struct* tsec;
|
||||
u32 sid;
|
||||
int error;
|
||||
|
||||
cred = (struct cred *)__task_cred(current);
|
||||
|
||||
tsec = cred->security;
|
||||
if (!tsec) {
|
||||
pr_err("tsec == NULL!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
error = security_secctx_to_secid(domain, strlen(domain), &sid);
|
||||
pr_info("error: %d, sid: %d\n", error, sid);
|
||||
if (!error) {
|
||||
tsec->sid = sid;
|
||||
tsec->create_sid = 0;
|
||||
tsec->keycreate_sid = 0;
|
||||
tsec->sockcreate_sid = 0;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
static int set_domain_permissive() {
|
||||
u32 sid;
|
||||
struct selinux_policy *policy;
|
||||
struct sidtab_entry *entry;
|
||||
struct ebitmap *permissive;
|
||||
|
||||
sid = current_sid();
|
||||
pr_info("set sid (%d) to permissive", sid);
|
||||
|
||||
rcu_read_lock();
|
||||
policy = rcu_dereference(selinux_state.policy);
|
||||
|
||||
entry = sidtab_search_entry(policy->sidtab, sid);
|
||||
if (entry == NULL){
|
||||
pr_info("entry == NULL");
|
||||
rcu_read_unlock();
|
||||
return -EFAULT;
|
||||
}
|
||||
// FIXME: keep mls
|
||||
permissive = &(policy->policydb.permissive_map);
|
||||
ebitmap_set_bit(permissive, entry->context.type, 1);
|
||||
|
||||
rcu_read_unlock();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool is_domain_permissive;
|
||||
|
||||
void setup_selinux() {
|
||||
|
||||
if (transive_to_domain(KERNEL_SU_DOMAIN)) {
|
||||
pr_err("transive domain failed.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_domain_permissive) {
|
||||
if (set_domain_permissive() == 0) {
|
||||
is_domain_permissive = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
8
kernel/selinux/selinux.h
Normal file
8
kernel/selinux/selinux.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#ifndef __KSU_H_SELINUX
|
||||
#define __KSU_H_SELINUX
|
||||
|
||||
|
||||
|
||||
void setup_selinux();
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user