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

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