-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtestmod.c
314 lines (254 loc) · 6.74 KB
/
testmod.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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/unistd.h>
#include <asm/cacheflush.h>
#include <asm/page.h>
#include <asm/current.h>
#include <linux/sched.h>
#include <linux/syscalls.h>
#include <linux/proc_fs.h>
#include <asm/uaccess.h>
#include <linux/namei.h>
#include <linux/kallsyms.h>
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/fcntl.h>
#include <linux/version.h>
#include <linux/slab.h>
/*
* This kernel module tries to intercept the 'open'
* system call and tries to deny access to 'filename'
* defined below.
* This 'filename' is a configuration parameter and
* it can be passed to 'insmod' while loading
* the module.
* Please refer to the README in this source tree
* for more information on how to specify configuration
* parameter during 'insmod'.
*/
#define KMALLOC kmalloc
#define KFREE kfree
#define FILENAME "/file1"
#define VERSION_PATH "/proc/version"
#define SYSMAP_PATH "/boot/System.map-"
#define MODULE_PARAM module_param
#define MAXPATHLEN 256
#define BUFSIZE 1024
#define MAXLEN 256
/*
* By default (when no configuration parameter is specified
* along with 'insmod'), the file name is "/file1".
*/
char *filename = FILENAME;
MODULE_PARAM(filename, charp, S_IRUSR | S_IWUSR);
MODULE_PARM_DESC(filename, "File name used to prevent access.");
/*
* Pointer to the system call table.
* It is obtained dynamically in get_syscall_table_addr().
*/
unsigned long *syscall_table = NULL;
/*
* Pointer to the original open system call funcion.
*/
asmlinkage int (*original_open)(const char __user *filename, int flags, int mode);
/*
* This function is called when 'open' system call is executed.
* It first gets the inode pointers corresponding to the input file
* path (on which 'open' is called) and the 'filename' using
* 'kernel_path()' function.
* After comparing the two inode pointers, if they turn out to be
* same, then -EACCES is returned.
* If they don't match, then original system call function is called.
*/
asmlinkage int open_wrapper(const char __user *fname, int flags, int mode)
{
struct inode *ip, *target_ip;
struct path pth1, pth2;
mm_segment_t fs;
int error;
error = kern_path(fname, LOOKUP_FOLLOW, &pth1);
if (!error) {
ip = pth1.dentry->d_inode;
fs = get_fs();
set_fs(get_fs());
error = kern_path(filename, LOOKUP_FOLLOW | LOOKUP_REVAL, &pth2);
set_fs(fs);
if (!error) {
target_ip = pth2.dentry->d_inode;
if (ip == target_ip) {
return -EACCES;
}
}
}
return (*original_open)(fname, flags, mode);
}
/*
* In order to get the system call table address,
* we need to first get the version number from
* '/proc/version' file.
* The below function reads this file using 'vfs_read()'
* and returns the version string to the caller.
*/
char *
read_version_file(
char *buf)
{
struct file *fp = NULL;
char *version;
fp = filp_open(VERSION_PATH, O_RDONLY, 0);
if (fp == NULL || IS_ERR(fp)) {
return NULL;
}
memset(buf, 0, MAXPATHLEN);
vfs_read(fp, buf, 256, &fp->f_pos);
version = strsep(&buf, " ");
version = strsep(&buf, " ");
version = strsep(&buf, " ");
filp_close(fp, 0);
return version;
}
/*
* The following function reads the system map file
* ('/boot/System.map-<version>) to get the address
* of 'sys_call_table' global.
* The input is kernel version string obtained from
* 'read_version_file()'.
* It first opens the system map file using 'filp_open()'
* and reads it line-by-line.
* Each line is checked whether it contains
* 'sys_call_table'. Once it is found, it's converted
* to unsigned long type and assigned to 'syscall_table'.
*
* This 'syscall_table' will be further used to index to
* according to system call number (e.g. __NR_open).
*/
static int
read_sysmap_file(
char *version)
{
unsigned long var;
struct file *fp = NULL;
char *buf = NULL, *str = NULL;
char *fname = NULL, *ptr;
int i = 0;
buf = KMALLOC(BUFSIZE, GFP_KERNEL);
if (buf == NULL) {
return -1;
}
fname = KMALLOC(strlen(version) + strlen(SYSMAP_PATH) + 1, GFP_KERNEL);
if (fname == NULL) {
KFREE(buf);
return -1;
}
memset(fname, 0, strlen(version) + strlen(SYSMAP_PATH) + 1);
strncpy(fname, SYSMAP_PATH, strlen(SYSMAP_PATH));
strncat(fname, version, strlen(version));
fp = filp_open(fname, O_RDONLY, 0);
if (fp == NULL || IS_ERR(fp)) {
KFREE(buf);
KFREE(fname);
return -1;
}
memset(buf, 0x0, BUFSIZE);
ptr = buf;
while (vfs_read(fp, ptr + i, 1, &fp->f_pos) == 1) {
if (ptr[i] == '\n' || i == 255) {
i = 0;
if (strstr(ptr, "sys_call_table") != NULL) {
str = KMALLOC(MAXLEN, GFP_KERNEL);
if (str == NULL) {
KFREE(buf);
KFREE(fname);
filp_close(fp, 0);
return -1;
}
memset(str, 0, MAXLEN);
strncpy(str, strsep(&ptr, " "), MAXLEN);
kstrtoul(str, 16, &var);
syscall_table = (unsigned long *) var;
break;
}
memset(buf, 0, MAXLEN);
continue;
}
i++;
}
KFREE(buf);
KFREE(fname);
KFREE(str);
filp_close(fp, 0);
return 0;
}
static int
get_syscall_table_addr(void)
{
char *buf = NULL, *ver;
buf = KMALLOC(MAXPATHLEN, GFP_KERNEL);
if (buf == NULL) {
return -1;
}
if ((ver = read_version_file(buf)) == NULL) {
KFREE(buf);
return -1;
}
if (read_sysmap_file(ver) != 0) {
KFREE(buf);
return -1;
}
KFREE(buf);
return 0;
}
/*
* This is module entry function which is executed
* when a module is loaded.
* It first changes the permission to write permission
* to the kernel address space using 'write_cr0()',
* so that we can make changes to the memory space of
* 'sys_call_table' for the purpose of changing the
* entry point of 'open' system call.
* It undoes this operation at the end phase of module
* loading.
* We also need to change the content of one register
* to enable us accessing kernel addresses in the
* routines which expect user addresses (e.g. vfs_read)
* using set_fs().
*/
static int modinit(void)
{
int error = 0;
mm_segment_t fs;
printk(KERN_ALERT "New module loading..\n");
write_cr0(read_cr0() & (~0x10000));
fs = get_fs();
set_fs(KERNEL_DS);
if((error = get_syscall_table_addr()) != 0) {
set_fs(fs);
write_cr0(read_cr0() | 0x10000);
return error;
}
original_open = (void *)syscall_table[__NR_open];
syscall_table[__NR_open] = open_wrapper;
set_fs(fs);
write_cr0(read_cr0() | 0x10000);
return 0;
}
/*
* Module unload function.
* It undoes all the operations performed by module load
* function (modinit()).
* It restores the original 'open' system call function
* in 'sys_call_table'.
*/
static void modexit(void)
{
write_cr0(read_cr0() & (~0x10000));
syscall_table[__NR_open] = original_open;
write_cr0(read_cr0() | 0x10000);
printk(KERN_ALERT "Module exiting..\n");
return;
}
module_init(modinit);
module_exit(modexit);