/
trace.c
307 lines (284 loc) · 7.62 KB
/
trace.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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
/* radare - LGPL - Copyright 2008-2020 - pancake */
#include <r_debug.h>
// DO IT WITH SDB
R_API RDebugTrace *r_debug_trace_new(void) {
RDebugTrace *t = R_NEW0 (RDebugTrace);
if (!t) {
return NULL;
}
t->tag = 1; // UT32_MAX;
t->addresses = NULL;
t->enabled = false;
t->traces = r_list_new ();
if (!t->traces) {
r_debug_trace_free (t);
return NULL;
}
t->traces->free = free;
t->ht = ht_pp_new0 ();
if (!t->ht) {
r_debug_trace_free (t);
return NULL;
}
return t;
}
R_API void r_debug_trace_free(RDebugTrace *trace) {
if (!trace) {
return;
}
r_list_purge (trace->traces);
free (trace->traces);
ht_pp_free (trace->ht);
R_FREE (trace);
}
// TODO: added overlap/mask support here... wtf?
// TODO: think about tagged traces
R_API int r_debug_trace_tag(RDebug *dbg, int tag) {
//if (tag>0 && tag<31) core->dbg->trace->tag = 1<<(sz-1);
return (dbg->trace->tag = (tag>0)? tag: UT32_MAX);
}
R_API bool r_debug_trace_ins_before(RDebug *dbg) {
RListIter *it, *it_tmp;
RAnalValue *val;
ut8 buf_pc[32];
// Analyze current instruction
ut64 pc = r_debug_reg_get (dbg, dbg->reg->name[R_REG_NAME_PC]);
if (!dbg->iob.read_at) {
return false;
}
if (!dbg->iob.read_at (dbg->iob.io, pc, buf_pc, sizeof (buf_pc))) {
return false;
}
dbg->cur_op = R_NEW0 (RAnalOp);
if (!dbg->cur_op) {
return false;
}
if (!r_anal_op (dbg->anal, dbg->cur_op, pc, buf_pc, sizeof (buf_pc), R_ANAL_OP_MASK_VAL)) {
r_anal_op_free (dbg->cur_op);
dbg->cur_op = NULL;
return false;
}
// resolve mem write address
r_list_foreach_safe (dbg->cur_op->access, it, it_tmp, val) {
switch (val->type) {
case R_ANAL_VAL_REG:
if (!(val->access & R_ANAL_ACC_W)) {
r_list_delete (dbg->cur_op->access, it);
}
break;
case R_ANAL_VAL_MEM:
if (val->memref > 32) {
eprintf ("Error: adding changes to %d bytes in memory.\n", val->memref);
r_list_delete (dbg->cur_op->access, it);
break;
}
if (val->access & R_ANAL_ACC_W) {
// resolve memory address
ut64 addr = 0;
addr += val->delta;
if (val->seg) {
addr += r_reg_get_value (dbg->reg, val->seg);
}
if (val->reg) {
addr += r_reg_get_value (dbg->reg, val->reg);
}
if (val->regdelta) {
int mul = val->mul ? val->mul : 1;
addr += mul * r_reg_get_value (dbg->reg, val->regdelta);
}
// resolve address into base for ins_after
val->base = addr;
} else {
r_list_delete (dbg->cur_op->access, it);
}
default:
break;
}
}
return true;
}
R_API bool r_debug_trace_ins_after(RDebug *dbg) {
RListIter *it;
RAnalValue *val;
// Add reg/mem write change
r_debug_reg_sync (dbg, R_REG_TYPE_ALL, false);
r_list_foreach (dbg->cur_op->access, it, val) {
if (!(val->access & R_ANAL_ACC_W)) {
continue;
}
switch (val->type) {
case R_ANAL_VAL_REG:
{
if (!val->reg) {
R_LOG_ERROR("invalid register, unable to trace register state\n");
continue;
}
ut64 data = r_reg_get_value (dbg->reg, val->reg);
// add reg write
r_debug_session_add_reg_change (dbg->session, val->reg->arena, val->reg->offset, data);
break;
}
case R_ANAL_VAL_MEM:
{
ut8 buf[32] = {0};
if (!dbg->iob.read_at (dbg->iob.io, val->base, buf, val->memref)) {
eprintf ("Error reading memory at 0x%"PFMT64x"\n", val->base);
break;
}
// add mem write
size_t i;
for (i = 0; i < val->memref; i++) {
r_debug_session_add_mem_change (dbg->session, val->base + i, buf[i]);
}
break;
}
default:
break;
}
}
r_anal_op_free (dbg->cur_op);
dbg->cur_op = NULL;
return true;
}
/*
* something happened at the given pc that we need to trace
*/
R_API int r_debug_trace_pc(RDebug *dbg, ut64 pc) {
ut8 buf[32];
RAnalOp op = {0};
if (!dbg->iob.is_valid_offset (dbg->iob.io, pc, 0)) {
eprintf ("trace_pc: cannot read memory at 0x%"PFMT64x"\n", pc);
return false;
}
(void)dbg->iob.read_at (dbg->iob.io, pc, buf, sizeof (buf));
if (r_anal_op (dbg->anal, &op, pc, buf, sizeof (buf), R_ANAL_OP_MASK_ESIL) < 1) {
eprintf ("trace_pc: cannot get opcode size at 0x%"PFMT64x"\n", pc);
return false;
}
r_debug_trace_op (dbg, &op);
r_anal_op_fini (&op);
return true;
}
R_API void r_debug_trace_op(RDebug *dbg, RAnalOp *op) {
static ut64 oldpc = UT64_MAX; // Must trace the previously traced instruction
if (dbg->trace->enabled) {
if (dbg->anal->esil) {
r_anal_esil_trace_op (dbg->anal->esil, op);
} else {
if (dbg->verbose) {
eprintf ("Run aeim to get dbg->anal->esil initialized\n");
}
}
if (oldpc != UT64_MAX) {
r_debug_trace_add (dbg, oldpc, op->size); //XXX review what this line really do
}
}
oldpc = op->addr;
}
R_API void r_debug_trace_at(RDebug *dbg, const char *str) {
// TODO: parse offsets and so use ut64 instead of strstr()
free (dbg->trace->addresses);
dbg->trace->addresses = (str&&*str)? strdup (str): NULL;
}
R_API RDebugTracepoint *r_debug_trace_get(RDebug *dbg, ut64 addr) {
int tag = dbg->trace->tag;
r_strf_var (key, 64, "trace.%d.%"PFMT64x, tag, addr);
return ht_pp_find (dbg->trace->ht, key, NULL);
}
static int cmpaddr(const void *_a, const void *_b) {
const RListInfo *a = _a, *b = _b;
return (r_itv_begin (a->pitv) > r_itv_begin (b->pitv))? 1:
(r_itv_begin (a->pitv) < r_itv_begin (b->pitv))? -1: 0;
}
R_API void r_debug_trace_list(RDebug *dbg, int mode, ut64 offset) {
int tag = dbg->trace->tag;
RListIter *iter;
bool flag = false;
RList *info_list = r_list_new ();
if (!info_list && mode == '=') {
return;
}
RDebugTracepoint *trace;
r_list_foreach (dbg->trace->traces, iter, trace) {
if (!trace->tag || (tag & trace->tag)) {
switch (mode) {
case 'q':
dbg->cb_printf ("0x%"PFMT64x"\n", trace->addr);
break;
case '=': {
RListInfo *info = R_NEW0 (RListInfo);
if (!info) {
return;
}
info->pitv = (RInterval) {trace->addr, trace->size};
info->vitv = info->pitv;
info->perm = -1;
info->name = r_str_newf ("%d", trace->times);
info->extra = r_str_newf ("%d", trace->count);
r_list_append (info_list, info);
flag = true;
} break;
case 1:
case '*':
dbg->cb_printf ("dt+ 0x%"PFMT64x" %d\n", trace->addr, trace->times);
break;
default:
dbg->cb_printf ("0x%08"PFMT64x" size=%d count=%d times=%d tag=%d\n",
trace->addr, trace->size, trace->count, trace->times, trace->tag);
break;
}
}
}
if (flag) {
r_list_sort (info_list, cmpaddr);
RTable *table = r_table_new ("traces");
table->cons = r_cons_singleton();
RIO *io = dbg->iob.io;
r_table_visual_list (table, info_list, offset, 1,
r_cons_get_size (NULL), io->va);
io->cb_printf ("\n%s\n", r_table_tostring (table));
r_table_free (table);
r_list_free (info_list);
}
}
// XXX: find better name, make it public?
static bool r_debug_trace_is_traceable(RDebug *dbg, ut64 addr) {
if (dbg->trace->addresses) {
char addr_str[32];
snprintf (addr_str, sizeof (addr_str), "0x%08"PFMT64x, addr);
if (!strstr (dbg->trace->addresses, addr_str)) {
return false;
}
}
return true;
}
R_API RDebugTracepoint *r_debug_trace_add(RDebug *dbg, ut64 addr, int size) {
RDebugTracepoint *tp;
int tag = dbg->trace->tag;
if (!r_debug_trace_is_traceable (dbg, addr)) {
return NULL;
}
r_anal_trace_bb (dbg->anal, addr);
tp = R_NEW0 (RDebugTracepoint);
if (!tp) {
return NULL;
}
tp->stamp = r_time_now ();
tp->addr = addr;
tp->tags = tag;
tp->size = size;
tp->count = ++dbg->trace->count;
tp->times = 1;
r_list_append (dbg->trace->traces, tp);
r_strf_var (key, 64, "trace.%d.%"PFMT64x, tag, addr);
ht_pp_update (dbg->trace->ht, key, tp);
return tp;
}
R_API void r_debug_trace_reset(RDebug *dbg) {
RDebugTrace *t = dbg->trace;
r_list_purge (t->traces);
ht_pp_free (t->ht);
t->ht = ht_pp_new0 ();
t->traces = r_list_new ();
t->traces->free = free;
}