├── .gitignore ├── .indent.pro ├── Makefile ├── README.md ├── descriptor.c ├── descriptor.h ├── handlers.c ├── handlers.h ├── ldpreload.c ├── main.c ├── main.h ├── nl_stub.c ├── nl_stub.h ├── process.c ├── process.h ├── syscalls.c ├── syscalls.h ├── test.c ├── tracer.c └── tracer.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.[oais] 2 | /nltrace 3 | *~ 4 | /.idea 5 | /preload.so 6 | -------------------------------------------------------------------------------- /.indent.pro: -------------------------------------------------------------------------------- 1 | -bli0 2 | -npsl 3 | -cli0 4 | -l160 5 | -nut 6 | -TBITARRAY_TYPE 7 | -Targs_t 8 | -Tsocklen_t 9 | -TFILE 10 | -Tsize_t 11 | -Tssize_t 12 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TARGETS += nltrace 2 | TARGETS += preload.so 3 | 4 | all: $(TARGETS) 5 | 6 | CFLAGS += -D_GNU_SOURCE -Wall -Wextra -I/usr/include/libnl3 -g -fPIC 7 | LFLAGS += -lnl-3 8 | LFLAGS += -lnl-route-3 9 | LFLAGS += -lnl-genl-3 10 | LFLAGS += -lnl-nf-3 11 | 12 | .c.o: 13 | $(CC) $(CFLAGS) -c $< -o $@ 14 | 15 | COMMON_SRC += process.o 16 | COMMON_SRC += nl_stub.o 17 | COMMON_SRC += handlers.o 18 | COMMON_SRC += descriptor.o 19 | 20 | NLTRACE_SRC = $(COMMON_SRC) 21 | NLTRACE_SRC += main.o 22 | NLTRACE_SRC += syscalls.o 23 | NLTRACE_SRC += tracer.o 24 | 25 | PRELOAD_LFLAGS = $(LFLAGS) 26 | PRELOAD_LFLAGS += -ldl -shared 27 | PRELOAD_SRC = $(COMMON_SRC) 28 | PRELOAD_SRC += ldpreload.o 29 | 30 | 31 | nltrace: $(NLTRACE_SRC) 32 | $(CC) $(NLTRACE_SRC) $(LFLAGS) -o $@ 33 | 34 | preload.so: $(PRELOAD_SRC) 35 | $(CC) $(PRELOAD_SRC) $(PRELOAD_LFLAGS) -o $@ 36 | 37 | indent: 38 | indent *.c *.h 39 | 40 | clean: 41 | rm -f *.[oais] *~ $(TARGETS) 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | nltrace 2 | ======= 3 | 4 | nltrace -------------------------------------------------------------------------------- /descriptor.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include /* pid_t */ 3 | #include /* malloc */ 4 | #include /* perror */ 5 | #include /* strcmp */ 6 | #include /* AF_NETLINK */ 7 | #include /* readlink */ 8 | #include 9 | 10 | #include "descriptor.h" 11 | #include "nl_stub.h" 12 | 13 | struct descriptor 14 | { 15 | int fd; /* should be the first member - primary key */ 16 | int family; 17 | int protocol; 18 | }; 19 | 20 | static int _compare_descriptors (const struct descriptor *a, const struct descriptor *b) 21 | { 22 | /* Note: void pointer may point to either struct descriptor or it's primary key (int fd) */ 23 | pid_t pa = a->fd; 24 | pid_t pb = b->fd; 25 | 26 | if (pa == pb) 27 | return 0; 28 | else 29 | return (pa > pb) ? 1 : -1; 30 | } 31 | 32 | int compare_descriptors (const void *a, const void *b) 33 | { 34 | /* Note: void pointer may point to either struct descriptor or it's primary key (int fd) */ 35 | return _compare_descriptors ((const struct descriptor *) a, (const struct descriptor *) b); 36 | } 37 | 38 | struct descriptor *descriptor_alloc (int fd, int family, int protocol) 39 | { 40 | struct descriptor *descriptor; 41 | 42 | if (!(descriptor = malloc (sizeof (*descriptor)))) 43 | { 44 | perror ("malloc"); 45 | return NULL; 46 | } 47 | 48 | descriptor->fd = fd; 49 | descriptor->family = family; 50 | descriptor->protocol = protocol; 51 | return descriptor; 52 | } 53 | 54 | void descriptor_destroy (struct descriptor *descriptor) 55 | { 56 | fprintf (stderr, "Destroying descriptor fd %d\n", descriptor->fd); 57 | free (descriptor); 58 | } 59 | 60 | /*. 61 | returns: 62 | -1 on error 63 | 0 if given inode is not netlink socket 64 | 1 if given inode is netlink socket, writes its protocol to ret_protocol 65 | */ 66 | static int scan_proc_net_netlink (unsigned long inode, int *ret_protocol) 67 | { 68 | FILE *netlink; 69 | char result[256]; 70 | /* http://lxr.linux.no/linux+v3.8.8/net/netlink/af_netlink.c#L2055 */ 71 | static const char *header = "sk Eth Pid Groups Rmem Wmem Dump Locks Drops Inode\n"; 72 | int protocol; 73 | unsigned long scanned_inode; 74 | int retval = -1; 75 | 76 | 77 | if (!(netlink = fopen ("/proc/net/netlink", "rte"))) 78 | { 79 | perror ("Opening /proc/net/netlink"); 80 | goto end; 81 | } 82 | 83 | if (!fgets (result, sizeof (result), netlink)) 84 | { 85 | fprintf (stderr, "Error reading header of /proc/net/netlink\n"); 86 | goto end; 87 | } 88 | 89 | if (strcmp (result, header)) 90 | { 91 | fprintf (stderr, "header on your kernel does not match ours we expect!:\n" /* */ 92 | " our:%s" /* */ 93 | "your:%s", /* */ 94 | header, result); 95 | goto end; 96 | } 97 | 98 | while (fgets (result, sizeof (result), netlink)) 99 | { 100 | 101 | if (strlen (result) == sizeof (result) - 1) 102 | { 103 | fprintf (stderr, "/proc/net/netlink line truncated\n"); 104 | goto end; 105 | } 106 | 107 | // TODO: fscanf? 108 | // 0000000000000000 0 4195573 00000000 0 0 0000000000000000 2 0 8636 109 | if (sscanf (result, "%*s %d %*s %*s %*s %*s %*s %*s %*s %lu", &protocol, &scanned_inode) != 2) 110 | { 111 | fprintf (stderr, "Can not parse string %s\n", result); 112 | goto end; 113 | } 114 | 115 | if (inode != scanned_inode) 116 | continue; 117 | 118 | fprintf (stderr, "Found that socket inode %lu is the netlink socket of protocol %d\n", inode, protocol); 119 | *ret_protocol = protocol; 120 | retval = 1; 121 | goto end; 122 | } 123 | 124 | fprintf (stderr, "Found that socket inode %lu is not netlink socket\n", inode); 125 | retval = 0; 126 | 127 | end: 128 | if (netlink) 129 | fclose (netlink); 130 | return retval; 131 | } 132 | 133 | 134 | 135 | struct descriptor *descriptor_alloc_detect_proc (int fd, pid_t pid) 136 | { 137 | unsigned long inode; 138 | int tmp; 139 | 140 | char path[128]; 141 | char description[256]; 142 | 143 | 144 | tmp = snprintf (path, sizeof (path), "/proc/%u/fd/%d", (unsigned int) pid, fd); 145 | 146 | if (tmp <= 0) 147 | { 148 | perror ("sprintf of path"); 149 | return NULL; 150 | } 151 | 152 | if (tmp >= (int) (sizeof (path))) 153 | { 154 | fprintf (stderr, "printf buffer overflow\n"); 155 | return NULL; 156 | } 157 | 158 | if (readlink (path, description, sizeof (description)) == -1) 159 | { 160 | perror ("readlink"); 161 | // TODO: if very long link value => non-netlnik 162 | return NULL; 163 | } 164 | 165 | /* this is not socket at all */ 166 | if (sscanf (description, "socket:[%lu]", &inode) != 1) 167 | return descriptor_alloc (fd, AF_UNSPEC, -1); 168 | 169 | int protocol; 170 | if ((tmp = scan_proc_net_netlink (inode, &protocol)) == -1) 171 | { 172 | fprintf (stderr, "/proc/net/... scanning failed\n"); 173 | return NULL; 174 | } 175 | 176 | if (!tmp) 177 | return descriptor_alloc (fd, AF_UNSPEC, -1); 178 | 179 | return descriptor_alloc (fd, AF_NETLINK, protocol); 180 | } 181 | 182 | struct descriptor *descriptor_alloc_detect_live (int fd) 183 | { 184 | struct sockaddr addr; 185 | socklen_t addrlen = sizeof (addr); 186 | 187 | 188 | if (getsockname (fd, &addr, &addrlen) == -1) 189 | { 190 | int error = errno; 191 | switch (error) 192 | { 193 | case EBADF: 194 | fprintf (stderr, "getsockname(%d) returns EBADF\n", fd); 195 | return NULL; 196 | case ENOTSOCK: 197 | return descriptor_alloc (fd, AF_UNSPEC, -1); 198 | } 199 | 200 | fprintf (stderr, "getsockname returns %d\n", error); 201 | return NULL; 202 | } 203 | 204 | if (addr.sa_family != AF_NETLINK) 205 | return descriptor_alloc (fd, AF_UNSPEC, -1); 206 | 207 | int proto; 208 | addrlen = sizeof (proto); 209 | 210 | if (getsockopt (fd, SOL_SOCKET, SO_PROTOCOL, &proto, &addrlen) == -1) 211 | { 212 | fprintf (stderr, "getsockopt for socket does not work...\n"); 213 | return NULL; 214 | } 215 | 216 | return descriptor_alloc (fd, AF_NETLINK, proto); 217 | } 218 | 219 | 220 | 221 | int descriptor_get_family (const struct descriptor *descriptor) 222 | { 223 | return descriptor->family; 224 | } 225 | 226 | int descriptor_get_protocol (const struct descriptor *descriptor) 227 | { 228 | return descriptor->protocol; 229 | } 230 | 231 | 232 | 233 | void descriptor_handle_send (struct descriptor *descriptor, unsigned char *data, size_t length) 234 | { 235 | if (descriptor->family != AF_NETLINK) 236 | return; 237 | 238 | fprintf (stderr, "netlink send(%d):\n", descriptor->fd); 239 | handle_netlink_data (descriptor->protocol, data, length); 240 | } 241 | 242 | void descriptor_handle_recv (struct descriptor *descriptor, unsigned char *data, size_t length) 243 | { 244 | if (descriptor->family != AF_NETLINK) 245 | return; 246 | 247 | fprintf (stderr, "netlink recv(%d):\n", descriptor->fd); 248 | handle_netlink_data (descriptor->protocol, data, length); 249 | } 250 | -------------------------------------------------------------------------------- /descriptor.h: -------------------------------------------------------------------------------- 1 | #ifndef NLTRACE_DESCRIPTOR_H 2 | #define NLTRACE_DESCRIPTOR_H 3 | struct descriptor; 4 | 5 | int compare_descriptors (const void *a, const void *b); 6 | 7 | struct descriptor *descriptor_alloc (int fd, int family, int protocol); 8 | struct descriptor *descriptor_alloc_detect_proc (int fd, pid_t pid); 9 | struct descriptor *descriptor_alloc_detect_live (int fd); 10 | 11 | void descriptor_destroy (struct descriptor *descriptor); 12 | int descriptor_get_family (const struct descriptor *descriptor); 13 | int descriptor_get_protocol (const struct descriptor *descriptor); 14 | 15 | void descriptor_handle_send (struct descriptor *descriptor, unsigned char *data, size_t length); 16 | void descriptor_handle_recv (struct descriptor *descriptor, unsigned char *data, size_t length); 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /handlers.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include /* For SYS_xxx definitions */ 11 | 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "syscalls.h" 20 | #include "descriptor.h" 21 | #include "process.h" 22 | #include "handlers.h" 23 | 24 | #define UNUSED __attribute__((unused)) 25 | 26 | void handle_socket (struct process *process, int ret, int domain, int type UNUSED, int protocol) 27 | { 28 | if (ret < 0) 29 | return; 30 | 31 | struct descriptor *descriptor; 32 | 33 | if (!(descriptor = descriptor_alloc (ret, domain, protocol))) 34 | { 35 | fprintf (stderr, "descriptor allocation failed\n"); 36 | return; 37 | } 38 | 39 | if (process_add_descriptor (process, descriptor) == -1) 40 | { 41 | fprintf (stderr, "Cannot add descriptor to process\n"); 42 | descriptor_destroy (descriptor); 43 | return; 44 | } 45 | 46 | } 47 | 48 | static struct descriptor *get_netlink_descriptor (struct process *process, int fd) 49 | { 50 | 51 | struct descriptor *descriptor; 52 | 53 | if (!(descriptor = process_get_descriptor (process, fd))) 54 | { 55 | fprintf (stderr, "Cannot get descriptor from process\n"); 56 | return NULL; 57 | } 58 | 59 | if (descriptor_get_family (descriptor) != AF_NETLINK) 60 | return NULL; 61 | 62 | return descriptor; 63 | 64 | } 65 | 66 | void handle_sendto (struct process *process, ssize_t ret, int sockfd, const char *buf, size_t buflen, int flags, const struct sockaddr *dest_addr, 67 | socklen_t addrlen) 68 | { 69 | 70 | (void) buf; 71 | (void) buflen; 72 | (void) dest_addr; 73 | (void) addrlen; 74 | (void) flags; 75 | 76 | struct descriptor *descriptor; 77 | 78 | if (!(descriptor = get_netlink_descriptor (process, sockfd))) 79 | return; 80 | 81 | if (ret < 0) 82 | { 83 | // TODO: do not parse if EINVAL or so 84 | fprintf (stderr, "sendto() failed\n"); 85 | } 86 | else if ((size_t) ret != buflen) 87 | { 88 | fprintf (stderr, "Warning sent only part of the message\n"); 89 | } 90 | 91 | unsigned char *data = NULL; 92 | 93 | if (!(data = process_dup_memory (process, buf, buflen))) 94 | { 95 | perror ("malloc"); 96 | return; 97 | } 98 | 99 | descriptor_handle_send (descriptor, data, buflen); 100 | } 101 | 102 | 103 | void handle_recvfrom (struct process *process, ssize_t ret, int sockfd, const char *buf, size_t buflen, int flags, const struct sockaddr *src_addr, 104 | const socklen_t *addrlen) 105 | { 106 | if (ret < 0) 107 | return; 108 | 109 | 110 | (void) buf; 111 | (void) buflen; 112 | (void) flags; 113 | (void) src_addr; 114 | (void) addrlen; 115 | 116 | struct descriptor *descriptor; 117 | 118 | if (!(descriptor = get_netlink_descriptor (process, sockfd))) 119 | return; 120 | 121 | 122 | if (ret == 0) 123 | { 124 | fprintf (stderr, "netlink recv returns zero...\n"); 125 | return; 126 | } 127 | 128 | unsigned char *data = NULL; 129 | 130 | if (!(data = process_dup_memory (process, buf, ret))) 131 | { 132 | perror ("malloc"); 133 | return; 134 | } 135 | 136 | descriptor_handle_recv (descriptor, data, ret); 137 | } 138 | 139 | 140 | static int handle_msg (void *data, size_t datalen, const struct process *process, const struct msghdr *msg) 141 | { 142 | const struct iovec *msg_iov; 143 | size_t msg_iovlen; 144 | 145 | 146 | if (process_memcpy (process, &msg_iov, &msg->msg_iov, sizeof (msg_iov)) == -1) 147 | { 148 | fprintf (stderr, "process_memcpy() [recvmsg1] failed\n"); 149 | return -1; 150 | } 151 | 152 | if (process_memcpy (process, &msg_iovlen, &msg->msg_iovlen, sizeof (msg_iovlen)) == -1) 153 | { 154 | fprintf (stderr, "process_memcpy() [recvmsg2] failed\n"); 155 | return -1; 156 | } 157 | 158 | // fprintf (stderr, "datalen=%ld, msg_iov=%p, iov_len=%lu\n", (long) datalen, msg_iov, (unsigned long) msg_iovlen); 159 | 160 | for (; msg_iovlen && datalen; msg_iovlen--, msg_iov++) 161 | { 162 | void *iov_base; 163 | size_t iov_len; 164 | 165 | // fprintf (stderr, "RECVMSG: begin of copying iovec fields\n"); 166 | 167 | if (process_memcpy (process, &iov_base, &msg_iov->iov_base, sizeof (iov_base)) == -1) 168 | { 169 | fprintf (stderr, "process_memcpy() [recvmsg3] failed\n"); 170 | return -1; 171 | } 172 | 173 | if (process_memcpy (process, &iov_len, &msg_iov->iov_len, sizeof (iov_len)) == -1) 174 | { 175 | fprintf (stderr, "process_memcpy() [recvmsg4] failed\n"); 176 | return -1; 177 | } 178 | 179 | // fprintf (stderr, "RECVMSG: Starting to copy iovec data: %p, length=%lu\n", iov_base, (unsigned long) iov_len); 180 | 181 | if (iov_len > datalen) 182 | { 183 | // fprintf (stderr, "IOV was TOO LONG...\n"); 184 | iov_len = datalen; 185 | } 186 | 187 | if (process_memcpy (process, data, iov_base, iov_len) == -1) 188 | { 189 | fprintf (stderr, "process_memcpy() [recvmsg5] failed\n"); 190 | return -1; 191 | } 192 | 193 | data += iov_len; 194 | datalen -= iov_len; 195 | } 196 | 197 | if (msg_iovlen) 198 | { 199 | fprintf (stderr, "not all IOVs was read\n"); 200 | } 201 | 202 | if (datalen) 203 | { 204 | fprintf (stderr, "not all data was filled\n"); 205 | } 206 | 207 | return 0; 208 | } 209 | 210 | 211 | void handle_recvmsg (struct process *process, ssize_t ret, int sockfd, struct msghdr *msg, int flags) 212 | { 213 | if (ret < 0) 214 | return; 215 | 216 | (void) msg; 217 | (void) flags; 218 | 219 | struct descriptor *descriptor; 220 | 221 | if (!(descriptor = get_netlink_descriptor (process, sockfd))) 222 | return; 223 | 224 | unsigned char *data = NULL; 225 | 226 | if (!(data = malloc (ret))) 227 | { 228 | perror ("malloc"); 229 | goto end; 230 | } 231 | 232 | if (handle_msg (data, ret, process, msg) == -1) 233 | { 234 | fprintf (stderr, "handle_msg FAILED\n"); 235 | goto end; 236 | } 237 | 238 | descriptor_handle_recv (descriptor, data, ret); 239 | /* was already handled/freed */ 240 | data = NULL; 241 | 242 | end: 243 | free (data); 244 | } 245 | 246 | void handle_sendmsg (struct process *process, ssize_t ret, int sockfd, const struct msghdr *msg, int flags) 247 | { 248 | if (ret < 0) 249 | return; 250 | 251 | (void) msg; 252 | (void) flags; 253 | 254 | struct descriptor *descriptor; 255 | 256 | if (!(descriptor = get_netlink_descriptor (process, sockfd))) 257 | return; 258 | 259 | unsigned char *data = NULL; 260 | 261 | // TODO: we must detect total length of original message (!) 262 | if (!(data = malloc (ret))) 263 | { 264 | perror ("malloc"); 265 | goto end; 266 | } 267 | 268 | if (handle_msg (data, ret, process, msg) == -1) 269 | { 270 | fprintf (stderr, "handle_msg FAILED\n"); 271 | goto end; 272 | } 273 | 274 | descriptor_handle_send (descriptor, data, ret); 275 | /* was already handled/freed */ 276 | data = NULL; 277 | end: 278 | free (data); 279 | 280 | } 281 | 282 | 283 | void handle_close (struct process *process, int ret, int fd) 284 | { 285 | if (ret < 0) 286 | return; 287 | 288 | //will not fail if no such descriptor... 289 | process_delete_descriptor_int (process, fd); 290 | } 291 | 292 | void handle_dup2 (struct process *process, int ret, int oldfd, int newfd) 293 | { 294 | if (ret < 0) 295 | return; 296 | 297 | struct descriptor *olddescriptor, *newdescriptor; 298 | 299 | if (!(olddescriptor = process_get_descriptor (process, oldfd))) 300 | { 301 | fprintf (stderr, "Cannot get descriptor from process\n"); 302 | return; 303 | } 304 | 305 | //will not fail if no such descriptor... 306 | process_delete_descriptor_int (process, newfd); 307 | 308 | if (!(newdescriptor = descriptor_alloc (newfd, descriptor_get_family (olddescriptor), descriptor_get_protocol (olddescriptor)))) 309 | { 310 | fprintf (stderr, "cannot allocate new descriptor in dup2\n"); 311 | return; 312 | } 313 | 314 | if (!process_add_descriptor (process, newdescriptor)) 315 | { 316 | fprintf (stderr, "cannot add duplicated descriptor to process\n"); 317 | return; 318 | } 319 | } 320 | 321 | void handle_dup (struct process *process, int ret, int oldfd) 322 | { 323 | handle_dup2 (process, ret, oldfd, ret); 324 | } 325 | 326 | void handle_dup3 (struct process *process, int ret, int oldfd, int newfd, int flags) 327 | { 328 | (void) flags; 329 | handle_dup2 (process, ret, oldfd, newfd); 330 | } 331 | -------------------------------------------------------------------------------- /handlers.h: -------------------------------------------------------------------------------- 1 | #ifndef NLTRACE_HANDLERS_H 2 | #define NLTRACE_HANDLERS_H 3 | 4 | #include 5 | #include 6 | 7 | struct msghdr; 8 | struct process; 9 | 10 | void handle_socket (struct process *process, int ret, int domain, int type, int protocol); 11 | void handle_sendto (struct process *process, ssize_t ret, int sockfd, const char *buf, size_t buflen, int flags, const struct sockaddr *dest_addr, 12 | socklen_t addrlen); 13 | void handle_recvfrom (struct process *process, ssize_t ret, int sockfd, const char *buf, size_t buflen, int flags, const struct sockaddr *src_addr, 14 | const socklen_t *addrlen); 15 | void handle_close (struct process *process, int ret, int fd); 16 | 17 | 18 | void handle_dup (struct process *process, int ret, int oldfd); 19 | void handle_dup2 (struct process *process, int ret, int oldfd, int newfd); 20 | void handle_dup3 (struct process *process, int ret, int oldfd, int newfd, int flags); 21 | 22 | 23 | void handle_recvmsg (struct process *process, ssize_t ret, int sockfd, struct msghdr *msg, int flags); 24 | void handle_sendmsg (struct process *process, ssize_t ret, int sockfd, const struct msghdr *msg, int flags); 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /ldpreload.c: -------------------------------------------------------------------------------- 1 | #include /* dlsym */ 2 | #include /* abort */ 3 | #include /* fprintf */ 4 | #include /* socklen_t */ 5 | 6 | #include "process.h" 7 | #include "handlers.h" 8 | 9 | #define QWE(name) \ 10 | static typeof (&name) orig_ ## name; \ 11 | if (!orig_ ## name && !(orig_ ## name = (typeof (&name)) dlsym (RTLD_NEXT, #name))) { \ 12 | abort(); \ 13 | } 14 | 15 | static struct process *process; 16 | 17 | static void myinit (void) __attribute__ ((constructor)); 18 | void myinit (void) 19 | { 20 | if (!(process = process_alloc (0))) 21 | abort (); 22 | } 23 | 24 | int socket (int domain, int type, int protocol) 25 | { 26 | int ret; 27 | QWE (socket); 28 | ret = orig_socket (domain, type, protocol); 29 | handle_socket (process, ret, domain, type, protocol); 30 | return ret; 31 | } 32 | 33 | ssize_t send (int sockfd, const void *buf, size_t len, int flags) 34 | { 35 | ssize_t ret; 36 | QWE (send); 37 | ret = orig_send (sockfd, buf, len, flags); 38 | handle_sendto (process, ret, sockfd, buf, len, flags, NULL, 0); 39 | return ret; 40 | 41 | } 42 | 43 | ssize_t sendto (int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen) 44 | { 45 | ssize_t ret; 46 | QWE (sendto); 47 | ret = orig_sendto (sockfd, buf, len, flags, dest_addr, addrlen); 48 | handle_sendto (process, ret, sockfd, buf, len, flags, dest_addr, addrlen); 49 | return ret; 50 | 51 | } 52 | 53 | ssize_t sendmsg (int sockfd, const struct msghdr *msg, int flags) 54 | { 55 | ssize_t ret; 56 | QWE (sendmsg); 57 | ret = orig_sendmsg (sockfd, msg, flags); 58 | handle_sendmsg (process, ret, sockfd, msg, flags); 59 | return ret; 60 | } 61 | 62 | ssize_t recv (int sockfd, void *buf, size_t len, int flags) 63 | { 64 | ssize_t ret; 65 | QWE (recv); 66 | ret = orig_recv (sockfd, buf, len, flags); 67 | handle_recvfrom (process, ret, sockfd, buf, len, flags, 0, NULL); 68 | return ret; 69 | } 70 | 71 | ssize_t recvfrom (int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen) 72 | { 73 | ssize_t ret; 74 | QWE (recvfrom); 75 | ret = orig_recvfrom (sockfd, buf, len, flags, src_addr, addrlen); 76 | handle_recvfrom (process, ret, sockfd, buf, len, flags, src_addr, addrlen); 77 | return ret; 78 | 79 | } 80 | 81 | ssize_t recvmsg (int sockfd, struct msghdr *msg, int flags) 82 | { 83 | ssize_t ret; 84 | QWE (recvmsg); 85 | ret = orig_recvmsg (sockfd, msg, flags); 86 | handle_recvmsg (process, ret, sockfd, msg, flags); 87 | return ret; 88 | } 89 | 90 | int close (int fd) 91 | { 92 | int ret; 93 | QWE (close); 94 | ret = orig_close (fd); 95 | handle_close (process, ret, fd); 96 | return ret; 97 | } 98 | 99 | int dup (int oldfd) 100 | { 101 | int ret; 102 | QWE (dup); 103 | ret = orig_dup (oldfd); 104 | handle_dup (process, ret, oldfd); 105 | return ret; 106 | } 107 | 108 | int dup2 (int oldfd, int newfd) 109 | { 110 | int ret; 111 | QWE (dup2); 112 | ret = orig_dup2 (oldfd, newfd); 113 | handle_dup2 (process, ret, oldfd, newfd); 114 | return ret; 115 | } 116 | 117 | int dup3 (int oldfd, int newfd, int flags) 118 | { 119 | int ret; 120 | QWE (dup3); 121 | ret = orig_dup3 (oldfd, newfd, flags); 122 | handle_dup3 (process, ret, oldfd, newfd, flags); 123 | return ret; 124 | } 125 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include /* memcpy */ 9 | 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include /* NT_PRSTATUS */ 17 | 18 | #include "syscalls.h" 19 | #include "tracer.h" 20 | #include "process.h" 21 | #include "main.h" 22 | 23 | static int wait_for_stopped (pid_t pid, bool sysgood, int *retstatus) 24 | { 25 | int status; 26 | 27 | for (;;) 28 | { 29 | 30 | if (waitpid (pid, &status, 0) == -1) 31 | { 32 | perror ("waitpid failed"); 33 | return -1; //FAIL 34 | } 35 | 36 | if (WIFEXITED (status)) 37 | { 38 | *retstatus = status; 39 | return -2; // FAIL 40 | } 41 | 42 | if (!WIFSTOPPED (status)) 43 | continue; 44 | 45 | if ((WSTOPSIG (status) & 0x7F) != SIGTRAP) 46 | continue; 47 | 48 | if (sysgood && !(WSTOPSIG (status) & 0x80)) 49 | continue; 50 | 51 | break; 52 | } 53 | 54 | return 0; // OK 55 | } 56 | 57 | static int wait_for_break (pid_t pid, struct user_regs_struct *state, int *status) 58 | { 59 | int ret; 60 | 61 | if (ptrace (PTRACE_SYSCALL, pid, 0, 0) == -1) 62 | return -1; // FAIL 63 | 64 | if ((ret = wait_for_stopped (pid, true, status))) 65 | return ret; 66 | 67 | #ifdef __aarch64__ 68 | struct iovec io; 69 | io.iov_base = state; 70 | io.iov_len = sizeof(*state); 71 | 72 | if (ptrace (PTRACE_GETREGSET, pid, NT_PRSTATUS, &io) == -1) 73 | return -1; 74 | 75 | #else 76 | if (ptrace (PTRACE_GETREGS, pid, 0, state) == -1) 77 | return -1; 78 | #endif 79 | return 0; 80 | } 81 | 82 | 83 | int ptrace_memcpy (pid_t pid, void *destination, const void *usermem, size_t length) 84 | { 85 | size_t tmp = ((size_t) usermem) % sizeof (long); 86 | 87 | if (tmp) 88 | { 89 | size_t useful_bytes = sizeof (long) - tmp; 90 | 91 | // fprintf (stderr, "usermemcpy: copying unaligned start (%lu bytes) of user memory\n", (unsigned long) useful_bytes); 92 | long chunk; 93 | if ((chunk = ptrace (PTRACE_PEEKDATA, pid, (void *) (((size_t) usermem) / sizeof (long) * sizeof (long)), NULL)) == -1 && errno) 94 | { 95 | perror ("ptrace"); 96 | return -1; 97 | } 98 | 99 | memcpy (destination, ((char *) &chunk) + tmp, useful_bytes); 100 | usermem += useful_bytes; 101 | destination += useful_bytes; 102 | length -= useful_bytes; 103 | } 104 | 105 | // if (length >= sizeof (long)) 106 | // fprintf (stderr, "usermemcpy: copying aligned %lu bytes of memory\n", (unsigned long) (length / sizeof (long) * sizeof (long))); 107 | 108 | while (length >= sizeof (long)) 109 | { 110 | long chunk; 111 | if ((chunk = ptrace (PTRACE_PEEKDATA, pid, usermem, NULL)) == -1 && errno) 112 | { 113 | perror ("ptrace"); 114 | return -1; 115 | } 116 | *(long *) destination = chunk; 117 | destination += sizeof (long); 118 | usermem += sizeof (long); 119 | length -= sizeof (long); 120 | } 121 | 122 | if (length) 123 | { 124 | // fprintf (stderr, "usermemcpy: copying last %lu bytes from aligned user memory\n", (unsigned long) length); 125 | 126 | long chunk; 127 | 128 | if ((chunk = ptrace (PTRACE_PEEKDATA, pid, usermem, NULL)) == -1 && errno) 129 | { 130 | perror ("trace"); 131 | 132 | return -1; 133 | } 134 | memcpy (destination, &chunk, length); 135 | } 136 | 137 | return 0; 138 | } 139 | 140 | 141 | 142 | static void __attribute__ ((noreturn)) fork_and_trace_child (char **argv) 143 | { 144 | 145 | /* child here */ 146 | if (prctl (PR_SET_PDEATHSIG, SIGTERM, 0, 0, 0) == -1) 147 | _exit (1); 148 | 149 | if (getppid () <= 1) 150 | _exit (2); 151 | 152 | if (ptrace (PTRACE_TRACEME, 0, 0, 0) == -1) 153 | _exit (3); 154 | 155 | /* will cause this child to super-stop */ 156 | execvp (argv[1], argv + 1); 157 | _exit (6); 158 | } 159 | 160 | int main (int argc, char *argv[]) 161 | { 162 | pid_t pid; 163 | int status = 1; 164 | struct tracer *tracer = NULL; 165 | struct process *process = NULL; 166 | 167 | 168 | if (argc < 2) 169 | return 1; 170 | 171 | pid = fork (); 172 | 173 | if (pid < 0) 174 | { 175 | perror ("fork"); 176 | return 2; 177 | } 178 | 179 | if (pid == 0) 180 | fork_and_trace_child (argv); 181 | 182 | if (wait_for_stopped (pid, false, &status)) 183 | { 184 | fprintf (stderr, "child process unexpectedly dead\n"); 185 | return 3; 186 | } 187 | 188 | /* 189 | When delivering syscall traps, set bit 7 in the signal number 190 | (i.e., deliver SIGTRAP | 0x80). This makes it easy for the tracer 191 | to tell the difference between normal traps and those caused by a 192 | syscall. (PTRACE_O_TRACESYSGOOD may not work on all architectures.) 193 | */ 194 | if (ptrace (PTRACE_SETOPTIONS, pid, 0, PTRACE_O_TRACESYSGOOD) == -1) 195 | return 5; 196 | 197 | status = 1; 198 | 199 | if (!(tracer = tracer_alloc ())) 200 | { 201 | fprintf (stderr, "Can not allocate tracer\n"); 202 | goto end; 203 | } 204 | 205 | if (!(process = process_alloc (pid))) 206 | { 207 | fprintf (stderr, "Can not allocate process\n"); 208 | goto end; 209 | } 210 | 211 | if (tracer_add_process (tracer, process) == -1) 212 | { 213 | fprintf (stderr, "Cannot add process to tracer\n"); 214 | goto end; 215 | } 216 | 217 | /* process = NULL should be here (!) */ 218 | 219 | for (;;) 220 | { 221 | struct user_regs_struct state1; 222 | struct user_regs_struct state2; 223 | if (wait_for_break (pid, &state1, &status)) 224 | break; 225 | if (wait_for_break (pid, &state2, &status)) 226 | break; 227 | trace_syscall (process, &state1, &state2); 228 | } 229 | 230 | process = NULL; 231 | 232 | status &= 0xff; 233 | 234 | end: 235 | if (process) 236 | process_destroy (process); 237 | if (tracer) 238 | tracer_destroy (tracer); 239 | 240 | return status; 241 | } 242 | -------------------------------------------------------------------------------- /main.h: -------------------------------------------------------------------------------- 1 | #ifndef NLTRACE_MAIN_H 2 | #define NLTRACE_MAIN_H 3 | int ptrace_memcpy (pid_t pid, void *destination, const void *usermem, size_t length); 4 | #endif 5 | -------------------------------------------------------------------------------- /nl_stub.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | #include "nl_stub.h" 8 | 9 | ////////////////////////////////////// 10 | // THIS IS NEEDED TO PREVENT LIBRARIES TO BE OPTIMIZED_OUT 11 | // cache regitration is done in __init constructors of the libnl libraries. 12 | #include 13 | #include 14 | #include 15 | void *references[] = { 16 | rtnl_route_alloc_cache, 17 | nfnl_connect, 18 | genl_connect, 19 | }; 20 | 21 | static int message_dumper (struct nl_msg *msg, void *arg) 22 | { 23 | int protocol; 24 | protocol = *(int *) arg; 25 | fprintf (stderr, "Setting msg proto to %d\n", protocol); 26 | 27 | nlmsg_set_proto (msg, protocol); 28 | nl_msg_dump (msg, stderr); 29 | 30 | return NL_OK; 31 | } 32 | 33 | 34 | 35 | 36 | void handle_netlink_data (int protocol, unsigned char *data, size_t length) 37 | { 38 | 39 | struct nl_sock *sk = NULL; 40 | struct nl_cb *cb = NULL; 41 | 42 | /* Yes, closure (!) 43 | * We cannot "subclass" original socket to add new fields :( 44 | * */ 45 | int my_faked_recv (struct nl_sock *sk1 /* input */ , struct sockaddr_nl *addr1 /* output */ , unsigned char **buf1 /* output */ , 46 | struct ucred **cred1 /*output */ ) 47 | { 48 | 49 | (void) sk1; 50 | (void) addr1; 51 | (void) cred1; 52 | 53 | /* prevent nl_recvmsgs to read this buffer again and again. 54 | * Just simulate that last recv() return 0 :) 55 | * */ 56 | if (!data) 57 | return 0; 58 | 59 | *buf1 = data; 60 | data = NULL; 61 | return length; 62 | } 63 | 64 | // initialize all hooks to DEBUG variant... 65 | // cb->refcount will be 1 after that 66 | if (!(cb = nl_cb_alloc (NL_CB_CUSTOM))) 67 | { 68 | fprintf (stderr, "nl_cb_alloc failed\n"); 69 | goto end; 70 | } 71 | 72 | /* nl_socket_alloc_cb(cb) */ 73 | if (!(sk = nl_socket_alloc ())) 74 | { 75 | fprintf (stderr, "nl_socket_alloc failed\n"); 76 | goto end; 77 | } 78 | 79 | nl_socket_disable_seq_check (sk); 80 | nl_socket_disable_auto_ack (sk); //is not really required 81 | nl_socket_disable_msg_peek (sk); //is not really needed ? 82 | 83 | nl_cb_overwrite_recv (cb, my_faked_recv); 84 | 85 | // required to call nlmsg_set_proto(msg, 123456); 86 | // as the libnl3 have no API to set socket's s_proto 87 | // message's proto is required for correct parsing of data... 88 | // last argument is a pointer to "int protocol" 89 | 90 | //fprintf(stderr, "Calling nl_cb_set for fd %d\n", sockfd); 91 | if (nl_cb_set (cb, NL_CB_MSG_IN, NL_CB_CUSTOM, message_dumper, &protocol) < 0) 92 | { 93 | fprintf (stderr, "nl_cb_set failed\n"); 94 | goto end; 95 | } 96 | 97 | /* 98 | if (nl_recvmsgs_default (sk) > 0) 99 | { 100 | fprintf (stderr, "nl_recvmsgs failed\n"); 101 | goto end; 102 | } 103 | */ 104 | /* TODO: log retval of nl_recvmsgs_report */ 105 | if (nl_recvmsgs (sk, cb) < 0) 106 | { 107 | fprintf (stderr, "nl_recvmsgs_report() returns error\n"); 108 | } 109 | 110 | 111 | end: 112 | if (data) 113 | { 114 | fprintf (stderr, "WARNING: data was not handled\n"); 115 | free (data); 116 | } 117 | 118 | if (sk) 119 | nl_socket_free (sk); 120 | 121 | if (cb) 122 | nl_cb_put (cb); 123 | } 124 | -------------------------------------------------------------------------------- /nl_stub.h: -------------------------------------------------------------------------------- 1 | #ifndef MM_NL_STUB_H 2 | #define MM_NL_STUB_H 3 | 4 | #include 5 | void handle_netlink_data (int protocol, unsigned char *data, size_t length); 6 | #endif 7 | -------------------------------------------------------------------------------- /process.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include /* malloc */ 6 | 7 | #include "descriptor.h" 8 | #include "process.h" 9 | #include "main.h" /* ptrace_memcpy */ 10 | 11 | struct process 12 | { 13 | pid_t pid; /* should be the first (primary key) */ 14 | void *descriptors; 15 | }; 16 | 17 | int process_add_descriptor (struct process *process, struct descriptor *descriptor) 18 | { 19 | void *tmp; 20 | 21 | if (!(tmp = tsearch (descriptor, &process->descriptors, compare_descriptors))) 22 | { 23 | fprintf (stderr, "This descriptor adding failure\n"); 24 | return -1; 25 | } 26 | 27 | if (*(struct descriptor **) tmp != descriptor) 28 | { 29 | fprintf (stderr, "This descriptor already in process\n"); 30 | return -1; 31 | } 32 | 33 | return 0; 34 | } 35 | 36 | struct process *process_alloc (pid_t pid) 37 | { 38 | struct process *process; 39 | 40 | if (!(process = malloc (sizeof (*process)))) 41 | { 42 | perror ("malloc"); 43 | return NULL; 44 | } 45 | 46 | process->pid = pid; 47 | process->descriptors = NULL; 48 | return process; 49 | } 50 | 51 | static void _descriptor_destroy (void *arg) 52 | { 53 | descriptor_destroy ((struct descriptor *) arg); 54 | } 55 | 56 | void process_destroy (struct process *process) 57 | { 58 | tdestroy (process->descriptors, _descriptor_destroy); 59 | free (process); 60 | } 61 | 62 | int compare_processes (const void *a, const void *b) 63 | { 64 | /* void pointer may point to either struct process or it's primary key (pid_t pid) */ 65 | 66 | pid_t pa = *(const pid_t *) a; 67 | pid_t pb = *(const pid_t *) b; 68 | 69 | if (pa == pb) 70 | return 0; 71 | else 72 | return (pa > pb) ? 1 : -1; 73 | } 74 | 75 | struct descriptor *process_get_descriptor (struct process *process, int fd) 76 | { 77 | struct descriptor *descriptor; 78 | void *tmp1; 79 | pid_t pid; 80 | 81 | if ((tmp1 = tfind (&fd, &process->descriptors, compare_descriptors))) 82 | return *(struct descriptor **) tmp1; 83 | 84 | fprintf (stderr, "Detecting descriptor type of fd %d!\n", fd); 85 | 86 | pid = process->pid; 87 | 88 | if (!pid) 89 | { 90 | if ((descriptor = descriptor_alloc_detect_live (fd))) 91 | goto descriptor_ready; 92 | 93 | fprintf (stderr, "descriptor_alloc_detect_live failed. Falling back to /proc detector\n"); 94 | pid = getpid (); 95 | } 96 | 97 | if (!(descriptor = descriptor_alloc_detect_proc (fd, pid))) 98 | { 99 | fprintf (stderr, "descriptor_alloc_detect_proc failed\n"); 100 | return NULL; 101 | } 102 | 103 | descriptor_ready: 104 | 105 | if (process_add_descriptor (process, descriptor) == -1) 106 | { 107 | fprintf (stderr, "Failed to add descriptor to process\n"); 108 | descriptor_destroy (descriptor); 109 | return NULL; 110 | } 111 | 112 | return descriptor; 113 | } 114 | 115 | void process_delete_descriptor_int (struct process *process, int fd) 116 | { 117 | void *tmp; 118 | struct descriptor *descriptor; 119 | 120 | //fprintf (stderr, "BUG: descriptor not found (or cannot be deleted)\n"); 121 | // should not bark, as it called in places where descriptor really may not exists... 122 | 123 | if (!(tmp = tfind (&fd, &process->descriptors, compare_descriptors))) 124 | return; 125 | 126 | descriptor = *(struct descriptor **) tmp; 127 | 128 | if (!tdelete (descriptor, &process->descriptors, compare_descriptors)) 129 | { 130 | fprintf (stderr, "Achtung! descriptor was found but cannot be deleted!\n"); 131 | return; 132 | } 133 | 134 | descriptor_destroy (descriptor); 135 | } 136 | 137 | 138 | int process_memcpy (const struct process *process, void *dst, const void *src, size_t length) 139 | { 140 | if (process->pid) 141 | return ptrace_memcpy (process->pid, dst, src, length); 142 | 143 | memcpy (dst, src, length); 144 | return 0; 145 | } 146 | 147 | unsigned char *process_dup_memory (const struct process *process, const char *buf, size_t buflen) 148 | { 149 | unsigned char *data; 150 | 151 | if (!(data = malloc (buflen))) 152 | { 153 | perror ("malloc"); 154 | return NULL; 155 | } 156 | 157 | if (process_memcpy (process, data, buf, buflen) == -1) 158 | { 159 | fprintf (stderr, "process_memcpy() failed\n"); 160 | free (data); 161 | return NULL; 162 | } 163 | 164 | return data; 165 | } 166 | -------------------------------------------------------------------------------- /process.h: -------------------------------------------------------------------------------- 1 | #ifndef NLTRACE_PROCESS_H 2 | #define NLTRACE_PROCESS_H 3 | #include 4 | 5 | struct process; 6 | struct descriptor; 7 | 8 | int process_add_descriptor (struct process *process, struct descriptor *descriptor); 9 | struct process *process_alloc (pid_t pid); 10 | void process_destroy (struct process *process); 11 | int compare_processes (const void *a, const void *b); 12 | struct descriptor *process_get_descriptor (struct process *process, int fd); 13 | void process_delete_descriptor_int (struct process *process, int fd); 14 | 15 | unsigned char *process_dup_memory (const struct process *process, const char *buf, size_t buflen); 16 | int process_memcpy (const struct process *process, void *dst, const void *src, size_t length); 17 | #endif 18 | -------------------------------------------------------------------------------- /syscalls.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include /* For SYS_xxx definitions */ 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "handlers.h" 19 | #include "syscalls.h" 20 | 21 | /* match order as in struct user_regs_struct */ 22 | typedef struct 23 | { 24 | unsigned long a1; 25 | unsigned long a2; 26 | unsigned long a3; 27 | unsigned long a4; 28 | unsigned long a5; 29 | unsigned long a6; 30 | unsigned long ret; 31 | unsigned long nr; 32 | } args_t; 33 | 34 | static void print_args_t (FILE *output, const args_t *args) 35 | { 36 | 37 | fprintf (output, " a1: 0x%lx\n", args->a1); 38 | fprintf (output, " a2: 0x%lx\n", args->a2); 39 | fprintf (output, " a3: 0x%lx\n", args->a3); 40 | fprintf (output, " a4: 0x%lx\n", args->a4); 41 | fprintf (output, " a5: 0x%lx\n", args->a5); 42 | fprintf (output, " a6: 0x%lx\n", args->a6); 43 | fprintf (output, "ret: 0x%lx\n", args->ret); 44 | fprintf (output, " nr: 0x%lx\n", args->nr); 45 | 46 | } 47 | 48 | static void get_args (const struct user_regs_struct *state, args_t *args) 49 | { 50 | /* match order as in struct user_regs_struct */ 51 | #ifdef __aarch64__ 52 | (void)state; (void) args; 53 | #else 54 | #ifdef __amd64__ 55 | args->a1 = state->rdi; 56 | args->a2 = state->rsi; 57 | args->a3 = state->rdx; 58 | args->a4 = state->rcx; 59 | args->a5 = state->r8; 60 | args->a6 = state->r9; 61 | args->ret = state->rax; 62 | args->nr = state->orig_rax; 63 | #else 64 | args->a1 = state->ebx; 65 | args->a2 = state->ecx; 66 | args->a3 = state->edx; 67 | args->a4 = state->esi; 68 | args->a5 = state->edi; 69 | args->a6 = state->ebp; 70 | args->ret = state->eax; 71 | args->nr = state->orig_eax; 72 | #endif 73 | #endif 74 | } 75 | 76 | void trace_syscall (struct process *process, const struct user_regs_struct *state1, const struct user_regs_struct *state2) 77 | { 78 | args_t args1; 79 | args_t args2; 80 | 81 | #ifdef __aarch64__ 82 | args1.a1 = state1->regs[0]; 83 | args1.a2 = state1->regs[1]; 84 | args1.a3 = state1->regs[2]; 85 | args1.a4 = state1->regs[3]; 86 | args1.a5 = state1->regs[4]; 87 | args1.a6 = state1->regs[5]; 88 | 89 | args1.nr = state1->regs[8]; 90 | 91 | args2.ret = state2->regs[0]; 92 | #else 93 | get_args (state1, &args1); 94 | get_args (state2, &args2); 95 | #endif 96 | 97 | // http://www.skyfree.org/linux/kernel_network/socket.html 98 | switch (args1.nr) 99 | { 100 | #ifdef SYS_socketcall 101 | case SYS_socketcall: 102 | print_args_t (stderr, &args1); 103 | print_args_t (stderr, &args2); 104 | break; 105 | #else 106 | case SYS_socket: 107 | handle_socket (process, args2.ret, args1.a1, args1.a2, args1.a3); 108 | break; 109 | case SYS_sendto: 110 | handle_sendto (process, args2.ret, args1.a1, (const char *) args1.a2, args1.a3, args1.a4, (const struct sockaddr *) args1.a5, args1.a6); 111 | break; 112 | case SYS_recvfrom: 113 | handle_recvfrom (process, args2.ret, args1.a1, (const char *) args1.a2, args1.a3, args1.a4, (const struct sockaddr *) args1.a5, 114 | (const socklen_t *) args1.a6); 115 | break; 116 | case SYS_recvmsg: 117 | handle_recvmsg (process, args2.ret, args1.a1, (struct msghdr *) args1.a2, args1.a3); 118 | break; 119 | case SYS_sendmsg: 120 | handle_sendmsg (process, args2.ret, args1.a1, (const struct msghdr *) args1.a2, args1.a3); 121 | break; 122 | case SYS_recvmmsg: 123 | break; 124 | case SYS_sendmmsg: 125 | break; 126 | #endif 127 | case SYS_close: 128 | handle_close (process, args2.ret, args1.a1); 129 | break; 130 | case SYS_dup3: 131 | handle_dup3 (process, args2.ret, args1.a1, args1.a2, args1.a3); 132 | break; 133 | #ifndef __aarch64__ 134 | case SYS_dup2: 135 | handle_dup2 (process, args2.ret, args1.a1, args1.a2); 136 | break; 137 | #endif 138 | case SYS_dup: 139 | handle_dup (process, args2.ret, args1.a1); 140 | break; 141 | /* 142 | case SYS_execve: 143 | case SYS_fork: 144 | case SYS_clone: 145 | 146 | 147 | pread64 148 | preadv 149 | pwrite64 150 | pwritev 151 | read 152 | readv 153 | recv 154 | sendfile (file -> socket) 155 | sendfile64 (file -> socket) 156 | splice (pipe -> socket), (socket->pipe) 157 | write 158 | writev 159 | */ 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /syscalls.h: -------------------------------------------------------------------------------- 1 | #ifndef NLTACE_SYCALLS_H 2 | #define NLTACE_SYCALLS_H 3 | 4 | #include 5 | 6 | struct user_regs_struct; 7 | struct process; 8 | 9 | void trace_syscall (struct process *process, const struct user_regs_struct *state1, const struct user_regs_struct *state2); 10 | int usermemcpy (void *destination, pid_t pid, const void *usermem, size_t length); 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main (void) 7 | { 8 | int qwe; 9 | 10 | 11 | qwe = socket (AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); 12 | 13 | 14 | // printf ("addr=%p\n", &qwe); 15 | // send(qwe, &qwe, sizeof(qwe), 0); 16 | /////////////////////////////////////////////////// 17 | 18 | struct iovec iov = { 19 | .iov_base = &qwe, 20 | .iov_len = sizeof (qwe) 21 | }; 22 | 23 | struct msghdr msg = { 24 | .msg_iovlen = 1, 25 | .msg_iov = &iov, 26 | }; 27 | 28 | printf ("sendmsg msg child addr: addr=%p\n", &msg); 29 | sendmsg (qwe, &msg, 0); 30 | 31 | } 32 | -------------------------------------------------------------------------------- /tracer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include /* malloc */ 3 | #include /* fprintf */ 4 | 5 | #include "tracer.h" 6 | #include "process.h" 7 | 8 | struct tracer 9 | { 10 | void *processes; 11 | }; 12 | 13 | /* 14 | Should return error if such process already here 15 | */ 16 | int tracer_add_process (struct tracer *tracer, struct process *process) 17 | { 18 | 19 | 20 | void *tmp; 21 | 22 | if (!(tmp = tsearch (process, &tracer->processes, compare_processes))) 23 | { 24 | fprintf (stderr, "Process adding failure\n"); 25 | return -1; 26 | } 27 | 28 | if (*(struct process **) tmp != process) 29 | { 30 | fprintf (stderr, "Process already exists\n"); 31 | return -1; 32 | } 33 | 34 | return 0; 35 | } 36 | 37 | 38 | struct tracer *tracer_alloc (void) 39 | { 40 | struct tracer *tracer; 41 | 42 | if (!(tracer = malloc (sizeof (*tracer)))) 43 | { 44 | perror ("malloc"); 45 | return NULL; 46 | } 47 | 48 | tracer->processes = NULL; 49 | return tracer; 50 | } 51 | 52 | 53 | static void _process_destroy (void *arg) 54 | { 55 | process_destroy ((struct process *) arg); 56 | } 57 | 58 | 59 | void tracer_destroy (struct tracer *tracer) 60 | { 61 | tdestroy (tracer->processes, _process_destroy); 62 | free (tracer); 63 | } 64 | -------------------------------------------------------------------------------- /tracer.h: -------------------------------------------------------------------------------- 1 | #ifndef NLTRACE_TRACER_H 2 | #define NLTRACE_TRACER_H 3 | 4 | struct tracer; 5 | struct process; 6 | 7 | int tracer_add_process (struct tracer *tracer, struct process *process); 8 | struct tracer *tracer_alloc (void); 9 | void tracer_destroy (struct tracer *tracer); 10 | #endif 11 | --------------------------------------------------------------------------------