source: neercs/trunk/src/grab.c @ 2508

Last change on this file since 2508 was 2508, checked in by Sam Hocevar, 13 years ago
  • Fix memcpy_from_target() and memcpy_to_target() alignment issues.
  • Property svn:eol-style set to native
File size: 9.8 KB
Line 
1/*
2 *  neercs        console-based window manager
3 *  Copyright (c) 2008 Pascal Terjan
4 *                All Rights Reserved
5 *
6 *  $Id$
7 *
8 *  This program is free software. It comes without any warranty, to
9 *  the extent permitted by applicable law. You can redistribute it
10 *  and/or modify it under the terms of the Do What The Fuck You Want
11 *  To Public License, Version 2, as published by Sam Hocevar. See
12 *  http://sam.zoy.org/wtfpl/COPYING for more details.
13 */
14
15#include "config.h"
16
17#include <errno.h>
18#include <fcntl.h>
19#include <signal.h>
20#include <stdio.h>
21#include <stdlib.h>
22#include <string.h>
23#include <unistd.h>
24
25#if defined USE_GRAB
26#   include <sys/ioctl.h>
27#   include <sys/ptrace.h>
28#   include <sys/stat.h>
29#   include <sys/syscall.h>
30#   include <sys/user.h>
31#   include <sys/wait.h>
32
33#   include <linux/kdev_t.h>
34#   include <linux/major.h>
35#endif
36
37#include "neercs.h"
38
39#define X(x) #x
40#define STRINGIFY(x) X(x)
41
42#if defined __x86_64__
43#   define RAX rax
44#   define RBX rbx
45#   define RCX rcx
46#   define RDX rdx
47#   define RSP rsp
48#   define RIP rip
49#   define RDI rdi
50#   define RSI rsi
51#   define SYSCALL 0x050fL /* 0F 05 = syscall */
52#   define FMT "%016lx"
53#else
54#   define RAX eax
55#   define RBX ebx
56#   define RCX ecx
57#   define RDX edx
58#   define RSP esp
59#   define RIP eip
60#   define RDI edi
61#   define RSI esi
62#   define SYSCALL 0x80cd /* CD 80 = int $0x80 */
63#   define FMT "%08lx"
64#endif
65
66#if defined USE_GRAB
67/* For debugging purposes only. Prints register and stack information. */
68static void print_registers(pid_t pid)
69{
70    union { long int l; unsigned char data[sizeof(long int)]; } inst;
71    struct user_regs_struct regs;
72    int i;
73
74    if(ptrace(PTRACE_GETREGS, pid, NULL, &regs) < 0)
75    {
76        perror("ptrace_getregs");
77        exit(errno);
78    }
79
80    fprintf(stderr, "  / %s: "FMT"   ", STRINGIFY(RAX), regs.RAX);
81    fprintf(stderr, "%s: "FMT"\n", STRINGIFY(RBX), regs.RBX);
82    fprintf(stderr, "  | %s: "FMT"   ", STRINGIFY(RCX), regs.RCX);
83    fprintf(stderr, "%s: "FMT"\n", STRINGIFY(RDX), regs.RDX);
84    fprintf(stderr, "  | %s: "FMT"   ", STRINGIFY(RDI), regs.RDI);
85    fprintf(stderr, "%s: "FMT"\n", STRINGIFY(RSI), regs.RSI);
86    fprintf(stderr, "  | %s: "FMT"   ", STRINGIFY(RSP), regs.RSP);
87    fprintf(stderr, "%s: "FMT"\n", STRINGIFY(RIP), regs.RIP);
88
89    inst.l = ptrace(PTRACE_PEEKTEXT, pid, regs.RIP - 4, 0);
90    fprintf(stderr, "  | code: ... %02x %02x %02x %02x <---> ",
91            inst.data[0], inst.data[1], inst.data[2], inst.data[3]);
92    inst.l = ptrace(PTRACE_PEEKTEXT, pid, regs.RIP, 0);
93    fprintf(stderr, "%02x %02x %02x %02x ...\n",
94            inst.data[0], inst.data[1], inst.data[2], inst.data[3]);
95
96    fprintf(stderr, "  \\ stack: ... ");
97    for(i = -16; i < 24; i += sizeof(long))
98    {
99        inst.l = ptrace(PTRACE_PEEKDATA, pid, regs.RSP + i, 0);
100#if defined __x86_64__
101        fprintf(stderr, "%02x %02x %02x %02x %02x %02x %02x %02x ",
102                inst.data[0], inst.data[1], inst.data[2], inst.data[3],
103                inst.data[4], inst.data[5], inst.data[6], inst.data[7]);
104#else
105        fprintf(stderr, "%02x %02x %02x %02x ",
106                inst.data[0], inst.data[1], inst.data[2], inst.data[3]);
107#endif
108        if(i == 0)
109            fprintf(stderr, "[%s] ", STRINGIFY(RSP));
110    }
111    fprintf(stderr, "...\n");
112}
113
114static int memcpy_from_target(pid_t pid, char* dest, long src, size_t n)
115{
116    while(n)
117    {
118        long data;
119        int align = sizeof(long) - 1;
120        size_t todo = sizeof(long) - (src & align);
121
122        if(n < todo)
123            todo = n;
124
125        data = ptrace(PTRACE_PEEKTEXT, pid, src - (src & align), 0);
126        if(errno)
127        {
128            perror("ptrace_peektext");
129            return -1;
130        }
131        memcpy(dest, (char *)&data + (src & align), todo);
132
133        dest += todo;
134        src += todo;
135        n -= todo;
136    }
137
138    return 0;
139}
140
141static int memcpy_into_target(pid_t pid, long dest, char* src, size_t n)
142{
143    while(n)
144    {
145        long data;
146        int align = sizeof(long) - 1;
147        size_t todo = sizeof(long) - (dest & align);
148
149        if(n < todo)
150            todo = n;
151        if(todo != sizeof(long))
152        {
153            data = ptrace(PTRACE_PEEKTEXT, pid, dest - (dest & align), 0);
154            if(errno)
155            {
156                perror("ptrace_peektext");
157                return -1;
158            }
159        }
160
161        memcpy((char *)&data + (dest & align), src, todo);
162        ptrace(PTRACE_POKETEXT, pid, dest - (dest & align), data);
163        if(errno)
164        {
165            perror("ptrace_poketext");
166            return -1;
167        }
168
169        src += todo;
170        dest += todo;
171        n -= todo;
172    }
173
174    return 0;
175}
176
177static long do_syscall(pid_t pid, long call, long arg1, long arg2, long arg3)
178{
179    /* Method for remote syscall:
180     *  - wait until the traced application exits from a syscall
181     *  - save registers
182     *  - rewind eip/rip to point on the syscall instruction
183     *  - single step: execute syscall instruction
184     *  - restore registers */
185    struct user_regs_struct regs, oldregs;
186    long oinst;
187
188    for(;;)
189    {
190        if(ptrace(PTRACE_GETREGS, pid, NULL, &oldregs) < 0)
191        {
192            fprintf(stderr, "PTRACE_GETREGS failed\n");
193            return -1;
194        }
195
196        oinst = ptrace(PTRACE_PEEKTEXT, pid, oldregs.RIP - 2, 0) & 0xffff;
197
198        if(oinst == SYSCALL)
199            break;
200
201        if(ptrace(PTRACE_SYSCALL, pid, NULL, 0) < 0)
202        {
203            perror("ptrace_syscall (1)");
204            return -1;
205        }
206        waitpid(pid, NULL, 0);
207
208        if(ptrace(PTRACE_SYSCALL, pid, NULL, 0) < 0)
209        {
210            perror("ptrace_syscall (2)");
211            return -1;
212        }
213        waitpid(pid, NULL, 0);
214    }
215
216    regs = oldregs;
217    regs.RIP = regs.RIP - 2;
218    regs.RAX = call;
219#if defined __x86_64__
220    regs.RDI = arg1;
221    regs.RSI = arg2;
222    regs.RDX = arg3;
223#else
224    regs.RBX = arg1;
225    regs.RCX = arg2;
226    regs.RDX = arg3;
227#endif
228
229    if(ptrace(PTRACE_SETREGS, pid, NULL, &regs) < 0)
230    {
231        fprintf(stderr, "PTRACE_SETREGS failed\n");
232        return -1;
233    }
234
235    if(ptrace(PTRACE_SINGLESTEP, pid, NULL, NULL) < 0)
236    {
237        fprintf(stderr, "PTRACE_SINGLESTEP failed\n");
238        return -1;
239    }
240    waitpid(pid, NULL, 0);
241
242    if(ptrace(PTRACE_GETREGS, pid, NULL, &regs) < 0)
243    {
244        fprintf(stderr, "PTRACE_GETREGS failed\n");
245        return -1;
246    }
247
248    if(ptrace(PTRACE_SETREGS, pid, NULL, &oldregs) < 0)
249    {
250        fprintf(stderr, "PTRACE_SETREGS failed\n");
251        return -1;
252    }
253
254    debug("syscall %ld returned %ld", call, regs.RAX);
255
256    if((long)regs.RAX < 0)
257    {
258        errno = -(long)regs.RAX;
259        perror("syscall");
260        return -1;
261    }
262
263    return regs.RAX;
264}
265
266static int do_close(pid_t pid, int fd)
267{
268    return do_syscall(pid, SYS_close, fd, 0, 0);
269}
270
271static int do_dup2(pid_t pid, int oldfd, int newfd)
272{
273    return do_syscall(pid, SYS_dup2, oldfd, newfd, 0);
274}
275
276static int do_open(pid_t pid, char *path, int mode)
277{
278    char backup_data[4096];
279    struct user_regs_struct regs;
280    size_t size = strlen(path) + 1;
281    int ret;
282
283    if(ptrace(PTRACE_GETREGS, pid, NULL, &regs) < 0)
284    {
285        fprintf(stderr, "PTRACE_GETREGS failed\n");
286        return errno;
287    }
288
289    /* Backup the data that we will use */
290    if(memcpy_from_target(pid, backup_data, regs.RSP, size) < 0)
291        return -1;
292
293    /* +4 (or 8) because it's truncated on a multiple of 4 (or 8)
294     * and we need 1 */
295    sprintf(path, "%s", path);
296    memcpy_into_target(pid, regs.RSP, path, size);
297
298    ret = do_syscall(pid, SYS_open, regs.RSP, O_RDWR, 0755);
299
300    /* Restore the datas */
301    memcpy_into_target(pid, regs.RSP, backup_data, size);
302
303    if(ret < 0)
304    {
305        errno = ret;
306        return -1;
307    }
308
309    return ret;
310}
311
312static int do_setsid(pid_t pid)
313{
314    long ret;
315
316    debug("Running setsid on process %d (sid=%d)", pid, getsid(pid));
317
318    ret = do_syscall(pid, SYS_setpgid, 0, getsid(pid), 0);
319    if(ret < 0)
320    {
321        fprintf(stderr, "syscall setpgid failed\n");
322        return ret;
323    }
324
325    if(ret != 0)
326    {
327        fprintf(stderr, "setpgid returned %ld\n", ret);
328        return -ret;
329    }
330
331    ret = do_syscall(pid, SYS_setsid, 0, 0, 0);
332    if(ret < 0)
333    {
334        fprintf(stderr, "syscall setsid failed\n");
335        return ret;
336    }
337
338    debug("pid %d has now sid %d", pid, getsid(pid));
339
340    return 0;
341}
342#endif
343
344int grab_process(long pid, char *ptyname, int ptyfd)
345{
346#if defined USE_GRAB
347    int i, fd = 0, mode, ret;
348    char fdstr[1024];
349    char to_open[3];
350    struct stat stat_buf;
351
352    debug("pty is %s", ptyname);
353
354    if(ptrace(PTRACE_ATTACH, pid, 0, 0) < 0)
355    {
356        fprintf(stderr, "Cannot access process %ld\n", pid);
357        return errno;
358    }
359    waitpid(pid, NULL, 0);
360
361    for(i = 0; i <= 2; i++)
362    {
363        snprintf(fdstr, sizeof(fdstr), "/proc/%ld/fd/%d", pid, i);
364        to_open[i] = 0;
365        lstat(fdstr, &stat_buf);
366        if((stat_buf.st_mode & S_IRUSR) && (stat_buf.st_mode & S_IWUSR))
367            mode = O_RDWR;
368        else if(stat_buf.st_mode & S_IWUSR)
369            mode = O_WRONLY;
370        else
371            mode = O_RDONLY;
372
373        if(stat(fdstr, &stat_buf) < 0)
374            continue;
375
376        if(!S_ISCHR(stat_buf.st_mode)
377            || MAJOR(stat_buf.st_rdev) != UNIX98_PTY_SLAVE_MAJOR)
378            continue;
379
380        debug("found pty %d", i);
381
382        if((ret = do_close(pid, i)))
383        {
384            fprintf(stderr, "do_close %s\n", strerror(ret));
385        }
386        to_open[i] = 1;
387    }
388
389    if((ret = do_setsid(pid)))
390    {
391        fprintf(stderr, "do_setsid %s\n", strerror(ret));
392    }
393
394    for(i = 0; i <= 2; i++)
395    {
396        if(!to_open[i])
397            continue;
398        fd = do_open(pid, ptyname, mode);
399        if(fd < 0)
400        {
401            perror("do_open");
402        }
403        ret = do_dup2(pid, fd, i);
404        if(ret < 0)
405        {
406            perror("do_dup2");
407        }
408    }
409
410    kill(pid, SIGWINCH);
411    ptrace(PTRACE_DETACH, pid, 0, 0);
412
413    return 0;
414#else
415    return -1;
416#endif
417}
418
Note: See TracBrowser for help on using the repository browser.