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

Last change on this file since 2505 was 2505, checked in by sam, 6 years ago
  • Start refactoring grab.c: instead of writing code to the stack, we wait for a system call to be made and simply rewind eip/rip. Should now work on amd64, too.
  • Property svn:eol-style set to native
File size: 9.4 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
114/* FIXME: this function and the following have alignment issues */
115static int memcpy_from_target(pid_t pid, void* dest, long src, size_t n)
116{
117    long *d = (long*)dest;
118    unsigned int i;
119
120    for(i = 0; i < n / sizeof(long); i++)
121    {
122        d[i] = ptrace(PTRACE_PEEKTEXT, pid, src + i * sizeof(long), 0);
123        if(errno)
124        {
125            perror("ptrace_peektext");
126            return -1;
127        }
128    }
129    return 0;
130}
131
132static int memcpy_into_target(pid_t pid, long dest, void* src, size_t n)
133{
134    long *s = (long*) src;
135    unsigned int i;
136
137    for(i = 0; i < n / sizeof(long); i++)
138    {
139        if(ptrace(PTRACE_POKETEXT, pid, dest + i * sizeof(long), s[i]) == -1)
140        {
141            perror("ptrace_poketext");
142            return -1;
143        }
144    }
145    return 0;
146}
147
148static long do_syscall(pid_t pid, long call, long arg1, long arg2, long arg3)
149{
150    /* Method for remote syscall:
151     *  - wait until the traced application exits from a syscall
152     *  - save registers
153     *  - rewind eip/rip to point on the syscall instruction
154     *  - single step: execute syscall instruction
155     *  - restore registers */
156    struct user_regs_struct regs, oldregs;
157    long oinst;
158
159    for(;;)
160    {
161        if(ptrace(PTRACE_GETREGS, pid, NULL, &oldregs) < 0)
162        {
163            fprintf(stderr, "PTRACE_GETREGS failed\n");
164            return -1;
165        }
166
167        oinst = ptrace(PTRACE_PEEKTEXT, pid, oldregs.RIP - 2, 0) & 0xffff;
168
169        if(oinst == SYSCALL)
170            break;
171
172        if(ptrace(PTRACE_SYSCALL, pid, NULL, 0) < 0)
173        {
174            perror("ptrace_syscall (1)");
175            return -1;
176        }
177        waitpid(pid, NULL, 0);
178
179        if(ptrace(PTRACE_SYSCALL, pid, NULL, 0) < 0)
180        {
181            perror("ptrace_syscall (2)");
182            return -1;
183        }
184        waitpid(pid, NULL, 0);
185    }
186
187    regs = oldregs;
188    regs.RIP = regs.RIP - 2;
189    regs.RAX = call;
190#if defined __x86_64__
191    regs.RDI = arg1;
192    regs.RSI = arg2;
193    regs.RDX = arg3;
194#else
195    regs.RBX = arg1;
196    regs.RCX = arg2;
197    regs.RDX = arg3;
198#endif
199
200    if(ptrace(PTRACE_SETREGS, pid, NULL, &regs) < 0)
201    {
202        fprintf(stderr, "PTRACE_SETREGS failed\n");
203        return -1;
204    }
205
206    if(ptrace(PTRACE_SINGLESTEP, pid, NULL, NULL) < 0)
207    {
208        fprintf(stderr, "PTRACE_SINGLESTEP failed\n");
209        return -1;
210    }
211    waitpid(pid, NULL, 0);
212
213    if(ptrace(PTRACE_GETREGS, pid, NULL, &regs) < 0)
214    {
215        fprintf(stderr, "PTRACE_GETREGS failed\n");
216        return -1;
217    }
218
219    if(ptrace(PTRACE_SETREGS, pid, NULL, &oldregs) < 0)
220    {
221        fprintf(stderr, "PTRACE_SETREGS failed\n");
222        return -1;
223    }
224
225    debug("syscall %ld returned %ld", call, regs.RAX);
226
227    if((long)regs.RAX < 0)
228    {
229        errno = -(long)regs.RAX;
230        perror("syscall");
231        return -1;
232    }
233
234    return regs.RAX;
235}
236
237static int do_close(pid_t pid, int fd)
238{
239    return do_syscall(pid, SYS_close, fd, 0, 0);
240}
241
242static int do_dup2(pid_t pid, int oldfd, int newfd)
243{
244    return do_syscall(pid, SYS_dup2, oldfd, newfd, 0);
245}
246
247static int do_open(pid_t pid, char *path, int mode)
248{
249    char path_data[4096], backup_data[4096];
250    struct user_regs_struct regs;
251    long target_data;
252    size_t size = (strlen(path) + sizeof(long)) & ~(sizeof(long) - 1L);
253    int ret;
254
255    if(ptrace(PTRACE_GETREGS, pid, NULL, &regs) < 0)
256    {
257        fprintf(stderr, "PTRACE_GETREGS failed\n");
258        return errno;
259    }
260
261    target_data = (regs.RSP - size) & ~(sizeof(long) - 1L);
262
263    /* Backup the data that we will use */
264    if(memcpy_from_target(pid, backup_data, target_data, size) < 0)
265        return -1;
266
267    /* +4 (or 8) because it's truncated on a multiple of 4 (or 8)
268     * and we need 1 */
269    sprintf(path_data, "%s", path);
270    memcpy_into_target(pid, target_data, path_data, size);
271
272    ret = do_syscall(pid, SYS_open, target_data, O_RDWR, 0755);
273
274    /* Restore the datas */
275    memcpy_into_target(pid, target_data, backup_data, size);
276
277    if(ret < 0)
278    {
279        errno = ret;
280        return -1;
281    }
282
283    return ret;
284}
285
286static int do_setsid(pid_t pid)
287{
288    long ret;
289
290    debug("Running setsid on process %d (sid=%d)", pid, getsid(pid));
291
292    ret = do_syscall(pid, SYS_setpgid, 0, getsid(pid), 0);
293    if(ret < 0)
294    {
295        fprintf(stderr, "syscall setpgid failed\n");
296        return ret;
297    }
298
299    if(ret != 0)
300    {
301        fprintf(stderr, "setpgid returned %ld\n", ret);
302        return -ret;
303    }
304
305    ret = do_syscall(pid, SYS_setsid, 0, 0, 0);
306    if(ret < 0)
307    {
308        fprintf(stderr, "syscall setsid failed\n");
309        return ret;
310    }
311
312    debug("pid %d has now sid %d", pid, getsid(pid));
313
314    return 0;
315}
316#endif
317
318int grab_process(long pid, char *ptyname, int ptyfd)
319{
320#if defined USE_GRAB
321    int i, fd = 0, mode, ret;
322    char fdstr[1024];
323    char to_open[3];
324    struct stat stat_buf;
325
326    debug("pty is %s", ptyname);
327
328    if(ptrace(PTRACE_ATTACH, pid, 0, 0) < 0)
329    {
330        fprintf(stderr, "Cannot access process %ld\n", pid);
331        return errno;
332    }
333    waitpid(pid, NULL, 0);
334
335    for(i = 0; i <= 2; i++)
336    {
337        snprintf(fdstr, sizeof(fdstr), "/proc/%ld/fd/%d", pid, i);
338        to_open[i] = 0;
339        lstat(fdstr, &stat_buf);
340        if((stat_buf.st_mode & S_IRUSR) && (stat_buf.st_mode & S_IWUSR))
341            mode = O_RDWR;
342        else if(stat_buf.st_mode & S_IWUSR)
343            mode = O_WRONLY;
344        else
345            mode = O_RDONLY;
346
347        if(stat(fdstr, &stat_buf) < 0)
348            continue;
349
350        if(!S_ISCHR(stat_buf.st_mode)
351            || MAJOR(stat_buf.st_rdev) != UNIX98_PTY_SLAVE_MAJOR)
352            continue;
353
354        debug("found pty %d", i);
355
356        if((ret = do_close(pid, i)))
357        {
358            fprintf(stderr, "do_close %s\n", strerror(ret));
359        }
360        to_open[i] = 1;
361    }
362
363    if((ret = do_setsid(pid)))
364    {
365        fprintf(stderr, "do_setsid %s\n", strerror(ret));
366    }
367
368    for(i = 0; i <= 2; i++)
369    {
370        if(!to_open[i])
371            continue;
372        fd = do_open(pid, ptyname, mode);
373        if(fd < 0)
374        {
375            perror("do_open");
376        }
377        ret = do_dup2(pid, fd, i);
378        if(ret < 0)
379        {
380            perror("do_dup2");
381        }
382    }
383
384    kill(pid, SIGWINCH);
385    ptrace(PTRACE_DETACH, pid, 0, 0);
386
387    return 0;
388#else
389    return -1;
390#endif
391}
392
Note: See TracBrowser for help on using the repository browser.