radare2/shlr/java/code.c

289 lines
6.9 KiB
C

/* radare - LGPL - Copyright 2007-2026 - pancake */
#include <r_anal.h>
#include "ops.h"
#include "code.h"
#include "class.h"
#ifndef R_API
#define R_API
#endif
typedef struct current_table_switch_t {
ut64 addr;
int def_jmp;
int min_val;
int max_val;
int sz;
} CurrentTableSwitch;
static CurrentTableSwitch enter_switch_op(ut64 addr, const ut8 *bytes, int len) {
CurrentTableSwitch sw = { 0 };
if (len >= 16) {
sw.addr = addr;
sw.def_jmp = (UINT (bytes, 4));
sw.min_val = (UINT (bytes, 8));
sw.max_val = (UINT (bytes, 12));
sw.sz = 16;
}
return sw;
}
static bool isRelative(ut32 type) {
return (type & (ut32)R_ANAL_JAVA_CODEOP_CJMP) || (type & (ut32)R_ANAL_JAVA_CODEOP_JMP);
}
R_API int java_print_opcode(RBinJavaObj *obj, ut64 addr, int idx, const ut8 *bytes, int len, char *output, int outlen) {
if (idx < 0 || idx >= JAVA_OPS_COUNT) {
return -1;
}
char *arg = NULL;
ut32 val_one = 0;
ut32 val_two = 0;
ut8 op_byte = JAVA_OPS[idx].byte;
switch (op_byte) {
case 0x10: // "bipush"
if (len > 1) {
snprintf(output, outlen, "%s %d", JAVA_OPS[idx].name, (st8)bytes[1]);
output[outlen - 1] = 0;
return JAVA_OPS[idx].size;
}
return -1;
case 0x11:
if (len > 2) {
snprintf (output, outlen, "%s %d", JAVA_OPS[idx].name, (int)USHORT (bytes, 1));
output[outlen - 1] = 0;
return JAVA_OPS[idx].size;
}
return -1;
case 0x15: // "iload"
case 0x16: // "lload"
case 0x17: // "fload"
case 0x18: // "dload"
case 0x19: // "aload"
case 0x37: // "lstore"
case 0x38: // "fstore"
case 0x39: // "dstore"
case 0x3a: // "astore"
case 0xbc: // "newarray"
case 0xa9: // ret <var-num>
if (len > 1) {
snprintf (output, outlen, "%s %d", JAVA_OPS[idx].name, bytes[1]);
output[outlen - 1] = 0;
return JAVA_OPS[idx].size;
}
return 0;
case 0x12: // ldc
if (len > 1) {
arg = r_bin_java_resolve_without_space (obj, (ut16)bytes[1]);
if (arg) {
snprintf (output, outlen, "%s %s", JAVA_OPS[idx].name, arg);
free (arg);
} else {
const int num = (len > 2)? USHORT (bytes, 1): bytes[1];
snprintf (output, outlen, "%s #%d", JAVA_OPS[idx].name, num);
}
output[outlen - 1] = 0;
return JAVA_OPS[idx].size;
}
return -1;
case 0x13:
case 0x14:
if (len > 2) {
arg = r_bin_java_resolve_without_space (obj, (int)USHORT (bytes, 1));
if (arg) {
snprintf (output, outlen, "%s %s", JAVA_OPS[idx].name, arg);
free (arg);
} else {
snprintf (output, outlen, "%s #%d", JAVA_OPS[idx].name, USHORT (bytes, 1));
}
output[outlen - 1] = 0;
return JAVA_OPS[idx].size;
}
return -1;
case 0x84: // iinc
if (len > 2) {
val_one = (ut32)bytes[1];
val_two = (ut32)bytes[2];
snprintf (output, outlen, "%s %d %d", JAVA_OPS[idx].name, val_one, val_two);
output[outlen - 1] = 0;
return JAVA_OPS[idx].size;
}
return -1;
case 0x99: // ifeq
case 0x9a: // ifne
case 0x9b: // iflt
case 0x9c: // ifge
case 0x9d: // ifgt
case 0x9e: // ifle
case 0x9f: // if_icmpeq
case 0xa0: // if_icmpne
case 0xa1: // if_icmplt
case 0xa2: // if_icmpge
case 0xa3: // if_icmpgt
case 0xa4: // if_icmple
case 0xa5: // if_acmpne
case 0xa6: // if_acmpne
case 0xa7: // goto
case 0xa8: // jsr
if (len > 2) {
const short delta = USHORT (bytes, 1);
snprintf (output, outlen, "%s 0x%04" PFMT64x, JAVA_OPS[idx].name, addr + delta);
output[outlen - 1] = 0;
return JAVA_OPS[idx].size;
}
return -1;
case 0xab: // tableswitch
case 0xaa: // tableswitch
{
CurrentTableSwitch sw = enter_switch_op (addr, bytes, len);
snprintf (output, outlen, "%s default: 0x%04" PFMT64x, JAVA_OPS[idx].name, (ut64) (sw.def_jmp + sw.addr));
return sw.sz;
}
case 0xb6: // invokevirtual
case 0xb7: // invokespecial
case 0xb8: // invokestatic
case 0xb9: // invokeinterface
case 0xba: // invokedynamic
if (len > 2) {
arg = r_bin_java_resolve_without_space (obj, (int)USHORT (bytes, 1));
if (arg) {
snprintf (output, outlen, "%s %s", JAVA_OPS[idx].name, arg);
free (arg);
} else {
snprintf (output, outlen, "%s #%d", JAVA_OPS[idx].name, USHORT (bytes, 1));
}
output[outlen - 1] = 0;
return JAVA_OPS[idx].size;
}
return -1;
case 0xbb: // new
case 0xbd: // anewarray
case 0xc0: // checkcast
case 0xc1: // instance of
if (len > 2) {
arg = r_bin_java_resolve_without_space (obj, (int)USHORT (bytes, 1));
if (arg) {
snprintf (output, outlen, "%s %s", JAVA_OPS[idx].name, arg);
free (arg);
} else {
snprintf (output, outlen, "%s #%d", JAVA_OPS[idx].name, USHORT (bytes, 1));
}
output[outlen - 1] = 0;
return JAVA_OPS[idx].size;
}
return -1;
case 0xb2: // getstatic
case 0xb3: // putstatic
case 0xb4: // getfield
case 0xb5: // putfield
if (len > 2) {
arg = r_bin_java_resolve_with_space (obj, (int)USHORT (bytes, 1));
if (arg) {
snprintf (output, outlen, "%s %s", JAVA_OPS[idx].name, arg);
free (arg);
} else {
snprintf (output, outlen, "%s #%d", JAVA_OPS[idx].name, USHORT (bytes, 1));
}
output[outlen - 1] = 0;
return JAVA_OPS[idx].size;
}
return -1;
}
/* process arguments */
if (JAVA_OPS[idx].size > len) {
snprintf (output, outlen, "truncated");
return -1;
}
switch (JAVA_OPS[idx].size) {
case 1:
snprintf (output, outlen, "%s", JAVA_OPS[idx].name);
break;
case 2:
snprintf (output, outlen, "%s %d", JAVA_OPS[idx].name, bytes[1]);
break;
case 3:
snprintf (output, outlen, "%s 0x%04x 0x%04x", JAVA_OPS[idx].name, bytes[0], bytes[1]);
break;
case 5:
snprintf (output, outlen, "%s %d", JAVA_OPS[idx].name, bytes[1]);
break;
}
return JAVA_OPS[idx].size;
}
R_API int r_java_disasm(RBinJavaObj *obj, ut64 addr, const ut8 *bytes, int len, char *output, int outlen) {
R_RETURN_VAL_IF_FAIL (bytes && output && outlen > 0, -1);
if (len > 0) {
return java_print_opcode (obj, addr, bytes[0], bytes, len, output, outlen);
}
return -1;
}
static int parseJavaArgs(char *str, ut64 *args, int args_sz) {
int i, nargs = -1;
char *q, *p = strchr (str, ' ');
if (p) {
*p++ = 0;
nargs++;
for (i = 0; i < args_sz; i++) {
nargs++;
q = strchr (p, ' ');
if (q) {
*q++ = 0;
}
args[i] = r_num_math (NULL, p);
if (q) {
p = q;
} else {
break;
}
}
}
return nargs;
}
R_API int r_java_assemble(ut64 addr, ut8 *bytes, const char *string) {
char *name = strdup (string);
ut64 args[4] = { 0 };
int i, nargs = parseJavaArgs (name, args, 4);
int a = args[0];
int b = args[1];
int c = args[2];
int d = args[3];
for (i = 0; JAVA_OPS[i].name != NULL; i++) {
if (!strcmp (name, JAVA_OPS[i].name)) {
bytes[0] = JAVA_OPS[i].byte;
switch (JAVA_OPS[i].size) {
case 2:
bytes[1] = a;
break;
case 3:
if (nargs == 2) {
bytes[1] = a;
bytes[2] = b;
} else {
if (isRelative (JAVA_OPS[i].op_type)) {
// relative jmp
a -= addr;
}
bytes[1] = (a >> 8) & 0xff;
bytes[2] = a & 0xff;
}
break;
case 5:
bytes[1] = a;
bytes[2] = b;
bytes[3] = c;
bytes[4] = d;
break;
}
free (name);
return JAVA_OPS[i].size;
}
}
free (name);
return 0;
}