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

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