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

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