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

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