radare2/libr/anal/esil.c
pancake dfb9d11474 Initial implementation of the new ESIL. (RPNESIL)
Use the 's' key with static analysis to step
Anal backends must be updated to use the new syntax
Uses 'ar' to get/set regs, and anal->io for read/write mem
Some few commands implemented, no extend api yet
2014-06-16 05:58:00 +02:00

360 lines
7.6 KiB
C

#if 0
/* radare - LGPL - Copyright 2013-2014 - pancake */
#include <r_anal.h>
#include <r_types.h>
#define NARGS 64
typedef struct r_anal_esil_t {
RAnal *anal;
int rightside;
int opsize;
RList *stack;
RList *opstack;
int (*set)(struct r_anal_esil_t *c, const char *str, ut64 n);
ut64 (*get)(struct r_anal_esil_t *c, const char *str);
ut64 (*num)(struct r_anal_esil_t *c, const char *str);
int (*iterate)(struct r_anal_esil_t *c, char *buf, int *tkns);
} RAnalEsil;
#if 0
mov ecx, 3 ecx=3
rep push ebx esp-=4,[esp]=ebx,ecx--,@ecx
jb 0x804840 ?cf,eip=4[eip+1]
cmovc eax, 3 ?cf,eax=3
add eax, 44 cf=eax+44<eax,eax+=44
int 0x80 $0x80
#endif
static int token(char c) {
switch (c) {
case '(': // open scope
return 5;
case ')': // close scope
return 6;
case '$': // syscall
case '@': // repeat if condition matches
case '?': // conditional
case '[': // store
case ']': // store
case '.':
return 1;
case ',':
return 4;
case '+':
case '-':
case '=':
case '*':
case '/':
case '|':
case '&':
case '!':
case '>':
case '<':
return 2;
default:
if ((c>='a' && c<='z')
|| (c>='A' && c<='Z')
|| (c>='0' && c<='9'))
return 3;
}
return 0; // unknown
}
static int r_anal_esil (RAnalEsil *c, const char *str) {
char buf[128];
const char *chr = str;
int t, bufi, tok, tknsi, tkns[128];
bufi = tok = tknsi = 0;
for (; *chr; chr++) {
t = token (*chr);
if (!t) {
printf ("unknown!\n");
return -1;
}
if (tok != t) {
buf[bufi++] = 0;
tkns[tknsi++] = t;
tkns[tknsi++] = bufi;
tok = t;
}
buf[bufi++] = *chr;
}
tkns[tknsi++] = 0;
buf[bufi] = 0;
return c->iterate (c, buf, tkns);
}
#if 0
static int iterate (RAnalEsil *c, char *buf, int *tkns) {
char *str;
int i, type;
for (i=0; tkns[i]; i+=2) {
type = tkns[i];
str = buf+tkns[i+1];
eprintf ("(%d) (%s)\n", type, str);
if (type==1) {
if (!strcmp (str, "?")) {
printf (" CONDITIONAL\n");
}
}
}
return 0;
}
#endif
#define TOKEN_IS(x,y,z) (x[i]==y && !strcmp (x[i+1]==z))
#define TOKEN_GET(x,y) x=tkns[i]; y=buf+tkns[i+1]
#define IS(x) (!strcmp(x,op))
static int esil_set (RAnalEsil *e, const char *s, ut64 n) {
if (e->anal && e->anal->reg) {
RRegItem *item;
item = r_reg_get (e->anal->reg, s, 0); // GPR only wtf?
eprintf ("SET (%p)\n", item);
if (item) return r_reg_set_value (e->anal->reg, item, n);
}
return R_TRUE;
}
static ut64 esil_get (RAnalEsil *e, const char *s) {
RRegItem *item;
// check for register
if (!s) return 0LL;
item = r_reg_get (e->anal->reg, s, 0); // GPR only wtf?
if (item) return r_reg_get_value (e->anal->reg, item);
return r_num_get (NULL, s);
}
#define OPUSH(x) r_list_push (c->opstack,x)
#define PUSH(x) r_list_push (c->stack,(void*)x)
#define OPOP() r_list_pop (c->opstack)
#define POP() r_list_pop (c->stack)
static int esil_commit (RAnalEsil *c, const char *op) {
const char *q = POP();
const char *p = POP();
//const char *o = op;
int ss = c->opsize;
if (ss) {
// eprintf (";; GET %d[%s]\n", ss, q);
// eprintf ("PSUH %s %s\n", p, q);
eprintf (";; -> this means that we have to resolve before accessing memory %d\n", c->opsize);
c->opsize = 0;
PUSH (p);
PUSH (q);
return 0;
}
if (!op) {
eprintf ("COMMIT UNKNOWN OP.. THIs IS []\n");
return 0;
}
//eprintf (";;; COMMIT ;;; (%s) %s (%s)\n", p, o, q);
if (IS ("[=")) {
eprintf ("EQUAL------SET\n");
} else
if (IS ("+")) {
// push (get (p)+get (q));
ut64 n = esil_get (c, p) + esil_get (c, q);
char *ns = malloc (32); // XXX memleak
sprintf (ns, "0x%"PFMT64x, n);
PUSH (ns);
eprintf (";;; %s %s\n", p, q);
//eprintf (" (((0x%llx)))\n", esil_get (c, p));
eprintf (";;; +EQUAL! (%s)\n", ns);
} else
if (IS ("-")) {
// push (get (p)+get (q));
ut64 n = esil_get (c, p) - esil_get (c, q);
char *ns = malloc (32); // XXX memleak
sprintf (ns, "0x%"PFMT64x, n);
PUSH (ns);
eprintf (";;; %s %s\n", p, q);
eprintf (";;; -EQUAL! (%s)\n", ns);
} else
if (IS ("*")) {
// push (get (p)+get (q));
ut64 n = esil_get (c, p) * esil_get (c, q);
char *ns = malloc (32); // XXX memleak
sprintf (ns, "0x%"PFMT64x, n);
PUSH (ns);
eprintf (";;; %s %s\n", p, q);
eprintf (";;; *EQUAL! (%s)\n", ns);
}
if (IS ("=")) {
if (p == NULL || q == NULL) {
eprintf ("Invalid construction\n");
return -1;
}
// set (p, get (q))
c->set (c, p, c->get (c, q));
eprintf (";;; EQUAL! (%s=%s)\n", p, q);
}
return 0;
}
static int emulate (RAnalEsil *c, char *buf, int *tkns) {
ut64 num = 0;
char *op = NULL;
char *str = NULL;
int i, type;
c->opstack = r_list_new ();
c->stack = r_list_new ();
c->opsize = 0;
c->rightside = 0;
for (i=0; tkns[i]; i+=2) {
TOKEN_GET (type, str);
eprintf ("(%d) (%s)\n", type, str);
switch (type) {
// case 0 handled in for conditional
case 1: /* special command */
if (!strcmp (str, "[")) {
int curstack = r_list_length (c->stack);
eprintf ("STACK POINTER %d\n", curstack);
c->opsize = (int)num;
// TODO: test for size
// read tokens until ']'
// TOKEN_UNTIL (1, "]");
for (i+=2; tkns[i]; i+=2) {
TOKEN_GET (type, str);
eprintf ("--- %d (%s)\n", tkns[i], buf+tkns[i+1]);
switch (tkns[i]) {
case 1:
if (!strcmp (str, "]")) {
if (!c->opsize) c->opsize =
c->anal->bits==64?8:4;
//int j, len = r_list_length (c->stack) - curstack;
char *a;
OPUSH (op);
while ((a = OPOP ())) {
// eprintf ("---> op %s\n", op);
esil_commit (c, a);
}
//op = NULL;
printf (" %s (size %d)\n", c->rightside?"GET":"SET", (int)num);
goto dungeon;
// set destination for write
// expect '='
}
break;
case 2:
op = str;
OPUSH (op);
break;
case 3:
PUSH (str);
break;
}
}
if (!tkns[i]) {
printf ("Unexpected eof\n");
return 1;
}
} else
if (!strcmp (str, "?")) {
printf (" CONDITIONAL\n");
i += 2;
TOKEN_GET (type, str);
if (!type) {
eprintf (" UNEXPECTED EOF\n");
return 1;
}
if (type!=3) {
printf (" UNEXPECTED TOKEN\n");
return 1;
}
//while () { i += 2; }
}
break;
case 2:
if (op) {
//eprintf (" XXX Redefine op %s\n", op);
if (!strcmp (op, "*")) { // prio
esil_commit (c, op);
} else OPUSH (op);
}
op = str;
if (IS ("=")) {
c->rightside = 1;
}
break;
case 3:
num = c->num (c, str); //
// eprintf ("; push %s\n" , str);
PUSH (str);
break;
case 4:
esil_commit (c, op);
op = NULL;
break;
case 5:
// newcontext();
//esil_push_scope (c);
eprintf ("OPEN SCOPE\n");
break;
case 6:
{
//char *res = esil_pop_scope (c);
// if scope > 0 :
//PUSH (res);
esil_commit (c, op);
// free (res);
// commit()
// closecontext()
// push result
eprintf ("CLOSE SCOPE\n");
}
break;
}
dungeon:
{/*trick*/int/*label*/x/*parsing*/=/*fix*/0;x = !x;}
}
eprintf (";;; COMMIT (%s) (%s)\n", op, str);
esil_commit (c, op);
if (r_list_length (c->opstack)>0) {
char *a;
while ((a = OPOP ())) {
esil_commit (c, a);
}
}
op = NULL;
return 0;
}
static ut64 num(struct r_anal_esil_t *c, const char *str) {
return r_num_get (NULL, str);
}
#define C(x) r_anal_esil(&c,x)
#ifdef MAIN
int main() {
RAnalEsil c = {
.num = num,
.iterate = emulate //iterate
};
//C ("esp=32,eax++");
C ("4[eax+3]=123");
//C ("?cf,esp=32+2,eax++");
//C ("cf=eax+44<eax,eax+=44");
//C ("esp-=4,[esp]=ebx,ecx--,@ecx");
return 0;
}
#endif
R_API int r_anal_esil_eval(RAnal *anal, const char *str) {
RAnalEsil c = {
.anal = anal,
.get = esil_get,
.set = esil_set,
.num = num,
.iterate = emulate //iterate
};
C (str);
return 0;
}
#endif