rizin/librz/analysis/p/analysis_java.c
Riccardo Schirone 53bf5c497f SPDX Copyright text for all files based on history
- Add LICENSES directory
- Download additional licenses used
- Add .reuse directory
- Add doc about SPDX/reuse

Co-authored-by: Florian Märkl <info@florianmaerkl.de>
2021-03-05 19:39:15 +08:00

352 lines
12 KiB
C

// SPDX-FileCopyrightText: 2010-2019 pancake <pancake@nopcode.org>
// SPDX-FileCopyrightText: 2010-2019 Adam Pridgen <dso@rice.edu>
// SPDX-License-Identifier: Apache-2.0
#include <string.h>
#include <rz_types.h>
#include <rz_lib.h>
#include <rz_analysis.h>
#include "../../asm/arch/java/ops.h"
#include "../../asm/arch/java/code.h"
#include "../../bin/format/java/class.h"
#ifdef IFDBG
#define dprintf eprintf
#endif
#define DO_THE_DBG 0
#define IFDBG if (DO_THE_DBG)
#define IFINT if (0)
ut64 METHOD_START = 0;
static void java_update_analysis_types(RzAnalysis *analysis, RzBinJavaObj *bin_obj);
static int java_cmd_ext(RzAnalysis *analysis, const char *input);
static int java_op(RzAnalysis *analysis, RzAnalysisOp *op, ut64 addr, const ut8 *data, int len, RzAnalysisOpMask mask);
static RzBinJavaObj *get_java_bin_obj(RzAnalysis *analysis);
static RzBinJavaObj *get_java_bin_obj(RzAnalysis *analysis) {
RzBin *b = analysis->binb.bin;
RzBinPlugin *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 ut64 java_get_method_start(void) {
return METHOD_START;
}
static int java_switch_op(RzAnalysis *analysis, RzAnalysisOp *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)(rz_read_at_be32(data, pos + 4));
const int max_val = (ut32)(rz_read_at_be32(data, pos + 8));
ut32 default_loc = (ut32)(rz_read_at_be32(data, pos)), cur_case = 0;
op->switch_op = rz_analysis_switch_op_new(addr, min_val, max_val, default_loc);
pos += 12;
if (max_val > min_val && (((ut32)max_val - (ut32)min_val) < (UT16_MAX / 4))) {
//caseop = rz_analysis_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)(rz_read_at_be32 (data, pos));
if (pos + 4 >= len) {
// switch is too big can't read further
break;
}
st32 offset = (st32)(rz_read_at_be32(data, pos));
rz_analysis_switch_op_add_case(op->switch_op, addr + pos, cur_case + min_val, addr + offset);
}
} else {
eprintf("Invalid switch boundaries at 0x%" PFMT64x "\n", addr);
}
}
op->size = pos;
return op->size;
}
static ut64 extract_bin_op(ut64 ranal2_op_type) {
ut64 bin_op_val = ranal2_op_type & (RZ_ANALYSIS_JAVA_BIN_OP | 0x80000);
switch (bin_op_val) {
case RZ_ANALYSIS_JAVA_BINOP_XCHG: return RZ_ANALYSIS_OP_TYPE_XCHG;
case RZ_ANALYSIS_JAVA_BINOP_CMP: return RZ_ANALYSIS_OP_TYPE_CMP;
case RZ_ANALYSIS_JAVA_BINOP_ADD: return RZ_ANALYSIS_OP_TYPE_ADD;
case RZ_ANALYSIS_JAVA_BINOP_SUB: return RZ_ANALYSIS_OP_TYPE_SUB;
case RZ_ANALYSIS_JAVA_BINOP_MUL: return RZ_ANALYSIS_OP_TYPE_MUL;
case RZ_ANALYSIS_JAVA_BINOP_DIV: return RZ_ANALYSIS_OP_TYPE_DIV;
case RZ_ANALYSIS_JAVA_BINOP_SHR: return RZ_ANALYSIS_OP_TYPE_SHR;
case RZ_ANALYSIS_JAVA_BINOP_SHL: return RZ_ANALYSIS_OP_TYPE_SHL;
case RZ_ANALYSIS_JAVA_BINOP_SAL: return RZ_ANALYSIS_OP_TYPE_SAL;
case RZ_ANALYSIS_JAVA_BINOP_SAR: return RZ_ANALYSIS_OP_TYPE_SAR;
case RZ_ANALYSIS_JAVA_BINOP_OR: return RZ_ANALYSIS_OP_TYPE_OR;
case RZ_ANALYSIS_JAVA_BINOP_AND: return RZ_ANALYSIS_OP_TYPE_AND;
case RZ_ANALYSIS_JAVA_BINOP_XOR: return RZ_ANALYSIS_OP_TYPE_XOR;
case RZ_ANALYSIS_JAVA_BINOP_NOT: return RZ_ANALYSIS_OP_TYPE_NOT;
case RZ_ANALYSIS_JAVA_BINOP_MOD: return RZ_ANALYSIS_OP_TYPE_MOD;
case RZ_ANALYSIS_JAVA_BINOP_ROR: return RZ_ANALYSIS_OP_TYPE_ROR;
case RZ_ANALYSIS_JAVA_BINOP_ROL: return RZ_ANALYSIS_OP_TYPE_ROL;
default: break;
}
return RZ_ANALYSIS_OP_TYPE_UNK;
}
ut64 extract_unknown_op(ut64 ranal2_op_type) {
if ((ranal2_op_type & RZ_ANALYSIS_JAVA_CODEOP_JMP) == RZ_ANALYSIS_JAVA_CODEOP_JMP) {
return RZ_ANALYSIS_OP_TYPE_UJMP;
}
if ((ranal2_op_type & RZ_ANALYSIS_JAVA_CODEOP_CALL) == RZ_ANALYSIS_JAVA_CODEOP_CALL) {
return RZ_ANALYSIS_OP_TYPE_UCALL;
}
if ((ranal2_op_type & RZ_ANALYSIS_JAVA_LDST_OP_PUSH) == RZ_ANALYSIS_JAVA_LDST_OP_PUSH) {
return RZ_ANALYSIS_OP_TYPE_UPUSH;
}
return RZ_ANALYSIS_OP_TYPE_UNK;
}
static ut64 extract_code_op(ut64 ranal2_op_type) {
ut64 conditional = RZ_ANALYSIS_JAVA_COND_OP & ranal2_op_type ? RZ_ANALYSIS_OP_TYPE_COND : 0;
ut64 code_op_val = ranal2_op_type & (RZ_ANALYSIS_JAVA_CODE_OP | 0x1FF);
switch (code_op_val) {
case RZ_ANALYSIS_JAVA_CODEOP_CALL: return conditional | RZ_ANALYSIS_OP_TYPE_CALL;
case RZ_ANALYSIS_JAVA_CODEOP_JMP: return conditional | RZ_ANALYSIS_OP_TYPE_JMP;
case RZ_ANALYSIS_JAVA_CODEOP_RET: return conditional | RZ_ANALYSIS_OP_TYPE_RET;
case RZ_ANALYSIS_JAVA_CODEOP_LEAVE: return RZ_ANALYSIS_OP_TYPE_LEAVE;
case RZ_ANALYSIS_JAVA_CODEOP_SWI: return RZ_ANALYSIS_OP_TYPE_SWI;
case RZ_ANALYSIS_JAVA_CODEOP_TRAP: return RZ_ANALYSIS_OP_TYPE_TRAP;
case RZ_ANALYSIS_JAVA_CODEOP_SWITCH: return RZ_ANALYSIS_OP_TYPE_SWITCH;
}
return RZ_ANALYSIS_OP_TYPE_UNK;
}
ut64 extract_load_store_op(ut64 ranal2_op_type) {
if ((ranal2_op_type & RZ_ANALYSIS_JAVA_LDST_OP_PUSH) == RZ_ANALYSIS_JAVA_LDST_OP_PUSH) {
return RZ_ANALYSIS_OP_TYPE_PUSH;
}
if ((ranal2_op_type & RZ_ANALYSIS_JAVA_LDST_OP_POP) == RZ_ANALYSIS_JAVA_LDST_OP_POP) {
return RZ_ANALYSIS_OP_TYPE_POP;
}
if ((ranal2_op_type & RZ_ANALYSIS_JAVA_LDST_OP_MOV) == RZ_ANALYSIS_JAVA_LDST_OP_MOV) {
return RZ_ANALYSIS_OP_TYPE_MOV;
}
if ((ranal2_op_type & RZ_ANALYSIS_JAVA_LDST_OP_EFF_ADDR) == RZ_ANALYSIS_JAVA_LDST_OP_EFF_ADDR) {
return RZ_ANALYSIS_OP_TYPE_LEA;
}
return RZ_ANALYSIS_OP_TYPE_UNK;
}
static ut64 map_java_op_to_analysis_op_type(ut64 t) {
ut64 t2 = extract_bin_op(t);
if (t2 != RZ_ANALYSIS_OP_TYPE_UNK) {
return t2;
}
switch (t) {
case RZ_ANALYSIS_JAVA_NULL_OP: return RZ_ANALYSIS_OP_TYPE_NULL;
case RZ_ANALYSIS_JAVA_NOP: return RZ_ANALYSIS_OP_TYPE_NOP;
case RZ_ANALYSIS_JAVA_BINOP_ADD: return RZ_ANALYSIS_OP_TYPE_ADD;
case RZ_ANALYSIS_JAVA_BINOP_AND: return RZ_ANALYSIS_OP_TYPE_AND;
case RZ_ANALYSIS_JAVA_BINOP_MUL: return RZ_ANALYSIS_OP_TYPE_MUL;
case RZ_ANALYSIS_JAVA_BINOP_XOR: return RZ_ANALYSIS_OP_TYPE_XOR;
case RZ_ANALYSIS_JAVA_BINOP_XCHG: return RZ_ANALYSIS_OP_TYPE_MOV;
case RZ_ANALYSIS_JAVA_OBJOP_NEW: return RZ_ANALYSIS_OP_TYPE_UCALL;
case RZ_ANALYSIS_JAVA_OBJOP_SIZE: return RZ_ANALYSIS_OP_TYPE_UCALL;
case RZ_ANALYSIS_JAVA_ILL_OP: return RZ_ANALYSIS_OP_TYPE_ILL;
default:
if (t & RZ_ANALYSIS_JAVA_UNK_OP) {
return extract_unknown_op(t);
}
if (t & RZ_ANALYSIS_JAVA_CODE_OP) {
return extract_code_op(t);
}
if (t & RZ_ANALYSIS_JAVA_REP_OP) {
ut64 ret = map_java_op_to_analysis_op_type(t & ~RZ_ANALYSIS_JAVA_REP_OP);
return RZ_ANALYSIS_OP_TYPE_REP | ret;
}
if (t & (RZ_ANALYSIS_JAVA_LOAD_OP | RZ_ANALYSIS_JAVA_STORE_OP)) {
return extract_load_store_op(t);
}
if (t & RZ_ANALYSIS_JAVA_BIN_OP) {
return extract_bin_op(t);
}
break;
}
if (RZ_ANALYSIS_JAVA_OBJOP_CAST & t) {
return RZ_ANALYSIS_OP_TYPE_MOV;
}
return RZ_ANALYSIS_OP_TYPE_UNK;
}
static int rz_analysis_java_is_op_type_eop(ut64 x) {
ut8 result = (x & RZ_ANALYSIS_JAVA_CODE_OP) ? 1 : 0;
return result &&
((x & RZ_ANALYSIS_JAVA_CODEOP_LEAVE) == RZ_ANALYSIS_JAVA_CODEOP_LEAVE ||
(x & RZ_ANALYSIS_JAVA_CODEOP_RET) == RZ_ANALYSIS_JAVA_CODEOP_RET ||
(x & RZ_ANALYSIS_JAVA_CODEOP_JMP) == RZ_ANALYSIS_JAVA_CODEOP_JMP ||
(x & RZ_ANALYSIS_JAVA_CODEOP_SWITCH) == RZ_ANALYSIS_JAVA_CODEOP_SWITCH);
}
static int java_op(RzAnalysis *analysis, RzAnalysisOp *op, ut64 addr, const ut8 *data, int len, RzAnalysisOpMask mask) {
/* get opcode size */
if (len < 1) {
op->type = RZ_ANALYSIS_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 = RZ_ANALYSIS_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 = map_java_op_to_analysis_op_type(op->type2);
// handle lookup and table switch offsets
if (op_byte == 0xaa || op_byte == 0xab) {
java_switch_op(analysis, 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 = RZ_ANALYSIS_OP_TYPE_CASE
op->eob = 0;
return op->sizes;
}
*/
op->eob = rz_analysis_java_is_op_type_eop(op->type2);
IFDBG {
const char *ot_str = rz_analysis_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 == RZ_ANALYSIS_OP_TYPE_POP) {
op->stackop = RZ_ANALYSIS_STACK_INC;
op->stackptr = 8;
}
op->direction = RZ_ANALYSIS_OP_DIR_EXEC;
if (op->type == RZ_ANALYSIS_OP_TYPE_PUSH) {
op->stackop = RZ_ANALYSIS_STACK_INC;
op->stackptr = -8;
}
if (op->type == RZ_ANALYSIS_OP_TYPE_CJMP) {
op->jump = addr + (short)(rz_read_at_be16(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 == RZ_ANALYSIS_OP_TYPE_JMP) {
op->jump = addr + (short)(rz_read_at_be16(data, 1));
IFDBG eprintf("%s jmpto 0x%04" PFMT64x ".\n", JAVA_OPS[op_byte].name, op->jump);
} else if ((op->type & RZ_ANALYSIS_OP_TYPE_CALL) == RZ_ANALYSIS_OP_TYPE_CALL) {
op->jump = (int)(short)(rz_read_at_be16(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);
}
//rz_java_disasm(addr, data, len, output, outlen);
//IFDBG eprintf ("%s\n", output);
return op->size;
}
static void java_update_analysis_types(RzAnalysis *analysis, RzBinJavaObj *bin_obj) {
Sdb *D = analysis->sdb_types;
if (D && bin_obj) {
RzListIter *iter;
char *str;
RzList *the_list = rz_bin_java_extract_all_bin_type_values(bin_obj);
if (the_list) {
rz_list_foreach (the_list, iter, str) {
IFDBG eprintf("Adding type: %s to known types.\n", str);
if (str) {
sdb_set(D, str, "type", 0);
}
}
}
rz_list_free(the_list);
}
}
static int java_cmd_ext(RzAnalysis *analysis, const char *input) {
RzBinJavaObj *obj = (RzBinJavaObj *)get_java_bin_obj(analysis);
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
rz_java_new_method();
break;
case 'u':
switch (*(input + 1)) {
case 't': {
java_update_analysis_types(analysis, obj);
return true;
}
default: break;
}
break;
case 's':
switch (*(input + 1)) {
//case 'e': return java_resolve_cp_idx_b64 (analysis, input+2);
default: break;
}
break;
default: eprintf("Command not supported"); break;
}
return 0;
}
RzAnalysisPlugin rz_analysis_plugin_java = {
.name = "java",
.desc = "Java bytecode analysis plugin",
.license = "Apache",
.arch = "java",
.bits = 32,
.op = &java_op,
.cmd_ext = java_cmd_ext,
0
};
#ifndef RZ_PLUGIN_INCORE
RZ_API RzLibStruct rizin_plugin = {
.type = RZ_LIB_TYPE_ANALYSIS,
.data = &rz_analysis_plugin_java,
.version = RZ_VERSION
};
#endif