Skip to content

Commit a17514c

Browse files
Create Ftrace syscall hooking example
1 parent 9c25e59 commit a17514c

File tree

2 files changed

+174
-0
lines changed

2 files changed

+174
-0
lines changed

examples/Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ obj-m += kbleds.o
1717
obj-m += sched.o
1818
obj-m += chardev2.o
1919
obj-m += syscall.o
20+
obj-m += syscall-ftrace.o
2021
obj-m += intrpt.o
2122
obj-m += cryptosha256.o
2223
obj-m += cryptosk.o

examples/syscall-ftrace.c

+173
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
/**
2+
* syscall-ftrace.c
3+
*
4+
* System call "stealing" with Ftrace
5+
*/
6+
7+
#include <linux/kernel.h>
8+
#include <linux/init.h>
9+
#include <linux/module.h>
10+
#include <linux/moduleparam.h>
11+
#include <linux/version.h>
12+
#include <linux/unistd.h>
13+
#include <linux/kprobes.h>
14+
#include <linux/sched.h>
15+
#include <linux/uaccess.h>
16+
#include <linux/slab.h>
17+
/** This is what we're using here. */
18+
#include <linux/ftrace.h>
19+
20+
MODULE_LICENSE("GPL");
21+
22+
#define MAX_FILENAME_SIZE 200
23+
24+
/* UID we want to spy on - will be filled from the command line. */
25+
static int uid;
26+
module_param(uid, int, 0644);
27+
28+
/**
29+
* This is a helper structure that housekeeps all information
30+
* needed for hooking. Usage with `PREPARE_HOOK` is recommended.
31+
*
32+
* Example:
33+
* static ftrace_hook_t sys_clone_hook = PREPARE_HOOK(__NR_openat, my_sys_clone, &orig_sys_clone)
34+
*/
35+
typedef struct ftrace_hook {
36+
unsigned long nr; // syscall name
37+
void* new; // hook function
38+
void* orig; // original function
39+
40+
unsigned long address; // address to the original function
41+
struct ftrace_ops ops; // ftrace structure
42+
} ftrace_hook_t;
43+
44+
#define PREPARE_HOOK(_nr, _hook, _orig) { \
45+
.nr = (_nr), \
46+
.new = (_hook), \
47+
.orig = (_orig) \
48+
}
49+
50+
unsigned long **sys_call_table;
51+
52+
/**
53+
* For the sake of simplicity, only the kprobe method is included.
54+
* If you want to know more about different methods to get
55+
* kallsyms_lookup_name, see syscall.c.
56+
*/
57+
static int resolve_address(ftrace_hook_t *hook)
58+
{
59+
static struct kprobe kp = {
60+
.symbol_name = "kallsyms_lookup_name"
61+
};
62+
unsigned long (*kallsyms_lookup_name)(const char *name);
63+
register_kprobe(&kp);
64+
kallsyms_lookup_name = (unsigned long (*)(const char *))kp.addr;
65+
unregister_kprobe(&kp);
66+
67+
if (kallsyms_lookup_name) pr_info("[syscall-ftrace] kallsyms_lookup_name is found at 0x%lx\n", (unsigned long)kallsyms_lookup_name);
68+
else {
69+
pr_err("[syscall-ftrace] kallsyms_lookup_name is not found!\n");
70+
return -1;
71+
}
72+
73+
sys_call_table = (unsigned long **)kallsyms_lookup_name("sys_call_table");
74+
if (sys_call_table) pr_info("[syscall-ftrace] sys_call_table is found at 0x%lx\n", (unsigned long)sys_call_table);
75+
else {
76+
pr_err("[syscall-ftrace] sys_call_table is not found!\n");
77+
return -1;
78+
}
79+
80+
hook->address = (unsigned long)sys_call_table[hook->nr];
81+
*((unsigned long*) hook->orig) = hook->address;
82+
return 0;
83+
}
84+
85+
/**
86+
* This is where the magic happens.
87+
*
88+
*/
89+
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
90+
static void notrace ftrace_thunk(unsigned long ip, unsigned long parent_ip, struct ftrace_ops *ops, struct ftrace_regs *fregs)
91+
{
92+
ftrace_hook_t *hook = container_of(ops, ftrace_hook_t, ops);
93+
if (!within_module(parent_ip, THIS_MODULE)) fregs->regs.ip = (unsigned long) hook->new;
94+
}
95+
96+
#else
97+
static void notrace ftrace_thunk(unsigned long ip, unsigned long parent_ip, struct ftrace_ops *ops, struct pt_regs *regs)
98+
{
99+
ftrace_hook_t *hook = container_of(ops, ftrace_hook_t, ops);
100+
if (!within_module(parent_ip, THIS_MODULE)) regs->ip = (unsigned long) hook->new;
101+
}
102+
103+
#endif /** Version >= v5.11 */
104+
105+
int install_hook(ftrace_hook_t *hook)
106+
{
107+
int err;
108+
err = resolve_address(hook);
109+
if (err) return err;
110+
111+
hook->ops.func = ftrace_thunk;
112+
hook->ops.flags = FTRACE_OPS_FL_SAVE_REGS | FTRACE_OPS_FL_IPMODIFY;
113+
err = ftrace_set_filter_ip(&hook->ops, hook->address, 0, 0);
114+
if (err) {
115+
pr_err("[syscall-ftrace] ftrace_set_filter_ip() failed: %d\n", err);
116+
return err;
117+
}
118+
119+
err = register_ftrace_function(&hook->ops);
120+
if (err) {
121+
pr_err("[syscall-ftrace] register_ftrace_function() failed: %d\n", err);
122+
return err;
123+
}
124+
125+
return 0;
126+
}
127+
128+
void remove_hook(ftrace_hook_t *hook)
129+
{
130+
int err;
131+
err = unregister_ftrace_function(&hook->ops);
132+
if (err) pr_err("[syscall-ftrace] unregister_ftrace_function() failed: %d\n", err);
133+
134+
err = ftrace_set_filter_ip(&hook->ops, hook->address, 1, 0);
135+
if (err) pr_err("[syscall-ftrace] ftrace_set_filter_ip() failed: %d\n", err);
136+
}
137+
138+
/** For some reason the kernel segfaults when the arguments are expanded. */
139+
static asmlinkage long (*original_call)(struct pt_regs *regs);
140+
static asmlinkage long our_sys_openat(struct pt_regs *regs)
141+
{
142+
char *kfilename;
143+
kfilename = kmalloc(GFP_KERNEL, 200*sizeof(char));
144+
if (!kfilename) return original_call(regs);
145+
146+
if (copy_from_user(kfilename, (char __user *)regs->si, MAX_FILENAME_SIZE) < 0) {
147+
kfree(kfilename);
148+
return original_call(regs);
149+
}
150+
151+
pr_info("[syscall-ftrace] File opened by UID %d: %s\n", uid, kfilename);
152+
kfree(kfilename);
153+
154+
return original_call(regs);
155+
}
156+
157+
static ftrace_hook_t sys_openat_hook = PREPARE_HOOK(__NR_openat, our_sys_openat, &original_call);
158+
159+
static int __init syscall_ftrace_start(void) {
160+
int err;
161+
err = install_hook(&sys_openat_hook);
162+
if (err) return err;
163+
pr_info("[syscall-ftrace] hooked, spying on uid %d\n", uid);
164+
return 0;
165+
}
166+
167+
static void __exit syscall_ftrace_end(void) {
168+
remove_hook(&sys_openat_hook);
169+
pr_info("[syscall-ftrace] removed\n");
170+
}
171+
172+
module_init(syscall_ftrace_start);
173+
module_exit(syscall_ftrace_end);

0 commit comments

Comments
 (0)