mirror of
https://github.com/radareorg/radare2.git
synced 2024-12-12 23:46:36 +00:00
982 lines
31 KiB
C
982 lines
31 KiB
C
/* radare - Apache 2.0 - Copyright 2010-2019 - pancake and
|
|
Adam Pridgen <dso@rice.edu || adam.pridgen@thecoverofnight.com> */
|
|
|
|
#include <string.h>
|
|
|
|
#include <r_types.h>
|
|
#include <r_lib.h>
|
|
#include <r_asm.h>
|
|
#include <r_anal.h>
|
|
#include <r_anal_ex.h>
|
|
#include <r_cons.h>
|
|
|
|
#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, RAnalOpMask mask);
|
|
//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) {
|
|
r_return_val_if_fail (anal && state, R_ANAL_RET_ERROR);
|
|
RAnalBlock *head = state->current_bb_head;
|
|
RAnalBlock *bb = state->current_bb;
|
|
r_return_val_if_fail (bb && head, R_ANAL_RET_ERROR);
|
|
if (bb->type & R_ANAL_BB_TYPE_TAIL) {
|
|
r_anal_ex_update_bb_cfg_head_tail (head, head, 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) {
|
|
r_return_val_if_fail (anal && state && state->current_bb && state->current_bb_head, 0);
|
|
|
|
RAnalBlock *bb = state->current_bb;
|
|
RAnalBlock *head = state->current_bb_head;
|
|
|
|
if (head && bb->type & R_ANAL_BB_TYPE_TAIL) {
|
|
r_anal_ex_update_bb_cfg_head_tail (head, head, 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 conditions
|
|
switch (control_type) {
|
|
case R_ANAL_OP_TYPE_CALL:
|
|
IFDBG eprintf (" - Handling a call @ 0x%04"PFMT64x".\n", addr);
|
|
r_anal_xrefs_set (anal, 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 conditions
|
|
switch (control_type) {
|
|
case R_ANAL_OP_TYPE_CALL:
|
|
IFDBG eprintf (" - Handling a call @ 0x%04"PFMT64x"\n", addr);
|
|
r_anal_xrefs_set (anal, 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));
|
|
if (paddr64) {
|
|
*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) {
|
|
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);
|
|
|
|
fcn->name = r_str_newf ("sym.%08"PFMT64x, addr);
|
|
fcn->dsc = strdup ("unknown");
|
|
r_anal_fcn_set_size (NULL, 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 (NULL, fcn, state->bytes_consumed);
|
|
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;
|
|
int result = false;
|
|
|
|
if (!code_attr) {
|
|
fcn->name = strdup ("sym.UNKNOWN");
|
|
fcn->dsc = strdup ("unknown");
|
|
r_anal_fcn_set_size (NULL, fcn, 1); // code_length);
|
|
fcn->type = R_ANAL_FCN_TYPE_FCN;
|
|
fcn->addr = 0;
|
|
return R_ANAL_RET_ERROR;
|
|
}
|
|
|
|
ut64 code_length = code_attr->info.code_attr.code_length;
|
|
ut64 code_addr = code_attr->info.code_attr.code_offset;
|
|
ut8 *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);
|
|
if (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 functionCmp(const void *a, const void *b) {
|
|
const RAnalFunction *fa = (const RAnalFunction *)a;
|
|
const RAnalFunction *fb = (const RAnalFunction *)b;
|
|
|
|
return strcmp (fa->name, fb->name);
|
|
}
|
|
|
|
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);
|
|
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);
|
|
RList * anal_fcns = NULL;
|
|
|
|
ut8 analyze_all = 0;
|
|
int result = 0; // 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);
|
|
r_list_append (anal->fcns, fcn);
|
|
}
|
|
} // End of methods loop
|
|
}// end of bin_objs list loop
|
|
anal_fcns = r_list_clone (anal->fcns);
|
|
anal->fcns = r_list_uniq (anal_fcns, functionCmp);
|
|
r_list_free (anal_fcns);
|
|
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 + 8 > len) {
|
|
return op->size;
|
|
}
|
|
const int min_val = (ut32)(UINT (data, pos + 4));
|
|
const int 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 can't 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);
|
|
if (caseop) {
|
|
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, RAnalOpMask mask) {
|
|
/* get opcode size */
|
|
if (len < 1) {
|
|
op->type = R_ANAL_OP_TYPE_ILL;
|
|
return 1;
|
|
}
|
|
//ut8 op_byte = data[0];
|
|
ut8 op_byte = data[0];
|
|
int sz = JAVA_OPS[op_byte].size;
|
|
if (!op) {
|
|
op->type = R_ANAL_OP_TYPE_ILL;
|
|
return sz;
|
|
}
|
|
IFDBG {
|
|
//eprintf ("Extracting op from buffer (%d byte(s)) @ 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_POP) {
|
|
op->stackop = R_ANAL_STACK_INC;
|
|
op->stackptr = 8;
|
|
}
|
|
op->direction = R_ANAL_OP_DIR_EXEC;
|
|
if (op->type == R_ANAL_OP_TYPE_PUSH) {
|
|
op->stackop = R_ANAL_STACK_INC;
|
|
op->stackptr = -8;
|
|
}
|
|
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 = method ? r_bin_java_extract_type_values (method->descriptor) : NULL;
|
|
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;
|
|
}
|
|
|
|
#define USE_CUSTOM_ANAL 1
|
|
|
|
RAnalPlugin r_anal_plugin_java = {
|
|
.name = "java",
|
|
.desc = "Java bytecode analysis plugin",
|
|
.license = "Apache",
|
|
.arch = "java",
|
|
.bits = 32,
|
|
#if USE_CUSTOM_ANAL
|
|
.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,
|
|
#endif
|
|
.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,
|
|
#if USE_CUSTOM_ANAL
|
|
.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,
|
|
#endif
|
|
.op = &java_op,
|
|
.cmd_ext = java_cmd_ext,
|
|
0
|
|
};
|
|
|
|
#ifndef R2_PLUGIN_INCORE
|
|
R_API RLibStruct radare_plugin = {
|
|
.type = R_LIB_TYPE_ANAL,
|
|
//.data = &r_anal_plugin_java
|
|
.data = &r_anal_plugin_java_ls,
|
|
.version = R2_VERSION
|
|
};
|
|
#endif
|