source: neercs/trunk/src/mytrace.c @ 2516

Last change on this file since 2516 was 2516, checked in by sam, 6 years ago
  • Fork the grabbed process and exit the parent. Unfortunately for some reason it still survives as a zombie, there must be something wrong with the setsid/setpgid mechanism we use (maybe setpgrp is needed?).
  • Property svn:eol-style set to native
File size: 13.6 KB
Line 
1/*
2 *  neercs        console-based window manager
3 *  Copyright (c) 2008 Pascal Terjan
4 *            (c) 2008 Sam Hocevar <sam@zoy.org>
5 *                All Rights Reserved
6 *
7 *  $Id$
8 *
9 *  This program is free software. It comes without any warranty, to
10 *  the extent permitted by applicable law. You can redistribute it
11 *  and/or modify it under the terms of the Do What The Fuck You Want
12 *  To Public License, Version 2, as published by Sam Hocevar. See
13 *  http://sam.zoy.org/wtfpl/COPYING for more details.
14 */
15
16#include "config.h"
17
18#include <errno.h>
19#include <fcntl.h>
20#include <stdio.h>
21#include <stdlib.h>
22#include <string.h>
23
24#if defined USE_GRAB
25#   include <sys/ioctl.h>
26#   include <sys/ptrace.h>
27#   include <sys/stat.h>
28#   include <sys/syscall.h>
29#   include <sys/user.h>
30#   include <sys/wait.h>
31#endif
32
33#include "neercs.h"
34#include "mytrace.h"
35
36#if defined USE_GRAB
37static int memcpy_from_target(struct mytrace *t,
38                              char* dest, long src, size_t n);
39static int memcpy_into_target(struct mytrace *t,
40                              long dest, char const *src, size_t n);
41static long remote_syscall(struct mytrace *t, long call,
42                           long arg1, long arg2, long arg3);
43#   if defined DEBUG
44static void print_registers(pid_t pid);
45#   else
46#       define print_registers(x) do {} while(0)
47#   endif
48#endif
49
50#define X(x) #x
51#define STRINGIFY(x) X(x)
52
53#define SYSCALL_X86   0x80cd  /* CD 80 = int $0x80 */
54#define SYSCALL_AMD64 0x050fL /* 0F 05 = syscall */
55
56#if defined __x86_64__
57#   define RAX rax
58#   define RBX rbx
59#   define RCX rcx
60#   define RDX rdx
61#   define RSP rsp
62#   define RIP rip
63#   define RDI rdi
64#   define RSI rsi
65#   define FMT "%016lx"
66#else
67#   define RAX eax
68#   define RBX ebx
69#   define RCX ecx
70#   define RDX edx
71#   define RSP esp
72#   define RIP eip
73#   define RDI edi
74#   define RSI esi
75#   define FMT "%08lx"
76#endif
77
78#define MYCALL_OPEN     0
79#define MYCALL_CLOSE    1
80#define MYCALL_WRITE    2
81#define MYCALL_DUP2     3
82#define MYCALL_SETPGID  4
83#define MYCALL_SETSID   5
84#define MYCALL_KILL     6
85#define MYCALL_FORK     7
86#define MYCALL_EXIT     8
87
88#if defined __x86_64__
89/* from unistd_32.h on an amd64 system */
90int syscalls32[] = { 5, 6, 4, 63, 57, 66, 37, 2, 1 };
91int syscalls64[] =
92#else
93int syscalls32[] =
94#endif
95    { SYS_open, SYS_close, SYS_write, SYS_dup2, SYS_setpgid, SYS_setsid,
96      SYS_kill, SYS_fork, SYS_exit };
97
98char const *syscallnames[] =
99    { "open", "close", "write", "dup2", "setpgid", "setsid", "kill", "fork",
100      "exit" };
101
102struct mytrace
103{
104    pid_t pid, child;
105};
106
107struct mytrace* mytrace_attach(long int pid)
108{
109#if defined USE_GRAB
110    struct mytrace *t;
111    int status;
112
113    if(ptrace(PTRACE_ATTACH, pid, 0, 0) < 0)
114    {
115        perror("ptrace_attach");
116        return NULL;
117    }
118    if(waitpid(pid, &status, 0) < 0)
119    {
120        perror("waitpid");
121        return NULL;
122    }
123    if(!WIFSTOPPED(status))
124    {
125        fprintf(stderr, "traced process was not stopped\n");
126        ptrace(PTRACE_DETACH, pid, 0, 0);
127        return NULL;
128    }
129
130    t = malloc(sizeof(struct mytrace));
131    t->pid = pid;
132    t->child = 0;
133
134    return t;
135#else
136    errno = ENOSYS;
137    return NULL;
138#endif
139}
140
141struct mytrace* mytrace_fork(struct mytrace *t)
142{
143#if defined USE_GRAB
144    struct mytrace *child;
145
146    ptrace(PTRACE_SETOPTIONS, t->pid, NULL, PTRACE_O_TRACEFORK);
147    remote_syscall(t, MYCALL_FORK, 0, 0, 0);
148    waitpid(t->child, NULL, 0);
149
150    child = malloc(sizeof(struct mytrace));
151    child->pid = t->child;
152    child->child = 0;
153
154    return child;
155#else
156    errno = ENOSYS;
157    return NULL;
158#endif
159}
160
161int mytrace_detach(struct mytrace *t)
162{
163#if defined USE_GRAB
164    ptrace(PTRACE_DETACH, t->pid, 0, 0);
165    free(t);
166
167    return 0;
168#else
169    errno = ENOSYS;
170    return -1;
171#endif
172}
173
174long mytrace_getpid(struct mytrace *t)
175{
176#if defined USE_GRAB
177    return t->pid;
178#else
179    errno = ENOSYS;
180    return -1;
181#endif
182}
183
184int mytrace_open(struct mytrace *t, char const *path, int mode)
185{
186#if defined USE_GRAB
187    char backup_data[4096];
188    struct user_regs_struct regs;
189    size_t size = strlen(path) + 1;
190    int ret;
191
192    if(ptrace(PTRACE_GETREGS, t->pid, NULL, &regs) < 0)
193    {
194        fprintf(stderr, "PTRACE_GETREGS failed\n");
195        return errno;
196    }
197
198    /* Backup the data that we will use */
199    if(memcpy_from_target(t, backup_data, regs.RSP, size) < 0)
200        return -1;
201
202    memcpy_into_target(t, regs.RSP, path, size);
203
204    ret = remote_syscall(t, MYCALL_OPEN, regs.RSP, O_RDWR, 0755);
205
206    /* Restore the data */
207    memcpy_into_target(t, regs.RSP, backup_data, size);
208
209    if(ret < 0)
210    {
211        errno = ret;
212        return -1;
213    }
214
215    return ret;
216#else
217    errno = ENOSYS;
218    return -1;
219#endif
220}
221
222int mytrace_close(struct mytrace *t, int fd)
223{
224#if defined USE_GRAB
225    return remote_syscall(t, MYCALL_CLOSE, fd, 0, 0);
226#else
227    errno = ENOSYS;
228    return -1;
229#endif
230}
231
232int mytrace_write(struct mytrace *t, int fd, char const *data, size_t len)
233{
234#if defined USE_GRAB
235    struct user_regs_struct regs;
236    char *backup_data;
237    int ret;
238
239    if(ptrace(PTRACE_GETREGS, t->pid, NULL, &regs) < 0)
240    {
241        fprintf(stderr, "PTRACE_GETREGS failed\n");
242        return errno;
243    }
244
245    backup_data = malloc(len);
246
247    /* Backup the data that we will use */
248    if(memcpy_from_target(t, backup_data, regs.RSP, len) < 0)
249        return -1;
250
251    memcpy_into_target(t, regs.RSP, data, len);
252
253    ret = remote_syscall(t, MYCALL_WRITE, fd, regs.RSP, len);
254
255    /* Restore the data */
256    memcpy_into_target(t, regs.RSP, backup_data, len);
257
258    if(ret < 0)
259    {
260        errno = ret;
261        return -1;
262    }
263
264    return ret;
265#else
266    errno = ENOSYS;
267    return -1;
268#endif
269}
270
271int mytrace_dup2(struct mytrace *t, int oldfd, int newfd)
272{
273#if defined USE_GRAB
274    return remote_syscall(t, MYCALL_DUP2, oldfd, newfd, 0);
275#else
276    errno = ENOSYS;
277    return -1;
278#endif
279}
280
281int mytrace_setpgid(struct mytrace *t, long pid, long pgid)
282{
283#if defined USE_GRAB
284    return remote_syscall(t, MYCALL_SETPGID, pid, pgid, 0);
285#else
286    errno = ENOSYS;
287    return -1;
288#endif
289}
290
291int mytrace_setsid(struct mytrace *t)
292{
293#if defined USE_GRAB
294    return remote_syscall(t, MYCALL_SETSID, 0, 0, 0);
295#else
296    errno = ENOSYS;
297    return -1;
298#endif
299}
300
301int mytrace_kill(struct mytrace *t, long pid, int sig)
302{
303#if defined USE_GRAB
304    return remote_syscall(t, MYCALL_KILL, pid, sig, 0);
305#else
306    errno = ENOSYS;
307    return -1;
308#endif
309}
310
311int mytrace_exit(struct mytrace *t, int status)
312{
313#if defined USE_GRAB
314    ptrace(PTRACE_SETOPTIONS, t->pid, NULL, PTRACE_O_TRACEEXIT);
315    return remote_syscall(t, MYCALL_EXIT, status, 0, 0);
316#else
317    errno = ENOSYS;
318    return -1;
319#endif
320}
321
322/*
323 * XXX: the following functions are local
324 */
325
326#if defined USE_GRAB
327static int memcpy_from_target(struct mytrace *t,
328                              char* dest, long src, size_t n)
329{
330    static int const align = sizeof(long) - 1;
331
332    while(n)
333    {
334        long data;
335        size_t todo = sizeof(long) - (src & align);
336
337        if(n < todo)
338            todo = n;
339
340        data = ptrace(PTRACE_PEEKTEXT, t->pid, src - (src & align), 0);
341        if(errno)
342        {
343            perror("ptrace_peektext");
344            return -1;
345        }
346        memcpy(dest, (char *)&data + (src & align), todo);
347
348        dest += todo;
349        src += todo;
350        n -= todo;
351    }
352
353    return 0;
354}
355
356static int memcpy_into_target(struct mytrace *t,
357                              long dest, char const *src, size_t n)
358{
359    static int const align = sizeof(long) - 1;
360
361    while(n)
362    {
363        long data;
364        size_t todo = sizeof(long) - (dest & align);
365
366        if(n < todo)
367            todo = n;
368        if(todo != sizeof(long))
369        {
370            data = ptrace(PTRACE_PEEKTEXT, t->pid, dest - (dest & align), 0);
371            if(errno)
372            {
373                perror("ptrace_peektext");
374                return -1;
375            }
376        }
377
378        memcpy((char *)&data + (dest & align), src, todo);
379        ptrace(PTRACE_POKETEXT, t->pid, dest - (dest & align), data);
380        if(errno)
381        {
382            perror("ptrace_poketext");
383            return -1;
384        }
385
386        src += todo;
387        dest += todo;
388        n -= todo;
389    }
390
391    return 0;
392}
393
394static long remote_syscall(struct mytrace *t, long call,
395                           long arg1, long arg2, long arg3)
396{
397    /* Method for remote syscall:
398     *  - wait until the traced application exits from a syscall
399     *  - save registers
400     *  - rewind eip/rip to point on the syscall instruction
401     *  - single step: execute syscall instruction
402     *  - retrieve resulting registers
403     *  - restore registers */
404    struct user_regs_struct regs, oldregs;
405    long oinst;
406    int bits;
407
408    if(call < 0 || call >= (long)(sizeof(syscallnames)/sizeof(*syscallnames)))
409    {
410        fprintf(stderr, "unknown remote syscall %li\n", call);
411        return -1;
412    }
413
414    debug("remote syscall %s(%lu, %lu, %lu)",
415          syscallnames[call], arg1, arg2, arg3);
416
417    print_registers(t->pid);
418
419#if defined __x86_64__
420    bits = 64;
421#else
422    bits = 32;
423#endif
424
425    for(;;)
426    {
427        if(ptrace(PTRACE_GETREGS, t->pid, NULL, &oldregs) < 0)
428        {
429            fprintf(stderr, "PTRACE_GETREGS failed\n");
430            return -1;
431        }
432
433        oinst = ptrace(PTRACE_PEEKTEXT, t->pid, oldregs.RIP - 2, 0) & 0xffff;
434
435#if defined __x86_64__
436        if(oinst == SYSCALL_AMD64)
437            break;
438        if(oinst == SYSCALL_X86)
439        {
440            bits = 32;
441            break;
442        }
443#else
444        if(oinst == SYSCALL_X86)
445            break;
446#endif
447
448        if(ptrace(PTRACE_SYSCALL, t->pid, NULL, 0) < 0)
449        {
450            perror("ptrace_syscall (1)");
451            return -1;
452        }
453        waitpid(t->pid, NULL, 0);
454
455        if(ptrace(PTRACE_SYSCALL, t->pid, NULL, 0) < 0)
456        {
457            perror("ptrace_syscall (2)");
458            return -1;
459        }
460        waitpid(t->pid, NULL, 0);
461    }
462
463    print_registers(t->pid);
464
465    regs = oldregs;
466    regs.RIP = regs.RIP - 2;
467#if defined __x86_64__
468    if(bits == 64)
469    {
470        regs.RAX = syscalls64[call];
471        regs.RDI = arg1;
472        regs.RSI = arg2;
473        regs.RDX = arg3;
474    }
475    else
476#endif
477    {
478        regs.RAX = syscalls32[call];
479        regs.RBX = arg1;
480        regs.RCX = arg2;
481        regs.RDX = arg3;
482    }
483
484    if(ptrace(PTRACE_SETREGS, t->pid, NULL, &regs) < 0)
485    {
486        fprintf(stderr, "PTRACE_SETREGS failed\n");
487        return -1;
488    }
489
490    for(;;)
491    {
492        int status;
493
494        print_registers(t->pid);
495
496        if(ptrace(PTRACE_SINGLESTEP, t->pid, NULL, NULL) < 0)
497        {
498            fprintf(stderr, "PTRACE_SINGLESTEP failed\n");
499            return -1;
500        }
501        waitpid(t->pid, &status, 0);
502
503        if(WIFEXITED(status))
504            return 0;
505
506        if(!WIFSTOPPED(status) || WSTOPSIG(status) != SIGTRAP)
507            continue;
508
509        /* Fuck Linux: there is no macro for this */
510        switch((status >> 16) & 0xffff)
511        {
512        case PTRACE_EVENT_FORK:
513            if(ptrace(PTRACE_GETEVENTMSG, t->pid, 0, &t->child) < 0)
514            {
515                fprintf(stderr, "PTRACE_GETEVENTMSG failed\n");
516                return -1;
517            }
518            continue;
519        case PTRACE_EVENT_EXIT:
520            /* The process is about to exit, don't do anything else */
521            return 0;
522        }
523
524        break;
525    }
526
527    print_registers(t->pid);
528
529    if(ptrace(PTRACE_GETREGS, t->pid, NULL, &regs) < 0)
530    {
531        fprintf(stderr, "PTRACE_GETREGS failed\n");
532        return -1;
533    }
534
535    if(ptrace(PTRACE_SETREGS, t->pid, NULL, &oldregs) < 0)
536    {
537        fprintf(stderr, "PTRACE_SETREGS failed\n");
538        return -1;
539    }
540    print_registers(t->pid);
541
542    debug("syscall %s returned %ld", syscallnames[call], regs.RAX);
543
544    if((long)regs.RAX < 0)
545    {
546        errno = -(long)regs.RAX;
547        perror("syscall");
548        return -1;
549    }
550
551    return regs.RAX;
552}
553
554/* For debugging purposes only. Prints register and stack information. */
555#if defined DEBUG
556static void print_registers(pid_t pid)
557{
558    union { long int l; unsigned char data[sizeof(long int)]; } inst;
559    struct user_regs_struct regs;
560    int i;
561
562    if(ptrace(PTRACE_GETREGS, pid, NULL, &regs) < 0)
563    {
564        perror("ptrace_getregs");
565        exit(errno);
566    }
567
568    fprintf(stderr, "  / %s: "FMT"   ", STRINGIFY(RAX), regs.RAX);
569    fprintf(stderr, "%s: "FMT"\n", STRINGIFY(RBX), regs.RBX);
570    fprintf(stderr, "  | %s: "FMT"   ", STRINGIFY(RCX), regs.RCX);
571    fprintf(stderr, "%s: "FMT"\n", STRINGIFY(RDX), regs.RDX);
572    fprintf(stderr, "  | %s: "FMT"   ", STRINGIFY(RDI), regs.RDI);
573    fprintf(stderr, "%s: "FMT"\n", STRINGIFY(RSI), regs.RSI);
574    fprintf(stderr, "  | %s: "FMT"   ", STRINGIFY(RSP), regs.RSP);
575    fprintf(stderr, "%s: "FMT"\n", STRINGIFY(RIP), regs.RIP);
576
577    inst.l = ptrace(PTRACE_PEEKTEXT, pid, regs.RIP - 4, 0);
578    fprintf(stderr, "  | code: ... %02x %02x %02x %02x <---> ",
579            inst.data[0], inst.data[1], inst.data[2], inst.data[3]);
580    inst.l = ptrace(PTRACE_PEEKTEXT, pid, regs.RIP, 0);
581    fprintf(stderr, "%02x %02x %02x %02x ...\n",
582            inst.data[0], inst.data[1], inst.data[2], inst.data[3]);
583
584    fprintf(stderr, \\ stack: ... ");
585    for(i = -16; i < 24; i += sizeof(long))
586    {
587        inst.l = ptrace(PTRACE_PEEKDATA, pid, regs.RSP + i, 0);
588#if defined __x86_64__
589        fprintf(stderr, "%02x %02x %02x %02x %02x %02x %02x %02x ",
590                inst.data[0], inst.data[1], inst.data[2], inst.data[3],
591                inst.data[4], inst.data[5], inst.data[6], inst.data[7]);
592#else
593        fprintf(stderr, "%02x %02x %02x %02x ",
594                inst.data[0], inst.data[1], inst.data[2], inst.data[3]);
595#endif
596        if(i == 0)
597            fprintf(stderr, "[%s] ", STRINGIFY(RSP));
598    }
599    fprintf(stderr, "...\n");
600}
601#endif /* DEBUG */
602
603#endif /* USE_GRAB */
604
Note: See TracBrowser for help on using the repository browser.