Skip to content

Commit ebae689

Browse files
Ylarodtiann
authored andcommitted
Build KernelSU as LKM (#1254)
Co-authored-by: weishu <[email protected]>
1 parent 804d69f commit ebae689

File tree

3 files changed

+222
-17
lines changed

3 files changed

+222
-17
lines changed

drivers/kernelsu/Kconfig

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,11 @@ config KSU_DEBUG
1414
help
1515
Enable KernelSU debug mode
1616

17+
config KSU_MODULE
18+
bool "Build KernelSU as a module"
19+
depends on KSU
20+
default n
21+
help
22+
Build KernelSU as a loadable kernel module
23+
1724
endmenu

drivers/kernelsu/Makefile

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,42 @@
1-
obj-y += ksu.o
2-
obj-y += allowlist.o
3-
kernelsu-objs := apk_sign.o
1+
kernelsu-objs := ksu.o
2+
kernelsu-objs += allowlist.o
3+
kernelsu-objs += apk_sign.o
4+
kernelsu-objs += module_api.o
5+
kernelsu-objs += sucompat.o
6+
kernelsu-objs += uid_observer.o
7+
kernelsu-objs += manager.o
8+
kernelsu-objs += core_hook.o
9+
kernelsu-objs += ksud.o
10+
kernelsu-objs += embed_ksud.o
11+
kernelsu-objs += kernel_compat.o
12+
13+
kernelsu-objs += selinux/selinux.o
14+
kernelsu-objs += selinux/sepolicy.o
15+
kernelsu-objs += selinux/rules.o
16+
ccflags-y += -I$(srctree)/security/selinux -I$(srctree)/security/selinux/include
17+
ccflags-y += -I$(objtree)/security/selinux -include $(srctree)/include/uapi/asm-generic/errno.h
18+
19+
ifndef KSU_MODULE
420
obj-y += kernelsu.o
5-
obj-y += module_api.o
6-
obj-y += sucompat.o
7-
obj-y += uid_observer.o
8-
obj-y += manager.o
9-
obj-y += core_hook.o
10-
obj-y += ksud.o
11-
obj-y += embed_ksud.o
12-
obj-y += kernel_compat.o
13-
14-
obj-y += selinux/
21+
else
22+
obj-m += kernelsu.o
23+
endif
24+
1525
# .git is a text file while the module is imported by 'git submodule add'.
1626
ifeq ($(shell test -e $(srctree)/$(src)/../.git; echo $$?),0)
1727
# We must use the absolute path to git, otherwise the build will fail if git is not in the PATH
1828
KSU_GIT_VERSION := $(shell cd $(srctree)/$(src); /usr/bin/env PATH="$$PATH":/usr/bin:/usr/local/bin git rev-list --count HEAD)
1929
ccflags-y += -DKSU_GIT_VERSION=$(KSU_GIT_VERSION)
2030
endif
2131

32+
ifeq ($(shell grep -q " current_sid(void)" $(srctree)/security/selinux/include/objsec.h; echo $$?),0)
33+
ccflags-y += -DKSU_COMPAT_HAS_CURRENT_SID
34+
endif
35+
36+
ifeq ($(shell grep -q "struct selinux_state " $(srctree)/security/selinux/include/security.h; echo $$?),0)
37+
ccflags-y += -DKSU_COMPAT_HAS_SELINUX_STATE
38+
endif
39+
2240
ifndef KSU_EXPECTED_SIZE
2341
KSU_EXPECTED_SIZE := 0x033b
2442
endif
@@ -32,7 +50,11 @@ ccflags-y += -DKSU_MANAGER_PACKAGE=\"$(KSU_MANAGER_PACKAGE)\"
3250
$(info -- KernelSU Manager package name: $(KSU_MANAGER_PACKAGE))
3351
endif
3452

53+
$(info -- KernelSU Manager signature size: $(KSU_EXPECTED_SIZE))
54+
$(info -- KernelSU Manager signature hash: $(KSU_EXPECTED_HASH))
55+
3556
ccflags-y += -DEXPECTED_SIZE=$(KSU_EXPECTED_SIZE)
3657
ccflags-y += -DEXPECTED_HASH=\"$(KSU_EXPECTED_HASH)\"
58+
3759
ccflags-y += -Wno-implicit-function-declaration -Wno-strict-prototypes -Wno-int-conversion -Wno-gcc-compat
38-
ccflags-y += -Wno-declaration-after-statement
60+
ccflags-y += -Wno-declaration-after-statement -Wno-unused-function

drivers/kernelsu/core_hook.c

Lines changed: 179 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,21 @@
44
#include "linux/err.h"
55
#include "linux/init.h"
66
#include "linux/init_task.h"
7+
#include "linux/kallsyms.h"
78
#include "linux/kernel.h"
89
#include "linux/kprobes.h"
10+
#include "linux/list.h"
911
#include "linux/lsm_hooks.h"
1012
#include "linux/module.h"
13+
#include "linux/mm.h"
14+
#include "linux/mm_types.h"
1115
#include "linux/nsproxy.h"
1216
#include "linux/path.h"
1317
#include "linux/printk.h"
18+
#include "linux/sched.h"
19+
#include "linux/security.h"
20+
#include "linux/stddef.h"
21+
#include "linux/types.h"
1422
#include "linux/uaccess.h"
1523
#include "linux/uidgid.h"
1624
#include "linux/version.h"
@@ -28,6 +36,7 @@
2836
#include "klog.h" // IWYU pragma: keep
2937
#include "ksu.h"
3038
#include "ksud.h"
39+
#include "linux/vmalloc.h"
3140
#include "manager.h"
3241
#include "selinux/selinux.h"
3342
#include "uid_observer.h"
@@ -243,7 +252,7 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
243252
#ifdef CONFIG_KSU_DEBUG
244253
pr_info("manager already exist: %d\n",
245254
ksu_get_manager_uid());
246-
#endif
255+
#endif
247256
return 0;
248257
}
249258

@@ -735,14 +744,181 @@ void __init ksu_lsm_hook_init(void)
735744
#endif
736745
}
737746

747+
#ifdef MODULE
748+
static int override_security_head(void *head, const void *new_head, size_t len)
749+
{
750+
unsigned long base = (unsigned long)head & PAGE_MASK;
751+
unsigned long offset = offset_in_page(head);
752+
753+
// this is impossible for our case because the page alignment
754+
// but be careful for other cases!
755+
BUG_ON(offset + len > PAGE_SIZE);
756+
struct page *page = phys_to_page(__pa(base));
757+
if (!page) {
758+
return -EFAULT;
759+
}
760+
761+
void *addr = vmap(&page, 1, VM_MAP, PAGE_KERNEL);
762+
if (!addr) {
763+
return -ENOMEM;
764+
}
765+
memcpy(addr + offset, new_head, len);
766+
vunmap(addr);
767+
return 0;
768+
}
769+
770+
static void free_security_hook_list(struct hlist_head *head)
771+
{
772+
struct hlist_node *temp;
773+
struct security_hook_list *entry;
774+
775+
if (!head)
776+
return;
777+
778+
hlist_for_each_entry_safe (entry, temp, head, list) {
779+
hlist_del(&entry->list);
780+
kfree(entry);
781+
}
782+
783+
kfree(head);
784+
}
785+
786+
struct hlist_head *copy_security_hlist(struct hlist_head *orig)
787+
{
788+
struct hlist_head *new_head = kmalloc(sizeof(*new_head), GFP_KERNEL);
789+
if (!new_head)
790+
return NULL;
791+
792+
INIT_HLIST_HEAD(new_head);
793+
794+
struct security_hook_list *entry;
795+
struct security_hook_list *new_entry;
796+
797+
hlist_for_each_entry (entry, orig, list) {
798+
new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL);
799+
if (!new_entry) {
800+
free_security_hook_list(new_head);
801+
return NULL;
802+
}
803+
804+
*new_entry = *entry;
805+
806+
hlist_add_tail_rcu(&new_entry->list, new_head);
807+
}
808+
809+
return new_head;
810+
}
811+
812+
#define LSM_SEARCH_MAX 180 // This should be enough to iterate
813+
static void *find_head_addr(void *security_ptr, int *index)
814+
{
815+
if (!security_ptr) {
816+
return NULL;
817+
}
818+
struct hlist_head *head_start =
819+
(struct hlist_head *)&security_hook_heads;
820+
821+
for (int i = 0; i < LSM_SEARCH_MAX; i++) {
822+
struct hlist_head *head = head_start + i;
823+
struct security_hook_list *pos;
824+
hlist_for_each_entry (pos, head, list) {
825+
if (pos->hook.capget == security_ptr) {
826+
if (index) {
827+
*index = i;
828+
}
829+
return head;
830+
}
831+
}
832+
}
833+
834+
return NULL;
835+
}
836+
837+
#define GET_SYMBOL_ADDR(sym) \
838+
({ \
839+
void *addr = kallsyms_lookup_name(#sym ".cfi_jt"); \
840+
if (!addr) { \
841+
addr = kallsyms_lookup_name(#sym); \
842+
} \
843+
addr; \
844+
})
845+
846+
#define KSU_LSM_HOOK_HACK_INIT(head_ptr, name, func) \
847+
do { \
848+
static struct security_hook_list hook = { \
849+
.hook = { .name = func } \
850+
}; \
851+
hook.head = head_ptr; \
852+
hook.lsm = "ksu"; \
853+
struct hlist_head *new_head = copy_security_hlist(hook.head); \
854+
if (!new_head) { \
855+
pr_err("Failed to copy security list: %s\n", #name); \
856+
break; \
857+
} \
858+
hlist_add_tail_rcu(&hook.list, new_head); \
859+
if (override_security_head(hook.head, new_head, \
860+
sizeof(*new_head))) { \
861+
free_security_hook_list(new_head); \
862+
pr_err("Failed to hack lsm for: %s\n", #name); \
863+
} \
864+
} while (0)
865+
866+
void __init ksu_lsm_hook_init_hack(void)
867+
{
868+
void *cap_prctl = GET_SYMBOL_ADDR(cap_task_prctl);
869+
void *prctl_head = find_head_addr(cap_prctl, NULL);
870+
if (prctl_head) {
871+
if (prctl_head != &security_hook_heads.task_prctl) {
872+
pr_warn("prctl's address has shifted!\n");
873+
}
874+
KSU_LSM_HOOK_HACK_INIT(prctl_head, task_prctl, ksu_task_prctl);
875+
} else {
876+
pr_warn("Failed to find task_prctl!\n");
877+
}
878+
879+
int inode_killpriv_index = -1;
880+
void *cap_killpriv = GET_SYMBOL_ADDR(cap_inode_killpriv);
881+
find_head_addr(cap_killpriv, &inode_killpriv_index);
882+
if (inode_killpriv_index < 0) {
883+
pr_warn("Failed to find inode_rename, use kprobe instead!\n");
884+
register_kprobe(&renameat_kp);
885+
} else {
886+
int inode_rename_index = inode_killpriv_index +
887+
&security_hook_heads.inode_rename -
888+
&security_hook_heads.inode_killpriv;
889+
struct hlist_head *head_start =
890+
(struct hlist_head *)&security_hook_heads;
891+
void *inode_rename_head = head_start + inode_rename_index;
892+
if (inode_rename_head != &security_hook_heads.inode_rename) {
893+
pr_warn("inode_rename's address has shifted!\n");
894+
}
895+
KSU_LSM_HOOK_HACK_INIT(inode_rename_head, inode_rename,
896+
ksu_inode_rename);
897+
}
898+
void *cap_setuid = GET_SYMBOL_ADDR(cap_task_fix_setuid);
899+
void *setuid_head = find_head_addr(cap_setuid, NULL);
900+
if (setuid_head) {
901+
if (setuid_head != &security_hook_heads.task_fix_setuid) {
902+
pr_warn("setuid's address has shifted!\n");
903+
}
904+
KSU_LSM_HOOK_HACK_INIT(setuid_head, task_fix_setuid,
905+
ksu_task_fix_setuid);
906+
} else {
907+
pr_warn("Failed to find task_fix_setuid!\n");
908+
}
909+
smp_mb();
910+
}
911+
#endif
912+
738913
void __init ksu_core_init(void)
739914
{
740915
#ifndef MODULE
741916
pr_info("ksu_lsm_hook_init\n");
742917
ksu_lsm_hook_init();
918+
743919
#else
744-
pr_info("ksu_kprobe_init\n");
745-
ksu_kprobe_init();
920+
pr_info("ksu_lsm_hook_init hack!!!!\n");
921+
ksu_lsm_hook_init_hack();
746922
#endif
747923
}
748924

0 commit comments

Comments
 (0)