1 /* radare - LGPL - Copyright 2015 - pancake */
2 
3 #include <r_debug.h>
4 
5 #if 0
6 	/* debugesil performs step into + esil conditionals */
7 	ESIL conditionals can be used to detect when a specific address is
8 	accessed, or a register. Those esil conditionals must be evaluated
9 	every iteration to ensure the register values are updated. Think
10 	in DebugESIL as software-watchpoints.
11 
12 	[read|write|exec]-[reg|mem] [expression]
13 
14 	de rw reg eax
15 	de-*
16 
17 	# expression can be a number or a range (if .. is found)
18 	# The <=, >=, ==, <, > comparisons are also supported
19 
20 #endif
21 
22 typedef struct {
23 	int rwx;
24 	int dev;
25 	char *expr;
26 } EsilBreak;
27 
28 // TODO: Kill those globals
29 RDebug *dbg = NULL;
30 static int has_match = 0;
31 static int prestep = 1; // TODO: make it configurable
32 static ut64 opc = 0;
33 RList *esil_watchpoints = NULL;
34 #define EWPS esil_watchpoints
35 #define ESIL dbg->anal->esil
36 
exprmatch(RDebug * dbg,ut64 addr,const char * expr)37 static int exprmatch (RDebug *dbg, ut64 addr, const char *expr) {
38 	char *e = strdup (expr);
39 	if (!e) {
40 		return 0;
41 	}
42 	char *p = strstr (e, "..");
43 	ut64 a,b;
44 	int ret = 0;
45 	if (p) {
46 		*p = 0;
47 		p += 2;
48 		a = r_num_math (dbg->num, e);
49 		b = r_num_math (dbg->num, p);
50 		if (a<b) {
51 			if (addr >= a && addr <= b) {
52 				ret = 1;
53 			}
54 		} else {
55 			if (addr >= b && addr <= a) {
56 				ret = 1;
57 			}
58 		}
59 	} else {
60 		a = r_num_math (dbg->num, e);
61 		if (addr == a) {
62 			ret = 1;
63 		}
64 	}
65 	has_match = ret;
66 	free (e);
67 	return ret;
68 }
69 
esilbreak_check_pc(RDebug * dbg,ut64 pc)70 static int esilbreak_check_pc (RDebug *dbg, ut64 pc) {
71 	EsilBreak *ew;
72 	RListIter *iter;
73 	if (!pc) {
74 		pc = r_debug_reg_get (dbg, dbg->reg->name[R_REG_NAME_PC]);
75 	}
76 	r_list_foreach (EWPS, iter, ew) {
77 		if (ew->rwx & R_PERM_X) {
78 			if (exprmatch (dbg, pc, ew->expr)) {
79 				return 1;
80 			}
81 		}
82 	}
83 	return 0;
84 }
85 
esilbreak_mem_read(RAnalEsil * esil,ut64 addr,ut8 * buf,int len)86 static int esilbreak_mem_read(RAnalEsil *esil, ut64 addr, ut8 *buf, int len) {
87 	EsilBreak *ew;
88 	RListIter *iter;
89 	eprintf (Color_GREEN"MEM READ 0x%"PFMT64x"\n"Color_RESET, addr);
90 	r_list_foreach (EWPS, iter, ew) {
91 		if (ew->rwx & R_PERM_R && ew->dev == 'm') {
92 			if (exprmatch (dbg, addr, ew->expr)) {
93 				has_match = 1;
94 				return 1;
95 			}
96 		}
97 	}
98 	return 0; // fallback
99 }
100 
esilbreak_mem_write(RAnalEsil * esil,ut64 addr,const ut8 * buf,int len)101 static int esilbreak_mem_write(RAnalEsil *esil, ut64 addr, const ut8 *buf, int len) {
102 	EsilBreak *ew;
103 	RListIter *iter;
104 	eprintf (Color_RED"MEM WRTE 0x%"PFMT64x"\n"Color_RESET, addr);
105 	r_list_foreach (EWPS, iter, ew) {
106 		if (ew->rwx & R_PERM_W && ew->dev == 'm') {
107 			if (exprmatch (dbg, addr, ew->expr)) {
108 				has_match = 1;
109 				return 1;
110 			}
111 		}
112 	}
113 	return 1; // fallback
114 }
115 
esilbreak_reg_read(RAnalEsil * esil,const char * regname,ut64 * num,int * size)116 static int esilbreak_reg_read(RAnalEsil *esil, const char *regname, ut64 *num, int *size) {
117 	EsilBreak *ew;
118 	RListIter *iter;
119 	if (regname[0]>='0' && regname[0]<='9') {
120 		//eprintf (Color_CYAN"IMM READ %s\n"Color_RESET, regname);
121 		return 0;
122 	}
123 	eprintf (Color_YELLOW"REG READ %s\n"Color_RESET, regname);
124 	r_list_foreach (EWPS, iter, ew) {
125 		if (ew->rwx & R_PERM_R && ew->dev == 'r') {
126 			// XXX: support array of regs in expr
127 			if (!strcmp (regname, ew->expr)) {
128 				has_match = 1;
129 				return 1;
130 			}
131 		}
132 	}
133 	return 0; // fallback
134 }
135 
exprtoken(RDebug * dbg,char * s,const char * sep,char ** o)136 static int exprtoken(RDebug *dbg, char *s, const char *sep, char **o) {
137 	char *p = strstr (s, sep);
138 	if (p) {
139 		*p = 0;
140 		p += strlen (sep);
141 		*o = p;
142 		return 1;
143 	}
144 	*o = NULL;
145 	return 0;
146 }
147 
exprmatchreg(RDebug * dbg,const char * regname,const char * expr)148 static int exprmatchreg (RDebug *dbg, const char *regname, const char *expr) {
149 	int ret = 0;
150 	char *p;
151 	char *s = strdup (expr);
152 	if (!s) {
153 		return 0;
154 	}
155 	if (!strcmp (regname, s)) {
156 		ret = 1;
157 	} else {
158 #define CURVAL 0){}r_str_trim (s);if (!strcmp(regname,s) && regval
159 		ut64 regval = r_debug_reg_get_err (dbg, regname, NULL, NULL);
160 		if (exprtoken (dbg, s, ">=", &p)) {
161 			if (CURVAL >= r_num_math (dbg->num, p))
162 				ret = 1;
163 		} else if (exprtoken (dbg, s, "<=", &p)) {
164 			if (CURVAL <= r_num_math (dbg->num, p))
165 				ret = 1;
166 		} else if (exprtoken (dbg, s, "==", &p)) {
167 			if (CURVAL <= r_num_math (dbg->num, p))
168 				ret = 1;
169 		} else if (exprtoken (dbg, s, "<", &p)) {
170 			if (CURVAL < r_num_math (dbg->num, p))
171 				ret = 1;
172 		} else if (exprtoken (dbg, s, ">", &p)) {
173 			if (CURVAL > r_num_math (dbg->num, p))
174 				ret = 1;
175 		} else if (exprtoken (dbg, s, " ", &p)) {
176 			r_str_trim (s);
177 			if (!strcmp (regname, s)) {
178 				ut64 num = r_num_math (dbg->num, p);
179 				ret = exprmatch (dbg, num, s);
180 			}
181 		} else {
182 			if (!strcmp (regname, s)) {
183 				ret = 1;
184 			}
185 		}
186 	}
187 	free (s);
188 	return ret;
189 }
190 
esilbreak_reg_write(RAnalEsil * esil,const char * regname,ut64 * num)191 static int esilbreak_reg_write(RAnalEsil *esil, const char *regname, ut64 *num) {
192 	EsilBreak *ew;
193 	RListIter *iter;
194 	if (regname[0] >= '0' && regname[0] <= '9') {
195 		// wtf this should never happen
196 		//eprintf (Color_BLUE"IMM WRTE %s\n"Color_RESET, regname);
197 		return 0;
198 	}
199 	eprintf (Color_MAGENTA"REG WRTE %s 0x%"PFMT64x"\n"Color_RESET, regname, *num);
200 	r_list_foreach (EWPS, iter, ew) {
201 		if ((ew->rwx & R_PERM_W) && (ew->dev == 'r')) {
202 			// XXX: support array of regs in expr
203 			if (exprmatchreg (dbg, regname, ew->expr)) {
204 				has_match = 1;
205 				return 1;
206 			}
207 		}
208 	}
209 	return 1; // fallback
210 }
211 
r_debug_esil_prestep(RDebug * d,int p)212 R_API void r_debug_esil_prestep (RDebug *d, int p) {
213 	prestep = p;
214 }
215 
r_debug_esil_stepi(RDebug * d)216 R_API int r_debug_esil_stepi (RDebug *d) {
217 	RAnalOp op;
218 	ut8 obuf[64];
219 	int ret = 1;
220 	dbg = d;
221 	if (!ESIL) {
222 		ESIL = r_anal_esil_new (32, true, 64);
223 		// TODO setup something?
224 		if (!ESIL) {
225 			return 0;
226 		}
227 	}
228 
229 	r_debug_reg_sync (dbg, R_REG_TYPE_GPR, false);
230 	opc = r_debug_reg_get (dbg, dbg->reg->name[R_REG_NAME_PC]);
231 	dbg->iob.read_at (dbg->iob.io, opc, obuf, sizeof (obuf));
232 
233 	//dbg->iob.read_at (dbg->iob.io, npc, buf, sizeof (buf));
234 
235 	//dbg->anal->reg = dbg->reg; // hack
236 	ESIL->cb.hook_mem_read = &esilbreak_mem_read;
237 	ESIL->cb.hook_mem_write = &esilbreak_mem_write;
238 	ESIL->cb.hook_reg_read = &esilbreak_reg_read;
239 	ESIL->cb.hook_reg_write = &esilbreak_reg_write;
240 
241 	if (prestep) {
242 		// required when a exxpression is like <= == ..
243 		// otherwise it will stop at the next instruction
244 		if (r_debug_step (dbg, 1)<1) {
245 			eprintf ("Step failed\n");
246 			return 0;
247 		}
248 		r_debug_reg_sync (dbg, R_REG_TYPE_GPR, false);
249 		//	npc = r_debug_reg_get (dbg, dbg->reg->name[R_REG_NAME_PC]);
250 	}
251 
252 	if (r_anal_op (dbg->anal, &op, opc, obuf, sizeof (obuf), R_ANAL_OP_MASK_ESIL)) {
253 		if (esilbreak_check_pc (dbg, opc)) {
254 			eprintf ("STOP AT 0x%08"PFMT64x"\n", opc);
255 			ret = 0;
256 		} else {
257 			r_anal_esil_set_pc (ESIL, opc);
258 			eprintf ("0x%08"PFMT64x"  %s\n", opc, R_STRBUF_SAFEGET (&op.esil));
259 			(void)r_anal_esil_parse (ESIL, R_STRBUF_SAFEGET (&op.esil));
260 			//r_anal_esil_dumpstack (ESIL);
261 			r_anal_esil_stack_free (ESIL);
262 			ret = 1;
263 		}
264 	}
265 	if (!prestep) {
266 		if (ret && !has_match) {
267 			if (r_debug_step (dbg, 1)<1) {
268 				eprintf ("Step failed\n");
269 				return 0;
270 			}
271 			r_debug_reg_sync (dbg, R_REG_TYPE_GPR, false);
272 			//	npc = r_debug_reg_get (dbg, dbg->reg->name[R_REG_NAME_PC]);
273 		}
274 	}
275 	return ret;
276 }
277 
r_debug_esil_step(RDebug * dbg,ut32 count)278 R_API ut64 r_debug_esil_step(RDebug *dbg, ut32 count) {
279 	count++;
280 	has_match = 0;
281 	r_cons_break_push (NULL, NULL);
282 	do {
283 		if (r_cons_is_breaked ()) {
284 			break;
285 		}
286 		if (has_match) {
287 			eprintf ("EsilBreak match at 0x%08"PFMT64x"\n", opc);
288 			break;
289 		}
290 		if (count > 0) {
291 			count--;
292 			if (!count) {
293 				//eprintf ("Limit reached\n");
294 				break;
295 			}
296 		}
297 	} while (r_debug_esil_stepi (dbg));
298 	r_cons_break_pop ();
299 	return opc;
300 }
301 
r_debug_esil_continue(RDebug * dbg)302 R_API ut64 r_debug_esil_continue (RDebug *dbg) {
303 	return r_debug_esil_step (dbg, UT32_MAX);
304 }
305 
ewps_free(EsilBreak * ew)306 static void ewps_free(EsilBreak *ew) {
307 	R_FREE (ew->expr);
308 	free (ew);
309 }
310 
r_debug_esil_watch_empty(RDebug * dbg)311 R_API int r_debug_esil_watch_empty(RDebug *dbg) {
312 	return r_list_empty (EWPS);
313 }
314 
r_debug_esil_watch(RDebug * dbg,int rwx,int dev,const char * expr)315 R_API void r_debug_esil_watch(RDebug *dbg, int rwx, int dev, const char *expr) {
316 	if (!EWPS) {
317 		EWPS = r_list_new ();
318 		if (!EWPS) {
319 			return;
320 		}
321 		EWPS->free = (RListFree)ewps_free;
322 	}
323 	EsilBreak *ew = R_NEW0 (EsilBreak);
324 	if (!ew) {
325 		R_FREE (EWPS);
326 		return;
327 	}
328 	ew->rwx = rwx;
329 	ew->dev = dev;
330 	ew->expr = strdup (expr);
331 	r_list_append (EWPS, ew);
332 }
333 
r_debug_esil_watch_reset(RDebug * dbg)334 R_API void r_debug_esil_watch_reset(RDebug *dbg) {
335 	r_list_free (EWPS);
336 	EWPS = NULL;
337 }
338 
r_debug_esil_watch_list(RDebug * dbg)339 R_API void r_debug_esil_watch_list(RDebug *dbg) {
340 	EsilBreak *ew;
341 	RListIter *iter;
342 	r_list_foreach (EWPS, iter, ew) {
343 		dbg->cb_printf ("de %s %c %s\n", r_str_rwx_i (ew->rwx), ew->dev, ew->expr);
344 	}
345 }
346