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

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