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

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