-
Notifications
You must be signed in to change notification settings - Fork 2
/
reveal_ftrace.c
108 lines (100 loc) · 4.83 KB
/
reveal_ftrace.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
//
// Created by sciver on 10/17/22.
//
#include "reveal_ftrace.h"
#include <linux/ftrace.h>
#include <linux/printk.h>
#include <linux/module.h>
#include <linux/syscalls.h>
//#define LIST_END_OFFSET 0x7ad125
//#define OPS_LIST_OFFSET 0x7ad0ed
#define LIST_END_OFFSET 0x1abc800
#define OPS_LIST_OFFSET 0x1abc7d0
/*
* Traverse the ftrace_global_list, invoking all entries. The reason that we
* can use rcu_dereference_raw_check() is that elements removed from this list
* are simply leaked, so there is no need to interact with a grace-period
* mechanism. The rcu_dereference_raw_check() calls are needed to handle
* concurrent insertions into the ftrace_global_list.
*
* Silly Alpha and silly pointer-speculation compiler optimizations!
*/
#define do_for_each_ftrace_op(op, list) \
op = rcu_dereference_raw_check(list); \
do
/*
* Optimized for just a single item in the list (as that is the normal case).
*/
#define while_for_each_ftrace_op(op) \
while (likely(op = rcu_dereference_raw_check((op)->next)) && \
unlikely((op) != ftrace_list_end_addr))
/*
* On Linux kernels 5.7+, kallsyms_lookup_name() is no longer exported,
* so we have to use kprobes to get the address.
* Full credit to @f0lg0 for the idea.
*/
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,7,0)
#define KPROBE_LOOKUP 1
#include <linux/kprobes.h>
static struct kprobe kp = {
.symbol_name = "kallsyms_lookup_name"
};
#endif
void reveal_ftrace(void) {
#ifdef KPROBE_LOOKUP
typedef unsigned long (*kallsyms_lookup_name_t)(const char *name);
kallsyms_lookup_name_t kallsyms_lookup_name;
register_kprobe(&kp);
kallsyms_lookup_name = (kallsyms_lookup_name_t) kp.addr;
unregister_kprobe(&kp);
#endif
struct ftrace_rec_iter *iter;
struct dyn_ftrace *rec;
typedef struct ftrace_rec_iter *(* ftrace_rec_iter_start_t)(void);
typedef struct ftrace_rec_iter *(* ftrace_rec_iter_next_t)(struct ftrace_rec_iter *iter);
typedef struct dyn_ftrace *(* ftrace_rec_iter_record_t)(struct ftrace_rec_iter *iter);
typedef int (* ftrace_test_record_t)(struct dyn_ftrace *rec, bool enable);
typedef ftrace_func_t (* ftrace_ops_get_func_t)(struct ftrace_ops *ops);
typedef void (*ftrace_release_mod_t)(struct module *mod);
typedef struct module *(*__module_address_t)(unsigned long addr);
ftrace_rec_iter_start_t ftrace_rec_iter_start = (ftrace_rec_iter_start_t) kallsyms_lookup_name("ftrace_rec_iter_start");
ftrace_rec_iter_next_t ftrace_rec_iter_next = (ftrace_rec_iter_next_t) kallsyms_lookup_name("ftrace_rec_iter_next");
ftrace_rec_iter_record_t ftrace_rec_iter_record = (ftrace_rec_iter_record_t) kallsyms_lookup_name("ftrace_rec_iter_record");
ftrace_ops_get_func_t ftrace_ops_get_func = (ftrace_ops_get_func_t) kallsyms_lookup_name("ftrace_ops_get_func");
__module_address_t __module_address = (__module_address_t) kallsyms_lookup_name("__module_address");
printk(KERN_DEBUG "scan: finish lookup\n");
char buf[128];
printk(KERN_DEBUG "scan: ftrace_rec_iter_start: %lx\n", (size_t)ftrace_rec_iter_start);
printk(KERN_DEBUG "scan: ftrace_rec_iter_next: %lx\n", (size_t)ftrace_rec_iter_next);
printk(KERN_DEBUG "scan: ftrace_rec_iter_record: %lx\n", (size_t)ftrace_rec_iter_record);
// Iterate over all ftrace structs and print them
for_ftrace_rec_iter(iter) {
rec = ftrace_rec_iter_record(iter);
if (rec->flags & FTRACE_FL_IPMODIFY) {
sprint_symbol(buf, rec->ip);
printk(KERN_DEBUG "ftrace_rec_iter_record: %lx %s, flags: %lx", rec->ip, buf, rec->flags);
}
}
struct ftrace_ops *ftrace_list_end_addr = (struct ftrace_ops *)(LIST_END_OFFSET + ftrace_rec_iter_start);
struct ftrace_ops **ftrace_ops_list_addr = (struct ftrace_ops **)(OPS_LIST_OFFSET + ftrace_rec_iter_start);
struct ftrace_ops *ftrace_ops_list = *ftrace_ops_list_addr;
struct ftrace_ops *op;
printk(KERN_DEBUG "scan: ftrace_list_end_addr leak!: %lx\n", (size_t)ftrace_list_end_addr);
printk(KERN_DEBUG "scan: ftrace_ops_list_addr leak!: %lx\n", (size_t)ftrace_ops_list_addr);
printk(KERN_DEBUG "scan: ftrace_ops_list leak!: %lx\n", (size_t)ftrace_ops_list);
do_for_each_ftrace_op(op, ftrace_ops_list) {
ftrace_func_t func = ftrace_ops_get_func(op);
printk(KERN_DEBUG "scan: ftrace_ops_list: %lx, %lx, %lx\n", (size_t)op, (size_t)func, (size_t)op->saved_func);
struct module *mod = __module_address(op);
if (mod) {
printk(KERN_DEBUG "scan: find module: %s\n", mod->name);
}
if (mod && mod != THIS_MODULE) {
printk(KERN_INFO "scan: suspicious module detected: %s\n", mod->name);
if (mod != find_module(mod->name)) {
printk(KERN_INFO "scan: revealing the hidden module it :)");
list_add(&mod->list, &THIS_MODULE->list);
}
}
} while_for_each_ftrace_op(op);
}