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

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