Skip to content

Commit 9b7be18

Browse files
Update ftrace example
- uid should be initialized - update comments - add uid check in our_sys_openat - format
1 parent f7cc340 commit 9b7be18

File tree

1 file changed

+74
-31
lines changed

1 file changed

+74
-31
lines changed

examples/syscall-ftrace.c

Lines changed: 74 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
/**
22
* syscall-ftrace.c
33
*
4-
* System call "stealing" with Ftrace
4+
* System call "stealing" with ftrace
5+
*
6+
* We create a callback function that contains
7+
* an unconditional jump to our spying function,
8+
* which will then return control to the original one.
9+
*
10+
* The callback function is triggered by ftrace.
511
*/
612

713
#include <linux/kernel.h>
@@ -22,30 +28,30 @@ MODULE_LICENSE("GPL");
2228
#define MAX_FILENAME_SIZE 200
2329

2430
/* UID we want to spy on - will be filled from the command line. */
25-
static int uid;
31+
static int uid = 0;
2632
module_param(uid, int, 0644);
2733

2834
/**
29-
* This is a helper structure that housekeeps all information
35+
* This is a housekeeping structure that saves all information
3036
* needed for hooking. Usage with `PREPARE_HOOK` is recommended.
3137
*
3238
* Example:
33-
* static ftrace_hook_t sys_clone_hook = PREPARE_HOOK(__NR_openat, my_sys_clone, &orig_sys_clone)
39+
* static ftrace_hook_t sys_clone_hook =
40+
* PREPARE_HOOK(__NR_clone, my_sys_clone, &orig_sys_clone)
3441
*/
3542
typedef struct ftrace_hook {
36-
unsigned long nr; // syscall name
37-
void* new; // hook function
38-
void* orig; // original function
43+
unsigned long nr; // syscall name
44+
void *new; // hook function
45+
void *orig; // original function
3946

4047
unsigned long address; // address to the original function
4148
struct ftrace_ops ops; // ftrace structure
4249
} ftrace_hook_t;
4350

44-
#define PREPARE_HOOK(_nr, _hook, _orig) { \
45-
.nr = (_nr), \
46-
.new = (_hook), \
47-
.orig = (_orig) \
48-
}
51+
#define PREPARE_HOOK(_nr, _hook, _orig) \
52+
{ \
53+
.nr = (_nr), .new = (_hook), .orig = (_orig) \
54+
}
4955

5056
unsigned long **sys_call_table;
5157

@@ -56,48 +62,65 @@ unsigned long **sys_call_table;
5662
*/
5763
static int resolve_address(ftrace_hook_t *hook)
5864
{
59-
static struct kprobe kp = {
60-
.symbol_name = "kallsyms_lookup_name"
61-
};
65+
static struct kprobe kp = { .symbol_name = "kallsyms_lookup_name" };
6266
unsigned long (*kallsyms_lookup_name)(const char *name);
6367
register_kprobe(&kp);
6468
kallsyms_lookup_name = (unsigned long (*)(const char *))kp.addr;
6569
unregister_kprobe(&kp);
6670

67-
if (kallsyms_lookup_name) pr_info("[syscall-ftrace] kallsyms_lookup_name is found at 0x%lx\n", (unsigned long)kallsyms_lookup_name);
71+
if (kallsyms_lookup_name)
72+
pr_info("[syscall-ftrace] kallsyms_lookup_name is found at 0x%lx\n",
73+
(unsigned long)kallsyms_lookup_name);
6874
else {
6975
pr_err("[syscall-ftrace] kallsyms_lookup_name is not found!\n");
7076
return -1;
7177
}
7278

7379
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);
80+
if (sys_call_table)
81+
pr_info("[syscall-ftrace] sys_call_table is found at 0x%lx\n",
82+
(unsigned long)sys_call_table);
7583
else {
7684
pr_err("[syscall-ftrace] sys_call_table is not found!\n");
7785
return -1;
7886
}
7987

8088
hook->address = (unsigned long)sys_call_table[hook->nr];
81-
*((unsigned long*) hook->orig) = hook->address;
89+
*((unsigned long *)hook->orig) = hook->address;
8290
return 0;
8391
}
8492

8593
/**
8694
* This is where the magic happens.
8795
*
96+
* We check whether this function is called by the kernel or this module
97+
* by checking whether parent_ip is within this module.
98+
*
99+
* During the first call, parent_ip points to somewhere in the kernel
100+
* that's not in this module,
101+
* while the second call is in this module
102+
* since it's called from our_sys_openat.
103+
*
104+
* If it is the first call, we modify ip to be our_sys_openat,
105+
* which will pass control to it after ftrace is done.
88106
*/
89107
#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)
108+
static void notrace ftrace_thunk(unsigned long ip, unsigned long parent_ip,
109+
struct ftrace_ops *ops,
110+
struct ftrace_regs *fregs)
91111
{
92112
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;
113+
if (!within_module(parent_ip, THIS_MODULE))
114+
fregs->regs.ip = (unsigned long)hook->new;
94115
}
95116

96-
#else
97-
static void notrace ftrace_thunk(unsigned long ip, unsigned long parent_ip, struct ftrace_ops *ops, struct pt_regs *regs)
117+
#else /** Version < v5.11 */
118+
static void notrace ftrace_thunk(unsigned long ip, unsigned long parent_ip,
119+
struct ftrace_ops *ops, struct pt_regs *regs)
98120
{
99121
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;
122+
if (!within_module(parent_ip, THIS_MODULE))
123+
regs->ip = (unsigned long)hook->new;
101124
}
102125

103126
#endif /** Version >= v5.11 */
@@ -106,10 +129,14 @@ int install_hook(ftrace_hook_t *hook)
106129
{
107130
int err;
108131
err = resolve_address(hook);
109-
if (err) return err;
132+
if (err)
133+
return err;
110134

135+
/** The callback function */
111136
hook->ops.func = ftrace_thunk;
137+
/** We need registers and we're modifying ip */
112138
hook->ops.flags = FTRACE_OPS_FL_SAVE_REGS | FTRACE_OPS_FL_IPMODIFY;
139+
/** Only sys_openat should be traced */
113140
err = ftrace_set_filter_ip(&hook->ops, hook->address, 0, 0);
114141
if (err) {
115142
pr_err("[syscall-ftrace] ftrace_set_filter_ip() failed: %d\n", err);
@@ -129,20 +156,34 @@ void remove_hook(ftrace_hook_t *hook)
129156
{
130157
int err;
131158
err = unregister_ftrace_function(&hook->ops);
132-
if (err) pr_err("[syscall-ftrace] unregister_ftrace_function() failed: %d\n", err);
159+
if (err)
160+
pr_err("[syscall-ftrace] unregister_ftrace_function() failed: %d\n",
161+
err);
133162

163+
/** Disable the trace by setting remove to 1 */
134164
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);
165+
if (err)
166+
pr_err("[syscall-ftrace] ftrace_set_filter_ip() failed: %d\n", err);
136167
}
137168

138-
/** For some reason the kernel segfaults when the arguments are expanded. */
169+
/** For some reason the kernel segfaults when the parameters are expanded. */
139170
static asmlinkage long (*original_call)(struct pt_regs *regs);
140171
static asmlinkage long our_sys_openat(struct pt_regs *regs)
141172
{
142173
char *kfilename;
143-
kfilename = kmalloc(GFP_KERNEL, MAX_FILENAME_SIZE*sizeof(char));
144-
if (!kfilename) return original_call(regs);
174+
if (current->cred->uid.val != uid)
175+
return original_call(regs);
176+
kfilename = kmalloc(GFP_KERNEL, MAX_FILENAME_SIZE * sizeof(char));
177+
if (!kfilename)
178+
return original_call(regs);
145179

180+
/**
181+
* This may only work in x86_64 because getting parameters
182+
* from CPU registers is architecture-dependent.
183+
*
184+
* Change regs->si to appropriate registers
185+
* if you are trying on different architecture.
186+
*/
146187
if (copy_from_user(kfilename, (char __user *)regs->si, MAX_FILENAME_SIZE) < 0) {
147188
kfree(kfilename);
148189
return original_call(regs);
@@ -154,13 +195,15 @@ static asmlinkage long our_sys_openat(struct pt_regs *regs)
154195
return original_call(regs);
155196
}
156197

157-
static ftrace_hook_t sys_openat_hook = PREPARE_HOOK(__NR_openat, our_sys_openat, &original_call);
198+
static ftrace_hook_t sys_openat_hook =
199+
PREPARE_HOOK(__NR_openat, our_sys_openat, &original_call);
158200

159201
static int __init syscall_ftrace_start(void)
160202
{
161203
int err;
162204
err = install_hook(&sys_openat_hook);
163-
if (err) return err;
205+
if (err)
206+
return err;
164207
pr_info("[syscall-ftrace] hooked, spying on uid %d\n", uid);
165208
return 0;
166209
}

0 commit comments

Comments
 (0)