1#define _POSIX_C_SOURCE 200809L
2#include <arpa/inet.h>
3#include <alloc.h>
4#include <buffer.h>
5#include <byte.h>
6#include <errno.h>
7#include <fmt.h>
8#include <netinet/in.h>
9#include <signal.h>
10#include <stdbool.h>
11#include <strerr.h>
12#include <sys/ptrace.h>
13#include <sys/socket.h>
14#include <sys/syscall.h>
15#include <sys/types.h>
16#include <sys/un.h>
17#include <sys/user.h>
18#include <sys/wait.h>
19#include <unistd.h>
20
21struct proc {
22 pid_t pid;
23 int entering;
24};
25
26static struct proc *procs;
27static size_t nprocs;
28
29static void
30out(const char *s)
31{
32 buffer_puts(buffer_1, s);
33}
34
35static void
36outc(char c)
37{
38 buffer_put(buffer_1, &c, 1);
39}
40
41static void
42out_ulong(unsigned long u)
43{
44 char buf[FMT_ULONG];
45 unsigned int n;
46
47 n = fmt_ulong(buf, u);
48 buffer_put(buffer_1, buf, n);
49}
50
51static void
52die(const char *msg)
53{
54 strerr_die2sys(111, "peek: ", msg);
55}
56
57static char *
58unknown_string(void)
59{
60 char *s;
61
62 s = alloc(2);
63 if (!s)
64 die("alloc");
65 s[0] = '?';
66 s[1] = '\0';
67 return s;
68}
69
70static struct proc *
71procget(pid_t pid)
72{
73 size_t i;
74
75 for (i = 0; i < nprocs; i++) {
76 if (procs[i].pid == pid)
77 return &procs[i];
78 }
79 if (!alloc_re((char **)&procs, nprocs * sizeof(procs[0]),
80 (nprocs + 1) * sizeof(procs[0])))
81 die("alloc_re");
82 procs[nprocs] = (struct proc){.pid = pid};
83 return &procs[nprocs++];
84}
85
86static void
87procdel(pid_t pid)
88{
89 size_t i;
90
91 for (i = 0; i < nprocs; i++) {
92 if (procs[i].pid != pid)
93 continue;
94 procs[i] = procs[--nprocs];
95 return;
96 }
97}
98
99static void
100setopts(pid_t pid)
101{
102 long opts = PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEFORK |
103 PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE |
104 PTRACE_O_TRACEEXEC;
105 if (ptrace(PTRACE_SETOPTIONS, pid, 0, opts) < 0)
106 die("ptrace SETOPTIONS");
107}
108
109static void
110readmem(pid_t pid, unsigned long addr, void *buf, size_t n)
111{
112 size_t i;
113 unsigned char *p = buf;
114
115 for (i = 0; i < n; i += sizeof(long)) {
116 long v = ptrace(PTRACE_PEEKDATA, pid, addr + i, 0);
117 if (v == -1 && errno)
118 byte_zero(p + i, n - i);
119 else
120 byte_copy(p + i, n - i < sizeof(v) ? n - i : sizeof(v), (char *)&v);
121 }
122}
123
124static char *
125readstr(pid_t pid, unsigned long addr)
126{
127 char *s;
128 size_t cap, len;
129
130 if (!addr)
131 return unknown_string();
132 cap = 64;
133 len = 0;
134 s = alloc(cap);
135 if (!s)
136 die("alloc");
137 for (;;) {
138 long v = ptrace(PTRACE_PEEKDATA, pid, addr + len, 0);
139 size_t i;
140 char *b = (char *)&v;
141
142 if (v == -1 && errno) {
143 alloc_free(s);
144 return unknown_string();
145 }
146 for (i = 0; i < sizeof(v); i++) {
147 if (len + 1 >= cap) {
148 size_t oldcap = cap;
149 cap *= 2;
150 if (!alloc_re(&s, oldcap, cap))
151 s = NULL;
152 if (!s)
153 die("alloc_re");
154 }
155 s[len++] = b[i];
156 if (!b[i])
157 return s;
158 }
159 }
160}
161
162static void
163printnet(pid_t pid, unsigned long addr, unsigned long len)
164{
165 struct sockaddr_storage ss;
166 char host[INET6_ADDRSTRLEN + 32];
167
168 if (!addr || len < sizeof(sa_family_t))
169 return;
170 byte_zero(&ss, sizeof(ss));
171 readmem(pid, addr, &ss, len < sizeof(ss) ? len : sizeof(ss));
172 if (ss.ss_family == AF_UNIX) {
173 struct sockaddr_un *un = (struct sockaddr_un *)&ss;
174 out("net ");
175 out_ulong((unsigned long)pid);
176 out(" unix:");
177 out(un->sun_path[0] ? un->sun_path : "(anon)");
178 out("\n");
179 } else if (ss.ss_family == AF_INET) {
180 struct sockaddr_in *in = (struct sockaddr_in *)&ss;
181 if (!inet_ntop(AF_INET, &in->sin_addr, host, sizeof(host)))
182 return;
183 out("net ");
184 out_ulong((unsigned long)pid);
185 out(" ");
186 out(host);
187 out(":");
188 out_ulong((unsigned long)ntohs(in->sin_port));
189 out("\n");
190 } else if (ss.ss_family == AF_INET6) {
191 struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)&ss;
192 if (!inet_ntop(AF_INET6, &in6->sin6_addr, host, sizeof(host)))
193 return;
194 out("net ");
195 out_ulong((unsigned long)pid);
196 out(" [");
197 out(host);
198 out("]:");
199 out_ulong((unsigned long)ntohs(in6->sin6_port));
200 out("\n");
201 }
202}
203
204static void
205tracecall(pid_t pid, struct user_regs_struct *r)
206{
207 char *s;
208
209 switch ((long)r->orig_rax) {
210 case SYS_execve:
211 s = readstr(pid, r->rdi);
212 out("exec ");
213 out_ulong((unsigned long)pid);
214 out(" ");
215 out(s);
216 out("\n");
217 alloc_free(s);
218 break;
219 case SYS_execveat:
220 s = readstr(pid, r->rsi);
221 out("exec ");
222 out_ulong((unsigned long)pid);
223 out(" ");
224 out(s);
225 out("\n");
226 alloc_free(s);
227 break;
228 case SYS_open:
229 s = readstr(pid, r->rdi);
230 out("file ");
231 out_ulong((unsigned long)pid);
232 out(" ");
233 out(s);
234 out("\n");
235 alloc_free(s);
236 break;
237 case SYS_openat:
238 s = readstr(pid, r->rsi);
239 out("file ");
240 out_ulong((unsigned long)pid);
241 out(" ");
242 out(s);
243 out("\n");
244 alloc_free(s);
245 break;
246 case SYS_openat2:
247 s = readstr(pid, r->rsi);
248 out("file ");
249 out_ulong((unsigned long)pid);
250 out(" ");
251 out(s);
252 out("\n");
253 alloc_free(s);
254 break;
255 case SYS_connect:
256 printnet(pid, r->rsi, r->rdx);
257 break;
258 }
259}
260
261int
262main(int argc, char *argv[])
263{
264 int status;
265 pid_t pid;
266
267 if (argc < 2) {
268 buffer_puts(buffer_2, "usage: ");
269 buffer_puts(buffer_2, argv[0]);
270 buffer_puts(buffer_2, " cmd [args...]\n");
271 buffer_flush(buffer_2);
272 return 2;
273 }
274 pid = fork();
275 if (pid < 0)
276 die("fork");
277 if (pid == 0) {
278 if (ptrace(PTRACE_TRACEME, 0, 0, 0) < 0)
279 die("ptrace TRACEME");
280 raise(SIGSTOP);
281 execvp(argv[1], argv + 1);
282 die("execvp");
283 }
284 if (waitpid(pid, &status, 0) < 0)
285 die("waitpid");
286 procget(pid);
287 setopts(pid);
288 if (ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0)
289 die("ptrace SYSCALL");
290 while (nprocs > 0) {
291 unsigned int event;
292 struct proc *p;
293
294 pid = waitpid(-1, &status, __WALL);
295 if (pid < 0) {
296 if (errno == EINTR)
297 continue;
298 die("waitpid");
299 }
300 p = procget(pid);
301 if (WIFEXITED(status)) {
302 out("exit ");
303 out_ulong((unsigned long)pid);
304 out(" ");
305 out_ulong((unsigned long)WEXITSTATUS(status));
306 out("\n");
307 procdel(pid);
308 continue;
309 }
310 if (WIFSIGNALED(status)) {
311 out("exit ");
312 out_ulong((unsigned long)pid);
313 out(" sig");
314 out_ulong((unsigned long)WTERMSIG(status));
315 out("\n");
316 procdel(pid);
317 continue;
318 }
319 if (!WIFSTOPPED(status))
320 continue;
321 if (WSTOPSIG(status) == (SIGTRAP | 0x80)) {
322 struct user_regs_struct r;
323
324 if (!p->entering && ptrace(PTRACE_GETREGS, pid, 0, &r) == 0)
325 tracecall(pid, &r);
326 p->entering = !p->entering;
327 ptrace(PTRACE_SYSCALL, pid, 0, 0);
328 continue;
329 }
330 event = (unsigned int)status >> 16;
331 if (WSTOPSIG(status) == SIGTRAP && event) {
332 unsigned long msg = 0;
333
334 if ((event == PTRACE_EVENT_FORK || event == PTRACE_EVENT_VFORK ||
335 event == PTRACE_EVENT_CLONE) &&
336 ptrace(PTRACE_GETEVENTMSG, pid, 0, &msg) == 0) {
337 out("fork ");
338 out_ulong((unsigned long)pid);
339 out(" -> ");
340 out_ulong(msg);
341 out("\n");
342 procget((pid_t)msg);
343 }
344 ptrace(PTRACE_SYSCALL, pid, 0, 0);
345 continue;
346 }
347 setopts(pid);
348 ptrace(PTRACE_SYSCALL, pid, 0, WSTOPSIG(status) == SIGSTOP ? 0 : WSTOPSIG(status));
349 }
350 buffer_flush(buffer_1);
351 alloc_free((char *)procs);
352 return 0;
353}