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

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