Skip to content

Commit a28c491

Browse files
authored
ftrace_helper made by Harvey Phillips
1 parent 58aac81 commit a28c491

File tree

1 file changed

+199
-0
lines changed

1 file changed

+199
-0
lines changed

library/ftrace_helper.h

+199
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
/*
2+
* Helper library for ftrace hooking kernel functions
3+
* Author: Harvey Phillips ([email protected])
4+
* License: GPL
5+
* */
6+
7+
#include <linux/ftrace.h>
8+
#include <linux/linkage.h>
9+
#include <linux/slab.h>
10+
#include <linux/uaccess.h>
11+
#include <linux/version.h>
12+
13+
#if defined(CONFIG_X86_64) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4,17,0))
14+
#define PTREGS_SYSCALL_STUBS 1
15+
#endif
16+
17+
/*
18+
* On Linux kernels 5.7+, kallsyms_lookup_name() is no longer exported,
19+
* so we have to use kprobes to get the address.
20+
* Full credit to @f0lg0 for the idea.
21+
*/
22+
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,7,0)
23+
#define KPROBE_LOOKUP 1
24+
#include <linux/kprobes.h>
25+
static struct kprobe kp = {
26+
.symbol_name = "kallsyms_lookup_name"
27+
};
28+
#endif
29+
30+
#define HOOK(_name, _hook, _orig) \
31+
{ \
32+
.name = (_name), \
33+
.function = (_hook), \
34+
.original = (_orig), \
35+
}
36+
37+
/* We need to prevent recursive loops when hooking, otherwise the kernel will
38+
* panic and hang. The options are to either detect recursion by looking at
39+
* the function return address, or by jumping over the ftrace call. We use the
40+
* first option, by setting USE_FENTRY_OFFSET = 0, but could use the other by
41+
* setting it to 1. (Oridinarily ftrace provides it's own protections against
42+
* recursion, but it relies on saving return registers in $rip. We will likely
43+
* need the use of the $rip register in our hook, so we have to disable this
44+
* protection and implement our own).
45+
* */
46+
#define USE_FENTRY_OFFSET 0
47+
#if !USE_FENTRY_OFFSET
48+
#pragma GCC optimize("-fno-optimize-sibling-calls")
49+
#endif
50+
51+
/* We pack all the information we need (name, hooking function, original function)
52+
* into this struct. This makes is easier for setting up the hook and just passing
53+
* the entire struct off to fh_install_hook() later on.
54+
* */
55+
struct ftrace_hook {
56+
const char *name;
57+
void *function;
58+
void *original;
59+
60+
unsigned long address;
61+
struct ftrace_ops ops;
62+
};
63+
64+
/* Ftrace needs to know the address of the original function that we
65+
* are going to hook. As before, we just use kallsyms_lookup_name()
66+
* to find the address in kernel memory.
67+
* */
68+
static int fh_resolve_hook_address(struct ftrace_hook *hook)
69+
{
70+
#ifdef KPROBE_LOOKUP
71+
typedef unsigned long (*kallsyms_lookup_name_t)(const char *name);
72+
kallsyms_lookup_name_t kallsyms_lookup_name;
73+
register_kprobe(&kp);
74+
kallsyms_lookup_name = (kallsyms_lookup_name_t) kp.addr;
75+
unregister_kprobe(&kp);
76+
#endif
77+
hook->address = kallsyms_lookup_name(hook->name);
78+
79+
if (!hook->address)
80+
{
81+
printk(KERN_DEBUG "rootkit: unresolved symbol: %s\n", hook->name);
82+
return -ENOENT;
83+
}
84+
85+
#if USE_FENTRY_OFFSET
86+
*((unsigned long*) hook->original) = hook->address + MCOUNT_INSN_SIZE;
87+
#else
88+
*((unsigned long*) hook->original) = hook->address;
89+
#endif
90+
91+
return 0;
92+
}
93+
94+
/* See comment below within fh_install_hook() */
95+
static void notrace fh_ftrace_thunk(unsigned long ip, unsigned long parent_ip, struct ftrace_ops *ops, struct pt_regs *regs)
96+
{
97+
struct ftrace_hook *hook = container_of(ops, struct ftrace_hook, ops);
98+
99+
#if USE_FENTRY_OFFSET
100+
regs->ip = (unsigned long) hook->function;
101+
#else
102+
if(!within_module(parent_ip, THIS_MODULE))
103+
regs->ip = (unsigned long) hook->function;
104+
#endif
105+
}
106+
107+
/* Assuming we've already set hook->name, hook->function and hook->original, we
108+
* can go ahead and install the hook with ftrace. This is done by setting the
109+
* ops field of hook (see the comment below for more details), and then using
110+
* the built-in ftrace_set_filter_ip() and register_ftrace_function() functions
111+
* provided by ftrace.h
112+
* */
113+
int fh_install_hook(struct ftrace_hook *hook)
114+
{
115+
int err;
116+
err = fh_resolve_hook_address(hook);
117+
if(err)
118+
return err;
119+
120+
/* For many of function hooks (especially non-trivial ones), the $rip
121+
* register gets modified, so we have to alert ftrace to this fact. This
122+
* is the reason for the SAVE_REGS and IP_MODIFY flags. However, we also
123+
* need to OR the RECURSION_SAFE flag (effectively turning if OFF) because
124+
* the built-in anti-recursion guard provided by ftrace is useless if
125+
* we're modifying $rip. This is why we have to implement our own checks
126+
* (see USE_FENTRY_OFFSET). */
127+
hook->ops.func = fh_ftrace_thunk;
128+
hook->ops.flags = FTRACE_OPS_FL_SAVE_REGS
129+
| FTRACE_OPS_FL_RECURSION_SAFE
130+
| FTRACE_OPS_FL_IPMODIFY;
131+
132+
err = ftrace_set_filter_ip(&hook->ops, hook->address, 0, 0);
133+
if(err)
134+
{
135+
printk(KERN_DEBUG "rootkit: ftrace_set_filter_ip() failed: %d\n", err);
136+
return err;
137+
}
138+
139+
err = register_ftrace_function(&hook->ops);
140+
if(err)
141+
{
142+
printk(KERN_DEBUG "rootkit: register_ftrace_function() failed: %d\n", err);
143+
return err;
144+
}
145+
146+
return 0;
147+
}
148+
149+
/* Disabling our function hook is just a simple matter of calling the built-in
150+
* unregister_ftrace_function() and ftrace_set_filter_ip() functions (note the
151+
* opposite order to that in fh_install_hook()).
152+
* */
153+
void fh_remove_hook(struct ftrace_hook *hook)
154+
{
155+
int err;
156+
err = unregister_ftrace_function(&hook->ops);
157+
if(err)
158+
{
159+
printk(KERN_DEBUG "rootkit: unregister_ftrace_function() failed: %d\n", err);
160+
}
161+
162+
err = ftrace_set_filter_ip(&hook->ops, hook->address, 1, 0);
163+
if(err)
164+
{
165+
printk(KERN_DEBUG "rootkit: ftrace_set_filter_ip() failed: %d\n", err);
166+
}
167+
}
168+
169+
/* To make it easier to hook multiple functions in one module, this provides
170+
* a simple loop over an array of ftrace_hook struct
171+
* */
172+
int fh_install_hooks(struct ftrace_hook *hooks, size_t count)
173+
{
174+
int err;
175+
size_t i;
176+
177+
for (i = 0 ; i < count ; i++)
178+
{
179+
err = fh_install_hook(&hooks[i]);
180+
if(err)
181+
goto error;
182+
}
183+
return 0;
184+
185+
error:
186+
while (i != 0)
187+
{
188+
fh_remove_hook(&hooks[--i]);
189+
}
190+
return err;
191+
}
192+
193+
void fh_remove_hooks(struct ftrace_hook *hooks, size_t count)
194+
{
195+
size_t i;
196+
197+
for (i = 0 ; i < count ; i++)
198+
fh_remove_hook(&hooks[i]);
199+
}

0 commit comments

Comments
 (0)