Skip to content

Commit 86243d0

Browse files
committed
kernel: rp_sucompat: add kretprobes-hooked getname_flags for sucompat
This introduces a kretprobe on getname_flags that improves the stealth and reliability of sucompat feature. Changes: - CONFIG_KSU_KRETPROBES_SUCOMPAT option to enable this hooking method - Hooks getname_flags() via kretprobe to intercept and modify filename->name on the return - prevent timing-based detections since it avoids individual syscall hijacking (newfstat vs newfstatat timing detections) - prevents doing usercopies, which in turn increases reliability on pagefaulty moments This allows sucompat to operate against anti-root detection techniques known as - Delayed syscall - KSU (ND) - sucompat SCA (Discolusre) - Abnormal Environment (NT) This is still very experimental, so default n, but yeah, it works. Related: - #5 (comment) Signed-off-by: backslashxx <[email protected]>
1 parent 11daf70 commit 86243d0

File tree

4 files changed

+131
-0
lines changed

4 files changed

+131
-0
lines changed

kernel/Kconfig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,15 @@ config KSU_KPROBES_KSUD
1616
on early boot. Hooks are unregistered at boot complete
1717
to reduce overhead.
1818

19+
config KSU_KRETPROBES_SUCOMPAT
20+
bool "EXPERIMENTAL: kretprobes for sucompat"
21+
depends on KRETPROBES
22+
default n
23+
help
24+
EXPERIMENTAL: Use kretprobes for hooking getname_flags, mainly for
25+
sucompat. This method will hijack all fs-related syscalls, but
26+
thwarts timing based detections.
27+
1928
config KSU_DEBUG
2029
bool "KernelSU debug mode"
2130
depends on KSU

kernel/Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ ifeq ($(CONFIG_KSU_KPROBES_KSUD),y)
1616
kernelsu-objs += kp_ksud.o
1717
endif
1818

19+
ifeq ($(CONFIG_KSU_KRETPROBES_SUCOMPAT),y)
20+
kernelsu-objs += rp_sucompat.o
21+
endif
22+
1923
kernelsu-objs += selinux/selinux.o
2024
kernelsu-objs += selinux/sepolicy.o
2125
kernelsu-objs += selinux/rules.o

kernel/rp_sucompat.c

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
#include <linux/version.h>
2+
#include <linux/kprobes.h>
3+
#include <linux/printk.h>
4+
#include <linux/types.h>
5+
#include <linux/uaccess.h>
6+
#include <linux/sched.h>
7+
#include <linux/slab.h>
8+
#include <linux/namei.h>
9+
10+
#include "arch.h"
11+
#include "klog.h"
12+
#include "ksud.h"
13+
#include "kernel_compat.h"
14+
15+
static DEFINE_MUTEX(ksu_rp_sucompat_lock);
16+
17+
// struct filename *getname_flags(const char __user *filename, int flags, int *empty)
18+
// https://elixir.bootlin.com/linux/v4.9.337/source/samples/kprobes/kretprobe_example.c
19+
20+
extern int ksu_getname_flags_kernel(char **kname, int flags);
21+
22+
struct kretprobe *getname_rp;
23+
24+
static int getname_flags_ret_handler(struct kretprobe_instance *ri, struct pt_regs *regs)
25+
{
26+
int *flags = (int *)ri->data;
27+
28+
struct filename *ret = (struct filename *)PT_REGS_RC(regs);
29+
if (IS_ERR(ret) || !ret || !ret->name)
30+
return 0;
31+
32+
ksu_getname_flags_kernel((char **)&ret->name, *flags);
33+
return 0;
34+
}
35+
36+
static int getname_flags_entry_handler(struct kretprobe_instance *ri, struct pt_regs *regs)
37+
{
38+
int *flags = (int *)ri->data; // as per sample, we store everything on ri->data ?
39+
*flags = (int)PT_REGS_PARM2(regs); // keep a copy of arg2
40+
41+
return 0;
42+
}
43+
44+
#if 0
45+
static struct kretprobe getname_kretprobe = {
46+
.kp.symbol_name = "getname_flags",
47+
.entry_handler = getname_flags_entry_handler,
48+
.handler = getname_flags_ret_handler,
49+
.data_size = sizeof(int),
50+
.maxactive = 20,
51+
};
52+
#endif
53+
54+
// kanged from upstrteam
55+
// this method allows high volume register/unregister
56+
static struct kretprobe *init_kretprobe(const char *symbol,
57+
kretprobe_handler_t entry_handler,
58+
kretprobe_handler_t ret_handler,
59+
size_t data_size,
60+
int maxactive)
61+
{
62+
struct kretprobe *rp = kzalloc(sizeof(struct kretprobe), GFP_KERNEL);
63+
if (!rp)
64+
return NULL;
65+
66+
rp->kp.symbol_name = symbol;
67+
rp->entry_handler = entry_handler;
68+
rp->handler = ret_handler;
69+
rp->data_size = data_size;
70+
rp->maxactive = maxactive;
71+
72+
mutex_lock(&ksu_rp_sucompat_lock);
73+
int ret = register_kretprobe(rp);
74+
mutex_unlock(&ksu_rp_sucompat_lock);
75+
if (ret) {
76+
kfree(rp);
77+
return NULL;
78+
}
79+
pr_info("rp_sucompat: planted kretprobe at %s: %p\n", rp->kp.symbol_name, rp->kp.addr);
80+
81+
return rp;
82+
}
83+
84+
static void destroy_kretprobe(struct kretprobe **rp_ptr)
85+
{
86+
if (!rp_ptr || !*rp_ptr)
87+
return;
88+
89+
mutex_lock(&ksu_rp_sucompat_lock);
90+
unregister_kretprobe(*rp_ptr);
91+
mutex_unlock(&ksu_rp_sucompat_lock);
92+
kfree(*rp_ptr);
93+
*rp_ptr = NULL;
94+
}
95+
96+
void rp_sucompat_exit()
97+
{
98+
pr_info("rp_sucompat: unregister getname_flags!\n");
99+
destroy_kretprobe(&getname_rp);
100+
}
101+
102+
void rp_sucompat_init()
103+
{
104+
pr_info("%s: register getname_flags!\n", __func__);
105+
getname_rp = init_kretprobe("getname_flags", getname_flags_entry_handler,
106+
getname_flags_ret_handler, sizeof(int), 20);
107+
}

kernel/sucompat.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,14 +254,25 @@ int ksu_getname_flags_kernel(char **kname, int flags)
254254
return ksu_sucompat_kernel_common((void *)*kname, "getname_flags", !!!flags);
255255
}
256256

257+
#ifdef CONFIG_KSU_KRETPROBES_SUCOMPAT
258+
extern void rp_sucompat_exit();
259+
extern void rp_sucompat_init();
260+
#endif
261+
257262
void ksu_sucompat_enable()
258263
{
264+
#ifdef CONFIG_KSU_KRETPROBES_SUCOMPAT
265+
rp_sucompat_init();
266+
#endif
259267
ksu_sucompat_enabled = true;
260268
pr_info("%s: hooks enabled: exec, faccessat, stat\n", __func__);
261269
}
262270

263271
void ksu_sucompat_disable()
264272
{
273+
#ifdef CONFIG_KSU_KRETPROBES_SUCOMPAT
274+
rp_sucompat_exit();
275+
#endif
265276
ksu_sucompat_enabled = false;
266277
pr_info("%s: hooks disabled: exec, faccessat, stat\n", __func__);
267278
}

0 commit comments

Comments
 (0)