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

Last change on this file since 3044 was 3044, checked in by Pascal Terjan, 13 years ago
  • APPLE should be handled like other cases where USE_GRAB is not defined (please test)
  • Property svn:eol-style set to native
File size: 15.5 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 <limits.h>
21#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24
25#if defined USE_GRAB
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
49#define X(x) #x
50#define STRINGIFY(x) X(x)
51
52#define SYSCALL_X86     0x80cd  /* CD 80 = int $0x80 */
53#define SYSCALL_X86_NEW 0xf3eb  /* EB F3 = jmp <__kernel_vsyscall+0x3> */
54#define SYSENTER        0x340f  /* 0F 34 = sysenter */
55#define SYSCALL_AMD64   0x050fL /* 0F 05 = syscall */
56
57#if defined __x86_64__
58#   define RAX rax
59#   define RBX rbx
60#   define RCX rcx
61#   define RDX rdx
62#   define RSP rsp
63#   define RBP rbp
64#   define RIP rip
65#   define RDI rdi
66#   define RSI rsi
67#   define FMT "%016lx"
68#else
69#   define RAX eax
70#   define RBX ebx
71#   define RCX ecx
72#   define RDX edx
73#   define RSP esp
74#   define RBP ebp
75#   define RIP eip
76#   define RDI edi
77#   define RSI esi
78#   define FMT "%08lx"
79#endif
80
81#define MYCALL_OPEN     0
82#define MYCALL_CLOSE    1
83#define MYCALL_WRITE    2
84#define MYCALL_DUP2     3
85#define MYCALL_SETPGID  4
86#define MYCALL_SETSID   5
87#define MYCALL_KILL     6
88#define MYCALL_FORK     7
89#define MYCALL_EXIT     8
90#define MYCALL_EXECVE   9
91
92#if defined __x86_64__
93/* from unistd_32.h on an amd64 system */
94int syscalls32[] = { 5, 6, 4, 63, 57, 66, 37, 2, 1, 11 };
95int syscalls64[] =
96#else
97int syscalls32[] =
98#endif
99    { SYS_open, SYS_close, SYS_write, SYS_dup2, SYS_setpgid, SYS_setsid,
100      SYS_kill, SYS_fork, SYS_exit, SYS_execve };
101
102char const *syscallnames[] =
103    { "open", "close", "write", "dup2", "setpgid", "setsid", "kill", "fork",
104      "exit", "execve" };
105
106#endif /* USE_GRAB */
107
108struct mytrace
109{
110    pid_t pid, child;
111};
112
113struct mytrace* mytrace_attach(long int pid)
114{
115#if defined USE_GRAB
116    struct mytrace *t;
117    int status;
118
119    if(ptrace(PTRACE_ATTACH, pid, 0, 0) < 0)
120    {
121        perror("ptrace_attach");
122        return NULL;
123    }
124    if(waitpid(pid, &status, 0) < 0)
125    {
126        perror("waitpid");
127        return NULL;
128    }
129    if(!WIFSTOPPED(status))
130    {
131        fprintf(stderr, "traced process was not stopped\n");
132        ptrace(PTRACE_DETACH, pid, 0, 0);
133        return NULL;
134    }
135
136    t = malloc(sizeof(struct mytrace));
137    t->pid = pid;
138    t->child = 0;
139
140    return t;
141#else
142    errno = ENOSYS;
143    return NULL;
144#endif
145}
146
147struct mytrace* mytrace_fork(struct mytrace *t)
148{
149#if defined USE_GRAB
150    struct mytrace *child;
151
152    ptrace(PTRACE_SETOPTIONS, t->pid, NULL, PTRACE_O_TRACEFORK);
153    remote_syscall(t, MYCALL_FORK, 0, 0, 0);
154    waitpid(t->child, NULL, 0);
155
156    child = malloc(sizeof(struct mytrace));
157    child->pid = t->child;
158    child->child = 0;
159
160    return child;
161#else
162    errno = ENOSYS;
163    return NULL;
164#endif
165}
166
167int mytrace_detach(struct mytrace *t)
168{
169#if defined USE_GRAB
170    ptrace(PTRACE_DETACH, t->pid, 0, 0);
171    free(t);
172
173    return 0;
174#else
175    errno = ENOSYS;
176    return -1;
177#endif
178}
179
180long mytrace_getpid(struct mytrace *t)
181{
182#if defined USE_GRAB
183    return t->pid;
184#else
185    errno = ENOSYS;
186    return -1;
187#endif
188}
189
190int mytrace_open(struct mytrace *t, char const *path, int mode)
191{
192#if defined USE_GRAB
193    char backup_data[4096];
194    struct user_regs_struct regs;
195    size_t size = strlen(path) + 1;
196    int ret;
197
198    if(ptrace(PTRACE_GETREGS, t->pid, NULL, &regs) < 0)
199    {
200        fprintf(stderr, "PTRACE_GETREGS failed\n");
201        return errno;
202    }
203
204    /* Backup the data that we will use */
205    if(memcpy_from_target(t, backup_data, regs.RSP, size) < 0)
206        return -1;
207
208    memcpy_into_target(t, regs.RSP, path, size);
209
210    ret = remote_syscall(t, MYCALL_OPEN, regs.RSP, O_RDWR, 0755);
211
212    /* Restore the data */
213    memcpy_into_target(t, regs.RSP, backup_data, size);
214
215    if(ret < 0)
216    {
217        errno = ret;
218        return -1;
219    }
220
221    return ret;
222#else
223    errno = ENOSYS;
224    return -1;
225#endif
226}
227
228int mytrace_close(struct mytrace *t, int fd)
229{
230#if defined USE_GRAB
231    return remote_syscall(t, MYCALL_CLOSE, fd, 0, 0);
232#else
233    errno = ENOSYS;
234    return -1;
235#endif
236}
237
238int mytrace_write(struct mytrace *t, int fd, char const *data, size_t len)
239{
240#if defined USE_GRAB
241    struct user_regs_struct regs;
242    char *backup_data;
243    int ret;
244
245    if(ptrace(PTRACE_GETREGS, t->pid, NULL, &regs) < 0)
246    {
247        fprintf(stderr, "PTRACE_GETREGS failed\n");
248        return errno;
249    }
250
251    backup_data = malloc(len);
252
253    /* Backup the data that we will use */
254    if(memcpy_from_target(t, backup_data, regs.RSP, len) < 0)
255        return -1;
256
257    memcpy_into_target(t, regs.RSP, data, len);
258
259    ret = remote_syscall(t, MYCALL_WRITE, fd, regs.RSP, len);
260
261    /* Restore the data */
262    memcpy_into_target(t, regs.RSP, backup_data, len);
263
264    if(ret < 0)
265    {
266        errno = ret;
267        return -1;
268    }
269
270    return ret;
271#else
272    errno = ENOSYS;
273    return -1;
274#endif
275}
276
277int mytrace_dup2(struct mytrace *t, int oldfd, int newfd)
278{
279#if defined USE_GRAB
280    return remote_syscall(t, MYCALL_DUP2, oldfd, newfd, 0);
281#else
282    errno = ENOSYS;
283    return -1;
284#endif
285}
286
287int mytrace_setpgid(struct mytrace *t, long pid, long pgid)
288{
289#if defined USE_GRAB
290    return remote_syscall(t, MYCALL_SETPGID, pid, pgid, 0);
291#else
292    errno = ENOSYS;
293    return -1;
294#endif
295}
296
297int mytrace_setsid(struct mytrace *t)
298{
299#if defined USE_GRAB
300    return remote_syscall(t, MYCALL_SETSID, 0, 0, 0);
301#else
302    errno = ENOSYS;
303    return -1;
304#endif
305}
306
307int mytrace_kill(struct mytrace *t, long pid, int sig)
308{
309#if defined USE_GRAB
310    return remote_syscall(t, MYCALL_KILL, pid, sig, 0);
311#else
312    errno = ENOSYS;
313    return -1;
314#endif
315}
316
317int mytrace_exit(struct mytrace *t, int status)
318{
319#if defined USE_GRAB
320    ptrace(PTRACE_SETOPTIONS, t->pid, NULL, PTRACE_O_TRACEEXIT);
321    return remote_syscall(t, MYCALL_EXIT, status, 0, 0);
322#else
323    errno = ENOSYS;
324    return -1;
325#endif
326}
327
328int mytrace_exec(struct mytrace *t, char const *command)
329{
330#if defined USE_GRAB
331    struct user_regs_struct regs;
332    char *env;
333    char envpath[PATH_MAX+1];
334    ssize_t envsize = 32*1024;
335    int ret, fd, l;
336    ssize_t r;
337
338    if(ptrace(PTRACE_GETREGS, t->pid, NULL, &regs) < 0)
339    {
340        fprintf(stderr, "PTRACE_GETREGS failed\n");
341        return errno;
342    }
343
344    env = malloc(envsize);
345    if (!env)
346        return -1;
347
348    snprintf(envpath, PATH_MAX, "/proc/%d/environ", t->pid);
349
350    fd = open(envpath, O_RDONLY);
351    if (fd == -1)
352        return -1;
353    r = read(fd, env, envsize);
354    close(fd);
355    if (r == -1)
356        return -1;
357    while (r == envsize)
358    {
359        free(env);
360        env = malloc(envsize);
361        if (!env)
362            return -1;
363        fd = open(envpath, O_RDONLY);
364        r = read(fd, env, envsize);
365        close(fd);
366        if (r == -1)
367            return -1;
368    }
369    l = strlen(command)+1;
370    memcpy_into_target(t, regs.RSP, command, l);
371    memcpy_into_target(t, regs.RSP+l, env, envsize);
372    free(env);
373    ret = remote_syscall(t, MYCALL_EXECVE, regs.RSP, 0, regs.RSP+l);
374
375    if(ret < 0)
376    {
377        errno = ret;
378        return -1;
379    }
380
381    return ret;
382#else
383    errno = ENOSYS;
384    return -1;
385#endif
386}
387
388/*
389 * XXX: the following functions are local
390 */
391
392#if defined USE_GRAB
393static int memcpy_from_target(struct mytrace *t,
394                              char* dest, long src, size_t n)
395{
396    static int const align = sizeof(long) - 1;
397
398    while(n)
399    {
400        long data;
401        size_t todo = sizeof(long) - (src & align);
402
403        if(n < todo)
404            todo = n;
405
406        data = ptrace(PTRACE_PEEKTEXT, t->pid, src - (src & align), 0);
407        if(errno)
408        {
409            perror("ptrace_peektext");
410            return -1;
411        }
412        memcpy(dest, (char *)&data + (src & align), todo);
413
414        dest += todo;
415        src += todo;
416        n -= todo;
417    }
418
419    return 0;
420}
421
422static int memcpy_into_target(struct mytrace *t,
423                              long dest, char const *src, size_t n)
424{
425    static int const align = sizeof(long) - 1;
426
427    while(n)
428    {
429        long data;
430        size_t todo = sizeof(long) - (dest & align);
431
432        if(n < todo)
433            todo = n;
434        if(todo != sizeof(long))
435        {
436            data = ptrace(PTRACE_PEEKTEXT, t->pid, dest - (dest & align), 0);
437            if(errno)
438            {
439                perror("ptrace_peektext");
440                return -1;
441            }
442        }
443
444        memcpy((char *)&data + (dest & align), src, todo);
445        ptrace(PTRACE_POKETEXT, t->pid, dest - (dest & align), data);
446        if(errno)
447        {
448            perror("ptrace_poketext");
449            return -1;
450        }
451
452        src += todo;
453        dest += todo;
454        n -= todo;
455    }
456
457    return 0;
458}
459
460static long remote_syscall(struct mytrace *t, long call,
461                           long arg1, long arg2, long arg3)
462{
463    /* Method for remote syscall:
464     *  - wait until the traced application exits from a syscall
465     *  - save registers
466     *  - rewind eip/rip to point on the syscall instruction
467     *  - single step: execute syscall instruction
468     *  - retrieve resulting registers
469     *  - restore registers */
470    struct user_regs_struct regs, oldregs;
471    long oinst;
472    int bits;
473    int offset = 2;
474
475    if(call < 0 || call >= (long)(sizeof(syscallnames)/sizeof(*syscallnames)))
476    {
477        fprintf(stderr, "unknown remote syscall %li\n", call);
478        return -1;
479    }
480
481    debug("remote syscall %s(%lu, %lu, %lu)",
482          syscallnames[call], arg1, arg2, arg3);
483
484    print_registers(t->pid);
485
486#if defined __x86_64__
487    bits = 64;
488#else
489    bits = 32;
490#endif
491
492    for(;;)
493    {
494        if(ptrace(PTRACE_GETREGS, t->pid, NULL, &oldregs) < 0)
495        {
496            fprintf(stderr, "PTRACE_GETREGS failed\n");
497            return -1;
498        }
499
500        oinst = ptrace(PTRACE_PEEKTEXT, t->pid, oldregs.RIP - 2, 0) & 0xffff;
501        fprintf(stderr, "%lx\n", oinst);
502
503#if defined __x86_64__
504        if(oinst == SYSCALL_AMD64)
505            break;
506        if(oinst == SYSCALL_X86 || oinst == SYSCALL_X86_NEW)
507        {
508            bits = 32;
509            break;
510        }
511#else
512        if(oinst == SYSCALL_X86 || oinst == SYSCALL_X86_NEW)
513            break;
514#endif
515
516        if(ptrace(PTRACE_SYSCALL, t->pid, NULL, 0) < 0)
517        {
518            perror("ptrace_syscall (1)");
519            return -1;
520        }
521        waitpid(t->pid, NULL, 0);
522        if(ptrace(PTRACE_SYSCALL, t->pid, NULL, 0) < 0)
523        {
524            perror("ptrace_syscall (2)");
525            return -1;
526        }
527        waitpid(t->pid, NULL, 0);
528    }
529
530    print_registers(t->pid);
531
532    if(oinst == SYSCALL_X86_NEW)
533    {
534        /*  Get back to sysenter */
535        while((ptrace(PTRACE_PEEKTEXT, t->pid, oldregs.RIP - offset, 0) & 0xffff) != 0x340f)
536            offset++;
537        oldregs.RBP = oldregs.RSP;
538    }
539
540    regs = oldregs;
541    regs.RIP = regs.RIP - offset;
542#if defined __x86_64__
543    if(bits == 64)
544    {
545        regs.RAX = syscalls64[call];
546        regs.RDI = arg1;
547        regs.RSI = arg2;
548        regs.RDX = arg3;
549    }
550    else
551#endif
552    {
553        regs.RAX = syscalls32[call];
554        regs.RBX = arg1;
555        regs.RCX = arg2;
556        regs.RDX = arg3;
557    }
558
559    if(ptrace(PTRACE_SETREGS, t->pid, NULL, &regs) < 0)
560    {
561        fprintf(stderr, "PTRACE_SETREGS failed\n");
562        return -1;
563    }
564
565    for(;;)
566    {
567        int status;
568
569        print_registers(t->pid);
570
571        if(ptrace(PTRACE_SINGLESTEP, t->pid, NULL, NULL) < 0)
572        {
573            fprintf(stderr, "PTRACE_SINGLESTEP failed\n");
574            return -1;
575        }
576        waitpid(t->pid, &status, 0);
577
578        if(WIFEXITED(status))
579            return 0;
580
581        if(!WIFSTOPPED(status) || WSTOPSIG(status) != SIGTRAP)
582            continue;
583
584        /* Fuck Linux: there is no macro for this */
585        switch((status >> 16) & 0xffff)
586        {
587        case PTRACE_EVENT_FORK:
588            if(ptrace(PTRACE_GETEVENTMSG, t->pid, 0, &t->child) < 0)
589            {
590                fprintf(stderr, "PTRACE_GETEVENTMSG failed\n");
591                return -1;
592            }
593            debug("PTRACE_GETEVENTMSG %d", t->child);
594            continue;
595        case PTRACE_EVENT_EXIT:
596            debug("PTRACE_EVENT_EXIT");
597            /* The process is about to exit, don't do anything else */
598            return 0;
599        }
600
601        break;
602    }
603
604    print_registers(t->pid);
605
606    if(ptrace(PTRACE_GETREGS, t->pid, NULL, &regs) < 0)
607    {
608        fprintf(stderr, "PTRACE_GETREGS failed\n");
609        return -1;
610    }
611
612    if(ptrace(PTRACE_SETREGS, t->pid, NULL, &oldregs) < 0)
613    {
614        fprintf(stderr, "PTRACE_SETREGS failed\n");
615        return -1;
616    }
617    print_registers(t->pid);
618
619    debug("syscall %s returned %ld", syscallnames[call], regs.RAX);
620
621    if((long)regs.RAX < 0)
622    {
623        errno = -(long)regs.RAX;
624        perror("syscall");
625        return -1;
626    }
627
628    return regs.RAX;
629}
630
631/* For debugging purposes only. Prints register and stack information. */
632#if defined DEBUG
633static void print_registers(pid_t pid)
634{
635    union { long int l; unsigned char data[sizeof(long int)]; } inst;
636    struct user_regs_struct regs;
637    int i;
638
639    if(ptrace(PTRACE_GETREGS, pid, NULL, &regs) < 0)
640    {
641        perror("ptrace_getregs");
642        exit(errno);
643    }
644
645    fprintf(stderr, "  / %s: "FMT"   ", STRINGIFY(RAX), regs.RAX);
646    fprintf(stderr, "%s: "FMT"\n", STRINGIFY(RBX), regs.RBX);
647    fprintf(stderr, "  | %s: "FMT"   ", STRINGIFY(RCX), regs.RCX);
648    fprintf(stderr, "%s: "FMT"\n", STRINGIFY(RDX), regs.RDX);
649    fprintf(stderr, "  | %s: "FMT"   ", STRINGIFY(RDI), regs.RDI);
650    fprintf(stderr, "%s: "FMT"\n", STRINGIFY(RSI), regs.RSI);
651    fprintf(stderr, "  | %s: "FMT"   ", STRINGIFY(RSP), regs.RSP);
652    fprintf(stderr, "%s: "FMT"\n", STRINGIFY(RIP), regs.RIP);
653
654    inst.l = ptrace(PTRACE_PEEKTEXT, pid, regs.RIP - 4, 0);
655    fprintf(stderr, "  | code: ... %02x %02x %02x %02x <---> ",
656            inst.data[0], inst.data[1], inst.data[2], inst.data[3]);
657    inst.l = ptrace(PTRACE_PEEKTEXT, pid, regs.RIP, 0);
658    fprintf(stderr, "%02x %02x %02x %02x ...\n",
659            inst.data[0], inst.data[1], inst.data[2], inst.data[3]);
660
661    fprintf(stderr, "  \\ stack: ... ");
662    for(i = -16; i < 24; i += sizeof(long))
663    {
664        inst.l = ptrace(PTRACE_PEEKDATA, pid, regs.RSP + i, 0);
665#if defined __x86_64__
666        fprintf(stderr, "%02x %02x %02x %02x %02x %02x %02x %02x ",
667                inst.data[0], inst.data[1], inst.data[2], inst.data[3],
668                inst.data[4], inst.data[5], inst.data[6], inst.data[7]);
669#else
670        fprintf(stderr, "%02x %02x %02x %02x ",
671                inst.data[0], inst.data[1], inst.data[2], inst.data[3]);
672#endif
673        if(i == 0)
674            fprintf(stderr, "[%s] ", STRINGIFY(RSP));
675    }
676    fprintf(stderr, "...\n");
677}
678#endif /* DEBUG */
679
680#endif /* USE_GRAB */
Note: See TracBrowser for help on using the repository browser.