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

Last change on this file since 2501 was 2501, checked in by Sam Hocevar, 13 years ago
  • Preparing the amd64 port of grab.c by first making the current i386-specific code compile.
  • Property svn:eol-style set to native
File size: 6.9 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#include "config.h"
26
27#if defined USE_GRAB
28#   include <sys/ioctl.h>
29#   include <sys/ptrace.h>
30#   include <sys/stat.h>
31#   include <sys/syscall.h>
32#   include <sys/user.h>
33#   include <sys/wait.h>
34
35#   include <linux/kdev_t.h>
36#   include <linux/major.h>
37#endif
38
39#include "neercs.h"
40
41#if defined __x86_64__
42#   define RAX rax
43#   define RBX rbx
44#   define RCX rcx
45#   define RDX rdx
46#   define RSP rsp
47#   define RIP rip
48#else
49#   define RAX eax
50#   define RBX ebx
51#   define RCX ecx
52#   define RDX edx
53#   define RSP esp
54#   define RIP eip
55#endif
56
57#if defined USE_GRAB
58static int memcpy_from_target(pid_t pid, void* dest, void* src, size_t n)
59{
60    unsigned int i;
61    long *d, *s;
62    d = (long*) dest;
63    s = (long*) src;
64    n /= sizeof(long);
65    for (i = 0; i < n; i++) {
66        d[i] = ptrace(PTRACE_PEEKTEXT, pid, s+i, 0);
67        if (errno) {
68            perror("ptrace(PTRACE_PEEKTEXT)");
69            return -1;
70        }
71    }
72    return 0;
73}
74
75static int memcpy_into_target(pid_t pid, void* dest, void* src, size_t n)
76{
77    unsigned int i;
78    long *d, *s;
79    d = (long*) dest;
80    s = (long*) src;
81    for (i = 0; i < n / sizeof(long); i++) {
82        if (ptrace(PTRACE_POKETEXT, pid, d+i, s[i]) == -1) {
83            perror("ptrace(PTRACE_POKETEXT)");
84            return -1;
85        }
86    }
87    return 0;
88}
89
90static int do_syscall(pid_t pid, struct user_regs_struct *regs)
91{
92
93    struct user_regs_struct oregs;
94    long oinst;
95
96    if(ptrace(PTRACE_GETREGS, pid, NULL, &oregs) < 0)
97    {
98        return errno;
99    }
100
101    regs->RSP = oregs.RSP - 4;
102
103    oinst = ptrace(PTRACE_PEEKTEXT, pid, regs->RSP, 0);
104    if (errno) {
105        fprintf(stderr, "PTRACE_PEEKTEXT failed\n");
106        return errno;
107    }
108
109    if (ptrace(PTRACE_POKETEXT, pid, regs->RSP, 0x80cd) < 0) { /* int 0x80 */
110        fprintf(stderr, "PTRACE_POKETEXT failed\n");
111        return errno;
112    }
113
114    regs->RIP = regs->RSP;
115    if(ptrace(PTRACE_SETREGS, pid, NULL, regs) < 0)
116    {
117        return errno;
118    }
119
120    if(ptrace(PTRACE_SINGLESTEP, pid, NULL, NULL) < 0)
121    {
122        return errno;
123    }
124    waitpid(pid, NULL, 0);
125
126    if(ptrace(PTRACE_GETREGS, pid, NULL, regs) < 0)
127    {
128        return errno;
129    }
130
131    if(ptrace(PTRACE_SETREGS, pid, NULL, &oregs) < 0)
132    {
133        fprintf(stderr, "PTRACE_SETREGS failed\n");
134        return errno;
135    }
136
137    if(ptrace(PTRACE_POKETEXT, pid, oregs.RSP-4 , oinst) < 0)
138    {
139        fprintf(stderr, "PTRACE_POKETEXT failed\n");
140        return errno;
141    }
142
143    return 0;
144}
145
146static int do_close(pid_t pid, int fd)
147{
148    struct user_regs_struct regs;
149    if(ptrace(PTRACE_GETREGS, pid, NULL, &regs) < 0)
150    {
151        fprintf(stderr, "PTRACE_GETREGS failed\n");
152        return errno;
153    }
154
155    regs.RAX = SYS_close;
156    regs.RBX = fd;
157
158    return do_syscall(pid, &regs);
159}
160
161static int do_dup2(pid_t pid, int oldfd, int newfd)
162{
163    struct user_regs_struct regs;
164    if(ptrace(PTRACE_GETREGS, pid, NULL, &regs) < 0)
165    {
166        fprintf(stderr, "PTRACE_GETREGS failed\n");
167        return errno;
168    }
169
170    regs.RAX = SYS_dup2;
171    regs.RBX = oldfd;
172    regs.RCX = newfd;
173
174    return do_syscall(pid, &regs);
175}
176
177static int do_open(pid_t pid, char *path, int mode, int *fd)
178{
179    struct user_regs_struct regs;
180    void *backup_page;
181    void *target_page = (void*)(0x08048000);
182    size_t size = (1<<12); /* 4K */
183    int ret;
184
185    /* Backup the page that we will use */
186    backup_page = malloc(size);
187    if (memcpy_from_target(pid, backup_page, target_page, size) < 0)
188        return -1;
189
190    if(ptrace(PTRACE_GETREGS, pid, NULL, &regs) < 0)
191    {
192        fprintf(stderr, "PTRACE_GETREGS failed\n");
193        return errno;
194    }
195
196    /* +4 because it's trcuncated on a multiple of 4 and we need 1 */
197    memcpy_into_target(pid, target_page, path, strlen(path)+4);
198
199    regs.RAX = SYS_open;
200    regs.RBX = (long)target_page;
201    regs.RCX = O_RDWR;
202    regs.RDX = 0755;
203
204    if((ret = do_syscall(pid, &regs)) != 0)
205    {
206        return ret;
207    }
208
209    /* Restore the pages */
210    memcpy_into_target(pid, target_page, backup_page, size);
211
212    *fd  = regs.RAX;
213
214    return 0;
215}
216
217static int do_setsid(pid_t pid) {
218    struct user_regs_struct regs;
219    int ret;
220
221    if(ptrace(PTRACE_GETREGS, pid, NULL, &regs) < 0)
222    {
223        fprintf(stderr, "PTRACE_GETREGS failed\n");
224        return errno;
225    }
226
227    debug("Running setsid on process %d (sid=%d)", pid, getsid(pid));
228
229    regs.RAX = SYS_setpgid;
230    regs.RBX = 0;
231    regs.RCX = getsid(pid);
232
233    if((ret = do_syscall(pid, &regs)) != 0)
234    {
235        return ret;
236    }
237
238    if (regs.RAX != 0) {
239        fprintf(stderr, "setpgid failed\n");
240        return -regs.RAX;
241    }
242
243    regs.RAX = SYS_setsid;
244
245    if((ret = do_syscall(pid, &regs)) != 0)
246    {
247        return ret;
248    }
249
250    debug("pid %d has now sid %d", pid, getsid(pid));
251
252    if (regs.RAX == -1) {
253        fprintf(stderr, "getsid failed\n");
254        return -regs.RAX;
255    }
256
257    return 0;
258}
259#endif
260
261int grab_process(pid_t pid, char *ptyname, int ptyfd)
262{
263#if defined USE_GRAB
264    int i, fd = 0, mode, ret;
265    char fdstr[1024];
266    char to_open[3];
267    struct stat stat_buf;
268
269    debug("pty is %s", ptyname);
270
271    kill(pid, SIGSTOP);
272    if(ptrace(PTRACE_ATTACH, pid, 0, 0) < 0)
273    {
274        fprintf(stderr, "Cannot access process %d\n", pid);
275        return errno;
276    }
277    kill(pid, SIGCONT);
278    waitpid(pid, NULL, 0);
279
280    for(i=0; i<=2; i++)
281    {
282        snprintf(fdstr, sizeof(fdstr), "/proc/%d/fd/%d", pid, i);
283        to_open[i]=0;
284        lstat(fdstr, &stat_buf);
285        if ((stat_buf.st_mode & S_IRUSR) && (stat_buf.st_mode & S_IWUSR))
286            mode = O_RDWR;
287        else if (stat_buf.st_mode & S_IWUSR)
288            mode = O_WRONLY;
289        else
290            mode = O_RDONLY;
291
292        if (stat(fdstr, &stat_buf) < 0)
293            continue;
294
295        if (!S_ISCHR(stat_buf.st_mode) || MAJOR(stat_buf.st_rdev) != UNIX98_PTY_SLAVE_MAJOR)
296            continue;
297
298        debug("fd %d is a pty", i);
299        if((ret = do_close(pid, i)))
300        {
301            fprintf(stderr, "do_close %s\n", strerror(ret));
302        }
303        to_open[i]=1;
304    }
305
306    if((ret = do_setsid(pid)))
307    {
308        fprintf(stderr, "do_setsid %s\n", strerror(ret));
309    }
310
311    for(i=0; i<=2; i++)
312    {
313        if(!to_open[i])
314            continue;
315        if((ret = do_open(pid, ptyname, mode, &fd)))
316        {
317            fprintf(stderr, "do_open %s\n", strerror(ret));
318        }
319        if((ret = do_dup2(pid, fd, i)))
320        {
321            fprintf(stderr, "do_dup2 %s\n", strerror(ret));
322        }
323    }
324
325    kill(pid, SIGWINCH);
326    ptrace(PTRACE_DETACH, pid, 0, 0);
327
328    return 0;
329#else
330    return -1;
331#endif
332}
333
Note: See TracBrowser for help on using the repository browser.