/* radare - Apache 2.0 - Copyright 2010-2015 - pancake and Adam Pridgen */ #include #include #include #include #include #include #include #include "../../../shlr/java/code.h" #include "../../../shlr/java/class.h" #ifdef IFDBG #define dprintf eprintf #endif #define DO_THE_DBG 0 #define IFDBG if(DO_THE_DBG) #define IFINT if(0) struct r_anal_java_access_t; typedef struct r_anal_java_access_t { char *method; ut64 addr; ut64 value; ut64 op_type; struct r_anal_java_access_t *next; struct r_anal_java_access_t *previous; } RAnalJavaAccess; typedef struct r_anal_java_local_var_t { char *name; char *type; RList *writes; RList *reads; RList *binops; } RAnalJavaLocalVar; typedef struct r_anal_ex_java_lin_sweep { RList *cfg_node_addrs; }RAnalJavaLinearSweep; ut64 METHOD_START = 0; // XXX - TODO add code in the java_op that is aware of when it is in a // switch statement, like in the shlr/java/code.c so that this does not // report bad blocks. currently is should be easy to ignore these blocks, // in output for the pdj //static int java_print_ssa_bb (RAnal *anal, char *addr); static int java_reset_counter (RAnal *anal, ut64 addr); static int java_new_method (ut64 addr); static void java_update_anal_types (RAnal *anal, RBinJavaObj *bin_obj); static void java_set_function_prototype (RAnal *anal, RAnalFunction *fcn, RBinJavaField *method); static int java_cmd_ext(RAnal *anal, const char* input); static int analyze_from_code_buffer (RAnal *anal, RAnalFunction *fcn, ut64 addr, const ut8 *code_buf, ut64 code_length); static int analyze_from_code_attr (RAnal *anal, RAnalFunction *fcn, RBinJavaField *method, ut64 loadaddr); static int analyze_method(RAnal *anal, RAnalFunction *fcn, RAnalState *state); static int java_op(RAnal *anal, RAnalOp *op, ut64 addr, const ut8 *data, int len); //static int java_bb(RAnal *anal, RAnalFunction *fcn, ut64 addr, ut8 *buf, ut64 len, int reftype); //static int java_fn(RAnal *anal, RAnalFunction *fcn, ut64 addr, ut8 *buf, ut64 len, int reftype); static int java_recursive_descent(RAnal *anal, RAnalState *state, ut64 addr); static int handle_bb_cf_recursive_descent (RAnal *anal, RAnalState *state); static int java_linear_sweep(RAnal *anal, RAnalState *state, ut64 addr); static int handle_bb_cf_linear_sweep (RAnal *anal, RAnalState *state); static int java_post_anal_linear_sweep(RAnal *anal, RAnalState *state, ut64 addr); static RBinJavaObj * get_java_bin_obj(RAnal *anal); static RList * get_java_bin_obj_list(RAnal *anal); static int java_analyze_fns( RAnal *anal, ut64 start, ut64 end, int reftype, int depth); //static RAnalOp * java_op_from_buffer(RAnal *anal, RAnalState *state, ut64 addr); //static RAnalBlock * java_bb_from_buffer(RAnal *anal, RAnalState *state, ut64 addr); //static RAnalFunction * java_fn_from_buffer(RAnal *anal, RAnalState *state, ut64 addr); static int check_addr_in_code (RBinJavaField *method, ut64 addr); static int check_addr_less_end (RBinJavaField *method, ut64 addr); static int check_addr_less_start (RBinJavaField *method, ut64 addr); static int java_revisit_bb_anal_recursive_descent(RAnal *anal, RAnalState *state, ut64 addr); static RBinJavaObj * get_java_bin_obj(RAnal *anal) { RBin *b = anal->binb.bin; RBinPlugin *plugin = b->cur && b->cur->o ? b->cur->o->plugin : NULL; ut8 is_java = (plugin && strcmp (plugin->name, "java") == 0) ? 1 : 0; return is_java ? b->cur->o->bin_obj : NULL; } static RList * get_java_bin_obj_list(RAnal *anal) { RBinJavaObj *bin_obj = (RBinJavaObj * )get_java_bin_obj(anal); // See libr/bin/p/bin_java.c to see what is happening here. The original intention // was to use a shared global db variable from shlr/java/class.c, but the // BIN_OBJS_ADDRS variable kept getting corrupted on Mac, so I (deeso) switched the // way the access to the db was taking place by using the bin_obj as a proxy back // to the BIN_OBJS_ADDRS which is instantiated in libr/bin/p/bin_java.c // not the easiest way to make sausage, but its getting made. return r_bin_java_get_bin_obj_list_thru_obj (bin_obj); } static int check_addr_less_end (RBinJavaField *method, ut64 addr) { ut64 end = r_bin_java_get_method_code_size (method); return (addr < end); } static int check_addr_in_code (RBinJavaField *method, ut64 addr) { return !check_addr_less_start (method, addr) && \ check_addr_less_end ( method, addr); } static int check_addr_less_start (RBinJavaField *method, ut64 addr) { ut64 start = r_bin_java_get_method_code_offset (method); return (addr < start); } static int java_new_method (ut64 method_start) { METHOD_START = method_start; // reset the current bytes consumed counter r_java_new_method (); return 0; } static ut64 java_get_method_start () { return METHOD_START; } static int java_revisit_bb_anal_recursive_descent(RAnal *anal, RAnalState *state, ut64 addr) { RAnalBlock *current_head = state && state->current_bb_head ? state->current_bb_head : NULL; if (current_head && state->current_bb && state->current_bb->type & R_ANAL_BB_TYPE_TAIL) { r_anal_ex_update_bb_cfg_head_tail (current_head, current_head, state->current_bb); // XXX should i do this instead -> r_anal_ex_perform_post_anal_bb_cb (anal, state, addr+offset); state->done = 1; } return R_ANAL_RET_END; } static int java_recursive_descent(RAnal *anal, RAnalState *state, ut64 addr) { RAnalBlock *bb; RAnalBlock *current_head; if (!anal || !state || !state->current_bb || state->current_bb_head) return 0; bb = state->current_bb; current_head = state->current_bb_head; if (current_head && state->current_bb->type & R_ANAL_BB_TYPE_TAIL) { r_anal_ex_update_bb_cfg_head_tail (current_head, current_head, state->current_bb); } // basic filter for handling the different type of operations // depending on flags some may be called more than once // if (bb->type2 & R_ANAL_EX_ILL_OP) handle_bb_ill_op (anal, state); // if (bb->type2 & R_ANAL_EX_COND_OP) handle_bb_cond_op (anal, state); // if (bb->type2 & R_ANAL_EX_UNK_OP) handle_bb_unknown_op (anal, state); // if (bb->type2 & R_ANAL_EX_NULL_OP) handle_bb_null_op (anal, state); // if (bb->type2 & R_ANAL_EX_NOP_OP) handle_bb_nop_op (anal, state); // if (bb->type2 & R_ANAL_EX_REP_OP) handle_bb_rep_op (anal, state); // if (bb->type2 & R_ANAL_EX_STORE_OP) handle_bb_store_op (anal, state); // if (bb->type2 & R_ANAL_EX_LOAD_OP) handle_bb_load_op (anal, state // if (bb->type2 & R_ANAL_EX_REG_OP) handle_bb_reg_op (anal, state); // if (bb->type2 & R_ANAL_EX_OBJ_OP) handle_bb_obj_op (anal, state); // if (bb->type2 & R_ANAL_EX_STACK_OP) handle_bb_stack_op (anal, state); // if (bb->type2 & R_ANAL_EX_BIN_OP) handle_bb_bin_op (anal, state); if (bb->type2 & R_ANAL_EX_CODE_OP) handle_bb_cf_recursive_descent (anal, state); // if (bb->type2 & R_ANAL_EX_DATA_OP) handle_bb_data_op (anal, state); return 0; } static int java_linear_sweep(RAnal *anal, RAnalState *state, ut64 addr) { RAnalBlock *bb = state->current_bb; if (state->current_bb_head && state->current_bb->type & R_ANAL_BB_TYPE_TAIL) { //r_anal_ex_update_bb_cfg_head_tail (state->current_bb_head, state->current_bb_head, state->current_bb); } // basic filter for handling the different type of operations // depending on flags some may be called more than once // if (bb->type2 & R_ANAL_EX_ILL_OP) handle_bb_ill_op (anal, state); // if (bb->type2 & R_ANAL_EX_COND_OP) handle_bb_cond_op (anal, state); // if (bb->type2 & R_ANAL_EX_UNK_OP) handle_bb_unknown_op (anal, state); // if (bb->type2 & R_ANAL_EX_NULL_OP) handle_bb_null_op (anal, state); // if (bb->type2 & R_ANAL_EX_NOP_OP) handle_bb_nop_op (anal, state); // if (bb->type2 & R_ANAL_EX_REP_OP) handle_bb_rep_op (anal, state); // if (bb->type2 & R_ANAL_EX_STORE_OP) handle_bb_store_op (anal, state); // if (bb->type2 & R_ANAL_EX_LOAD_OP) handle_bb_load_op (anal, state // if (bb->type2 & R_ANAL_EX_REG_OP) handle_bb_reg_op (anal, state); // if (bb->type2 & R_ANAL_EX_OBJ_OP) handle_bb_obj_op (anal, state); // if (bb->type2 & R_ANAL_EX_STACK_OP) handle_bb_stack_op (anal, state); // if (bb->type2 & R_ANAL_EX_BIN_OP) handle_bb_bin_op (anal, state); if (bb->type2 & R_ANAL_EX_CODE_OP) handle_bb_cf_linear_sweep (anal, state); // if (bb->type2 & R_ANAL_EX_DATA_OP) handle_bb_data_op (anal, state); return 0; } static int handle_bb_cf_recursive_descent (RAnal *anal, RAnalState *state) { RAnalBlock *bb = state->current_bb; ut64 addr = 0; int result = 0; if (!bb) { eprintf ("Error: unable to handle basic block @ 0x%08"PFMT64x"\n", addr); return R_ANAL_RET_ERROR; } else if (state->max_depth <= state->current_depth) { return R_ANAL_RET_ERROR; } state->current_depth++; addr = bb->addr; IFDBG eprintf ("Handling a control flow change @ 0x%04"PFMT64x".\n", addr); ut64 control_type = r_anal_ex_map_anal_ex_to_anal_op_type (bb->type2); // XXX - transition to type2 control flow condtions switch (control_type) { case R_ANAL_OP_TYPE_CALL: IFDBG eprintf (" - Handling a call @ 0x%04"PFMT64x".\n", addr); r_anal_fcn_xref_add (anal, state->current_fcn, bb->addr, bb->jump, R_ANAL_REF_TYPE_CALL); result = R_ANAL_RET_ERROR; break; case R_ANAL_OP_TYPE_JMP: { RList * jmp_list; IFDBG eprintf (" - Handling a jmp @ 0x%04"PFMT64x" to 0x%04"PFMT64x".\n", addr, bb->jump); // visited some other time if (!r_anal_state_search_bb (state, bb->jump)) { jmp_list = r_anal_ex_perform_analysis ( anal, state, bb->jump ); if (jmp_list) bb->jumpbb = (RAnalBlock *) r_list_get_n (jmp_list, 0); if (bb->jumpbb) bb->jump = bb->jumpbb->addr; } else { bb->jumpbb = r_anal_state_search_bb (state, bb->jump); if (bb->jumpbb) bb->jump = bb->jumpbb->addr; } if (state->done == 1) { IFDBG eprintf (" Looks like this jmp (bb @ 0x%04"PFMT64x") found a return.\n", addr); } result = R_ANAL_RET_END; } break; case R_ANAL_OP_TYPE_CJMP: { RList *jmp_list; ut8 encountered_stop = 0; IFDBG eprintf (" - Handling a cjmp @ 0x%04"PFMT64x" jmp to 0x%04"PFMT64x" and fail to 0x%04"PFMT64x".\n", addr, bb->jump, bb->fail); IFDBG eprintf (" - Handling jmp to 0x%04"PFMT64x".\n", bb->jump); // visited some other time if (!r_anal_state_search_bb (state, bb->jump)) { jmp_list = r_anal_ex_perform_analysis ( anal, state, bb->jump ); if (jmp_list) bb->jumpbb = (RAnalBlock *) r_list_get_n (jmp_list, 0); if (bb->jumpbb) { bb->jump = bb->jumpbb->addr; } } else { bb->jumpbb = r_anal_state_search_bb (state, bb->jump); bb->jump = bb->jumpbb->addr; } if (state->done == 1) { IFDBG eprintf (" Looks like this jmp (bb @ 0x%04"PFMT64x") found a return.\n", addr); state->done = 0; encountered_stop = 1; } if (!r_anal_state_search_bb (state, bb->fail)) { jmp_list = r_anal_ex_perform_analysis ( anal, state, bb->fail ); if (jmp_list) bb->failbb = (RAnalBlock *) r_list_get_n (jmp_list, 0); if (bb->failbb) { bb->fail = bb->failbb->addr; } } else { bb->failbb = r_anal_state_search_bb (state, bb->fail); if (bb->failbb) { bb->fail = bb->failbb->addr; } } IFDBG eprintf (" - Handling an cjmp @ 0x%04"PFMT64x" jmp to 0x%04"PFMT64x" and fail to 0x%04"PFMT64x".\n", addr, bb->jump, bb->fail); IFDBG eprintf (" - Handling fail to 0x%04"PFMT64x".\n", bb->fail); // r_anal_state_merge_bb_list (state, fail_list); if (state->done == 1) { IFDBG eprintf (" Looks like this fail (bb @ 0x%04"PFMT64x") found a return.\n", addr); } result = R_ANAL_RET_END; if (encountered_stop) state->done = 1; } break; case R_ANAL_OP_TYPE_SWITCH: { IFDBG eprintf (" - Handling an switch @ 0x%04"PFMT64x".\n", addr); if (bb->switch_op) { RAnalCaseOp *caseop; RListIter *iter; RList *jmp_list = NULL; ut8 encountered_stop = 0; r_list_foreach (bb->switch_op->cases, iter, caseop) { if (caseop) { if (r_anal_state_addr_is_valid (state, caseop->jump) ) { jmp_list = r_anal_ex_perform_analysis ( anal, state, caseop->jump ); if (jmp_list) caseop->jumpbb = (RAnalBlock *) r_list_get_n (jmp_list, 0); if (state->done == 1) { IFDBG eprintf (" Looks like this jmp (bb @ 0x%04"PFMT64x") found a return.\n", addr); state->done = 0; encountered_stop = 1; } } } } r_list_free (jmp_list); if (encountered_stop) state->done = 1; } result = R_ANAL_RET_END; } break; case R_ANAL_OP_TYPE_TRAP: case R_ANAL_OP_TYPE_UJMP: case R_ANAL_OP_TYPE_IJMP: case R_ANAL_OP_TYPE_RJMP: case R_ANAL_OP_TYPE_IRJMP: case R_ANAL_OP_TYPE_RET: case R_ANAL_OP_TYPE_ILL: IFDBG eprintf (" - Handling an ret @ 0x%04"PFMT64x".\n", addr); state->done = 1; result = R_ANAL_RET_END; break; default: break; } state->current_depth--; return result; } static int java_post_anal_linear_sweep(RAnal *anal, RAnalState *state, ut64 addr) { RAnalJavaLinearSweep *nodes = state->user_state; RList *jmp_list = NULL; ut64 *paddr64; state->done = 0; if (!nodes || !nodes->cfg_node_addrs) { state->done = 1; return R_ANAL_RET_ERROR; } while (r_list_length (nodes->cfg_node_addrs) > 0) { paddr64 = r_list_get_n (nodes->cfg_node_addrs, 0); r_list_del_n (nodes->cfg_node_addrs, 0); if (paddr64 && !r_anal_state_search_bb (state, *paddr64)) { ut64 list_length = 0; IFDBG eprintf (" - Visiting 0x%04"PFMT64x" for analysis.\n", *paddr64); jmp_list = r_anal_ex_perform_analysis ( anal, state, *paddr64 ); list_length = r_list_length (jmp_list); r_list_free (jmp_list); if ( list_length > 0) { IFDBG eprintf (" - Found %"PFMT64d" more basic blocks missed on the initial pass.\n", *paddr64); } } } return R_ANAL_RET_END; } static int handle_bb_cf_linear_sweep (RAnal *anal, RAnalState *state) { ut64 * paddr64; RAnalBlock *bb = state->current_bb; RAnalJavaLinearSweep *nodes = state->user_state; if (!nodes || !nodes->cfg_node_addrs) { state->done = 1; return R_ANAL_RET_ERROR; } ut64 addr = 0; int result = 0; if (!bb) { eprintf ("Error: unable to handle basic block @ 0x%08"PFMT64x"\n", addr); return R_ANAL_RET_ERROR; } else if (state->max_depth <= state->current_depth) { return R_ANAL_RET_ERROR; } state->current_depth++; addr = bb->addr; IFDBG eprintf ("Handling a control flow change @ 0x%04"PFMT64x".\n", addr); ut32 control_type = r_anal_ex_map_anal_ex_to_anal_op_type (bb->type2); // XXX - transition to type2 control flow condtions switch (control_type) { case R_ANAL_OP_TYPE_CALL: IFDBG eprintf (" - Handling a call @ 0x%04"PFMT64x"\n", addr); r_anal_fcn_xref_add (anal, state->current_fcn, bb->addr, bb->jump, R_ANAL_REF_TYPE_CALL); result = R_ANAL_RET_ERROR; break; case R_ANAL_OP_TYPE_JMP: paddr64 = malloc (sizeof(ut64)); *paddr64 = bb->jump; IFDBG eprintf (" - Handling a jmp @ 0x%04"PFMT64x", adding for future visit\n", addr); r_list_append (nodes->cfg_node_addrs, paddr64); result = R_ANAL_RET_END; break; case R_ANAL_OP_TYPE_CJMP: paddr64 = malloc (sizeof(ut64)); *paddr64 = bb->jump; IFDBG eprintf (" - Handling a bb->jump @ 0x%04"PFMT64x", adding 0x%04"PFMT64x" for future visit\n", addr, *paddr64); r_list_append (nodes->cfg_node_addrs, paddr64); paddr64 = malloc (sizeof(ut64)); *paddr64 = bb->fail; IFDBG eprintf (" - Handling a bb->fail @ 0x%04"PFMT64x", adding 0x%04"PFMT64x" for future visit\n", addr, *paddr64); r_list_append (nodes->cfg_node_addrs, paddr64); result = R_ANAL_RET_END; break; case R_ANAL_OP_TYPE_SWITCH: if (bb->switch_op) { RAnalCaseOp *caseop; RListIter *iter; //RList *jmp_list = NULL; IFDBG eprintf (" - Handling a switch_op @ 0x%04"PFMT64x":\n", addr); r_list_foreach (bb->switch_op->cases, iter, caseop) { ut64 * paddr64; if (caseop) { paddr64 = malloc (sizeof(ut64)); *paddr64 = caseop->jump; IFDBG eprintf ("Adding 0x%04"PFMT64x" for future visit\n", *paddr64); r_list_append (nodes->cfg_node_addrs, paddr64); } } } result = R_ANAL_RET_END; break; case R_ANAL_OP_TYPE_TRAP: case R_ANAL_OP_TYPE_UJMP: case R_ANAL_OP_TYPE_RJMP: case R_ANAL_OP_TYPE_IJMP: case R_ANAL_OP_TYPE_IRJMP: case R_ANAL_OP_TYPE_RET: IFDBG eprintf (" - Handling an ret @ 0x%04"PFMT64x".\n", addr); state->done = 1; result = R_ANAL_RET_END; break; default: break; } state->current_depth--; return result; } //many flaws UAF static int analyze_from_code_buffer(RAnal *anal, RAnalFunction *fcn, ut64 addr, const ut8 *code_buf, ut64 code_length) { char gen_name[1025]; RListIter *bb_iter; RAnalBlock *bb; ut64 actual_size = 0; RAnalState *state = NULL; int result = R_ANAL_RET_ERROR; RAnalJavaLinearSweep *nodes; free (fcn->name); free (fcn->dsc); snprintf (gen_name, 1024, "sym.%08"PFMT64x"", addr); fcn->name = strdup (gen_name); fcn->dsc = strdup ("unknown"); r_anal_fcn_set_size (fcn, code_length); fcn->type = R_ANAL_FCN_TYPE_FCN; fcn->addr = addr; state = r_anal_state_new (addr, (ut8*) code_buf, code_length); nodes = R_NEW0 (RAnalJavaLinearSweep); nodes->cfg_node_addrs = r_list_new (); nodes->cfg_node_addrs->free = free; state->user_state = nodes; result = analyze_method (anal, fcn, state); r_list_foreach (fcn->bbs, bb_iter, bb) { actual_size += bb->size; } r_anal_fcn_set_size (fcn, state->bytes_consumed); result = state->anal_ret_val; r_list_free (nodes->cfg_node_addrs); free (nodes); //leak to avoid UAF is the easy solution otherwise a whole rewrite is needed //r_anal_state_free (state); if (r_anal_fcn_size (fcn) != code_length) { return R_ANAL_RET_ERROR; #if 0 eprintf ("WARNING Analysis of %s Incorrect: Code Length: 0x%"PFMT64x", Function size reported 0x%x\n", fcn->name, code_length, r_anal_fcn_size(fcn)); eprintf ("Deadcode detected, setting code length to: 0x%"PFMT64x"\n", code_length); r_anal_fcn_set_size (fcn, code_length); #endif } return result; } static int analyze_from_code_attr (RAnal *anal, RAnalFunction *fcn, RBinJavaField *method, ut64 loadaddr) { RBinJavaAttrInfo* code_attr = method ? r_bin_java_get_method_code_attribute(method) : NULL; ut8 * code_buf = NULL; int result = false; ut64 code_length = 0; ut64 code_addr = -1; if (!code_attr) { fcn->name = strdup ("sym.UNKNOWN"); fcn->dsc = strdup ("unknown"); r_anal_fcn_set_size (fcn, code_length); fcn->type = R_ANAL_FCN_TYPE_FCN; fcn->addr = 0; return R_ANAL_RET_ERROR; } code_length = code_attr->info.code_attr.code_length; code_addr = code_attr->info.code_attr.code_offset; code_buf = calloc (1, code_length); anal->iob.read_at (anal->iob.io, code_addr + loadaddr, code_buf, code_length); result = analyze_from_code_buffer (anal, fcn, code_addr + loadaddr, code_buf, code_length); free (code_buf); { char *name = strdup (method->name); r_name_filter (name, 80); free (fcn->name); if (method->class_name) { char *cname = strdup (method->class_name); r_name_filter (cname, 50); fcn->name = r_str_newf ("sym.%s.%s", cname, name); free (cname); } else { fcn->name = r_str_newf ("sym.%s", name); } free (name); } free (fcn->dsc); fcn->dsc = strdup (method->descriptor); IFDBG eprintf ("Completed analysing code from attr, name: %s, desc: %s", fcn->name, fcn->dsc); return result; } static int analyze_method(RAnal *anal, RAnalFunction *fcn, RAnalState *state) { // deallocate niceties r_list_free (fcn->bbs); fcn->bbs = r_anal_bb_list_new (); java_new_method (fcn->addr); state->current_fcn = fcn; // Not a resource leak. Basic blocks should be stored in the state->fcn // TODO: ? RList *bbs = r_anal_ex_perform_analysis (anal, state, fcn->addr); return state->anal_ret_val; } static int java_analyze_fns_from_buffer( RAnal *anal, ut64 start, ut64 end, int reftype, int depth) { int result = R_ANAL_RET_ERROR; ut64 addr = start; ut64 offset = 0; ut64 buf_len = end - start; ut8 analyze_all = 0, *buffer = NULL; if (end == UT64_MAX) { //analyze_all = 1; buf_len = anal->iob.desc_size (anal->iob.io->desc); if (buf_len == UT64_MAX) buf_len = 1024; end = start + buf_len; } buffer = malloc (buf_len); if (!buffer) { return R_ANAL_RET_ERROR; } anal->iob.read_at (anal->iob.io, addr, buffer, buf_len); while (offset < buf_len) { ut64 length = buf_len - offset; RAnalFunction *fcn = r_anal_fcn_new (); fcn->cc = r_str_const (r_anal_cc_default (anal)); result = analyze_from_code_buffer ( anal, fcn, addr, buffer+offset, length ); if (result == R_ANAL_RET_ERROR) { eprintf ("Failed to parse java fn: %s @ 0x%04"PFMT64x"\n", fcn->name, fcn->addr); // XXX - TO Stop or not to Stop ?? break; } //r_listrange_add (anal->fcnstore, fcn); r_anal_fcn_tree_insert (&anal->fcn_tree, fcn); r_list_append (anal->fcns, fcn); offset += r_anal_fcn_size (fcn); if (!analyze_all) break; } free (buffer); return result; } static int java_analyze_fns( RAnal *anal, ut64 start, ut64 end, int reftype, int depth) { //anal->iob.read_at (anal->iob.io, op.jump, bbuf, sizeof (bbuf)); RBinJavaObj *bin = NULL;// = get_java_bin_obj (anal); RBinJavaField *method = NULL; RListIter *methods_iter, *bin_obs_iter; RList * bin_objs_list = get_java_bin_obj_list (anal); ut8 analyze_all = 0; //RAnalRef *ref = NULL; int result = R_ANAL_RET_ERROR; if (end == UT64_MAX) { analyze_all = 1; } if (!bin_objs_list || r_list_empty (bin_objs_list)) { r_list_free (bin_objs_list); return java_analyze_fns_from_buffer (anal, start, end, reftype, depth); } r_list_foreach (bin_objs_list, bin_obs_iter, bin) { // loop over all bin object that are loaded java_update_anal_types (anal, bin); RList *methods_list = (RList *) r_bin_java_get_methods_list (bin); ut64 loadaddr = bin->loadaddr; // loop over all methods in the binary object and analyse // the functions r_list_foreach (methods_list, methods_iter, method) { if ((method && analyze_all) || (check_addr_less_start (method, end) || check_addr_in_code (method, end))) { RAnalFunction *fcn = r_anal_fcn_new (); fcn->cc = r_str_const (r_anal_cc_default (anal)); java_set_function_prototype (anal, fcn, method); result = analyze_from_code_attr (anal, fcn, method, loadaddr); if (result == R_ANAL_RET_ERROR) { //eprintf ("Failed to parse java fn: %s @ 0x%04"PFMT64x"\n", fcn->name, fcn->addr); return result; // XXX - TO Stop or not to Stop ?? } //r_listrange_add (anal->fcnstore, fcn); r_anal_fcn_update_tinyrange_bbs (fcn); r_anal_fcn_tree_insert (&anal->fcn_tree, fcn); r_list_append (anal->fcns, fcn); } } // End of methods loop }// end of bin_objs list loop return result; } /*static int java_fn(RAnal *anal, RAnalFunction *fcn, ut64 addr, ut8 *buf, ut64 len, int reftype) { // XXX - this may clash with malloc:// uris because the file name is // malloc:// ** RBinJavaObj *bin = (RBinJavaObj *) get_java_bin_obj (anal); RBinJavaField *method = bin ? r_bin_java_get_method_code_attribute_with_addr (bin, addr) : NULL; ut64 loadaddr = bin ? bin->loadaddr : 0; IFDBG eprintf ("Analyzing java functions for %s\n", anal->iob.io->fd->name); if (method) return analyze_from_code_attr (anal, fcn, method, loadaddr); return analyze_from_code_buffer (anal, fcn, addr, buf, len); }*/ static int java_switch_op(RAnal *anal, RAnalOp *op, ut64 addr, const ut8 *data, int len) { ut8 op_byte = data[0]; ut64 offset = addr - java_get_method_start (); ut8 pos = (offset+1)%4 ? 1 + 4 - (offset+1)%4 : 1; if (op_byte == 0xaa) { // handle a table switch condition if (pos + 8 > len) { return op->size; } int min_val = (ut32)(UINT (data, pos + 4)), max_val = (ut32)(UINT (data, pos + 8)); ut32 default_loc = (ut32) (UINT (data, pos)), cur_case = 0; op->switch_op = r_anal_switch_op_new (addr, min_val, default_loc); RAnalCaseOp *caseop = NULL; pos += 12; if (max_val > min_val && ((max_val - min_val)<(UT16_MAX/4))) { //caseop = r_anal_switch_op_add_case(op->switch_op, addr+default_loc, -1, addr+offset); for (cur_case = 0; cur_case <= max_val - min_val; pos += 4, cur_case++) { //ut32 value = (ut32)(UINT (data, pos)); if (pos + 4 >= len) { // switch is too big cant read further break; } int offset = (int)(ut32)(R_BIN_JAVA_UINT (data, pos)); caseop = r_anal_switch_op_add_case (op->switch_op, addr+pos, cur_case+min_val, addr+offset); caseop->bb_ref_to = addr+offset; caseop->bb_ref_from = addr; // TODO figure this one out } } else { eprintf ("Invalid switch boundaries at 0x%"PFMT64x"\n", addr); } } op->size = pos; return op->size; } static int java_op(RAnal *anal, RAnalOp *op, ut64 addr, const ut8 *data, int len) { int sz = 1; /* get opcode size */ //ut8 op_byte = data[0]; ut8 op_byte = data[0]; sz = JAVA_OPS[op_byte].size; if (!op) return sz; memset (op, '\0', sizeof (RAnalOp)); IFDBG { //eprintf ("Extracting op from buffer (%d bytes) @ 0x%04x\n", len, addr); //eprintf ("Parsing op: (0x%02x) %s.\n", op_byte, JAVA_OPS[op_byte].name); } op->addr = addr; op->size = sz; op->id = data[0]; op->type2 = JAVA_OPS[op_byte].op_type; op->type = r_anal_ex_map_anal_ex_to_anal_op_type (op->type2); // handle lookup and table switch offsets if (op_byte == 0xaa || op_byte == 0xab) { java_switch_op (anal, op, addr, data, len); // IN_SWITCH_OP = 1; } /* TODO: // not sure how to handle the states for IN_SWITCH_OP, SWITCH_OP_CASES, // and NUM_CASES_SEEN, because these are dependent on whether or not we // are in a switch, and given the non-reentrant state of opcode analysis // this can't always be guaranteed. Below is the pseudo code for handling // the easy parts though if (IN_SWITCH_OP) { NUM_CASES_SEEN++; if (NUM_CASES_SEEN == SWITCH_OP_CASES) IN_SWITCH_OP=0; op->addr = addr; op->size = 4; op->type2 = 0; op->type = R_ANAL_OP_TYPE_CASE op->eob = 0; return op->sizes; } */ op->eob = r_anal_ex_is_op_type_eop (op->type2); IFDBG { const char *ot_str = r_anal_optype_to_string (op->type); eprintf ("op_type2: %s @ 0x%04"PFMT64x" 0x%08"PFMT64x" op_type: (0x%02"PFMT64x") %s.\n", JAVA_OPS[op_byte].name, addr, (ut64)op->type2, (ut64)op->type, ot_str); //eprintf ("op_eob: 0x%02x.\n", op->eob); //eprintf ("op_byte @ 0: 0x%02x op_byte @ 0x%04x: 0x%02x.\n", data[0], addr, data[addr]); } if (len < 4) { // incomplete analysis here return 0; } if (op->type == R_ANAL_OP_TYPE_CJMP) { op->jump = addr + (short)(USHORT (data, 1)); op->fail = addr + sz; IFDBG eprintf ("%s jmpto 0x%04"PFMT64x" failto 0x%04"PFMT64x".\n", JAVA_OPS[op_byte].name, op->jump, op->fail); } else if (op->type == R_ANAL_OP_TYPE_JMP) { op->jump = addr + (short)(USHORT (data, 1)); IFDBG eprintf ("%s jmpto 0x%04"PFMT64x".\n", JAVA_OPS[op_byte].name, op->jump); } else if ( (op->type & R_ANAL_OP_TYPE_CALL) == R_ANAL_OP_TYPE_CALL ) { op->jump = (int)(short)(USHORT (data, 1)); op->fail = addr + sz; //IFDBG eprintf ("%s callto 0x%04x failto 0x%04x.\n", JAVA_OPS[op_byte].name, op->jump, op->fail); } //r_java_disasm(addr, data, len, output, outlen); //IFDBG eprintf ("%s\n", output); return op->size; } /* static RAnalOp * java_op_from_buffer(RAnal *anal, RAnalState *state, ut64 addr) { RAnalOp *op = r_anal_op_new (); // get opcode size if (!op) return 0; memset (op, '\0', sizeof (RAnalOp)); java_op (anal, op, addr, state->buffer, state->len - (addr - state->start) ); return op; } */ static void java_set_function_prototype (RAnal *anal, RAnalFunction *fcn, RBinJavaField *method) { RList *the_list = r_bin_java_extract_type_values (method->descriptor); Sdb *D = anal->sdb_types; Sdb *A = anal->sdb_args; const char *type_fmt = "%08"PFMT64x".arg.%d.type", *namek_fmt = "%08"PFMT64x".var.%d.name", *namev_fmt = "%08"PFMT64x"local.%d"; char key_buf[1024], value_buf [1024]; RListIter *iter; char *str; if (the_list) { ut8 start = 0, stop = 0; int idx = 0; r_list_foreach (the_list, iter, str) { IFDBG eprintf ("Adding type: %s to known types.\n", str); if (str && *str == '('){ start = 1; continue; } if (str && start && *str != ')') { // set type // set arg type snprintf (key_buf, sizeof(key_buf)-1, type_fmt, (ut64)fcn->addr, idx); sdb_set (A, str, key_buf, 0); sdb_set (D, str, "type", 0); // set value snprintf (key_buf, sizeof(key_buf)-1, namek_fmt, fcn->addr, idx); snprintf (value_buf, sizeof(value_buf)-1, namev_fmt, fcn->addr, idx); sdb_set (A, value_buf, key_buf, 0); idx ++; } if (start && str && *str == ')') { stop = 1; continue; } if ((start & stop & 1) && str) { sdb_set (A, str, "ret.type", 0); sdb_set (D, str, "type", 0); } } r_list_free (the_list); } } static void java_update_anal_types (RAnal *anal, RBinJavaObj *bin_obj) { Sdb *D = anal->sdb_types; if (D && bin_obj) { RListIter *iter; char *str; RList * the_list = r_bin_java_extract_all_bin_type_values (bin_obj); if (the_list) { r_list_foreach (the_list, iter, str) { IFDBG eprintf ("Adding type: %s to known types.\n", str); if (str) sdb_set (D, str, "type", 0); } } r_list_free (the_list); } } static int java_cmd_ext(RAnal *anal, const char* input) { RBinJavaObj *obj = (RBinJavaObj *) get_java_bin_obj (anal); if (!obj) { eprintf ("Execute \"af\" to set the current bin, and this will bind the current bin\n"); return -1; } switch (*input) { case 'c': // reset bytes counter for case operations r_java_new_method (); break; case 'u': switch (*(input+1)) { case 't': {java_update_anal_types (anal, obj); return true;} default: break; } break; case 's': switch (*(input+1)) { //case 'e': return java_resolve_cp_idx_b64 (anal, input+2); default: break; } break; default: eprintf("Command not supported"); break; } return 0; } static int java_reset_counter (RAnal *anal, ut64 start_addr ) { IFDBG eprintf ("Setting the new METHOD_START to 0x%08"PFMT64x" was 0x%08"PFMT64x"\n", start_addr, METHOD_START); METHOD_START = start_addr; r_java_new_method (); return true; } RAnalPlugin r_anal_plugin_java = { .name = "java", .desc = "Java bytecode analysis plugin", .license = "Apache", .arch = "java", .bits = 32, .custom_fn_anal = 1, .reset_counter = java_reset_counter, .analyze_fns = java_analyze_fns, .post_anal_bb_cb = java_recursive_descent, .revisit_bb_anal = java_revisit_bb_anal_recursive_descent, .op = &java_op, .cmd_ext = java_cmd_ext, 0 }; RAnalPlugin r_anal_plugin_java_ls = { .name = "java_ls", .desc = "Java bytecode analysis plugin with linear sweep", .license = "Apache", .arch = "java", .bits = 32, .custom_fn_anal = 1, .analyze_fns = java_analyze_fns, .post_anal_bb_cb = java_linear_sweep, .post_anal = java_post_anal_linear_sweep, .revisit_bb_anal = java_revisit_bb_anal_recursive_descent, .op = &java_op, .cmd_ext = java_cmd_ext, 0 }; #ifndef CORELIB RLibStruct radare_plugin = { .type = R_LIB_TYPE_ANAL, //.data = &r_anal_plugin_java .data = &r_anal_plugin_java_ls, .version = R2_VERSION }; #endif