1 /* radare - LGPL - Copyright 2008-2020 - pancake */
2 
3 #include <r_debug.h>
4 
5 // DO IT WITH SDB
6 
r_debug_trace_new(void)7 R_API RDebugTrace *r_debug_trace_new (void) {
8 	RDebugTrace *t = R_NEW0 (RDebugTrace);
9 	if (!t) {
10 		return NULL;
11 	}
12 	t->tag = 1; // UT32_MAX;
13 	t->addresses = NULL;
14 	t->enabled = false;
15 	t->traces = r_list_new ();
16 	if (!t->traces) {
17 		r_debug_trace_free (t);
18 		return NULL;
19 	}
20 	t->traces->free = free;
21 	t->ht = ht_pp_new0 ();
22 	if (!t->ht) {
23 		r_debug_trace_free (t);
24 		return NULL;
25 	}
26 	return t;
27 }
28 
r_debug_trace_free(RDebugTrace * trace)29 R_API void r_debug_trace_free (RDebugTrace *trace) {
30 	if (!trace) {
31 		return;
32 	}
33 	r_list_purge (trace->traces);
34 	free (trace->traces);
35 	ht_pp_free (trace->ht);
36 	R_FREE (trace);
37 }
38 
39 // TODO: added overlap/mask support here... wtf?
40 // TODO: think about tagged traces
r_debug_trace_tag(RDebug * dbg,int tag)41 R_API int r_debug_trace_tag (RDebug *dbg, int tag) {
42 	//if (tag>0 && tag<31) core->dbg->trace->tag = 1<<(sz-1);
43 	return (dbg->trace->tag = (tag>0)? tag: UT32_MAX);
44 }
45 
r_debug_trace_ins_before(RDebug * dbg)46 R_API bool r_debug_trace_ins_before(RDebug *dbg) {
47 	RListIter *it, *it_tmp;
48 	RAnalValue *val;
49 	ut8 buf_pc[32];
50 
51 	// Analyze current instruction
52 	ut64 pc = r_debug_reg_get (dbg, dbg->reg->name[R_REG_NAME_PC]);
53 	if (!dbg->iob.read_at) {
54 		return false;
55 	}
56 	if (!dbg->iob.read_at (dbg->iob.io, pc, buf_pc, sizeof (buf_pc))) {
57 		return false;
58 	}
59 	dbg->cur_op = R_NEW0 (RAnalOp);
60 	if (!dbg->cur_op) {
61 		return false;
62 	}
63 	if (!r_anal_op (dbg->anal, dbg->cur_op, pc, buf_pc, sizeof (buf_pc), R_ANAL_OP_MASK_VAL)) {
64 		r_anal_op_free (dbg->cur_op);
65 		dbg->cur_op = NULL;
66 		return false;
67 	}
68 
69 	// resolve mem write address
70 	r_list_foreach_safe (dbg->cur_op->access, it, it_tmp, val) {
71 		switch (val->type) {
72 		case R_ANAL_VAL_REG:
73 			if (!(val->access & R_ANAL_ACC_W)) {
74 				r_list_delete (dbg->cur_op->access, it);
75 			}
76 			break;
77 		case R_ANAL_VAL_MEM:
78 			if (val->memref > 32) {
79 				eprintf ("Error: adding changes to %d bytes in memory.\n", val->memref);
80 				r_list_delete (dbg->cur_op->access, it);
81 				break;
82 			}
83 
84 			if (val->access & R_ANAL_ACC_W) {
85 				// resolve memory address
86 				ut64 addr = 0;
87 				addr += val->delta;
88 				if (val->seg) {
89 					addr += r_reg_get_value (dbg->reg, val->seg);
90 				}
91 				if (val->reg) {
92 					addr += r_reg_get_value (dbg->reg, val->reg);
93 				}
94 				if (val->regdelta) {
95 					int mul = val->mul ? val->mul : 1;
96 					addr += mul * r_reg_get_value (dbg->reg, val->regdelta);
97 				}
98 				// resolve address into base for ins_after
99 				val->base = addr;
100 			} else {
101 				r_list_delete (dbg->cur_op->access, it);
102 			}
103 		default:
104 			break;
105 		}
106 	}
107 	return true;
108 }
109 
r_debug_trace_ins_after(RDebug * dbg)110 R_API bool r_debug_trace_ins_after(RDebug *dbg) {
111 	RListIter *it;
112 	RAnalValue *val;
113 
114 	// Add reg/mem write change
115 	r_debug_reg_sync (dbg, R_REG_TYPE_ALL, false);
116 	r_list_foreach (dbg->cur_op->access, it, val) {
117 		if (!(val->access & R_ANAL_ACC_W)) {
118 			continue;
119 		}
120 
121 		switch (val->type) {
122 		case R_ANAL_VAL_REG:
123 		{
124 			if (!val->reg) {
125 				R_LOG_ERROR("invalid register, unable to trace register state\n");
126 				continue;
127 			}
128 			ut64 data = r_reg_get_value (dbg->reg, val->reg);
129 
130 			// add reg write
131 			r_debug_session_add_reg_change (dbg->session, val->reg->arena, val->reg->offset, data);
132 			break;
133 		}
134 		case R_ANAL_VAL_MEM:
135 		{
136 			ut8 buf[32] = { 0 };
137 			if (!dbg->iob.read_at (dbg->iob.io, val->base, buf, val->memref)) {
138 				eprintf ("Error reading memory at 0x%"PFMT64x"\n", val->base);
139 				break;
140 			}
141 
142 			// add mem write
143 			size_t i;
144 			for (i = 0; i < val->memref; i++) {
145 				r_debug_session_add_mem_change (dbg->session, val->base + i, buf[i]);
146 			}
147 			break;
148 		}
149 		default:
150 			break;
151 		}
152 	}
153 	r_anal_op_free (dbg->cur_op);
154 	dbg->cur_op = NULL;
155 	return true;
156 }
157 
158 /*
159  * something happened at the given pc that we need to trace
160  */
r_debug_trace_pc(RDebug * dbg,ut64 pc)161 R_API int r_debug_trace_pc(RDebug *dbg, ut64 pc) {
162 	ut8 buf[32];
163 	RAnalOp op = {0};
164 	if (!dbg->iob.is_valid_offset (dbg->iob.io, pc, 0)) {
165 		eprintf ("trace_pc: cannot read memory at 0x%"PFMT64x"\n", pc);
166 		return false;
167 	}
168 	(void)dbg->iob.read_at (dbg->iob.io, pc, buf, sizeof (buf));
169 	if (r_anal_op (dbg->anal, &op, pc, buf, sizeof (buf), R_ANAL_OP_MASK_ESIL) < 1) {
170 		eprintf ("trace_pc: cannot get opcode size at 0x%"PFMT64x"\n", pc);
171 		return false;
172 	}
173 	r_debug_trace_op (dbg, &op);
174 	r_anal_op_fini (&op);
175 	return true;
176 }
177 
r_debug_trace_op(RDebug * dbg,RAnalOp * op)178 R_API void r_debug_trace_op(RDebug *dbg, RAnalOp *op) {
179 	static ut64 oldpc = UT64_MAX; // Must trace the previously traced instruction
180 	if (dbg->trace->enabled) {
181 		if (dbg->anal->esil) {
182 			r_anal_esil_trace_op (dbg->anal->esil, op);
183 		} else {
184 			if (dbg->verbose) {
185 				eprintf ("Run aeim to get dbg->anal->esil initialized\n");
186 			}
187 		}
188 	}
189 	if (oldpc != UT64_MAX) {
190 		r_debug_trace_add (dbg, oldpc, op->size); //XXX review what this line really do
191 	}
192 	oldpc = op->addr;
193 }
194 
r_debug_trace_at(RDebug * dbg,const char * str)195 R_API void r_debug_trace_at(RDebug *dbg, const char *str) {
196 	// TODO: parse offsets and so use ut64 instead of strstr()
197 	free (dbg->trace->addresses);
198 	dbg->trace->addresses = (str&&*str)? strdup (str): NULL;
199 }
200 
r_debug_trace_get(RDebug * dbg,ut64 addr)201 R_API RDebugTracepoint *r_debug_trace_get(RDebug *dbg, ut64 addr) {
202 	int tag = dbg->trace->tag;
203 	return ht_pp_find (dbg->trace->ht,
204 		sdb_fmt ("trace.%d.%"PFMT64x, tag, addr), NULL);
205 }
206 
cmpaddr(const void * _a,const void * _b)207 static int cmpaddr(const void *_a, const void *_b) {
208 	const RListInfo *a = _a, *b = _b;
209 	return (r_itv_begin (a->pitv) > r_itv_begin (b->pitv))? 1:
210 		 (r_itv_begin (a->pitv) < r_itv_begin (b->pitv))? -1: 0;
211 }
212 
r_debug_trace_list(RDebug * dbg,int mode,ut64 offset)213 R_API void r_debug_trace_list(RDebug *dbg, int mode, ut64 offset) {
214 	int tag = dbg->trace->tag;
215 	RListIter *iter;
216 	bool flag = false;
217 	RList *info_list = r_list_new ();
218 	if (!info_list && mode == '=') {
219 		return;
220 	}
221 	RDebugTracepoint *trace;
222 	r_list_foreach (dbg->trace->traces, iter, trace) {
223 		if (!trace->tag || (tag & trace->tag)) {
224 			switch (mode) {
225 			case 'q':
226 				dbg->cb_printf ("0x%"PFMT64x"\n", trace->addr);
227 				break;
228 			case '=': {
229 				RListInfo *info = R_NEW0 (RListInfo);
230 				if (!info) {
231 					return;
232 				}
233 				info->pitv = (RInterval) {trace->addr, trace->size};
234 				info->vitv = info->pitv;
235 				info->perm = -1;
236 				info->name = r_str_newf ("%d", trace->times);
237 				info->extra = r_str_newf ("%d", trace->count);
238 				r_list_append (info_list, info);
239 				flag = true;
240 			}	break;
241 			case 1:
242 			case '*':
243 				dbg->cb_printf ("dt+ 0x%"PFMT64x" %d\n", trace->addr, trace->times);
244 				break;
245 			default:
246 				dbg->cb_printf ("0x%08"PFMT64x" size=%d count=%d times=%d tag=%d\n",
247 					trace->addr, trace->size, trace->count, trace->times, trace->tag);
248 				break;
249 			}
250 		}
251 	}
252 	if (flag) {
253 		r_list_sort (info_list, cmpaddr);
254 		RTable *table = r_table_new ("traces");
255 		table->cons = r_cons_singleton();
256 		RIO *io = dbg->iob.io;
257 		r_table_visual_list (table, info_list, offset, 1,
258 			r_cons_get_size (NULL), io->va);
259 		io->cb_printf ("\n%s\n", r_table_tostring (table));
260 		r_table_free (table);
261 		r_list_free (info_list);
262 	}
263 }
264 
265 // XXX: find better name, make it public?
r_debug_trace_is_traceable(RDebug * dbg,ut64 addr)266 static int r_debug_trace_is_traceable(RDebug *dbg, ut64 addr) {
267 	if (dbg->trace->addresses) {
268 		char addr_str[32];
269 		snprintf (addr_str, sizeof (addr_str), "0x%08"PFMT64x, addr);
270 		if (!strstr (dbg->trace->addresses, addr_str)) {
271 			return false;
272 		}
273 	}
274 	return true;
275 }
276 
r_debug_trace_add(RDebug * dbg,ut64 addr,int size)277 R_API RDebugTracepoint *r_debug_trace_add (RDebug *dbg, ut64 addr, int size) {
278 	RDebugTracepoint *tp;
279 	int tag = dbg->trace->tag;
280 	if (!r_debug_trace_is_traceable (dbg, addr)) {
281 		return NULL;
282 	}
283 	r_anal_trace_bb (dbg->anal, addr);
284 	tp = R_NEW0 (RDebugTracepoint);
285 	if (!tp) {
286 		return NULL;
287 	}
288 	tp->stamp = r_time_now ();
289 	tp->addr = addr;
290 	tp->tags = tag;
291 	tp->size = size;
292 	tp->count = ++dbg->trace->count;
293 	tp->times = 1;
294 	r_list_append (dbg->trace->traces, tp);
295 	ht_pp_update (dbg->trace->ht,
296 		sdb_fmt ("trace.%d.%"PFMT64x, tag, addr), tp);
297 	return tp;
298 }
299 
r_debug_trace_reset(RDebug * dbg)300 R_API void r_debug_trace_reset (RDebug *dbg) {
301 	RDebugTrace *t = dbg->trace;
302 	r_list_purge (t->traces);
303 	ht_pp_free (t->ht);
304 	t->ht = ht_pp_new0 ();
305 	t->traces = r_list_new ();
306 	t->traces->free = free;
307 }
308