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

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