-
Notifications
You must be signed in to change notification settings - Fork 160
/
ptrace.c
114 lines (90 loc) · 3.29 KB
/
ptrace.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
/*
# ptrace
Control a child process. You can observe anything in it's state,
including memory and registers.
The child stops transfers control to parent on the following events:
- signals are raised
- `PTRACE_SYSCALL` is used and a system call is reached. Yes, `strace` is "easy" to write.
- `PTRACE_SINGLESTEP` does a single instruction and then stops. So this is how GDB does it.
Bibliograpy:
- https://github.com/nelhage/ministrace/blob/for-blog/ministrace.c
*/
#include "common.h"
#ifdef __x86_64__
#define REGRAX(regs) (intmax_t)regs.rax
#define REGRIP(regs) (intmax_t)regs.rip
#elif defined __i386__
#define REGRAX(regs) (intmax_t)regs.eax
#define REGRIP(regs) (intmax_t)regs.eip
#endif
int main(void) {
pid_t child_pid;
int i;
struct user_regs_struct regs;
int status;
child_pid = fork();
if (child_pid == 0) {
/*
Says that this child can be traced now.
Alternatives inclue using PTRACE_ATTACH or PTRACE_SEIZE from the parent.
*/
ptrace(PTRACE_TRACEME, 0, NULL, NULL);
/* We must stop ourselves, or else the child might just run first and finish before the parent. */
kill(getpid(), SIGSTOP);
/* Not puts because it is cached. */
write(1, "child0\n", 7);
write(1, "child1\n", 7);
exit(EXIT_SUCCESS);
}
ptrace(PTRACE_SETOPTIONS, child_pid, 0, PTRACE_O_TRACESYSGOOD);
/*
This loop goes over: kill (one stop), 2x write (2 stops each).
kill stops because of the signal it raises. Stops signals happen only once.
Writes stop because of `PTRACE_SYSCALL`.
`exit()` calls `sys_exit_group` (x86_64 231) on Ubuntu 14.04,
not `sys_exit`, to deal with multithreading.
*/
for (i = 0; i < 7; i++) {
/*
Stops both when the system call enters and leaves!
The main thing to get out of the exiting syscall is the return value in rax.
*/
ptrace(PTRACE_SYSCALL, child_pid, NULL, NULL);
waitpid(child_pid, &status, 0);
ptrace(PTRACE_GETREGS, child_pid, NULL, ®s);
/*
TODO What is the -38 at exit?
http://stackoverflow.com/questions/7514837/why-this-ptrace-programe-always-saying-syscall-returned-38?lq=1
*/
puts("GETREGS");
printf(" rax = %jd\n", REGRAX(regs));
printf(" rip = 0x%jx\n", REGRIP(regs));
puts("PEEKUSER");
printf(
" ORIG_RAX = %jd\n",
ptrace(PTRACE_PEEKUSER, child_pid, sizeof(long)*ORIG_RAX)
);
printf(
" RAX = %jd\n",
ptrace(PTRACE_PEEKUSER, child_pid, sizeof(long)*RAX)
);
/* Print one word from the virtual memory of the tracee. */
printf(
"PEEKTEXT rip = 0x%jx\n",
ptrace(PTRACE_PEEKTEXT, child_pid, REGRIP(regs), NULL)
);
/* With PTRACE_O_TRACESYSGOOD this can be used to distinguish between TODO */
if (WIFSTOPPED(status) && WSTOPSIG(status) & 0x80)
puts("PTRACE_O_TRACESYSGOOD");
fflush(stdout);
if (i == 6) {
assert(WIFEXITED(status));
assert(!WIFSTOPPED(status));
} else {
assert(!WIFEXITED(status));
assert(WIFSTOPPED(status));
}
puts("");
}
return EXIT_SUCCESS;
}