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

Last change on this file since 2514 was 2514, checked in by sam, 6 years ago
  • Add kill() to the list of supported remote syscalls. We might need to send SIGCHLD to the grabbed process's parent.
  • Property svn:eol-style set to native
File size: 11.4 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 USE_GRAB
37static int memcpy_from_target(struct mytrace *t,
38                              char* dest, long src, size_t n);
39static int memcpy_into_target(struct mytrace *t,
40                              long dest, char const *src, size_t n);
41static long remote_syscall(pid_t pid, long call,
42                           long arg1, long arg2, long arg3);
43#   if defined DEBUG
44static void print_registers(pid_t pid);
45#   else
46#       define print_registers(x) do {} while(0)
47#   endif
48#endif
49
50#define X(x) #x
51#define STRINGIFY(x) X(x)
52
53#define SYSCALL_X86   0x80cd  /* CD 80 = int $0x80 */
54#define SYSCALL_AMD64 0x050fL /* 0F 05 = syscall */
55
56#define MYCALL_OPEN     0
57#define MYCALL_CLOSE    1
58#define MYCALL_WRITE    2
59#define MYCALL_DUP2     3
60#define MYCALL_SETPGID  4
61#define MYCALL_SETSID   5
62#define MYCALL_KILL     6
63
64#if defined __x86_64__
65#   define RAX rax
66#   define RBX rbx
67#   define RCX rcx
68#   define RDX rdx
69#   define RSP rsp
70#   define RIP rip
71#   define RDI rdi
72#   define RSI rsi
73#   define FMT "%016lx"
74#else
75#   define RAX eax
76#   define RBX ebx
77#   define RCX ecx
78#   define RDX edx
79#   define RSP esp
80#   define RIP eip
81#   define RDI edi
82#   define RSI esi
83#   define FMT "%08lx"
84#endif
85
86#if defined __x86_64__
87int syscalls32[] =
88    { 5, 6, 4, 63, 57, 66, 37 }; /* from unistd_32.h on an amd64 system */
89int syscalls64[] =
90#else
91int syscalls32[] =
92#endif
93    { SYS_open, SYS_close, SYS_write, SYS_dup2, SYS_setpgid, SYS_setsid,
94      SYS_kill };
95
96struct mytrace
97{
98    pid_t pid;
99};
100
101struct mytrace* mytrace_attach(long int pid)
102{
103#if defined USE_GRAB
104    struct mytrace *t;
105
106    if(ptrace(PTRACE_ATTACH, pid, 0, 0) < 0)
107    {
108        perror("ptrace_attach");
109        return NULL;
110    }
111    waitpid(pid, NULL, 0);
112
113    t = malloc(sizeof(struct mytrace));
114    t->pid = pid;
115
116    return t;
117#else
118    errno = ENOSYS;
119    return NULL;
120#endif
121}
122
123int mytrace_detach(struct mytrace *t)
124{
125#if defined USE_GRAB
126    ptrace(PTRACE_DETACH, t->pid, 0, 0);
127    free(t);
128
129    return 0;
130#else
131    errno = ENOSYS;
132    return -1;
133#endif
134}
135
136int mytrace_open(struct mytrace *t, char const *path, int mode)
137{
138#if defined USE_GRAB
139    char backup_data[4096];
140    struct user_regs_struct regs;
141    size_t size = strlen(path) + 1;
142    int ret;
143
144    if(ptrace(PTRACE_GETREGS, t->pid, NULL, &regs) < 0)
145    {
146        fprintf(stderr, "PTRACE_GETREGS failed\n");
147        return errno;
148    }
149
150    /* Backup the data that we will use */
151    if(memcpy_from_target(t, backup_data, regs.RSP, size) < 0)
152        return -1;
153
154    memcpy_into_target(t, regs.RSP, path, size);
155
156    ret = remote_syscall(t->pid, MYCALL_OPEN, regs.RSP, O_RDWR, 0755);
157
158    /* Restore the data */
159    memcpy_into_target(t, regs.RSP, backup_data, size);
160
161    if(ret < 0)
162    {
163        errno = ret;
164        return -1;
165    }
166
167    return ret;
168#else
169    errno = ENOSYS;
170    return -1;
171#endif
172}
173
174int mytrace_close(struct mytrace *t, int fd)
175{
176#if defined USE_GRAB
177    return remote_syscall(t->pid, MYCALL_CLOSE, fd, 0, 0);
178#else
179    errno = ENOSYS;
180    return -1;
181#endif
182}
183
184int mytrace_write(struct mytrace *t, int fd, char const *data, size_t len)
185{
186#if defined USE_GRAB
187    struct user_regs_struct regs;
188    char *backup_data;
189    int ret;
190
191    if(ptrace(PTRACE_GETREGS, t->pid, NULL, &regs) < 0)
192    {
193        fprintf(stderr, "PTRACE_GETREGS failed\n");
194        return errno;
195    }
196
197    backup_data = malloc(len);
198
199    /* Backup the data that we will use */
200    if(memcpy_from_target(t, backup_data, regs.RSP, len) < 0)
201        return -1;
202
203    memcpy_into_target(t, regs.RSP, data, len);
204
205    ret = remote_syscall(t->pid, MYCALL_WRITE, fd, regs.RSP, len);
206
207    /* Restore the data */
208    memcpy_into_target(t, regs.RSP, backup_data, len);
209
210    if(ret < 0)
211    {
212        errno = ret;
213        return -1;
214    }
215
216    return ret;
217#else
218    errno = ENOSYS;
219    return -1;
220#endif
221}
222
223int mytrace_dup2(struct mytrace *t, int oldfd, int newfd)
224{
225#if defined USE_GRAB
226    return remote_syscall(t->pid, MYCALL_DUP2, oldfd, newfd, 0);
227#else
228    errno = ENOSYS;
229    return -1;
230#endif
231}
232
233int mytrace_setpgid(struct mytrace *t, long pid, long pgid)
234{
235#if defined USE_GRAB
236    return remote_syscall(t->pid, MYCALL_SETPGID, pid, pgid, 0);
237#else
238    errno = ENOSYS;
239    return -1;
240#endif
241}
242
243int mytrace_setsid(struct mytrace *t)
244{
245#if defined USE_GRAB
246    return remote_syscall(t->pid, MYCALL_SETSID, 0, 0, 0);
247#else
248    errno = ENOSYS;
249    return -1;
250#endif
251}
252
253int mytrace_kill(struct mytrace *t, long pid, int sig)
254{
255#if defined USE_GRAB
256    return remote_syscall(t->pid, MYCALL_KILL, pid, sig, 0);
257#else
258    errno = ENOSYS;
259    return -1;
260#endif
261}
262
263/*
264 * XXX: the following functions are local
265 */
266
267#if defined USE_GRAB
268static int memcpy_from_target(struct mytrace *t,
269                              char* dest, long src, size_t n)
270{
271    static int const align = sizeof(long) - 1;
272
273    while(n)
274    {
275        long data;
276        size_t todo = sizeof(long) - (src & align);
277
278        if(n < todo)
279            todo = n;
280
281        data = ptrace(PTRACE_PEEKTEXT, t->pid, src - (src & align), 0);
282        if(errno)
283        {
284            perror("ptrace_peektext");
285            return -1;
286        }
287        memcpy(dest, (char *)&data + (src & align), todo);
288
289        dest += todo;
290        src += todo;
291        n -= todo;
292    }
293
294    return 0;
295}
296
297static int memcpy_into_target(struct mytrace *t,
298                              long dest, char const *src, size_t n)
299{
300    static int const align = sizeof(long) - 1;
301
302    while(n)
303    {
304        long data;
305        size_t todo = sizeof(long) - (dest & align);
306
307        if(n < todo)
308            todo = n;
309        if(todo != sizeof(long))
310        {
311            data = ptrace(PTRACE_PEEKTEXT, t->pid, dest - (dest & align), 0);
312            if(errno)
313            {
314                perror("ptrace_peektext");
315                return -1;
316            }
317        }
318
319        memcpy((char *)&data + (dest & align), src, todo);
320        ptrace(PTRACE_POKETEXT, t->pid, dest - (dest & align), data);
321        if(errno)
322        {
323            perror("ptrace_poketext");
324            return -1;
325        }
326
327        src += todo;
328        dest += todo;
329        n -= todo;
330    }
331
332    return 0;
333}
334
335static long remote_syscall(pid_t pid, long call,
336                            long arg1, long arg2, long arg3)
337{
338    /* Method for remote syscall:
339     *  - wait until the traced application exits from a syscall
340     *  - save registers
341     *  - rewind eip/rip to point on the syscall instruction
342     *  - single step: execute syscall instruction
343     *  - retrieve resulting registers
344     *  - restore registers */
345    struct user_regs_struct regs, oldregs;
346    long oinst;
347    int bits;
348
349print_registers(pid);
350#if defined __x86_64__
351    bits = 64;
352#else
353    bits = 32;
354#endif
355
356    for(;;)
357    {
358        if(ptrace(PTRACE_GETREGS, pid, NULL, &oldregs) < 0)
359        {
360            fprintf(stderr, "PTRACE_GETREGS failed\n");
361            return -1;
362        }
363
364        oinst = ptrace(PTRACE_PEEKTEXT, pid, oldregs.RIP - 2, 0) & 0xffff;
365
366#if defined __x86_64__
367        if(oinst == SYSCALL_AMD64)
368            break;
369        if(oinst == SYSCALL_X86)
370        {
371            bits = 32;
372            break;
373        }
374#else
375        if(oinst == SYSCALL_X86)
376            break;
377#endif
378
379        if(ptrace(PTRACE_SYSCALL, pid, NULL, 0) < 0)
380        {
381            perror("ptrace_syscall (1)");
382            return -1;
383        }
384        waitpid(pid, NULL, 0);
385
386        if(ptrace(PTRACE_SYSCALL, pid, NULL, 0) < 0)
387        {
388            perror("ptrace_syscall (2)");
389            return -1;
390        }
391        waitpid(pid, NULL, 0);
392    }
393
394    print_registers(pid);
395
396    regs = oldregs;
397    regs.RIP = regs.RIP - 2;
398#if defined __x86_64__
399    if(bits == 64)
400    {
401        regs.RAX = syscalls64[call];
402        regs.RDI = arg1;
403        regs.RSI = arg2;
404        regs.RDX = arg3;
405    }
406    else
407#endif
408    {
409        regs.RAX = syscalls32[call];
410        regs.RBX = arg1;
411        regs.RCX = arg2;
412        regs.RDX = arg3;
413    }
414
415    if(ptrace(PTRACE_SETREGS, pid, NULL, &regs) < 0)
416    {
417        fprintf(stderr, "PTRACE_SETREGS failed\n");
418        return -1;
419    }
420
421    print_registers(pid);
422
423    if(ptrace(PTRACE_SINGLESTEP, pid, NULL, NULL) < 0)
424    {
425        fprintf(stderr, "PTRACE_SINGLESTEP failed\n");
426        return -1;
427    }
428    waitpid(pid, NULL, 0);
429
430    print_registers(pid);
431
432    if(ptrace(PTRACE_GETREGS, pid, NULL, &regs) < 0)
433    {
434        fprintf(stderr, "PTRACE_GETREGS failed\n");
435        return -1;
436    }
437
438    if(ptrace(PTRACE_SETREGS, pid, NULL, &oldregs) < 0)
439    {
440        fprintf(stderr, "PTRACE_SETREGS failed\n");
441        return -1;
442    }
443    print_registers(pid);
444
445    debug("syscall %ld returned %ld", call, regs.RAX);
446
447    if((long)regs.RAX < 0)
448    {
449        errno = -(long)regs.RAX;
450        perror("syscall");
451        return -1;
452    }
453
454    return regs.RAX;
455}
456
457/* For debugging purposes only. Prints register and stack information. */
458#if defined DEBUG
459static void print_registers(pid_t pid)
460{
461    union { long int l; unsigned char data[sizeof(long int)]; } inst;
462    struct user_regs_struct regs;
463    int i;
464
465    if(ptrace(PTRACE_GETREGS, pid, NULL, &regs) < 0)
466    {
467        perror("ptrace_getregs");
468        exit(errno);
469    }
470
471    fprintf(stderr, "  / %s: "FMT"   ", STRINGIFY(RAX), regs.RAX);
472    fprintf(stderr, "%s: "FMT"\n", STRINGIFY(RBX), regs.RBX);
473    fprintf(stderr, "  | %s: "FMT"   ", STRINGIFY(RCX), regs.RCX);
474    fprintf(stderr, "%s: "FMT"\n", STRINGIFY(RDX), regs.RDX);
475    fprintf(stderr, "  | %s: "FMT"   ", STRINGIFY(RDI), regs.RDI);
476    fprintf(stderr, "%s: "FMT"\n", STRINGIFY(RSI), regs.RSI);
477    fprintf(stderr, "  | %s: "FMT"   ", STRINGIFY(RSP), regs.RSP);
478    fprintf(stderr, "%s: "FMT"\n", STRINGIFY(RIP), regs.RIP);
479
480    inst.l = ptrace(PTRACE_PEEKTEXT, pid, regs.RIP - 4, 0);
481    fprintf(stderr, "  | code: ... %02x %02x %02x %02x <---> ",
482            inst.data[0], inst.data[1], inst.data[2], inst.data[3]);
483    inst.l = ptrace(PTRACE_PEEKTEXT, pid, regs.RIP, 0);
484    fprintf(stderr, "%02x %02x %02x %02x ...\n",
485            inst.data[0], inst.data[1], inst.data[2], inst.data[3]);
486
487    fprintf(stderr, \\ stack: ... ");
488    for(i = -16; i < 24; i += sizeof(long))
489    {
490        inst.l = ptrace(PTRACE_PEEKDATA, pid, regs.RSP + i, 0);
491#if defined __x86_64__
492        fprintf(stderr, "%02x %02x %02x %02x %02x %02x %02x %02x ",
493                inst.data[0], inst.data[1], inst.data[2], inst.data[3],
494                inst.data[4], inst.data[5], inst.data[6], inst.data[7]);
495#else
496        fprintf(stderr, "%02x %02x %02x %02x ",
497                inst.data[0], inst.data[1], inst.data[2], inst.data[3]);
498#endif
499        if(i == 0)
500            fprintf(stderr, "[%s] ", STRINGIFY(RSP));
501    }
502    fprintf(stderr, "...\n");
503}
504#endif /* DEBUG */
505
506#endif /* USE_GRAB */
507
Note: See TracBrowser for help on using the repository browser.