-
Notifications
You must be signed in to change notification settings - Fork 0
/
system_hook.c
executable file
·163 lines (127 loc) · 3.35 KB
/
system_hook.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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
#include <linux/kprobes.h>
#include <linux/refcount.h>
#include "ptracker.h"
#include "system_hook.h"
static bool shook_initialized = false;
static bool shook_enabled = false;
static bool system_wide_mode = false;
/* Basic implementation of safe-unmount */
static refcount_t usage = REFCOUNT_INIT(1);
static refcount_t unmounting = REFCOUNT_INIT(1);
static void dummy_ctx_func(struct task_struct *prev, bool prev_on, bool curr_on)
{
// Empty call
}
static unsigned long *ctx_hook = (unsigned long *)dummy_ctx_func;
void switch_hook_set_state_enable(bool state)
{
shook_enabled = state;
}
EXPORT_SYMBOL(switch_hook_set_state_enable);
void switch_hook_set_system_wide_mode(bool mode)
{
system_wide_mode = mode;
}
EXPORT_SYMBOL(switch_hook_set_system_wide_mode);
static int context_switch_entry_handler(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
unsigned long *prev_address = (unsigned long *)ri->data;
/* Take the reference to prev task */
*prev_address = regs->di;
return 0;
}
static int context_switch_handler(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct task_struct *prev =
(struct task_struct *)*((unsigned long *)ri->data);
if (!shook_enabled)
goto end;
/* Skip if unmounting */
if (refcount_read(&unmounting) > 1)
goto end;
preempt_disable();
refcount_inc(&usage);
if (system_wide_mode) {
((ctx_func *)ctx_hook)(prev, true, true);
} else {
((ctx_func *)ctx_hook)(prev, query_tracker(prev),
query_tracker(current));
}
refcount_dec(&usage);
preempt_enable();
end:
return 0;
}
static struct kretprobe krp_post = { .handler = context_switch_handler,
.entry_handler =
context_switch_entry_handler,
.data_size = sizeof(struct task_struct *),
.maxactive = 8 + 1 };
/**
* TODO - Redefine
* This is an unsafe way to set the callback
*/
void set_hook_callback(ctx_func *hook)
{
if (!hook) {
pr_warn("Hook is NULL. Setting default callback\n");
ctx_hook = (unsigned long *)dummy_ctx_func;
} else {
ctx_hook = (unsigned long *)hook;
}
}
EXPORT_SYMBOL(set_hook_callback);
static __init int switch_hook_module_init(void)
{
int err = 0;
/* Hook post function */
krp_post.kp.symbol_name = SWITCH_POST_FUNC;
err = register_kretprobe(&krp_post);
if (err) {
pr_warn("Cannot hook post function - ERR_CODE: %d\n", err);
goto post_err;
};
err = tracker_init();
if (err) {
pr_warn("Cannot init tracker - ERR_CODE: %d\n", err);
goto tracker_err;
};
shook_initialized = 1;
pr_info("Module loaded\n");
return 0;
tracker_err:
unregister_kretprobe(&krp_post);
post_err:
return err;
}
static void __exit switch_hook_module_exit(void)
{
if (!shook_initialized) {
pr_err("Whoops. The system is not hooked\n");
return;
}
if (!refcount_inc_not_zero(&unmounting)) {
pr_err("Whoops. Module exit already called\n");
return;
}
shook_enabled = 0;
/*
* Grace period. This may waste several cpu cycles,
* but it will complete eventually.
*/
while (refcount_read(&usage) > 1)
;
tracker_fini();
unregister_kretprobe(&krp_post);
/* nmissed > 0 suggests that maxactive was set too low. */
if (krp_post.nmissed)
pr_info("Missed %u invocations\n", krp_post.nmissed);
shook_initialized = 0;
pr_info("Module unloaded\n");
}
module_init(switch_hook_module_init);
module_exit(switch_hook_module_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Stefano Carna'");