1#define _POSIX_C_SOURCE 200809L
2#include <arpa/inet.h>
3#include <errno.h>
4#include <netinet/in.h>
5#include <signal.h>
6#include <stdbool.h>
7#include <stdio.h>
8#include <stdlib.h>
9#include <string.h>
10#include <sys/ptrace.h>
11#include <sys/socket.h>
12#include <sys/syscall.h>
13#include <sys/types.h>
14#include <sys/un.h>
15#include <sys/user.h>
16#include <sys/wait.h>
17#include <unistd.h>
18
19struct proc {
20 pid_t pid;
21 int entering;
22};
23
24static struct proc *procs;
25static size_t nprocs;
26
27static void
28die(const char *msg)
29{
30 perror(msg);
31 exit(1);
32}
33
34static struct proc *
35procget(pid_t pid)
36{
37 size_t i;
38
39 for (i = 0; i < nprocs; i++) {
40 if (procs[i].pid == pid)
41 return &procs[i];
42 }
43 procs = realloc(procs, (nprocs + 1) * sizeof(procs[0]));
44 if (!procs)
45 die("realloc");
46 procs[nprocs] = (struct proc){.pid = pid};
47 return &procs[nprocs++];
48}
49
50static void
51procdel(pid_t pid)
52{
53 size_t i;
54
55 for (i = 0; i < nprocs; i++) {
56 if (procs[i].pid != pid)
57 continue;
58 procs[i] = procs[--nprocs];
59 return;
60 }
61}
62
63static void
64setopts(pid_t pid)
65{
66 long opts = PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEFORK |
67 PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE |
68 PTRACE_O_TRACEEXEC;
69 if (ptrace(PTRACE_SETOPTIONS, pid, 0, opts) < 0)
70 die("ptrace SETOPTIONS");
71}
72
73static void
74readmem(pid_t pid, unsigned long addr, void *buf, size_t n)
75{
76 size_t i;
77 unsigned char *p = buf;
78
79 for (i = 0; i < n; i += sizeof(long)) {
80 long v = ptrace(PTRACE_PEEKDATA, pid, addr + i, 0);
81 if (v == -1 && errno)
82 memset(p + i, 0, n - i);
83 else
84 memcpy(p + i, &v, n - i < sizeof(v) ? n - i : sizeof(v));
85 }
86}
87
88static char *
89readstr(pid_t pid, unsigned long addr)
90{
91 char *s;
92 size_t cap, len;
93
94 if (!addr)
95 return strdup("?");
96 cap = 64;
97 len = 0;
98 s = malloc(cap);
99 if (!s)
100 die("malloc");
101 for (;;) {
102 long v = ptrace(PTRACE_PEEKDATA, pid, addr + len, 0);
103 size_t i;
104 char *b = (char *)&v;
105
106 if (v == -1 && errno) {
107 free(s);
108 return strdup("?");
109 }
110 for (i = 0; i < sizeof(v); i++) {
111 if (len + 1 >= cap) {
112 cap *= 2;
113 s = realloc(s, cap);
114 if (!s)
115 die("realloc");
116 }
117 s[len++] = b[i];
118 if (!b[i])
119 return s;
120 }
121 }
122}
123
124static void
125printnet(pid_t pid, unsigned long addr, unsigned long len)
126{
127 struct sockaddr_storage ss;
128 char host[INET6_ADDRSTRLEN + 32];
129
130 if (!addr || len < sizeof(sa_family_t))
131 return;
132 memset(&ss, 0, sizeof(ss));
133 readmem(pid, addr, &ss, len < sizeof(ss) ? len : sizeof(ss));
134 if (ss.ss_family == AF_UNIX) {
135 struct sockaddr_un *un = (struct sockaddr_un *)&ss;
136 printf("net %d unix:%s\n", pid, un->sun_path[0] ? un->sun_path : "(anon)");
137 } else if (ss.ss_family == AF_INET) {
138 struct sockaddr_in *in = (struct sockaddr_in *)&ss;
139 if (!inet_ntop(AF_INET, &in->sin_addr, host, sizeof(host)))
140 return;
141 printf("net %d %s:%u\n", pid, host, ntohs(in->sin_port));
142 } else if (ss.ss_family == AF_INET6) {
143 struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)&ss;
144 if (!inet_ntop(AF_INET6, &in6->sin6_addr, host, sizeof(host)))
145 return;
146 printf("net %d [%s]:%u\n", pid, host, ntohs(in6->sin6_port));
147 }
148}
149
150static void
151tracecall(pid_t pid, struct user_regs_struct *r)
152{
153 char *s;
154
155 switch ((long)r->orig_rax) {
156 case SYS_execve:
157 s = readstr(pid, r->rdi);
158 printf("exec %d %s\n", pid, s);
159 free(s);
160 break;
161 case SYS_execveat:
162 s = readstr(pid, r->rsi);
163 printf("exec %d %s\n", pid, s);
164 free(s);
165 break;
166 case SYS_open:
167 s = readstr(pid, r->rdi);
168 printf("file %d %s\n", pid, s);
169 free(s);
170 break;
171 case SYS_openat:
172 s = readstr(pid, r->rsi);
173 printf("file %d %s\n", pid, s);
174 free(s);
175 break;
176 case SYS_openat2:
177 s = readstr(pid, r->rsi);
178 printf("file %d %s\n", pid, s);
179 free(s);
180 break;
181 case SYS_connect:
182 printnet(pid, r->rsi, r->rdx);
183 break;
184 }
185}
186
187int
188main(int argc, char *argv[])
189{
190 int status;
191 pid_t pid;
192
193 if (argc < 2) {
194 fprintf(stderr, "usage: %s cmd [args...]\n", argv[0]);
195 return 2;
196 }
197 pid = fork();
198 if (pid < 0)
199 die("fork");
200 if (pid == 0) {
201 if (ptrace(PTRACE_TRACEME, 0, 0, 0) < 0)
202 die("ptrace TRACEME");
203 raise(SIGSTOP);
204 execvp(argv[1], argv + 1);
205 die("execvp");
206 }
207 if (waitpid(pid, &status, 0) < 0)
208 die("waitpid");
209 procget(pid);
210 setopts(pid);
211 if (ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0)
212 die("ptrace SYSCALL");
213 while (nprocs > 0) {
214 unsigned int event;
215 struct proc *p;
216
217 pid = waitpid(-1, &status, __WALL);
218 if (pid < 0) {
219 if (errno == EINTR)
220 continue;
221 die("waitpid");
222 }
223 p = procget(pid);
224 if (WIFEXITED(status)) {
225 printf("exit %d %d\n", pid, WEXITSTATUS(status));
226 procdel(pid);
227 continue;
228 }
229 if (WIFSIGNALED(status)) {
230 printf("exit %d sig%d\n", pid, WTERMSIG(status));
231 procdel(pid);
232 continue;
233 }
234 if (!WIFSTOPPED(status))
235 continue;
236 if (WSTOPSIG(status) == (SIGTRAP | 0x80)) {
237 struct user_regs_struct r;
238
239 if (!p->entering && ptrace(PTRACE_GETREGS, pid, 0, &r) == 0)
240 tracecall(pid, &r);
241 p->entering = !p->entering;
242 ptrace(PTRACE_SYSCALL, pid, 0, 0);
243 continue;
244 }
245 event = (unsigned int)status >> 16;
246 if (WSTOPSIG(status) == SIGTRAP && event) {
247 unsigned long msg = 0;
248
249 if ((event == PTRACE_EVENT_FORK || event == PTRACE_EVENT_VFORK ||
250 event == PTRACE_EVENT_CLONE) &&
251 ptrace(PTRACE_GETEVENTMSG, pid, 0, &msg) == 0) {
252 printf("fork %d -> %lu\n", pid, msg);
253 procget((pid_t)msg);
254 }
255 ptrace(PTRACE_SYSCALL, pid, 0, 0);
256 continue;
257 }
258 setopts(pid);
259 ptrace(PTRACE_SYSCALL, pid, 0, WSTOPSIG(status) == SIGSTOP ? 0 : WSTOPSIG(status));
260 }
261 free(procs);
262 return 0;
263}