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

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