-
Notifications
You must be signed in to change notification settings - Fork 0
/
debuglib.c
161 lines (130 loc) · 3.81 KB
/
debuglib.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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
/* Code sample: simplistic "library" of debugging tools.
**
**
** This code is in the public domain.
*/
#include <stdio.h>
#include <assert.h>
#include <stdarg.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/reg.h>
#include <sys/user.h>
#include <unistd.h>
#include <errno.h>
#include "debuglib.h"
/* Print a message to stdout, prefixed by the process ID
*/
void procmsg(const char* format, ...)
{
va_list ap;
fprintf(stdout, "[%d] ", getpid());
va_start(ap, format);
vfprintf(stdout, format, ap);
va_end(ap);
}
/* Run a target process in tracing mode by exec()-ing the given program name.
*/
void run_target(const char* programname)
{
procmsg("target started. will run '%s'\n", programname);
/* Allow tracing of this process */
if (ptrace(PTRACE_TRACEME, 0, 0, 0) < 0) {
perror("ptrace");
return;
}
/* Replace this process's image with the given program */
execl(programname, programname, 0);
}
long get_child_eip(pid_t pid)
{
struct user_regs_struct regs;
ptrace(PTRACE_GETREGS, pid, 0, ®s);
return regs.eip;
}
void dump_process_memory(pid_t pid, unsigned from_addr, unsigned to_addr)
{
procmsg("Dump of %d's memory [0x%08X : 0x%08X]\n", pid, from_addr, to_addr);
for (unsigned addr = from_addr; addr <= to_addr; ++addr) {
unsigned word = ptrace(PTRACE_PEEKTEXT, pid, addr, 0);
printf(" 0x%08X: %02x\n", addr, word & 0xFF);
}
}
/* Encapsulates a breakpoint. Holds the address at which the BP was placed
** and the original data word at that address (prior to int3) insertion.
*/
struct debug_breakpoint_t {
void* addr;
unsigned orig_data;
};
/* Enable the given breakpoint by inserting the trap instruction at its
** address, and saving the original data at that location.
*/
static void enable_breakpoint(pid_t pid, debug_breakpoint* bp)
{
assert(bp);
bp->orig_data = ptrace(PTRACE_PEEKTEXT, pid, bp->addr, 0);
ptrace(PTRACE_POKETEXT, pid, bp->addr, (bp->orig_data & 0xFFFFFF00) | 0xCC);
}
/* Disable the given breakpoint by replacing the byte it points to with
** the original byte that was there before trap insertion.
*/
static void disable_breakpoint(pid_t pid, debug_breakpoint* bp)
{
assert(bp);
unsigned data = ptrace(PTRACE_PEEKTEXT, pid, bp->addr, 0);
assert((data & 0xFF) == 0xCC);
ptrace(PTRACE_POKETEXT, pid, bp->addr, (data & 0xFFFFFF00) | (bp->orig_data & 0xFF));
}
debug_breakpoint* create_breakpoint(pid_t pid, void* addr)
{
debug_breakpoint* bp = malloc(sizeof(*bp));
bp->addr = addr;
enable_breakpoint(pid, bp);
return bp;
}
void cleanup_breakpoint(debug_breakpoint* bp)
{
free(bp);
}
int resume_from_breakpoint(pid_t pid, debug_breakpoint* bp)
{
struct user_regs_struct regs;
int wait_status;
ptrace(PTRACE_GETREGS, pid, 0, ®s);
/* Make sure we indeed are stopped at bp */
assert(regs.eip == (long) bp->addr + 1);
/* Now disable the breakpoint, rewind EIP back to the original instruction
** and single-step the process. This executes the original instruction that
** was replaced by the breakpoint.
*/
regs.eip = (long) bp->addr;
ptrace(PTRACE_SETREGS, pid, 0, ®s);
disable_breakpoint(pid, bp);
if (ptrace(PTRACE_SINGLESTEP, pid, 0, 0)) {
perror("ptrace");
return -1;
}
wait(&wait_status);
if (WIFEXITED(wait_status))
return 0;
/* Re-enable the breakpoint and let the process run.
*/
enable_breakpoint(pid, bp);
if (ptrace(PTRACE_CONT, pid, 0, 0) < 0) {
perror("ptrace");
return -1;
}
wait(&wait_status);
if (WIFEXITED(wait_status))
return 0;
else if (WIFSTOPPED(wait_status)) {
return 1;
}
else
return -1;
}