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