/* * Save 4096 bytes from %esp * TODO: Add support for reverse stack architectures * Also known as r_debug_inject() */ R_API ut64 r_debug_execute(RDebug *dbg, const ut8 *buf, int len, int restore) { int orig_sz; ut8 stackbackup[4096]; ut8 *backup, *orig = NULL; RRegItem *ri, *risp, *ripc; ut64 rsp, rpc, ra0 = 0LL; if (r_debug_is_dead (dbg)) return R_FALSE; ripc = r_reg_get (dbg->reg, dbg->reg->name[R_REG_NAME_PC], R_REG_TYPE_GPR); risp = r_reg_get (dbg->reg, dbg->reg->name[R_REG_NAME_SP], R_REG_TYPE_GPR); if (ripc) { r_debug_reg_sync (dbg, R_REG_TYPE_GPR, R_FALSE); orig = r_reg_get_bytes (dbg->reg, -1, &orig_sz); if (orig == NULL) { eprintf ("Cannot get register arena bytes\n"); return 0LL; } rpc = r_reg_get_value (dbg->reg, ripc); rsp = r_reg_get_value (dbg->reg, risp); backup = malloc (len); if (backup == NULL) { free (orig); return 0LL; } dbg->iob.read_at (dbg->iob.io, rpc, backup, len); dbg->iob.read_at (dbg->iob.io, rsp, stackbackup, len); r_bp_add_sw (dbg->bp, rpc+len, dbg->bpsize, R_BP_PROT_EXEC); /* execute code here */ dbg->iob.write_at (dbg->iob.io, rpc, buf, len); //r_bp_add_sw (dbg->bp, rpc+len, 4, R_BP_PROT_EXEC); r_debug_continue (dbg); //r_bp_del (dbg->bp, rpc+len); /* TODO: check if stopped in breakpoint or not */ r_bp_del (dbg->bp, rpc+len); dbg->iob.write_at (dbg->iob.io, rpc, backup, len); if (restore) { dbg->iob.write_at (dbg->iob.io, rsp, stackbackup, len); } r_debug_reg_sync (dbg, R_REG_TYPE_GPR, R_FALSE); ri = r_reg_get (dbg->reg, dbg->reg->name[R_REG_NAME_A0], R_REG_TYPE_GPR); ra0 = r_reg_get_value (dbg->reg, ri); if (restore) { r_reg_set_bytes (dbg->reg, -1, orig, orig_sz); } else { r_reg_set_value (dbg->reg, ripc, rpc); } r_debug_reg_sync (dbg, R_REG_TYPE_GPR, R_TRUE); free (backup); free (orig); eprintf ("ra0=0x%08"PFMT64x"\n", ra0); } else eprintf ("r_debug_execute: Cannot get program counter\n"); return (ra0); }
// XXX: Do this work correctly? static RList *backtrace_x86_64_anal(RDebug *dbg, ut64 at) { int i; ut8 buf[8]; RDebugFrame *frame; ut64 ptr, ebp2 = UT64_MAX; ut64 _rip, _rbp; RList *list; RReg *reg = dbg->reg; RIOBind *bio = &dbg->iob; RAnalFunction *fcn; _rip = r_reg_get_value (reg, r_reg_get (reg, "rip", R_REG_TYPE_GPR)); if (at == UT64_MAX) { //_rsp = r_reg_get_value (reg, r_reg_get (reg, "rsp", R_REG_TYPE_GPR)); _rbp = r_reg_get_value (reg, r_reg_get (reg, "rbp", R_REG_TYPE_GPR)); } else { _rbp = at; } list = r_list_new (); list->free = free; bio->read_at (bio->io, _rip, (ut8*)&buf, 8); // TODO : frame->size by using esil to emulate first instructions fcn = r_anal_get_fcn_in (dbg->anal, _rip, R_ANAL_FCN_TYPE_NULL); if (fcn) { frame = R_NEW0 (RDebugFrame); frame->addr = _rip; frame->size = 0; frame->sp = _rbp; frame->bp = _rbp + 8; // XXX r_list_append (list, frame); } for (i=1; i<dbg->btdepth; i++) { // TODO: make those two reads in a shot bio->read_at (bio->io, _rbp, (ut8*)&ebp2, 8); if (ebp2 == UT64_MAX) break; bio->read_at (bio->io, _rbp+8, (ut8*)&ptr, 8); if (!ptr || !_rbp) break; //fcn = r_anal_get_fcn_in (dbg->anal, ptr, R_ANAL_FCN_TYPE_NULL); frame = R_NEW0 (RDebugFrame); frame->addr = ptr; frame->size = 0; frame->sp = _rbp; frame->bp = _rbp + 8; //frame->name = (fcn && fcn->name) ? strdup (fcn->name) : NULL; r_list_append (list, frame); _rbp = ebp2; } return list; }
static RList *backtrace_x86_64(RDebug *dbg, ut64 at) { int i; ut8 buf[8]; RDebugFrame *frame; ut64 ptr, ebp2; ut64 _rip, _rsp, _rbp = 0; RList *list; RReg *reg = dbg->reg; RIOBind *bio = &dbg->iob; _rip = r_reg_get_value (reg, r_reg_get (reg, "rip", R_REG_TYPE_GPR)); if (at == UT64_MAX) { _rsp = r_reg_get_value (reg, r_reg_get (reg, "rsp", R_REG_TYPE_GPR)); _rbp = r_reg_get_value (reg, r_reg_get (reg, "rbp", R_REG_TYPE_GPR)); } else { _rsp = _rbp = at; } list = r_list_new (); list->free = free; bio->read_at (bio->io, _rip, (ut8*)&buf, 8); /* %rbp=old rbp, %rbp+4 points to ret */ /* Plugin before function prelude: push %rbp ; mov %rsp, %rbp */ if (!memcmp (buf, "\x55\x89\xe5", 3) || !memcmp (buf, "\x89\xe5\x57", 3)) { if (!bio->read_at (bio->io, _rsp, (ut8*)&ptr, 8)) { eprintf ("read error at 0x%08"PFMT64x"\n", _rsp); r_list_purge (list); free (list); return false; } frame = R_NEW0 (RDebugFrame); frame->addr = ptr; frame->size = 0; // TODO ? r_list_append (list, frame); _rbp = ptr; } for (i=1; i<dbg->btdepth; i++) { // TODO: make those two reads in a shot bio->read_at (bio->io, _rbp, (ut8*)&ebp2, 8); if (ebp2 == UT64_MAX) break; bio->read_at (bio->io, _rbp+8, (ut8*)&ptr, 8); if (!ptr || !_rbp) break; frame = R_NEW0 (RDebugFrame); frame->addr = ptr; frame->size = 0; // TODO ? r_list_append (list, frame); _rbp = ebp2; } return list; }
/* restore program counter after breakpoint hit */ static int r_debug_recoil(RDebug *dbg) { int recoil; RRegItem *ri; if (r_debug_is_dead (dbg)) return R_FALSE; r_debug_reg_sync (dbg, R_REG_TYPE_GPR, R_FALSE); ri = r_reg_get (dbg->reg, dbg->reg->name[R_REG_NAME_PC], -1); dbg->reason.bpi = NULL; if (ri) { ut64 addr = r_reg_get_value (dbg->reg, ri); recoil = r_bp_recoil (dbg->bp, addr); //eprintf ("[R2] Breakpoint recoil at 0x%"PFMT64x" = %d\n", addr, recoil); #if __arm__ if (recoil<1) recoil = 0; // XXX Hack :D #else if (recoil<1) recoil = 0; //1; // XXX Hack :D (x86 only?) #endif if (recoil) { dbg->reason.type = R_DEBUG_REASON_BREAKPOINT; dbg->reason.bpi = r_bp_get_at (dbg->bp, addr-recoil); dbg->reason.addr = addr - recoil; r_reg_set_value (dbg->reg, ri, addr-recoil); if (r_reg_get_value (dbg->reg, ri) != (addr-recoil)) { eprintf ("r_debug_recoil: Cannot set program counter\n"); return R_FALSE; } r_debug_reg_sync (dbg, R_REG_TYPE_GPR, R_TRUE); //eprintf ("[BP Hit] Setting pc to 0x%"PFMT64x"\n", (addr-recoil)); return R_TRUE; } } else eprintf ("r_debug_recoil: Cannot get program counter\n"); return R_FALSE; }
/* restore program counter after breakpoint hit */ static int r_debug_recoil(RDebug *dbg) { int recoil; RRegItem *ri; if (r_debug_is_dead (dbg)) { return false; } r_debug_reg_sync (dbg, R_REG_TYPE_GPR, false); ri = r_reg_get (dbg->reg, dbg->reg->name[R_REG_NAME_PC], -1); dbg->reason.bpi = NULL; if (ri) { ut64 addr = r_reg_get_value (dbg->reg, ri); recoil = r_bp_recoil (dbg->bp, addr - dbg->bpsize); //eprintf ("[R2] Breakpoint recoil at 0x%"PFMT64x" = %d\n", addr, recoil); if (recoil < 1) recoil = 0; // XXX Hack :D if (recoil) { dbg->in_recoil = true; dbg->reason.type = R_DEBUG_REASON_BREAKPOINT; dbg->reason.bpi = r_bp_get_at (dbg->bp, addr-recoil); dbg->reason.addr = addr - recoil; r_reg_set_value (dbg->reg, ri, addr-recoil); if (r_reg_get_value (dbg->reg, ri) != (addr-recoil)) { eprintf ("r_debug_recoil: Cannot set program counter\n"); return false; } r_debug_reg_sync (dbg, R_REG_TYPE_GPR, true); //eprintf ("[BP Hit] Setting pc to 0x%"PFMT64x"\n", (addr-recoil)); return true; } } else { eprintf ("r_debug_recoil: Cannot get program counter\n"); } return false; }
static const char *parse_def(RReg *reg, char **tok, const int n) { RRegItem *item; char *end; int type; if (n != 5 && n != 6) return "Invalid syntax"; type = r_reg_type_by_name (tok[0]); if (type < 0) { return "Invalid register type"; } item = R_NEW0 (RRegItem); if (!item) return "Unable to allocate memory"; item->type = type; item->name = strdup (tok[1]); // All the numeric arguments are strictly checked item->size = parse_size (tok[2], &end); if (*end != '\0' || !item->size) { r_reg_item_free (item); return "Invalid size"; } item->offset = parse_size (tok[3], &end); if (*end != '\0') { r_reg_item_free (item); return "Invalid offset"; } item->packed_size = parse_size (tok[4], &end); if (*end != '\0') { r_reg_item_free (item); return "Invalid packed size"; } // Dynamically update the list of supported bit sizes reg->bits |= item->size; // This is optional if (n == 6) item->flags = strdup (tok[5]); // Don't allow duplicate registers if (r_reg_get (reg, item->name, R_REG_TYPE_ALL)) { r_reg_item_free (item); return "Duplicate register definition"; } /* Hack to put flags in the same arena as gpr */ if (type == R_REG_TYPE_FLG) { type = R_REG_TYPE_GPR; } r_list_append (reg->regset[item->type].regs, item); // Update the overall profile size if (item->offset + item->size > reg->size) { reg->size = item->offset + item->size; } return NULL; }
/* io.mem_base = reg1; io.mem_index = reg2; io.disp = 0x0ff */ static RAnalValue *anal_fill_ai_mm(RAnal *anal, x86im_instr_object io) { RAnalValue *ret = r_anal_value_new (); st64 disp = r_hex_bin_truncate (io.disp, io.disp_size); ret->memref = anal->bits/8; if (io.mem_base == 0) { ret->base = disp; } else { ret->reg = r_reg_get (anal->reg, anal_reg (io.mem_base), R_REG_TYPE_GPR); ret->delta = disp; if (io.mem_index != 0) ret->regdelta = r_reg_get (anal->reg, anal_reg (io.mem_index), R_REG_TYPE_GPR); } return ret; }
static int reil_cmp(RAnalEsil *esil) { RAnalReilInst *ins; char tmp_buf[REGBUFSZ]; RAnalReilArg *op2, *op1; op2 = reil_pop_arg(esil); if (!op2) return false; op1 = reil_pop_arg(esil); if (!op1) { R_FREE (op2); return false; } ins = R_NEW0 (RAnalReilInst); if (!ins) { R_FREE (op1); R_FREE (op2); return false; } ins->opcode = REIL_EQ; ins->arg[0] = op2; ins->arg[1] = op1; ins->arg[2] = R_NEW0(RAnalReilArg); if (!ins->arg[2]) { reil_free_inst (ins); return false; } get_next_temp_reg(esil, tmp_buf); reil_make_arg(esil, ins->arg[2], tmp_buf); ins->arg[2]->size = 1; reil_print_inst(esil, ins); // Set vars needed to determine flags. snprintf(esil->Reil->cur, sizeof(esil->Reil->old) - 1, "%s:%d", ins->arg[2]->name, ins->arg[2]->size); snprintf(esil->Reil->old, sizeof(esil->Reil->cur) - 1, "%s:%d", op2->name, op2->size); if (r_reg_get(esil->anal->reg, op2->name, -1)) { esil->Reil->lastsz = op2->size; } else if (r_reg_get(esil->anal->reg, op1->name, -1)) { esil->Reil->lastsz = op1->size; } reil_push_arg(esil, ins->arg[2]); reil_free_inst(ins); return 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); }
static void ios_hwstep_enable32 (RDebug *dbg, task_t port, int enable) { int i; static ARMDebugState32 olds; ARMDebugState32 ds; mach_msg_type_number_t count = ARM_DEBUG_STATE32_COUNT; (void) thread_get_state (port, ARM_DEBUG_STATE32, (thread_state_t)&ds, &count); //static ut64 chainstep = UT64_MAX; if (enable) { RIOBind *bio = &dbg->iob; ut32 pc = r_reg_get_value (dbg->reg, r_reg_get (dbg->reg, "pc", R_REG_TYPE_GPR)); ut32 cpsr = r_reg_get_value (dbg->reg, r_reg_get (dbg->reg, "cpsr", R_REG_TYPE_GPR)); for (i = 0; i < 16 ; i++) { ds.bcr[i] = ds.bvr[i] = 0; } olds = ds; //chainstep = UT64_MAX; // state = old_state; ds.bvr[i] = pc & (UT32_MAX >> 2) << 2; ds.bcr[i] = BCR_M_IMVA_MISMATCH | S_USER | BCR_ENABLE; if (cpsr & 0x20) { ut16 op; if (pc & 2) { ds.bcr[i] |= BAS_IMVA_2_3; } else { ds.bcr[i] |= BAS_IMVA_0_1; } /* check for thumb */ bio->read_at (bio->io, pc, (void *)&op, 2); if (isThumb32 (op)) { eprintf ("Thumb32 chain stepping not supported yet\n"); //chainstep = pc + 2; } else { ds.bcr[i] |= BAS_IMVA_ALL; } } else { ds.bcr[i] |= BAS_IMVA_ALL; } } else {
int gb_write(emu *e, ut64 addr, ut8 *buf, ut32 len) { if(0x2000 <= addr && addr < 0x4000) { if(buf[0] == 0x20 || buf[0] == 0x40 || buf[0] == 0x60) return r_reg_set_value(e->reg, r_reg_get(e->reg, "mbcrom", -1), 0); if(!buf[0]) return r_reg_set_value(e->reg, r_reg_get(e->reg, "mbcrom", -1), 0); return r_reg_set_value(e->reg, r_reg_get(e->reg, "mbcrom", -1), buf[0]-1); } if(0x4000 <= addr && addr < 0x6000) { if(!buf[0]) return r_reg_set_value(e->reg, r_reg_get(e->reg, "mbcram", -1), 0); return r_reg_set_value(e->reg, r_reg_get(e->reg, "mbcram", -1), buf[0]-1); } if(0xa000 <= addr && addr < 0xc000) return emu_write(e, addr + (r_reg_getv(e->reg, "mbcram") << 16), buf, len); return emu_write(e, addr, buf, len); }
int reg_write(RAnalEsil *esil, const char *regname, ut64 num) { RRegItem *reg = r_reg_get (esil->anal->reg, regname, -1); if (reg) { if (num) r_reg_set_value (esil->anal->reg, reg,num); return 1; } return 0; }
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; }
int reg_read(RAnalEsil *esil, const char *regname, ut64 *num) { RRegItem *reg = r_reg_get (esil->anal->reg, regname, -1); if (reg) { if (num) *num = r_reg_get_value (esil->anal->reg, reg); return 1; } return 0; }
// Get size of a register. static ut8 esil_internal_sizeof_reg(RAnalEsil *esil, const char *r) { RRegItem *i; if (!esil || !esil->anal || !esil->anal->reg || !r) return false; i = r_reg_get(esil->anal->reg, r, -1); if (!i) return false; return (ut8)i->size; }
static ut32 i8051_reg_read (RReg *reg, const char *regname) { if (reg) { RRegItem *item = r_reg_get (reg, regname, R_REG_TYPE_GPR); if (item) { return r_reg_get_value (reg, item); } } return 0; }
R_API boolt r_anal_cc_update (RAnal *anal, RAnalCC *cc, RAnalOp *op) { RRegItem *it; cc->off = op->addr; switch (op->type) { case R_ANAL_OP_TYPE_CALL: case R_ANAL_OP_TYPE_UCALL: cc->type = R_ANAL_CC_TYPE_STDCALL; // TODO: check if next instruction after call is restoring stack cc->jump = op->jump; return R_FALSE; case R_ANAL_OP_TYPE_SWI: // syscall cc->type = R_ANAL_CC_TYPE_FASTCALL; cc->off = op->jump; cc->jump = op->val; // syscall number return R_FALSE; case R_ANAL_OP_TYPE_XOR: if (op->src[0] && op->src[0]->reg && op->dst && op->dst->reg && op->dst->reg->name) { char *n1 = op->dst->reg->name; char *n2 = op->src[0]->reg->name; // XXX: must handle XOR operation properly // if n1 == n2 then set to 0 if (!strcmp (n1, n2)) { it = r_reg_get (anal->reg, n1, R_REG_TYPE_GPR); r_reg_set_value (anal->reg, it, 0); } } return R_TRUE; case R_ANAL_OP_TYPE_MOV: if (op->dst && op->dst->reg) { it = r_reg_get (anal->reg, op->dst->reg->name, R_REG_TYPE_GPR); if (it && op->src[0]) r_reg_set_value (anal->reg, it, op->src[0]->imm); } return R_TRUE; case R_ANAL_OP_TYPE_PUSH: case R_ANAL_OP_TYPE_UPUSH: // add argument cc->nargs ++; if (cc->nargs>0 && cc->nargs < R_ANAL_CC_ARGS) cc->args[cc->nargs] = op->val; return R_TRUE; } // must update internal stuff to recognize parm return R_TRUE; }
static bool i8051_reg_write (RReg *reg, const char *regname, ut32 num) { if (reg) { RRegItem *item = r_reg_get (reg, regname, R_REG_TYPE_GPR); if (item) { r_reg_set_value (reg, item, num); return true; } } return false; }
R_API int r_debug_continue_until(RDebug *dbg, ut64 addr) { // TODO: use breakpoint+continue... more efficient RRegItem *ripc = r_reg_get (dbg->reg, dbg->reg->name[R_REG_NAME_PC], R_REG_TYPE_GPR); int n = 0; ut64 pc = r_reg_get_value (dbg->reg, ripc); while (pc != addr && !r_debug_is_dead (dbg)) { r_debug_step (dbg, 1); // TODO: obey breakpoints too? /* TODO: check if the debugger stops at the right address */ pc = r_reg_get_value (dbg->reg, ripc); n++; } return n; }
int gb_set_reg_profile(emu *e) { int ret = r_anal_set_reg_profile (e->anal); e->reg = e->anal->reg; r_reg_set_value (e->reg, r_reg_get (e->reg,"mpc",-1), ((RBinAddr *) r_list_get_n (r_bin_get_entries (e->bin), 0))->offset); r_reg_set_value (e->reg, r_reg_get (e->reg,"sp",-1), 0xfffe); r_reg_set_value (e->reg, r_reg_get (e->reg,"af",-1), 0x01b0); r_reg_set_value (e->reg, r_reg_get (e->reg,"bc",-1), 0x0013); r_reg_set_value (e->reg, r_reg_get (e->reg,"de",-1), 0x00d8); r_reg_set_value (e->reg, r_reg_get (e->reg,"hl",-1), 0x014d); r_reg_set_value (e->reg, r_reg_get (e->reg,"ime",-1), R_TRUE); return ret; }
static int esil_6502_init (RAnalEsil *esil) { if (esil->anal && esil->anal->reg) { //initial values r_reg_set_value (esil->anal->reg, r_reg_get (esil->anal->reg, "pc", -1), 0x0000); r_reg_set_value (esil->anal->reg, r_reg_get (esil->anal->reg, "sp", -1), 0xff); r_reg_set_value (esil->anal->reg, r_reg_get (esil->anal->reg, "a", -1), 0x00); r_reg_set_value (esil->anal->reg, r_reg_get (esil->anal->reg, "x", -1), 0x00); r_reg_set_value (esil->anal->reg, r_reg_get (esil->anal->reg, "y", -1), 0x00); r_reg_set_value (esil->anal->reg, r_reg_get (esil->anal->reg, "flags", -1), 0x00); } return true; }
static int r_debug_gdb_reg_write(RDebug *dbg, int type, const ut8 *buf, int size) { check_connection (dbg); if (!reg_buf) { // we cannot write registers before we once read them return -1; } int buflen = 0; int bits = dbg->anal->bits; const char *pcname = r_reg_get_name (dbg->anal->reg, R_REG_NAME_PC); RRegItem *reg = r_reg_get (dbg->anal->reg, pcname, 0); if (reg) { if (dbg->anal->bits != reg->size) bits = reg->size; } free (r_reg_get_bytes (dbg->reg, type, &buflen)); // some implementations of the gdb protocol are acting weird. // so winedbg is not able to write registers through the <G> packet // and also it does not return the whole gdb register profile after // calling <g> // so this workaround resizes the small register profile buffer // to the whole set and fills the rest with 0 if (buf_size < buflen) { ut8* new_buf = realloc (reg_buf, buflen * sizeof (ut8)); if (!new_buf) { return -1; } reg_buf = new_buf; memset (new_buf + buf_size, 0, buflen - buf_size); } RRegItem* current = NULL; for (;;) { current = r_reg_next_diff (dbg->reg, type, reg_buf, buflen, current, bits); if (!current) break; ut64 val = r_reg_get_value (dbg->reg, current); int bytes = bits / 8; gdbr_write_reg (desc, current->name, (char*)&val, bytes); } return true; }
//= PC+4+R<reg> static RAnalValue *anal_regrel_jump(RAnal* anal, RAnalOp* op, ut8 reg){ RAnalValue *ret = r_anal_value_new (); ret->reg = r_reg_get (anal->reg, regs[reg], R_REG_TYPE_GPR); ret->base = op->addr+4; return ret; }
/* @(R0,Rx) references for all sizes */ static RAnalValue *anal_fill_r0_reg_ref(RAnal *anal, int reg, st64 size){ RAnalValue *ret = anal_fill_ai_rg (anal, 0); ret->regdelta = r_reg_get (anal->reg, regs[reg], R_REG_TYPE_GPR); ret->memref = size; return ret; }
static RAnalValue *anal_fill_ai_rg(RAnal *anal, int idx) { RAnalValue *ret = r_anal_value_new (); ret->reg = r_reg_get (anal->reg, regs[idx], R_REG_TYPE_GPR); return ret; }
int gb_step (emu *e, ut8 *buf) { int ret = R_FALSE; GBCpuStats *cpu = (GBCpuStats *)e->data; cpu->cycles += e->anop->cycles; r_reg_set_value (e->reg, r_reg_get (e->reg, "pc", -1), r_reg_getv(e->reg, "pc") + e->op->size); switch (e->anop->type) { case R_ANAL_OP_TYPE_NOP: ret = R_TRUE; case R_ANAL_OP_TYPE_TRAP: case R_ANAL_OP_TYPE_ILL: break; case R_ANAL_OP_TYPE_MOV: ret = gb_mov (e); break; case R_ANAL_OP_TYPE_PUSH: ret = gb_push (e); break; case R_ANAL_OP_TYPE_POP: ret = gb_pop (e); break; case R_ANAL_OP_TYPE_JMP: ret = gb_jmp (e); break; case R_ANAL_OP_TYPE_CJMP: ret = gb_cjmp (e); break; case R_ANAL_OP_TYPE_UJMP: ret = gb_ujmp (e); break; case R_ANAL_OP_TYPE_UCALL: case R_ANAL_OP_TYPE_CALL: ret = gb_call (e); break; case R_ANAL_OP_TYPE_CCALL: ret = gb_ccall (e); break; case R_ANAL_OP_TYPE_RET: ret = gb_ret (e); break; case R_ANAL_OP_TYPE_CRET: ret = gb_cret (e); break; case R_ANAL_OP_TYPE_CMP: ret = gb_cp (e); break; case R_ANAL_OP_TYPE_SUB: ret = gb_sub (e); break; case R_ANAL_OP_TYPE_ADD: ret = gb_add (e); break; case R_ANAL_OP_TYPE_XOR: ret = gb_xor (e); break; case R_ANAL_OP_TYPE_AND: ret = gb_and (e); break; case R_ANAL_OP_TYPE_OR: ret = gb_or (e); break; case R_ANAL_OP_TYPE_ACMP: ret = gb_bit (e); break; case R_ANAL_OP_TYPE_ROL: ret = gb_rol (e); break; case R_ANAL_OP_TYPE_STORE: ret = gb_store (e); break; case R_ANAL_OP_TYPE_LOAD: ret = gb_load (e); break; } gb_interrupts (e); cpu->cycles &= 0xffffff; //prevent overflows cpu->prev_cycles = cpu->cycles; return ret; }
// XXX: RVM must be inside RAnal ???? imho no. rsyscall must provide ret reg info //XXX: may overflow. this is vulnerable. needs fix R_API char *r_anal_cc_to_string (RAnal *anal, RAnalCC* cc) { RSyscallItem *si; RAnalFunction *fcn; char str[1024], buf[64]; int i, eax = 0; // eax = arg0 int str_len = 0; int buf_len = 0; str[0] = 0; switch (cc->type) { case R_ANAL_CC_TYPE_FASTCALL: // INT { RRegItem *item; const char *a0 = r_reg_get_name (anal->reg, R_REG_NAME_A0); // A0 or RET ?? item = r_reg_get (anal->reg, a0, R_REG_TYPE_GPR); if (!item) { //eprintf ("cannot get reg a0\n"); return R_FALSE; } eax = (int)r_reg_get_value (anal->reg, item); si = r_syscall_get (anal->syscall, eax, (int)cc->jump); if (si) { //DEBUG r_cons_printf (" ; sc[0x%x][%d]=%s(", (int)analop.value, eax, si->name); snprintf (str, sizeof (str), "%s (", si->name); for (i=0; i<si->args; i++) { const char *reg = r_syscall_reg (anal->syscall, i+1, si->args); if (!reg) break; // no registers? item = r_reg_get (anal->reg, reg, R_REG_TYPE_GPR); if (item) { snprintf (buf, sizeof (buf), "0x%"PFMT64x, r_reg_get_value (anal->reg, item)); strcat (str, buf); // XXX: do not use strcat } //else eprintf ("Unknown reg '%s'\n", reg); if (i<si->args-1) strcat (str, ","); // XXX: do not use strcat } strcat (str, ")"); } else { int n = (int)cc->jump; //if (n == 3) return NULL; // XXX: hack for x86 snprintf (str, sizeof (str), "syscall[0x%x][%d]=?", n, eax); } } break; case R_ANAL_CC_TYPE_STDCALL: // CALL fcn = r_anal_fcn_find (anal, cc->jump, R_ANAL_FCN_TYPE_FCN|R_ANAL_FCN_TYPE_SYM|R_ANAL_FCN_TYPE_IMP); if (fcn && fcn->name) snprintf (str, sizeof (str), "%s(", fcn->name); else if (cc->jump != -1LL) snprintf (str, sizeof (str), "0x%08"PFMT64x"(", cc->jump); else strncpy (str, "unk(", sizeof (str)-1); str_len = strlen (str); if (fcn) cc->nargs = (fcn->nargs>cc->nargs?fcn->nargs:cc->nargs); if (cc->nargs>8) { //eprintf ("too many arguments for stdcall. chop to 8\n"); cc->nargs = 8; } // TODO: optimize string concat for (i=0; i<cc->nargs; i++) { if (cc->args[cc->nargs-i] != -1LL) snprintf (buf, sizeof (buf), "0x%"PFMT64x, cc->args[cc->nargs-i]); else strncpy (buf, "unk", sizeof (buf)-1); buf_len = strlen (buf); if ((buf_len+str_len+5)>=sizeof (str)) { strcat (str, "..."); break; } strcat (str, buf); str_len += buf_len; if (i<cc->nargs-1) strcat (str, ", "); } strcat (str, ")"); break; } return strdup (str); }
static const char *parse_def(RReg *reg, char **tok, const int n) { RRegItem *item; char *end, *p; int type, type2; if (n != 5 && n != 6) { return "Invalid syntax: Wrong number of columns"; } p = strchr (tok[0], '@'); if (p) { char *tok0 = strdup (tok[0]); char *at = tok0 + (p - tok[0]); *at++ = 0; type = r_reg_type_by_name (tok0); type2 = r_reg_type_by_name (at); free (tok0); } else { type2 = type = r_reg_type_by_name (tok[0]); if (type == R_REG_TYPE_FLG) { type2 = R_REG_TYPE_GPR; } } if (type < 0 || type2 < 0) { return "Invalid register type"; } item = R_NEW0 (RRegItem); if (!item) { return "Unable to allocate memory"; } item->type = type; item->arena = type2; item->name = strdup (tok[1]); // All the numeric arguments are strictly checked item->size = parse_size (tok[2], &end); if (*end != '\0' || !item->size) { r_reg_item_free (item); return "Invalid size"; } item->offset = parse_size (tok[3], &end); if (*end != '\0') { r_reg_item_free (item); return "Invalid offset"; } item->packed_size = parse_size (tok[4], &end); if (*end != '\0') { r_reg_item_free (item); return "Invalid packed size"; } // Dynamically update the list of supported bit sizes reg->bits |= item->size; // This is optional if (n == 6) { item->flags = strdup (tok[5]); } // Don't allow duplicate registers if (r_reg_get (reg, item->name, R_REG_TYPE_ALL)) { r_reg_item_free (item); return "Duplicate register definition"; } /* Hack to put flags in the same arena as gpr */ if (type == R_REG_TYPE_FLG) { type2 = R_REG_TYPE_GPR; } r_list_append (reg->regset[type2].regs, item); // Update the overall profile size if (item->offset + item->size > reg->size) { reg->size = item->offset + item->size; } // Update the overall type of registers into a regset reg->regset[type2].maskregstype |= ((int)1 << type); return NULL; }
/* io.rop[0] = reg */ static RAnalValue *anal_fill_ai_rg(RAnal *anal, x86im_instr_object io, int idx) { RAnalValue *ret = r_anal_value_new (); ret->reg = r_reg_get (anal->reg, anal_reg (io.rop[idx]), R_REG_TYPE_GPR); return ret; }
static int analop(RAnal *a, RAnalOp *op, ut64 addr, const ut8 *buf, int len) { static int omode = 0; #if USE_ITER_API static #endif cs_insn *insn = NULL; int mode = (a->bits==64)? CS_MODE_64: (a->bits==32)? CS_MODE_32: (a->bits==16)? CS_MODE_16: 0; int n, ret; int regsz = 4; if (handle && mode != omode) { cs_close (&handle); handle = 0; } omode = mode; if (handle == 0) { ret = cs_open (CS_ARCH_X86, mode, &handle); if (ret != CS_ERR_OK) { handle = 0; return 0; } } #if 0 if (len>3 && !memcmp (buf, "\xff\xff\xff\xff", 4)) return 0; #endif switch (a->bits) { case 64: regsz = 8; break; case 16: regsz = 2; break; default: regsz = 4; break; // 32 } memset (op, '\0', sizeof (RAnalOp)); op->cycles = 1; // aprox op->type = R_ANAL_OP_TYPE_NULL; op->jump = UT64_MAX; op->fail = UT64_MAX; op->ptr = op->val = UT64_MAX; op->src[0] = NULL; op->src[1] = NULL; op->size = 0; op->delay = 0; r_strbuf_init (&op->esil); cs_option (handle, CS_OPT_DETAIL, CS_OPT_ON); // capstone-next #if USE_ITER_API { ut64 naddr = addr; size_t size = len; if (insn == NULL) insn = cs_malloc (handle); n = cs_disasm_iter (handle, (const uint8_t**)&buf, &size, (uint64_t*)&naddr, insn); } #else n = cs_disasm (handle, (const ut8*)buf, len, addr, 1, &insn); #endif struct Getarg gop = { .handle = handle, .insn = insn, .bits = a->bits }; if (n<1) { op->type = R_ANAL_OP_TYPE_ILL; } else { int rs = a->bits/8; const char *pc = (a->bits==16)?"ip": (a->bits==32)?"eip":"rip"; const char *sp = (a->bits==16)?"sp": (a->bits==32)?"esp":"rsp"; const char *bp = (a->bits==16)?"bp": (a->bits==32)?"ebp":"rbp"; op->size = insn->size; op->family = R_ANAL_OP_FAMILY_CPU; // almost everything is CPU op->prefix = 0; switch (insn->detail->x86.prefix[0]) { case X86_PREFIX_REPNE: op->prefix |= R_ANAL_OP_PREFIX_REPNE; break; case X86_PREFIX_REP: op->prefix |= R_ANAL_OP_PREFIX_REP; break; case X86_PREFIX_LOCK: op->prefix |= R_ANAL_OP_PREFIX_LOCK; break; } switch (insn->id) { case X86_INS_FNOP: op->family = R_ANAL_OP_FAMILY_FPU; /* fallthru */ case X86_INS_NOP: case X86_INS_PAUSE: op->type = R_ANAL_OP_TYPE_NOP; if (a->decode) esilprintf (op, ","); break; case X86_INS_HLT: op->type = R_ANAL_OP_TYPE_TRAP; break; case X86_INS_FBLD: case X86_INS_FBSTP: case X86_INS_FCOMPP: case X86_INS_FDECSTP: case X86_INS_FEMMS: case X86_INS_FFREE: case X86_INS_FICOM: case X86_INS_FICOMP: case X86_INS_FINCSTP: case X86_INS_FNCLEX: case X86_INS_FNINIT: case X86_INS_FNSTCW: case X86_INS_FNSTSW: case X86_INS_FPATAN: case X86_INS_FPREM: case X86_INS_FPREM1: case X86_INS_FPTAN: #if CS_API_MAJOR >=4 case X86_INS_FFREEP: #endif case X86_INS_FRNDINT: case X86_INS_FRSTOR: case X86_INS_FNSAVE: case X86_INS_FSCALE: case X86_INS_FSETPM: case X86_INS_FSINCOS: case X86_INS_FNSTENV: case X86_INS_FXAM: case X86_INS_FXSAVE: case X86_INS_FXSAVE64: case X86_INS_FXTRACT: case X86_INS_FYL2X: case X86_INS_FYL2XP1: case X86_INS_FISTTP: case X86_INS_FSQRT: case X86_INS_FXCH: op->family = R_ANAL_OP_FAMILY_FPU; op->type = R_ANAL_OP_TYPE_STORE; break; case X86_INS_FTST: case X86_INS_FUCOMPI: case X86_INS_FUCOMI: case X86_INS_FUCOMPP: case X86_INS_FUCOMP: case X86_INS_FUCOM: op->family = R_ANAL_OP_FAMILY_FPU; op->type = R_ANAL_OP_TYPE_CMP; break; case X86_INS_FABS: op->type = R_ANAL_OP_TYPE_ABS; op->family = R_ANAL_OP_FAMILY_FPU; break; case X86_INS_FLDCW: case X86_INS_FLDENV: case X86_INS_FLDL2E: case X86_INS_FLDL2T: case X86_INS_FLDLG2: case X86_INS_FLDLN2: case X86_INS_FLDPI: case X86_INS_FLDZ: case X86_INS_FLD1: case X86_INS_FLD: op->type = R_ANAL_OP_TYPE_LOAD; op->family = R_ANAL_OP_FAMILY_FPU; break; case X86_INS_FIST: case X86_INS_FISTP: case X86_INS_FST: case X86_INS_FSTP: case X86_INS_FSTPNCE: case X86_INS_FXRSTOR: case X86_INS_FXRSTOR64: op->type = R_ANAL_OP_TYPE_STORE; op->family = R_ANAL_OP_FAMILY_FPU; break; case X86_INS_FDIV: case X86_INS_FIDIV: case X86_INS_FDIVP: case X86_INS_FDIVR: case X86_INS_FIDIVR: case X86_INS_FDIVRP: op->type = R_ANAL_OP_TYPE_DIV; op->family = R_ANAL_OP_FAMILY_FPU; break; case X86_INS_FSUBR: case X86_INS_FISUBR: case X86_INS_FSUBRP: case X86_INS_FSUB: case X86_INS_FISUB: case X86_INS_FSUBP: op->type = R_ANAL_OP_TYPE_SUB; op->family = R_ANAL_OP_FAMILY_FPU; break; case X86_INS_FMUL: case X86_INS_FIMUL: case X86_INS_FMULP: op->type = R_ANAL_OP_TYPE_MUL; op->family = R_ANAL_OP_FAMILY_FPU; break; case X86_INS_CLI: case X86_INS_STI: op->type = R_ANAL_OP_TYPE_SWI; op->family = R_ANAL_OP_FAMILY_PRIV; break; case X86_INS_CLC: case X86_INS_STC: case X86_INS_CLAC: case X86_INS_CLGI: case X86_INS_CLTS: #if CS_API_MAJOR >= 4 case X86_INS_CLWB: #endif case X86_INS_STAC: case X86_INS_STGI: op->type = R_ANAL_OP_TYPE_MOV; break; // cmov case X86_INS_SETNE: case X86_INS_SETNO: case X86_INS_SETNP: case X86_INS_SETNS: case X86_INS_SETO: case X86_INS_SETP: case X86_INS_SETS: case X86_INS_SETL: case X86_INS_SETLE: case X86_INS_SETB: case X86_INS_SETG: case X86_INS_SETAE: case X86_INS_SETA: case X86_INS_SETBE: case X86_INS_SETE: case X86_INS_SETGE: op->type = R_ANAL_OP_TYPE_CMOV; op->family = 0; if (a->decode) { char *dst = getarg (&gop, 0, 0, NULL); switch (insn->id) { case X86_INS_SETE: esilprintf (op, "zf,%s,=", dst); break; case X86_INS_SETNE: esilprintf (op, "zf,!,%s,=", dst); break; case X86_INS_SETO: esilprintf (op, "of,%s,=", dst); break; case X86_INS_SETNO: esilprintf (op, "of,!,%s,=", dst); break; case X86_INS_SETP: esilprintf (op, "pf,%s,=", dst); break; case X86_INS_SETNP: esilprintf (op, "pf,!,%s,=", dst); break; case X86_INS_SETS: esilprintf (op, "sf,%s,=", dst); break; case X86_INS_SETNS: esilprintf (op, "sf,!,%s,=", dst); break; case X86_INS_SETB: esilprintf (op, "cf,%s,=", dst); break; case X86_INS_SETAE: esilprintf (op, "cf,!,%s,=", dst); break; /* TODO */ #if 0 SETLE/SETNG Sets the byte in the operand to 1 if the Zero Flag is set or the Sign Flag is not equal to the Overflow Flag, otherwise sets the operand to 0. SETBE/SETNA Sets the byte in the operand to 1 if the Carry Flag or the Zero Flag is set, otherwise sets the operand to 0. SETL/SETNGE Sets the byte in the operand to 1 if the Sign Flag is not equal to the Overflow Flag, otherwise sets the operand to 0. case X86_INS_SETL: esilprintf (op, "pf,!,%s,=", dst); break; case X86_INS_SETLE: esilprintf (op, "pf,!,%s,=", dst); break; case X86_INS_SETG: esilprintf (op, "pf,!,%s,=", dst); break; case X86_INS_SETA: esilprintf (op, "pf,!,%s,=", dst); break; case X86_INS_SETBE: esilprintf (op, "pf,!,%s,=", dst); break; case X86_INS_SETGE: esilprintf (op, "pf,!,%s,=", dst); break; break; #endif } free (dst); } break; // cmov case X86_INS_FCMOVBE: case X86_INS_FCMOVB: case X86_INS_FCMOVNBE: case X86_INS_FCMOVNB: case X86_INS_FCMOVE: case X86_INS_FCMOVNE: case X86_INS_FCMOVNU: case X86_INS_FCMOVU: op->family = R_ANAL_OP_FAMILY_FPU; op->type = R_ANAL_OP_TYPE_MOV; break; case X86_INS_CMOVA: case X86_INS_CMOVAE: case X86_INS_CMOVB: case X86_INS_CMOVBE: case X86_INS_CMOVE: case X86_INS_CMOVG: case X86_INS_CMOVGE: case X86_INS_CMOVL: case X86_INS_CMOVLE: case X86_INS_CMOVNE: case X86_INS_CMOVNO: case X86_INS_CMOVNP: case X86_INS_CMOVNS: case X86_INS_CMOVO: case X86_INS_CMOVP: case X86_INS_CMOVS: op->type = R_ANAL_OP_TYPE_CMOV; break; // mov case X86_INS_MOVSS: case X86_INS_MOV: case X86_INS_MOVAPS: case X86_INS_MOVAPD: case X86_INS_MOVZX: case X86_INS_MOVUPS: case X86_INS_MOVABS: case X86_INS_MOVHPD: case X86_INS_MOVHPS: case X86_INS_MOVLPD: case X86_INS_MOVLPS: case X86_INS_MOVBE: case X86_INS_MOVSB: case X86_INS_MOVSD: case X86_INS_MOVSQ: case X86_INS_MOVSX: case X86_INS_MOVSXD: case X86_INS_MOVSW: case X86_INS_MOVD: case X86_INS_MOVQ: case X86_INS_MOVDQ2Q: { op->type = R_ANAL_OP_TYPE_MOV; op->ptr = UT64_MAX; switch (INSOP(0).type) { case X86_OP_MEM: op->ptr = INSOP(0).mem.disp; op->refptr = INSOP(0).size; if (INSOP(0).mem.base == X86_REG_RIP) { op->ptr += addr + insn->size; } else if (INSOP(0).mem.base == X86_REG_RBP || INSOP(0).mem.base == X86_REG_EBP) { op->stackop = R_ANAL_STACK_SET; op->stackptr = regsz; } else { if (op->ptr < 0x1000) op->ptr = UT64_MAX; } if (a->decode) { if (op->prefix & R_ANAL_OP_PREFIX_REP) { int width = INSOP(0).size; const char *src = cs_reg_name(handle, INSOP(1).mem.base); const char *dst = cs_reg_name(handle, INSOP(0).mem.base); const char *counter = (a->bits==16)?"cx": (a->bits==32)?"ecx":"rcx"; esilprintf (op, "%s,!,?{,BREAK,},%s,NUM,%s,NUM,"\ "%s,[%d],%s,=[%d],df,?{,%d,%s,-=,%d,%s,-=,},"\ "df,!,?{,%d,%s,+=,%d,%s,+=,},%s,--=,%s," \ "?{,8,GOTO,},%s,=,%s,=", counter, src, dst, src, width, dst, width, width, src, width, dst, width, src, width, dst, counter, counter, dst, src); } else { char *src = getarg (&gop, 1, 0, NULL); char *dst = getarg (&gop, 0, 1, NULL); esilprintf (op, "%s,%s", src, dst); free (src); free (dst); } } break; case X86_OP_REG: { char *dst = getarg (&gop, 0, 0, NULL); op->dst = r_anal_value_new (); op->dst->reg = r_reg_get (a->reg, dst, R_REG_TYPE_GPR); op->src[0] = r_anal_value_new (); if (INSOP(1).type == X86_OP_MEM) { op->src[0]->delta = INSOP(1).mem.disp; } free (dst); } default: if (a->decode) { char *src = getarg (&gop, 1, 0, NULL); char *dst = getarg (&gop, 0, 0, NULL); esilprintf (op, "%s,%s,=", src, dst); free (src); free (dst); } break; } if (op->refptr<1 || op->ptr == UT64_MAX) { switch (INSOP(1).type) { case X86_OP_MEM: op->ptr = INSOP(1).mem.disp; op->refptr = INSOP(1).size; if (INSOP(1).mem.base == X86_REG_RIP) { op->ptr += addr + insn->size; } else if (INSOP(1).mem.base == X86_REG_RBP || INSOP(1).mem.base == X86_REG_EBP) { op->stackop = R_ANAL_STACK_GET; op->stackptr = regsz; } break; case X86_OP_IMM: if (INSOP(1).imm > 10) op->ptr = INSOP(1).imm; break; default: break; } } } break; case X86_INS_ROL: case X86_INS_RCL: // TODO: RCL Still does not work as intended // - Set flags op->type = R_ANAL_OP_TYPE_ROL; if (a->decode) { char *src = getarg (&gop, 1, 0, NULL); char *dst = getarg (&gop, 0, 0, NULL); esilprintf (op, "%s,%s,<<<,%s,=", src, dst, dst); free (src); free (dst); } break; case X86_INS_ROR: case X86_INS_RCR: // TODO: RCR Still does not work as intended // - Set flags op->type = R_ANAL_OP_TYPE_ROR; if (a->decode) { char *src = getarg (&gop, 1, 0, NULL); char *dst = getarg (&gop, 0, 0, NULL); esilprintf (op, "%s,%s,>>>,%s,=", src, dst, dst); free (src); free (dst); } break; case X86_INS_SHL: case X86_INS_SHLD: case X86_INS_SHLX: // TODO: Set CF: Carry flag is the last bit shifted out due to // this operation. It is undefined for SHL and SHR where the // number of bits shifted is greater than the size of the // destination. op->type = R_ANAL_OP_TYPE_SHL; if (a->decode) { char *src = getarg (&gop, 1, 0, NULL); char *dst = getarg (&gop, 0, 1, "<<"); esilprintf (op, "%s,%s,$z,zf,=,$p,pf,=,$s,sf,=", src, dst); free (src); free (dst); } break; case X86_INS_SAR: case X86_INS_SARX: // TODO: Set CF. See case X86_INS_SHL for more details. op->type = R_ANAL_OP_TYPE_SAR; if (a->decode) { char *src = getarg (&gop, 1, 0, NULL); char *dst = getarg (&gop, 0, 1, ">>"); esilprintf (op, "%s,%s,$z,zf,=,$p,pf,=,$s,sf,=", src, dst); free (src); free (dst); } break; case X86_INS_SAL: // TODO: Set CF: See case X86_INS_SAL for more details. op->type = R_ANAL_OP_TYPE_SAL; if (a->decode) { char *src = getarg (&gop, 1, 0, NULL); char *dst = getarg (&gop, 0, 1, "<<"); esilprintf (op, "%s,%s,$z,zf,=,$p,pf,=,$s,sf,=", src, dst); free (src); free (dst); } break; case X86_INS_SALC: op->type = R_ANAL_OP_TYPE_SAL; if (a->decode) { esilprintf (op, "$z,DUP,zf,=,al,="); } break; case X86_INS_SHR: case X86_INS_SHRD: case X86_INS_SHRX: // TODO: Set CF: See case X86_INS_SAL for more details. op->type = R_ANAL_OP_TYPE_SHR; if (a->decode) { char *src = getarg (&gop, 1, 0, NULL); char *dst = getarg (&gop, 0, 0, NULL); esilprintf (op, "%s,%s,>>=,$z,zf,=,$p,pf,=,$s,sf,=", src, dst); free (src); free (dst); } break; case X86_INS_CMP: case X86_INS_CMPPD: case X86_INS_CMPPS: case X86_INS_CMPSW: case X86_INS_CMPSD: case X86_INS_CMPSQ: case X86_INS_CMPSB: case X86_INS_CMPSS: case X86_INS_TEST: if (insn->id == X86_INS_TEST) { op->type = R_ANAL_OP_TYPE_ACMP; //compare via and if (a->decode) { char *src = getarg (&gop, 1, 0, NULL); char *dst = getarg (&gop, 0, 0, NULL); esilprintf (op, "0,%s,%s,&,==,$z,zf,=,$p,pf,=,$s,sf,=,0,cf,=,0,of,=", src, dst); free (src); free (dst); } } else { op->type = R_ANAL_OP_TYPE_CMP; if (a->decode) { char *src = getarg (&gop, 1, 0, NULL); char *dst = getarg (&gop, 0, 0, NULL); esilprintf (op, "%s,%s,==,$z,zf,=,$b%d,cf,=,$p,pf,=,$s,sf,=", src, dst, (INSOP(0).size*8)); free (src); free (dst); } } switch (INSOP(0).type) { case X86_OP_MEM: op->ptr = INSOP(0).mem.disp; op->refptr = INSOP(0).size; if (INSOP(0).mem.base == X86_REG_RIP) { op->ptr += addr + insn->size; } else if (INSOP(0).mem.base == X86_REG_RBP || INSOP(0).mem.base == X86_REG_EBP) { op->stackop = R_ANAL_STACK_SET; op->stackptr = regsz; } op->ptr = INSOP(1).imm; break; default: switch (INSOP(1).type) { case X86_OP_MEM: op->ptr = INSOP(1).mem.disp; op->refptr = INSOP(1).size; if (INSOP(1).mem.base == X86_REG_RIP) { op->ptr += addr + insn->size; } else if (INSOP(1).mem.base == X86_REG_RBP || INSOP(1).mem.base == X86_REG_EBP) { op->stackop = R_ANAL_STACK_SET; op->stackptr = regsz; } break; case X86_OP_IMM: op->ptr = INSOP(1).imm; break; default: break; } break; } break; case X86_INS_LEA: op->type = R_ANAL_OP_TYPE_LEA; if (a->decode) { char *src = getarg (&gop, 0, 0, NULL); char *dst = getarg (&gop, 1, 2, NULL); esilprintf (op, "%s,%s,=", dst, src); free (src); free (dst); } switch (INSOP(1).type) { case X86_OP_MEM: op->ptr = INSOP(1).mem.disp; op->refptr = INSOP(1).size; switch (INSOP(1).mem.base) { case X86_REG_RIP: op->ptr += addr + op->size; break; case X86_REG_RBP: case X86_REG_EBP: op->stackop = R_ANAL_STACK_GET; op->stackptr = regsz; break; default: /* unhandled */ break; } break; case X86_OP_IMM: if (INSOP(1).imm > 10) op->ptr = INSOP(1).imm; break; default: break; } break; case X86_INS_ENTER: case X86_INS_PUSH: case X86_INS_PUSHAW: case X86_INS_PUSHAL: case X86_INS_PUSHF: { char *dst = getarg (&gop, 0, 0, NULL); esilprintf (op, "%d,%s,-=,%s,%s,=[%d]", rs, sp, dst, sp, rs); free (dst); } switch (INSOP(0).type) { case X86_OP_IMM: op->ptr = INSOP(0).imm; op->type = R_ANAL_OP_TYPE_PUSH; break; default: op->type = R_ANAL_OP_TYPE_UPUSH; break; } op->stackop = R_ANAL_STACK_INC; op->stackptr = regsz; break; case X86_INS_LEAVE: op->type = R_ANAL_OP_TYPE_POP; if (a->decode) { esilprintf (op, "%s,%s,=,%s,[%d],%s,=,%d,%s,+=", bp, sp, sp, rs, bp, rs, sp); } op->stackop = R_ANAL_STACK_INC; op->stackptr = -regsz; break; case X86_INS_POP: case X86_INS_POPF: case X86_INS_POPAW: case X86_INS_POPAL: case X86_INS_POPCNT: op->type = R_ANAL_OP_TYPE_POP; if (a->decode) { char *dst = getarg (&gop, 0, 0, NULL); esilprintf (op, "%s,[%d],%s,=,%d,%s,+=", sp, rs, dst, rs, sp); free (dst); } op->stackop = R_ANAL_STACK_INC; op->stackptr = -regsz; break; case X86_INS_RET: case X86_INS_RETF: case X86_INS_RETFQ: case X86_INS_IRET: case X86_INS_IRETD: case X86_INS_IRETQ: case X86_INS_SYSRET: op->type = R_ANAL_OP_TYPE_RET; if (a->decode) esilprintf (op, "%s,[%d],%s,=,%d,%s,+=", sp, rs, pc, rs, sp); op->stackop = R_ANAL_STACK_INC; op->stackptr = -regsz; break; case X86_INS_INT3: if (a->decode) esilprintf (op, "3,$"); op->type = R_ANAL_OP_TYPE_TRAP; // TRAP break; case X86_INS_INT1: if (a->decode) esilprintf (op, "1,$"); op->type = R_ANAL_OP_TYPE_SWI; // TRAP break; case X86_INS_INT: if (a->decode) esilprintf (op, "%d,$", R_ABS((int)INSOP(0).imm)); op->type = R_ANAL_OP_TYPE_SWI; break; case X86_INS_SYSCALL: op->type = R_ANAL_OP_TYPE_SWI; break; case X86_INS_INTO: case X86_INS_VMCALL: case X86_INS_VMMCALL: op->type = R_ANAL_OP_TYPE_TRAP; if (a->decode) esilprintf (op, "%d,$", (int)INSOP(0).imm); break; case X86_INS_JL: case X86_INS_JLE: case X86_INS_JA: case X86_INS_JAE: case X86_INS_JB: case X86_INS_JBE: case X86_INS_JCXZ: case X86_INS_JECXZ: case X86_INS_JRCXZ: case X86_INS_JO: case X86_INS_JNO: case X86_INS_JS: case X86_INS_JNS: case X86_INS_JP: case X86_INS_JNP: case X86_INS_JE: case X86_INS_JNE: case X86_INS_JG: case X86_INS_JGE: case X86_INS_LOOP: case X86_INS_LOOPE: case X86_INS_LOOPNE: op->type = R_ANAL_OP_TYPE_CJMP; op->jump = INSOP(0).imm; op->fail = addr+op->size; const char *cnt = (a->bits==16)?"cx":(a->bits==32)?"ecx":"rcx"; if (a->decode) { char *dst = getarg (&gop, 0, 2, NULL); switch (insn->id) { case X86_INS_JL: esilprintf (op, "of,sf,^,?{,%s,%s,=,}", dst, pc); break; case X86_INS_JLE: esilprintf (op, "of,sf,^,zf,|,?{,%s,%s,=,}", dst, pc); break; case X86_INS_JA: esilprintf (op, "cf,zf,|,!,?{,%s,%s,=,}",dst, pc); break; case X86_INS_JAE: esilprintf (op, "cf,!,?{,%s,%s,=,}", dst, pc); break; case X86_INS_JB: esilprintf (op, "cf,?{,%s,%s,=,}", dst, pc); break; case X86_INS_JO: esilprintf (op, "of,?{,%s,%s,=,}", dst, pc); break; case X86_INS_JNO: esilprintf (op, "of,!,?{,%s,%s,=,}", dst, pc); break; case X86_INS_JE: esilprintf (op, "zf,?{,%s,%s,=,}", dst, pc); break; case X86_INS_JGE: esilprintf (op, "of,!,sf,^,?{,%s,%s,=,}", dst, pc); break; case X86_INS_JNE: esilprintf (op, "zf,!,?{,%s,%s,=,}", dst, pc); break; case X86_INS_JG: esilprintf (op, "sf,of,!,^,zf,!,&,?{,%s,%s,=,}", dst, pc); break; case X86_INS_JS: esilprintf (op, "sf,?{,%s,%s,=,}", dst, pc); break; case X86_INS_JNS: esilprintf (op, "sf,!,?{,%s,%s,=,}", dst, pc); break; case X86_INS_JP: esilprintf (op, "pf,?{,%s,%s,=,}", dst, pc); break; case X86_INS_JNP: esilprintf (op, "pf,!,?{,%s,%s,=,}", dst, pc); break; case X86_INS_JBE: esilprintf (op, "zf,cf,|,?{,%s,%s,=,}", dst, pc); break; case X86_INS_JCXZ: esilprintf (op, "cx,!,?{,%s,%s,=,}", dst, pc); break; case X86_INS_JECXZ: esilprintf (op, "ecx,!,?{,%s,%s,=,}", dst, pc); break; case X86_INS_JRCXZ: esilprintf (op, "rcx,!,?{,%s,%s,=,}", dst, pc); break; case X86_INS_LOOP: esilprintf (op, "1,%s,-=,%s,?{,%s,%s,=,}", cnt, cnt, dst, pc); break; case X86_INS_LOOPE: esilprintf (op, "1,%s,-=,%s,?{,zf,?{,%s,%s,=,},}", cnt, cnt, dst, pc); break; case X86_INS_LOOPNE: esilprintf (op, "1,%s,-=,%s,?{,zf,!,?{,%s,%s,=,},}", cnt, cnt, dst, pc); break; } free (dst); } break; case X86_INS_CALL: case X86_INS_LCALL: switch (INSOP(0).type) { case X86_OP_IMM: op->type = R_ANAL_OP_TYPE_CALL; // TODO: what if UCALL? // TODO: use imm_size op->jump = INSOP(0).imm; op->fail = addr+op->size; break; case X86_OP_MEM: op->type = R_ANAL_OP_TYPE_UCALL; op->jump = UT64_MAX; if (INSOP(0).mem.base == 0) { op->ptr = INSOP(0).mem.disp; } break; default: op->type = R_ANAL_OP_TYPE_UCALL; op->jump = UT64_MAX; break; } if (a->decode) { char* arg = getarg (&gop, 0, 0, NULL); esilprintf (op, "%s," "%d,%s,-=,%s," "=[]," "%s,%s,=", pc, rs, sp, sp, arg, pc); free (arg); } break; case X86_INS_JMP: case X86_INS_LJMP: if (a->decode) { char *src = getarg (&gop, 0, 0, NULL); esilprintf (op, "%s,%s,=", src, pc); free (src); } // TODO: what if UJMP? switch (INSOP(0).type) { case X86_OP_IMM: op->jump = INSOP(0).imm; op->type = R_ANAL_OP_TYPE_JMP; if (a->decode) { ut64 dst = INSOP(0).imm; esilprintf (op, "0x%"PFMT64x",%s,=", dst, pc); } break; case X86_OP_MEM: op->type = R_ANAL_OP_TYPE_UJMP; op->ptr = INSOP(0).mem.disp; if (INSOP(0).mem.base == X86_REG_RIP) { op->ptr += addr + insn->size; op->refptr = 8; } else { cs_x86_op in = INSOP(0); if (in.mem.index == 0 && in.mem.base == 0 && in.mem.scale == 1) { if (a->decode) { esilprintf (op, "0x%"PFMT64x",[],%s,=", op->ptr, pc); } } } break; case X86_OP_REG: { char *src = getarg (&gop, 0, 0, NULL); op->src[0] = r_anal_value_new (); op->src[0]->reg = r_reg_get (a->reg, src, R_REG_TYPE_GPR); free (src); //XXX fallthrough } case X86_OP_FP: default: // other? op->type = R_ANAL_OP_TYPE_UJMP; op->ptr = UT64_MAX; break; } break; case X86_INS_IN: case X86_INS_INSW: case X86_INS_INSD: case X86_INS_INSB: op->type = R_ANAL_OP_TYPE_IO; op->type2 = 0; break; case X86_INS_OUT: case X86_INS_OUTSB: case X86_INS_OUTSD: case X86_INS_OUTSW: op->type = R_ANAL_OP_TYPE_IO; op->type2 = 1; break; case X86_INS_VXORPD: case X86_INS_VXORPS: case X86_INS_VPXORD: case X86_INS_VPXORQ: case X86_INS_VPXOR: case X86_INS_XORPS: case X86_INS_KXORW: case X86_INS_PXOR: case X86_INS_XOR: op->type = R_ANAL_OP_TYPE_XOR; if (a->decode) { char *src = getarg (&gop, 1, 0, NULL); char *dst = getarg (&gop, 0, 1, "^"); esilprintf (op, "%s,%s,$z,zf,=,$p,pf,=,$s,sf,=,0,cf,=,0,of,=", src, dst); free (src); free (dst); } break; case X86_INS_OR: // The OF and CF flags are cleared; the SF, ZF, and PF flags are // set according to the result. The state of the AF flag is // undefined. op->type = R_ANAL_OP_TYPE_OR; if (a->decode) { char *src = getarg (&gop, 1, 0, NULL); char *dst = getarg (&gop, 0, 0, NULL); esilprintf (op, "%s,%s,|=,0,of,=,0,cf,=,$s,sf,=,$z,zf,=,$p,pf,=", src, dst); free (src); free (dst); } break; case X86_INS_INC: // The CF flag is not affected. The OF, SF, ZF, AF, and PF flags // are set according to the result. op->type = R_ANAL_OP_TYPE_ADD; op->val = 1; if (a->decode) { char *src = getarg (&gop, 0, 0, NULL); if (strchr (src, '[')) { char *dst = r_str_replace (strdup (src), "[", "=[", 1); esilprintf (op, "1,%s,++,%s,$o,of,=,$s,sf,=,$z,zf,=,$p,pf,=", src, dst); free (dst); } else { esilprintf (op, "%s,++=,$o,of,=,$s,sf,=,$z,zf,=,$p,pf,=", src); } free (src); } break; case X86_INS_DEC: // The CF flag is not affected. The OF, SF, ZF, AF, and PF flags // are set according to the result. op->type = R_ANAL_OP_TYPE_SUB; op->val = 1; if (a->decode) { char *src = getarg (&gop, 0, 0, NULL); //esilprintf (op, "%s,--=,$o,of,=,$s,sf,=,$z,zf,=,$p,pf,=", src); esilprintf (op, "1,%s,[4],-,%s,=[4],$o,of,=,$s,sf,=,$z,zf,=,$p,pf,=", src, src); free (src); } break; case X86_INS_PSUBB: case X86_INS_PSUBW: case X86_INS_PSUBD: case X86_INS_PSUBQ: case X86_INS_PSUBSB: case X86_INS_PSUBSW: case X86_INS_PSUBUSB: case X86_INS_PSUBUSW: op->type = R_ANAL_OP_TYPE_SUB; if (a->decode) { char *src = getarg (&gop, 1, 0, NULL); char *dst = getarg (&gop, 0, 1, "-"); esilprintf (op, "%s,%s", src, dst); free(src); free(dst); } break; case X86_INS_SUB: op->type = R_ANAL_OP_TYPE_SUB; if (a->decode) { char *src = getarg (&gop, 1, 0, NULL); char *dst = getarg (&gop, 0, 1, "-"); // Set OF, SF, ZF, AF, PF, and CF flags. // We use $b rather than $c here as the carry flag really // represents a "borrow" esilprintf (op, "%s,%s,$o,of,=,$s,sf,=,$z,zf,=,$p,pf,=,$b,cf,=", src, dst); free (src); free (dst); } if (INSOP(0).type == X86_OP_REG && INSOP(1).type == X86_OP_IMM) { if (INSOP(0).reg == X86_REG_RSP || INSOP(0).reg == X86_REG_ESP) { op->stackop = R_ANAL_STACK_INC; op->stackptr = INSOP(1).imm; } } break; case X86_INS_SBB: // dst = dst - (src + cf) op->type = R_ANAL_OP_TYPE_SUB; if (a->decode) { char *src = getarg (&gop, 1, 0, NULL); char *dst = getarg (&gop, 0, 0, NULL); esilprintf (op, "cf,%s,+,%s,-=,$o,of,=,$s,sf,=,$z,zf,=,$p,pf,=,$b,cf,=", src, dst); free (src); free (dst); } break; case X86_INS_LIDT: op->type = R_ANAL_OP_TYPE_LOAD; op->family = R_ANAL_OP_FAMILY_PRIV; break; case X86_INS_SIDT: op->type = R_ANAL_OP_TYPE_STORE; op->family = R_ANAL_OP_FAMILY_PRIV; break; case X86_INS_RDRAND: case X86_INS_RDSEED: case X86_INS_RDMSR: case X86_INS_RDPMC: case X86_INS_RDTSC: case X86_INS_RDTSCP: case X86_INS_CRC32: case X86_INS_SHA1MSG1: case X86_INS_SHA1MSG2: case X86_INS_SHA1NEXTE: case X86_INS_SHA1RNDS4: case X86_INS_SHA256MSG1: case X86_INS_SHA256MSG2: case X86_INS_SHA256RNDS2: case X86_INS_AESDECLAST: case X86_INS_AESDEC: case X86_INS_AESENCLAST: case X86_INS_AESENC: case X86_INS_AESIMC: case X86_INS_AESKEYGENASSIST: // AES instructions op->family = R_ANAL_OP_FAMILY_CRYPTO; op->type = R_ANAL_OP_TYPE_MOV; // XXX break; case X86_INS_AND: case X86_INS_ANDN: case X86_INS_ANDPD: case X86_INS_ANDPS: case X86_INS_ANDNPD: case X86_INS_ANDNPS: op->type = R_ANAL_OP_TYPE_AND; if (a->decode) { char *src = getarg (&gop, 1, 0, NULL); char *dst = getarg (&gop, 0, 1, "&"); esilprintf (op, "%s,%s,0,of,=,0,cf,=,$z,zf,=,$s,sf,=,$o,pf,=", src, dst); free (src); free (dst); } break; case X86_INS_IDIV: op->type = R_ANAL_OP_TYPE_DIV; if (a->decode) { char *a0 = getarg (&gop, 0, 0, NULL); char *a1 = getarg (&gop, 1, 0, NULL); char *a2 = getarg (&gop, 2, 0, NULL); // TODO update flags & handle signedness if (!a2 && !a1) { // TODO: IDIV rbx not implemented. this is just a workaround // http://www.tptp.cc/mirrors/siyobik.info/instruction/IDIV.html // Divides (signed) the value in the AX, DX:AX, or EDX:EAX registers (dividend) by the source operand (divisor) and stores the result in the AX (AH:AL), DX:AX, or EDX:EAX registers. The source operand can be a general-purpose register or a memory location. The action of this instruction depends on the operand size (dividend/divisor), as shown in the following table: // IDIV RBX == RDX:RAX /= RBX esilprintf (op, "%s,%s,/=", a0, "rax"); } else { esilprintf (op, "%s,%s,/,%s,=", a2, a1, a0); } free (a0); free (a1); free (a2); } break; case X86_INS_DIV: op->type = R_ANAL_OP_TYPE_DIV; if (a->decode) { int width = INSOP(0).size; char *dst = getarg (&gop, 0, 0, NULL); const char *r_ax = (width==2)?"ax": (width==4)?"eax":"rax"; const char *r_dx = (width==2)?"dx": (width==4)?"edx":"rdx"; // TODO update flags & handle signedness esilprintf (op, "%s,%s,%%,%s,=,%s,%s,/,%s,=", dst, r_ax, r_dx, dst, r_ax, r_ax); free (dst); } break; case X86_INS_IMUL: op->type = R_ANAL_OP_TYPE_MUL; if (a->decode) { char *a0 = getarg (&gop, 0, 0, NULL); char *a1 = getarg (&gop, 1, 0, NULL); char *a2 = getarg (&gop, 2, 0, NULL); if (a2) { // TODO update flags & handle signedness esilprintf (op, "%s,%s,*,%s,=", a2, a1, a0); free (a2); } else { if (a1) { esilprintf (op, "%s,%s,*=", a1, a0); } else { esilprintf (op, "%s,%s,*=", a0, "rax"); } } free (a0); free (a1); } break; case X86_INS_MUL: case X86_INS_MULX: case X86_INS_MULPD: case X86_INS_MULPS: case X86_INS_MULSD: case X86_INS_MULSS: op->type = R_ANAL_OP_TYPE_MUL; if (a->decode) { char *src = getarg (&gop, 1, 0, NULL); char *dst = getarg (&gop, 0, 1, "*"); if (!src && dst) { switch (dst[0]) { case 'r': src = strdup ("rax"); break; case 'e': src = strdup ("eax"); break; default: src = strdup ("al"); break; } } esilprintf (op, "%s,%s", src, dst); free (src); free (dst); } break; case X86_INS_PACKSSDW: case X86_INS_PACKSSWB: case X86_INS_PACKUSWB: op->type = R_ANAL_OP_TYPE_MOV; op->family = R_ANAL_OP_FAMILY_MMX; break; case X86_INS_PADDB: case X86_INS_PADDD: case X86_INS_PADDW: case X86_INS_PADDSB: case X86_INS_PADDSW: case X86_INS_PADDUSB: case X86_INS_PADDUSW: op->type = R_ANAL_OP_TYPE_ADD; op->family = R_ANAL_OP_FAMILY_MMX; break; case X86_INS_XCHG: op->type = R_ANAL_OP_TYPE_MOV; op->family = R_ANAL_OP_FAMILY_CPU; { char *src = getarg (&gop, 1, 0, NULL); char *dst = getarg (&gop, 0, 1, NULL); esilprintf (op, "%s,%s,%s,=,%s,=", src, dst, src, dst); free (src); free (dst); } break; case X86_INS_XADD: /* xchg + add */ op->type = R_ANAL_OP_TYPE_ADD; op->family = R_ANAL_OP_FAMILY_CPU; { char *src = getarg (&gop, 1, 0, NULL); char *dst = getarg (&gop, 0, 1, NULL); if (src == dst) { esilprintf (op, "%s,%s,+,%s", src, dst, dst); } else { esilprintf (op, "%s,%s,%s,=,%s,=," "%s,%s,+,%s", src, dst, src, dst, src, dst, dst); } free (src); free (dst); } break; case X86_INS_FADD: case X86_INS_FADDP: op->family = R_ANAL_OP_FAMILY_FPU; /* pass thru */ case X86_INS_ADDPS: case X86_INS_ADDSD: case X86_INS_ADDSS: case X86_INS_ADDSUBPD: case X86_INS_ADDSUBPS: case X86_INS_ADDPD: // The OF, SF, ZF, AF, CF, and PF flags are set according to the // result. op->type = R_ANAL_OP_TYPE_ADD; if (a->decode) { if (INSOP(0).type == X86_OP_MEM) { char *src = getarg (&gop, 1, 0, NULL); char *src2 = getarg (&gop, 0, 0, NULL); char *dst = getarg (&gop, 0, 1, NULL); esilprintf (op, "%s,%s,+,%s", src, src2, dst); free (src); free (src2); free (dst); } else { char *src = getarg (&gop, 1, 0, NULL); char *dst = getarg (&gop, 0, 1, "+"); esilprintf (op, "%s,%s", src, dst); free (src); free (dst); } } if (INSOP(0).type == X86_OP_REG && INSOP(1).type == X86_OP_IMM) { if (INSOP(0).reg == X86_REG_RSP || INSOP(0).reg == X86_REG_ESP) { op->stackop = R_ANAL_STACK_INC; op->stackptr = -INSOP(1).imm; } } break; case X86_INS_ADD: // The OF, SF, ZF, AF, CF, and PF flags are set according to the // result. op->type = R_ANAL_OP_TYPE_ADD; if (a->decode) { if (INSOP(0).type == X86_OP_MEM) { char *src = getarg (&gop, 1, 0, NULL); char *src2 = getarg (&gop, 0, 0, NULL); char *dst = getarg (&gop, 0, 1, NULL); esilprintf (op, "%s,%s,+,%s,$o,of,=,$s,sf,=,$z,zf,=,$p,pf,=,$c,cf,=", src, src2, dst); free (src); free (src2); free (dst); } else { char *src = getarg (&gop, 1, 0, NULL); char *dst = getarg (&gop, 0, 1, "+"); esilprintf (op, "%s,%s,$o,of,=,$s,sf,=,$z,zf,=,$p,pf,=,$c,cf,=", src, dst); free (src); free (dst); } } if (INSOP(0).type == X86_OP_REG && INSOP(1).type == X86_OP_IMM) { if (INSOP(0).reg == X86_REG_RSP || INSOP(0).reg == X86_REG_ESP) { op->stackop = R_ANAL_STACK_INC; op->stackptr = -INSOP(1).imm; } } break; case X86_INS_ADC: op->type = R_ANAL_OP_TYPE_ADD; if (a->decode) { char *src = getarg (&gop, 1, 0, NULL); char *dst = getarg (&gop, 0, 0, NULL); // dst = dst + src + cf // NOTE: We would like to add the carry first before adding the // source to ensure that the flag computation from $c belongs // to the operation of adding dst += src rather than the one // that adds carry (as esil only keeps track of the last // addition to set the flags). esilprintf (op, "cf,%s,+,%s,+=,$o,of,=,$s,sf,=,$z,zf,=,$p,pf,=,$c,cf,=", src, dst); free (src); free (dst); } break; /* Direction flag */ case X86_INS_CLD: op->type = R_ANAL_OP_TYPE_MOV; if (a->decode) esilprintf (op, "0,df,="); break; case X86_INS_STD: op->type = R_ANAL_OP_TYPE_MOV; if (a->decode) esilprintf (op, "1,df,="); break; } switch (insn->id) { case X86_INS_MOVAPS: //cvtss2sd case X86_INS_ADDSD: //cvtss2sd case X86_INS_SUBSD: //cvtss2sd case X86_INS_MULSD: //cvtss2sd case X86_INS_CVTSS2SD: //cvtss2sd case X86_INS_MOVSS: case X86_INS_MOVSD: op->family = R_ANAL_OP_FAMILY_MMX; break; } } //#if X86_GRP_PRIVILEGE>0 if (insn) { #if HAVE_CSGRP_PRIVILEGE if (cs_insn_group (handle, insn, X86_GRP_PRIVILEGE)) op->family = R_ANAL_OP_FAMILY_PRIV; #endif #if !USE_ITER_API cs_free (insn, n); #endif } //cs_close (&handle); return op->size; }