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

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