source: trunk/tools/neercs/old/mytrace.c @ 1642

Last change on this file since 1642 was 1642, checked in by sam, 8 years ago

neercs: import files from the (unfinished) old neercs code.

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