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

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