From 34e8f7e76811e434e5acf90055bea9e9db41cf7c Mon Sep 17 00:00:00 2001 From: Giovanni <561184+wargio@users.noreply.github.com> Date: Thu, 25 Mar 2021 10:52:36 +0100 Subject: [PATCH] Java assembler (#903) --- librz/asm/arch/java/assembler.c | 488 ++++++++++++++++++++++++++++++++ librz/asm/arch/java/assembler.h | 12 + librz/asm/arch/java/bytecode.h | 2 +- librz/asm/arch/java/jvm.c | 8 +- librz/asm/meson.build | 1 + librz/asm/p/asm_java.c | 15 + test/db/asm/java | 275 +++++++++++++++--- 7 files changed, 761 insertions(+), 40 deletions(-) create mode 100644 librz/asm/arch/java/assembler.c create mode 100644 librz/asm/arch/java/assembler.h diff --git a/librz/asm/arch/java/assembler.c b/librz/asm/arch/java/assembler.c new file mode 100644 index 0000000000..f6969b9b85 --- /dev/null +++ b/librz/asm/arch/java/assembler.c @@ -0,0 +1,488 @@ +// SPDX-FileCopyrightText: 2021 deroad +// SPDX-License-Identifier: LGPL-3.0-only + +#include "assembler.h" +#include "const.h" + +#define return_error_if_size_lt(a, b) \ + do { \ + if (a < b) { \ + RZ_LOG_ERROR("[!] java_assembler: no enough output buffer (requires %d bytes).\n", b); \ + return false; \ + } \ + } while (0) + +#define return_error_if_empty_input(a, b) \ + do { \ + if (RZ_STR_ISEMPTY(a) || b < 1) { \ + RZ_LOG_ERROR("[!] java_assembler: the input is empty.\n"); \ + return false; \ + } \ + } while (0) + +typedef bool (*AsmEncoder)(ut8 bytecode, const char *input, st32 input_size, ut8 *output, st32 output_size, ut64 pc, st32 *written); + +typedef struct _jasm { + const char *opcode; + st32 length; + ut8 bytecode; + AsmEncoder encode; +} JavaAsm; + +static bool encode_not_implemented(ut8 bytecode, const char *input, st32 input_size, ut8 *output, st32 output_size, ut64 pc, st32 *written) { + RZ_LOG_ERROR("[!] java_assembler: not implemented.\n"); + return false; +} + +static bool encode_only_bcode(ut8 bytecode, const char *input, st32 input_size, ut8 *output, st32 output_size, ut64 pc, st32 *written) { + return_error_if_size_lt(output_size, 1); + + *written = 1; + output[0] = bytecode; + return true; +} + +static bool encode_st8(ut8 bytecode, const char *input, st32 input_size, ut8 *output, st32 output_size, ut64 pc, st32 *written) { + return_error_if_size_lt(output_size, 2); + return_error_if_empty_input(input, input_size); + + if (!rz_is_valid_input_num_value(NULL, input)) { + RZ_LOG_ERROR("[!] java_assembler: '%s' is not a valid number between %d and %d (inclusive).\n", input, INT8_MIN, INT8_MAX); + return false; + } + + *written = 2; + output[0] = bytecode; + ((st8 *)output)[1] = (st8)strtoll(input, NULL, 0); + return true; +} + +static bool encode_ut8(ut8 bytecode, const char *input, st32 input_size, ut8 *output, st32 output_size, ut64 pc, st32 *written) { + return_error_if_size_lt(output_size, 2); + return_error_if_empty_input(input, input_size); + + if (!rz_is_valid_input_num_value(NULL, input)) { + RZ_LOG_ERROR("[!] java_assembler: '%s' is not a valid number between %d and %d (inclusive).\n", input, INT8_MIN, INT8_MAX); + return false; + } + + *written = 2; + output[0] = bytecode; + output[1] = (ut8)strtoll(input, NULL, 0); + return true; +} + +static bool encode_addr32(ut8 bytecode, const char *input, st32 input_size, ut8 *output, st32 output_size, ut64 pc, st32 *written) { + return_error_if_size_lt(output_size, 5); + return_error_if_empty_input(input, input_size); + + if (!rz_is_valid_input_num_value(NULL, input)) { + RZ_LOG_ERROR("[!] java_assembler: '%s' is not a valid number between %d and %d (inclusive).\n", input, INT16_MIN, INT16_MAX); + return false; + } + + *written = 5; + output[0] = bytecode; + st64 n = strtoll(input, NULL, 0); + st32 addr = n - pc; + rz_write_be32(output + 1, addr); + return true; +} + +static bool encode_addr16(ut8 bytecode, const char *input, st32 input_size, ut8 *output, st32 output_size, ut64 pc, st32 *written) { + return_error_if_size_lt(output_size, 3); + return_error_if_empty_input(input, input_size); + + if (!rz_is_valid_input_num_value(NULL, input)) { + RZ_LOG_ERROR("[!] java_assembler: '%s' is not a valid number between %d and %d (inclusive).\n", input, INT16_MIN, INT16_MAX); + return false; + } + + *written = 3; + output[0] = bytecode; + st64 n = strtoll(input, NULL, 0); + st16 addr = n - pc; + rz_write_be16(output + 1, addr); + return true; +} + +static bool encode_st16(ut8 bytecode, const char *input, st32 input_size, ut8 *output, st32 output_size, ut64 pc, st32 *written) { + return_error_if_size_lt(output_size, 3); + return_error_if_empty_input(input, input_size); + + if (!rz_is_valid_input_num_value(NULL, input)) { + RZ_LOG_ERROR("[!] java_assembler: '%s' is not a valid number between %d and %d (inclusive).\n", input, INT16_MIN, INT16_MAX); + return false; + } + + *written = 3; + output[0] = bytecode; + st16 n = strtoll(input, NULL, 0); + rz_write_be16(output + 1, n); + return true; +} + +static bool encode_const_pool8(ut8 bytecode, const char *input, st32 input_size, ut8 *output, st32 output_size, ut64 pc, st32 *written) { + return_error_if_size_lt(output_size, 2); + return_error_if_empty_input(input, input_size); + + ut32 cpool_len = strlen(JAVA_ASM_CONSTANT_POOL_STR); + if (!strncmp(input, JAVA_ASM_CONSTANT_POOL_STR, cpool_len)) { + input += cpool_len; + } + + if (!rz_is_valid_input_num_value(NULL, input)) { + RZ_LOG_ERROR("[!] java_assembler: '%s' is not a valid number between 0 and %u (inclusive).\n", input, UINT8_MAX); + return false; + } + + *written = 2; + output[0] = bytecode; + output[1] = (ut8)strtoll(input, NULL, 0); + return true; +} + +static bool encode_const_pool16(ut8 bytecode, const char *input, st32 input_size, ut8 *output, st32 output_size, ut64 pc, st32 *written) { + return_error_if_size_lt(output_size, 3); + return_error_if_empty_input(input, input_size); + + ut32 cpool_len = strlen(JAVA_ASM_CONSTANT_POOL_STR); + if (!strncmp(input, JAVA_ASM_CONSTANT_POOL_STR, cpool_len)) { + input += cpool_len; + } + + if (!rz_is_valid_input_num_value(NULL, input)) { + RZ_LOG_ERROR("[!] java_assembler: '%s' is not a valid number between 0 and %u (inclusive).\n", input, UINT16_MAX); + return false; + } + + *written = 3; + output[0] = bytecode; + ut16 n = (ut16)strtoll(input, NULL, 0); + rz_write_be16(output + 1, n); + return true; +} + +static bool encode_const_pool16_ut8(ut8 bytecode, const char *input, st32 input_size, ut8 *output, st32 output_size, ut64 pc, st32 *written) { + return_error_if_size_lt(output_size, 4); + return_error_if_empty_input(input, input_size); + + ut32 cpool_len = strlen(JAVA_ASM_CONSTANT_POOL_STR); + if (!strncmp(input, JAVA_ASM_CONSTANT_POOL_STR, cpool_len)) { + input += cpool_len; + } + + if (!rz_is_valid_input_num_value(NULL, input)) { + RZ_LOG_ERROR("[!] java_assembler: '%s' is not a valid number between 0 and %u (inclusive).\n", input, UINT16_MAX); + return false; + } + + const char *next = NULL; + char *tmp = NULL; + ut16 cpool = (ut16)strtoll(input, &tmp, 0); + if (!tmp || tmp == (input + input_size) || !(next = rz_str_trim_head_ro(tmp))) { + RZ_LOG_ERROR("[!] java_assembler: '%s' is not a valid number between 0 and %u (inclusive).\n", tmp, UINT8_MAX); + } + + if (!rz_is_valid_input_num_value(NULL, next)) { + RZ_LOG_ERROR("[!] java_assembler: '%s' is not a valid number between 0 and %u (inclusive).\n", input, UINT8_MAX); + return false; + } + ut8 num = (ut8)strtoll(next, NULL, 0); + + *written = 4; + output[0] = bytecode; + rz_write_be16(output + 1, cpool); + output[3] = num; + return true; +} + +static bool encode_ut8x2(ut8 bytecode, const char *input, st32 input_size, ut8 *output, st32 output_size, ut64 pc, st32 *written) { + return_error_if_size_lt(output_size, 3); + return_error_if_empty_input(input, input_size); + + if (!rz_is_valid_input_num_value(NULL, input)) { + RZ_LOG_ERROR("[!] java_assembler: '%s' is not a valid number between 0 and %u (inclusive).\n", input, UINT8_MAX); + return false; + } + + const char *next = NULL; + char *tmp = NULL; + ut16 arg0 = (ut16)strtoll(input, &tmp, 0); + if (!tmp || tmp == (input + input_size) || !(next = rz_str_trim_head_ro(tmp))) { + RZ_LOG_ERROR("[!] java_assembler: '%s' is not a valid number between 0 and %u (inclusive).\n", tmp, UINT8_MAX); + } + + if (!rz_is_valid_input_num_value(NULL, next)) { + RZ_LOG_ERROR("[!] java_assembler: '%s' is not a valid number between 0 and %u (inclusive).\n", input, UINT8_MAX); + return false; + } + ut8 arg1 = (ut8)strtoll(next, NULL, 0); + + *written = 3; + output[0] = bytecode; + output[1] = arg0; + output[2] = arg1; + return true; +} + +static bool encode_atype(ut8 bytecode, const char *input, st32 input_size, ut8 *output, st32 output_size, ut64 pc, st32 *written) { + return_error_if_size_lt(output_size, 2); + return_error_if_empty_input(input, input_size); + + ut8 byte = 0; + /* bool 4, char 5, float 6, double 7, byte 8, short 9, int 10, long 11 */ + if (!strncmp(input, "bool", strlen("bool"))) { + byte = 4; + } else if (!strncmp(input, "char", strlen("char"))) { + byte = 5; + } else if (!strncmp(input, "float", strlen("float"))) { + byte = 6; + } else if (!strncmp(input, "double", strlen("double"))) { + byte = 7; + } else if (!strncmp(input, "byte", strlen("byte"))) { + byte = 8; + } else if (!strncmp(input, "short", strlen("short"))) { + byte = 9; + } else if (!strncmp(input, "int", strlen("int"))) { + byte = 10; + } else if (!strncmp(input, "long", strlen("long"))) { + byte = 11; + } else { + RZ_LOG_ERROR("[!] java_assembler: '%s' is not a valid native type (accepted: bool, char, float, double, byte, short, int, long).\n", input); + return false; + } + + *written = 2; + output[0] = bytecode; + output[1] = byte; + return true; +} + +#define NS(x) x, (sizeof(x) - 1) +static const JavaAsm instructions[205] = { + { NS("wide") /* */, BYTECODE_C4_WIDE /* */, encode_only_bcode }, + { NS("tableswitch") /* */, BYTECODE_AA_TABLESWITCH /* */, encode_not_implemented }, + { NS("swap") /* */, BYTECODE_5F_SWAP /* */, encode_only_bcode }, + { NS("sipush") /* */, BYTECODE_11_SIPUSH /* */, encode_st16 }, + { NS("sastore") /* */, BYTECODE_56_SASTORE /* */, encode_only_bcode }, + { NS("saload") /* */, BYTECODE_35_SALOAD /* */, encode_only_bcode }, + { NS("return") /* */, BYTECODE_B1_RETURN /* */, encode_only_bcode }, + { NS("ret") /* */, BYTECODE_A9_RET /* */, encode_ut8 }, + { NS("putstatic") /* */, BYTECODE_B3_PUTSTATIC /* */, encode_const_pool16 }, + { NS("putfield") /* */, BYTECODE_B5_PUTFIELD /* */, encode_const_pool16 }, + { NS("pop2") /* */, BYTECODE_58_POP2 /* */, encode_only_bcode }, + { NS("pop") /* */, BYTECODE_57_POP /* */, encode_only_bcode }, + { NS("nop") /* */, BYTECODE_00_NOP /* */, encode_only_bcode }, + { NS("newarray") /* */, BYTECODE_BC_NEWARRAY /* */, encode_atype }, + { NS("new") /* */, BYTECODE_BB_NEW /* */, encode_const_pool16 }, + { NS("multianewarray") /* */, BYTECODE_C5_MULTIANEWARRAY /* */, encode_const_pool16_ut8 }, + { NS("monitorexit") /* */, BYTECODE_C3_MONITOREXIT /* */, encode_only_bcode }, + { NS("monitorenter") /* */, BYTECODE_C2_MONITORENTER /* */, encode_only_bcode }, + { NS("lxor") /* */, BYTECODE_83_LXOR /* */, encode_only_bcode }, + { NS("lushr") /* */, BYTECODE_7D_LUSHR /* */, encode_only_bcode }, + { NS("lsub") /* */, BYTECODE_65_LSUB /* */, encode_only_bcode }, + { NS("lstore_3") /* */, BYTECODE_42_LSTORE_3 /* */, encode_only_bcode }, + { NS("lstore_2") /* */, BYTECODE_41_LSTORE_2 /* */, encode_only_bcode }, + { NS("lstore_1") /* */, BYTECODE_40_LSTORE_1 /* */, encode_only_bcode }, + { NS("lstore_0") /* */, BYTECODE_3F_LSTORE_0 /* */, encode_only_bcode }, + { NS("lstore") /* */, BYTECODE_37_LSTORE /* */, encode_ut8 }, + { NS("lshr") /* */, BYTECODE_7B_LSHR /* */, encode_only_bcode }, + { NS("lshl") /* */, BYTECODE_79_LSHL /* */, encode_only_bcode }, + { NS("lreturn") /* */, BYTECODE_AD_LRETURN /* */, encode_only_bcode }, + { NS("lrem") /* */, BYTECODE_71_LREM /* */, encode_only_bcode }, + { NS("lor") /* */, BYTECODE_81_LOR /* */, encode_only_bcode }, + { NS("lookupswitch") /* */, BYTECODE_AB_LOOKUPSWITCH /* */, encode_not_implemented }, + { NS("lneg") /* */, BYTECODE_75_LNEG /* */, encode_only_bcode }, + { NS("lmul") /* */, BYTECODE_69_LMUL /* */, encode_only_bcode }, + { NS("lload_3") /* */, BYTECODE_21_LLOAD_3 /* */, encode_only_bcode }, + { NS("lload_2") /* */, BYTECODE_20_LLOAD_2 /* */, encode_only_bcode }, + { NS("lload_1") /* */, BYTECODE_1F_LLOAD_1 /* */, encode_only_bcode }, + { NS("lload_0") /* */, BYTECODE_1E_LLOAD_0 /* */, encode_only_bcode }, + { NS("lload") /* */, BYTECODE_16_LLOAD /* */, encode_ut8 }, + { NS("ldiv") /* */, BYTECODE_6D_LDIV /* */, encode_only_bcode }, + { NS("ldc_w") /* */, BYTECODE_13_LDC_W /* */, encode_const_pool16 }, + { NS("ldc2_w") /* */, BYTECODE_14_LDC2_W /* */, encode_const_pool16 }, + { NS("ldc") /* */, BYTECODE_12_LDC /* */, encode_const_pool8 }, + { NS("lconst_1") /* */, BYTECODE_0A_LCONST_1 /* */, encode_only_bcode }, + { NS("lconst_0") /* */, BYTECODE_09_LCONST_0 /* */, encode_only_bcode }, + { NS("lcmp") /* */, BYTECODE_94_LCMP /* */, encode_only_bcode }, + { NS("lastore") /* */, BYTECODE_50_LASTORE /* */, encode_only_bcode }, + { NS("land") /* */, BYTECODE_7F_LAND /* */, encode_only_bcode }, + { NS("laload") /* */, BYTECODE_2F_LALOAD /* */, encode_only_bcode }, + { NS("ladd") /* */, BYTECODE_61_LADD /* */, encode_only_bcode }, + { NS("l2i") /* */, BYTECODE_88_L2I /* */, encode_only_bcode }, + { NS("l2f") /* */, BYTECODE_89_L2F /* */, encode_only_bcode }, + { NS("l2d") /* */, BYTECODE_8A_L2D /* */, encode_only_bcode }, + { NS("jsr_w") /* */, BYTECODE_C9_JSR_W /* */, encode_addr32 }, + { NS("jsr") /* */, BYTECODE_A8_JSR /* */, encode_addr16 }, + { NS("ixor") /* */, BYTECODE_82_IXOR /* */, encode_only_bcode }, + { NS("iushr") /* */, BYTECODE_7C_IUSHR /* */, encode_only_bcode }, + { NS("isub") /* */, BYTECODE_64_ISUB /* */, encode_only_bcode }, + { NS("istore_3") /* */, BYTECODE_3E_ISTORE_3 /* */, encode_only_bcode }, + { NS("istore_2") /* */, BYTECODE_3D_ISTORE_2 /* */, encode_only_bcode }, + { NS("istore_1") /* */, BYTECODE_3C_ISTORE_1 /* */, encode_only_bcode }, + { NS("istore_0") /* */, BYTECODE_3B_ISTORE_0 /* */, encode_only_bcode }, + { NS("istore") /* */, BYTECODE_36_ISTORE /* */, encode_ut8 }, + { NS("ishr") /* */, BYTECODE_7A_ISHR /* */, encode_only_bcode }, + { NS("ishl") /* */, BYTECODE_78_ISHL /* */, encode_only_bcode }, + { NS("ireturn") /* */, BYTECODE_AC_IRETURN /* */, encode_only_bcode }, + { NS("irem") /* */, BYTECODE_70_IREM /* */, encode_only_bcode }, + { NS("ior") /* */, BYTECODE_80_IOR /* */, encode_only_bcode }, + { NS("invokevirtual") /* */, BYTECODE_B6_INVOKEVIRTUAL /* */, encode_const_pool16 }, + { NS("invokestatic") /* */, BYTECODE_B8_INVOKESTATIC /* */, encode_const_pool16 }, + { NS("invokespecial") /* */, BYTECODE_B7_INVOKESPECIAL /* */, encode_const_pool16 }, + { NS("invokeinterface") /**/, BYTECODE_B9_INVOKEINTERFACE /**/, encode_const_pool16_ut8 }, + { NS("invokedynamic") /* */, BYTECODE_BA_INVOKEDYNAMIC /* */, encode_const_pool16_ut8 }, + { NS("instanceof") /* */, BYTECODE_C1_INSTANCEOF /* */, encode_const_pool16 }, + { NS("ineg") /* */, BYTECODE_74_INEG /* */, encode_only_bcode }, + { NS("imul") /* */, BYTECODE_68_IMUL /* */, encode_only_bcode }, + { NS("impdep2") /* */, BYTECODE_FF_IMPDEP2 /* */, encode_only_bcode }, + { NS("impdep1") /* */, BYTECODE_FE_IMPDEP1 /* */, encode_only_bcode }, + { NS("iload_3") /* */, BYTECODE_1D_ILOAD_3 /* */, encode_only_bcode }, + { NS("iload_2") /* */, BYTECODE_1C_ILOAD_2 /* */, encode_only_bcode }, + { NS("iload_1") /* */, BYTECODE_1B_ILOAD_1 /* */, encode_only_bcode }, + { NS("iload_0") /* */, BYTECODE_1A_ILOAD_0 /* */, encode_only_bcode }, + { NS("iload") /* */, BYTECODE_15_ILOAD /* */, encode_ut8 }, + { NS("iinc") /* */, BYTECODE_84_IINC /* */, encode_ut8x2 }, + { NS("ifnull") /* */, BYTECODE_C6_IFNULL /* */, encode_addr16 }, + { NS("ifnonnull") /* */, BYTECODE_C7_IFNONNULL /* */, encode_addr16 }, + { NS("ifne") /* */, BYTECODE_9A_IFNE /* */, encode_addr16 }, + { NS("iflt") /* */, BYTECODE_9B_IFLT /* */, encode_addr16 }, + { NS("ifle") /* */, BYTECODE_9E_IFLE /* */, encode_addr16 }, + { NS("ifgt") /* */, BYTECODE_9D_IFGT /* */, encode_addr16 }, + { NS("ifge") /* */, BYTECODE_9C_IFGE /* */, encode_addr16 }, + { NS("ifeq") /* */, BYTECODE_99_IFEQ /* */, encode_addr16 }, + { NS("if_icmpne") /* */, BYTECODE_A0_IF_ICMPNE /* */, encode_addr16 }, + { NS("if_icmplt") /* */, BYTECODE_A1_IF_ICMPLT /* */, encode_addr16 }, + { NS("if_icmple") /* */, BYTECODE_A4_IF_ICMPLE /* */, encode_addr16 }, + { NS("if_icmpgt") /* */, BYTECODE_A3_IF_ICMPGT /* */, encode_addr16 }, + { NS("if_icmpge") /* */, BYTECODE_A2_IF_ICMPGE /* */, encode_addr16 }, + { NS("if_icmpeq") /* */, BYTECODE_9F_IF_ICMPEQ /* */, encode_addr16 }, + { NS("if_acmpne") /* */, BYTECODE_A6_IF_ACMPNE /* */, encode_addr16 }, + { NS("if_acmpeq") /* */, BYTECODE_A5_IF_ACMPEQ /* */, encode_addr16 }, + { NS("idiv") /* */, BYTECODE_6C_IDIV /* */, encode_only_bcode }, + { NS("iconst_m1") /* */, BYTECODE_02_ICONST_M1 /* */, encode_only_bcode }, + { NS("iconst_5") /* */, BYTECODE_08_ICONST_5 /* */, encode_only_bcode }, + { NS("iconst_4") /* */, BYTECODE_07_ICONST_4 /* */, encode_only_bcode }, + { NS("iconst_3") /* */, BYTECODE_06_ICONST_3 /* */, encode_only_bcode }, + { NS("iconst_2") /* */, BYTECODE_05_ICONST_2 /* */, encode_only_bcode }, + { NS("iconst_1") /* */, BYTECODE_04_ICONST_1 /* */, encode_only_bcode }, + { NS("iconst_0") /* */, BYTECODE_03_ICONST_0 /* */, encode_only_bcode }, + { NS("iastore") /* */, BYTECODE_4F_IASTORE /* */, encode_only_bcode }, + { NS("iand") /* */, BYTECODE_7E_IAND /* */, encode_only_bcode }, + { NS("iaload") /* */, BYTECODE_2E_IALOAD /* */, encode_only_bcode }, + { NS("iadd") /* */, BYTECODE_60_IADD /* */, encode_only_bcode }, + { NS("i2s") /* */, BYTECODE_93_I2S /* */, encode_only_bcode }, + { NS("i2l") /* */, BYTECODE_85_I2L /* */, encode_only_bcode }, + { NS("i2f") /* */, BYTECODE_86_I2F /* */, encode_only_bcode }, + { NS("i2d") /* */, BYTECODE_87_I2D /* */, encode_only_bcode }, + { NS("i2c") /* */, BYTECODE_92_I2C /* */, encode_only_bcode }, + { NS("i2b") /* */, BYTECODE_91_I2B /* */, encode_only_bcode }, + { NS("goto_w") /* */, BYTECODE_C8_GOTO_W /* */, encode_addr32 }, + { NS("goto") /* */, BYTECODE_A7_GOTO /* */, encode_addr16 }, + { NS("getstatic") /* */, BYTECODE_B2_GETSTATIC /* */, encode_const_pool16 }, + { NS("getfield") /* */, BYTECODE_B4_GETFIELD /* */, encode_const_pool16 }, + { NS("fsub") /* */, BYTECODE_66_FSUB /* */, encode_only_bcode }, + { NS("fstore_3") /* */, BYTECODE_46_FSTORE_3 /* */, encode_only_bcode }, + { NS("fstore_2") /* */, BYTECODE_45_FSTORE_2 /* */, encode_only_bcode }, + { NS("fstore_1") /* */, BYTECODE_44_FSTORE_1 /* */, encode_only_bcode }, + { NS("fstore_0") /* */, BYTECODE_43_FSTORE_0 /* */, encode_only_bcode }, + { NS("fstore") /* */, BYTECODE_38_FSTORE /* */, encode_ut8 }, + { NS("freturn") /* */, BYTECODE_AE_FRETURN /* */, encode_only_bcode }, + { NS("frem") /* */, BYTECODE_72_FREM /* */, encode_only_bcode }, + { NS("fneg") /* */, BYTECODE_76_FNEG /* */, encode_only_bcode }, + { NS("fmul") /* */, BYTECODE_6A_FMUL /* */, encode_only_bcode }, + { NS("fload_3") /* */, BYTECODE_25_FLOAD_3 /* */, encode_only_bcode }, + { NS("fload_2") /* */, BYTECODE_24_FLOAD_2 /* */, encode_only_bcode }, + { NS("fload_1") /* */, BYTECODE_23_FLOAD_1 /* */, encode_only_bcode }, + { NS("fload_0") /* */, BYTECODE_22_FLOAD_0 /* */, encode_only_bcode }, + { NS("fload") /* */, BYTECODE_17_FLOAD /* */, encode_ut8 }, + { NS("fdiv") /* */, BYTECODE_6E_FDIV /* */, encode_only_bcode }, + { NS("fconst_2") /* */, BYTECODE_0D_FCONST_2 /* */, encode_only_bcode }, + { NS("fconst_1") /* */, BYTECODE_0C_FCONST_1 /* */, encode_only_bcode }, + { NS("fconst_0") /* */, BYTECODE_0B_FCONST_0 /* */, encode_only_bcode }, + { NS("fcmpl") /* */, BYTECODE_95_FCMPL /* */, encode_only_bcode }, + { NS("fcmpg") /* */, BYTECODE_96_FCMPG /* */, encode_only_bcode }, + { NS("fastore") /* */, BYTECODE_51_FASTORE /* */, encode_only_bcode }, + { NS("faload") /* */, BYTECODE_30_FALOAD /* */, encode_only_bcode }, + { NS("fadd") /* */, BYTECODE_62_FADD /* */, encode_only_bcode }, + { NS("f2l") /* */, BYTECODE_8C_F2L /* */, encode_only_bcode }, + { NS("f2i") /* */, BYTECODE_8B_F2I /* */, encode_only_bcode }, + { NS("f2d") /* */, BYTECODE_8D_F2D /* */, encode_only_bcode }, + { NS("dup_x2") /* */, BYTECODE_5B_DUP_X2 /* */, encode_only_bcode }, + { NS("dup_x1") /* */, BYTECODE_5A_DUP_X1 /* */, encode_only_bcode }, + { NS("dup2_x2") /* */, BYTECODE_5E_DUP2_X2 /* */, encode_only_bcode }, + { NS("dup2_x1") /* */, BYTECODE_5D_DUP2_X1 /* */, encode_only_bcode }, + { NS("dup2") /* */, BYTECODE_5C_DUP2 /* */, encode_only_bcode }, + { NS("dup") /* */, BYTECODE_59_DUP /* */, encode_only_bcode }, + { NS("dsub") /* */, BYTECODE_67_DSUB /* */, encode_only_bcode }, + { NS("dstore_3") /* */, BYTECODE_4A_DSTORE_3 /* */, encode_only_bcode }, + { NS("dstore_2") /* */, BYTECODE_49_DSTORE_2 /* */, encode_only_bcode }, + { NS("dstore_1") /* */, BYTECODE_48_DSTORE_1 /* */, encode_only_bcode }, + { NS("dstore_0") /* */, BYTECODE_47_DSTORE_0 /* */, encode_only_bcode }, + { NS("dstore") /* */, BYTECODE_39_DSTORE /* */, encode_ut8 }, + { NS("dreturn") /* */, BYTECODE_AF_DRETURN /* */, encode_only_bcode }, + { NS("drem") /* */, BYTECODE_73_DREM /* */, encode_only_bcode }, + { NS("dneg") /* */, BYTECODE_77_DNEG /* */, encode_only_bcode }, + { NS("dmul") /* */, BYTECODE_6B_DMUL /* */, encode_only_bcode }, + { NS("dload_3") /* */, BYTECODE_29_DLOAD_3 /* */, encode_only_bcode }, + { NS("dload_2") /* */, BYTECODE_28_DLOAD_2 /* */, encode_only_bcode }, + { NS("dload_1") /* */, BYTECODE_27_DLOAD_1 /* */, encode_only_bcode }, + { NS("dload_0") /* */, BYTECODE_26_DLOAD_0 /* */, encode_only_bcode }, + { NS("dload") /* */, BYTECODE_18_DLOAD /* */, encode_ut8 }, + { NS("ddiv") /* */, BYTECODE_6F_DDIV /* */, encode_only_bcode }, + { NS("dconst_1") /* */, BYTECODE_0F_DCONST_1 /* */, encode_only_bcode }, + { NS("dconst_0") /* */, BYTECODE_0E_DCONST_0 /* */, encode_only_bcode }, + { NS("dcmpl") /* */, BYTECODE_97_DCMPL /* */, encode_only_bcode }, + { NS("dcmpg") /* */, BYTECODE_98_DCMPG /* */, encode_only_bcode }, + { NS("dastore") /* */, BYTECODE_52_DASTORE /* */, encode_only_bcode }, + { NS("daload") /* */, BYTECODE_31_DALOAD /* */, encode_only_bcode }, + { NS("dadd") /* */, BYTECODE_63_DADD /* */, encode_only_bcode }, + { NS("d2l") /* */, BYTECODE_8F_D2L /* */, encode_only_bcode }, + { NS("d2i") /* */, BYTECODE_8E_D2I /* */, encode_only_bcode }, + { NS("d2f") /* */, BYTECODE_90_D2F /* */, encode_only_bcode }, + { NS("checkcast") /* */, BYTECODE_C0_CHECKCAST /* */, encode_const_pool16 }, + { NS("castore") /* */, BYTECODE_55_CASTORE /* */, encode_only_bcode }, + { NS("caload") /* */, BYTECODE_34_CALOAD /* */, encode_only_bcode }, + { NS("breakpoint") /* */, BYTECODE_CA_BREAKPOINT /* */, encode_only_bcode }, + { NS("bipush") /* */, BYTECODE_10_BIPUSH /* */, encode_st8 }, + { NS("bastore") /* */, BYTECODE_54_BASTORE /* */, encode_only_bcode }, + { NS("baload") /* */, BYTECODE_33_BALOAD /* */, encode_only_bcode }, + { NS("athrow") /* */, BYTECODE_BF_ATHROW /* */, encode_only_bcode }, + { NS("astore_3") /* */, BYTECODE_4E_ASTORE_3 /* */, encode_only_bcode }, + { NS("astore_2") /* */, BYTECODE_4D_ASTORE_2 /* */, encode_only_bcode }, + { NS("astore_1") /* */, BYTECODE_4C_ASTORE_1 /* */, encode_only_bcode }, + { NS("astore_0") /* */, BYTECODE_4B_ASTORE_0 /* */, encode_only_bcode }, + { NS("astore") /* */, BYTECODE_3A_ASTORE /* */, encode_ut8 }, + { NS("arraylength") /* */, BYTECODE_BE_ARRAYLENGTH /* */, encode_only_bcode }, + { NS("areturn") /* */, BYTECODE_B0_ARETURN /* */, encode_only_bcode }, + { NS("anewarray") /* */, BYTECODE_BD_ANEWARRAY /* */, encode_const_pool16 }, + { NS("aload_3") /* */, BYTECODE_2D_ALOAD_3 /* */, encode_only_bcode }, + { NS("aload_2") /* */, BYTECODE_2C_ALOAD_2 /* */, encode_only_bcode }, + { NS("aload_1") /* */, BYTECODE_2B_ALOAD_1 /* */, encode_only_bcode }, + { NS("aload_0") /* */, BYTECODE_2A_ALOAD_0 /* */, encode_only_bcode }, + { NS("aload") /* */, BYTECODE_19_ALOAD /* */, encode_ut8 }, + { NS("aconst_null") /* */, BYTECODE_01_ACONST_NULL /* */, encode_only_bcode }, + { NS("aastore") /* */, BYTECODE_53_AASTORE /* */, encode_only_bcode }, + { NS("aaload") /* */, BYTECODE_32_AALOAD /* */, encode_only_bcode } +}; +#undef NS + +bool java_assembler(const char *input, st32 input_size, ut8 *output, st32 output_size, ut64 pc, st32 *written) { + rz_return_val_if_fail(input && output && input_size > 0 && output_size > 0, false); + + for (ut32 i = 0; i < RZ_ARRAY_SIZE(instructions); ++i) { + if (input_size < instructions[i].length) { + continue; + } + if (!rz_str_ncasecmp(input, instructions[i].opcode, instructions[i].length)) { + const char *p = rz_str_trim_head_ro(input + instructions[i].length); + st32 used = p ? (p - input) : input_size; + return instructions[i].encode(instructions[i].bytecode, p, input_size - used, output, output_size, pc, written); + } + } + + RZ_LOG_ERROR("[!] java_assembler: invalid assembly.\n"); + return false; +} diff --git a/librz/asm/arch/java/assembler.h b/librz/asm/arch/java/assembler.h new file mode 100644 index 0000000000..fee39eb2f4 --- /dev/null +++ b/librz/asm/arch/java/assembler.h @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: 2021 deroad +// SPDX-License-Identifier: LGPL-3.0-only + +#ifndef RZ_ASM_JAVA_ASSEMBLER_H +#define RZ_ASM_JAVA_ASSEMBLER_H +#include +#include +#include "bytecode.h" + +bool java_assembler(const char *input, st32 input_size, ut8 *output, st32 output_size, ut64 pc, st32 *written); + +#endif /* RZ_ASM_JAVA_ASSEMBLER_H */ diff --git a/librz/asm/arch/java/bytecode.h b/librz/asm/arch/java/bytecode.h index 1280ece0fc..3aca1fc179 100644 --- a/librz/asm/arch/java/bytecode.h +++ b/librz/asm/arch/java/bytecode.h @@ -202,7 +202,7 @@ #define BYTECODE_C2_MONITORENTER (0xC2) #define BYTECODE_C3_MONITOREXIT (0xC3) #define BYTECODE_C4_WIDE (0xC4) -#define BYTECODE_C5_MULTINEWARRAY (0xC5) +#define BYTECODE_C5_MULTIANEWARRAY (0xC5) #define BYTECODE_C6_IFNULL (0xC6) #define BYTECODE_C7_IFNONNULL (0xC7) #define BYTECODE_C8_GOTO_W (0xC8) diff --git a/librz/asm/arch/java/jvm.c b/librz/asm/arch/java/jvm.c index 32dd0533c2..745894c12d 100644 --- a/librz/asm/arch/java/jvm.c +++ b/librz/asm/arch/java/jvm.c @@ -36,9 +36,9 @@ #define load_ut16_ut8(bytecode, jvm, t0, t1, c0, c1) \ fail_if_no_enough_buffer_or_set(bytecode, jvm, 4); \ - bytecode->args[0] = (c0)rz_read_at_be16(jvm->buffer, jvm->current + 2); \ + bytecode->args[0] = (c0)rz_read_at_be16(jvm->buffer, jvm->current + 1); \ bytecode->type[0] = (t0); \ - bytecode->args[1] = (c1)rz_read_at_be8(jvm->buffer, jvm->current + 1); \ + bytecode->args[1] = (c1)rz_read_at_be8(jvm->buffer, jvm->current + 3); \ bytecode->type[1] = (t1) #define load_ut16x2(bytecode, jvm, t0, t1, c0, c1) \ @@ -1394,8 +1394,8 @@ static bool decode_instruction(JavaVM *jvm, Bytecode *bytecode) { strcpy(bytecode->name, "wide"); bytecode->size = 1; break; - case BYTECODE_C5_MULTINEWARRAY: - strcpy(bytecode->name, "multinewarray"); + case BYTECODE_C5_MULTIANEWARRAY: + strcpy(bytecode->name, "multianewarray"); load_ut16_ut8(bytecode, jvm, BYTECODE_TYPE_CONST_POOL, BYTECODE_TYPE_NUMBER, ut32, ut32); bytecode->stack_input = 1; bytecode->stack_output = 1; diff --git a/librz/asm/meson.build b/librz/asm/meson.build index 406b9750cf..713d17ba0e 100644 --- a/librz/asm/meson.build +++ b/librz/asm/meson.build @@ -112,6 +112,7 @@ rz_asm_sources = [ #'arch/i4004/i4004dis.c', #'arch/i8080/i8080dis.c', 'arch/java/jvm.c', + 'arch/java/assembler.c', 'arch/lanai/gnu/lanai-dis.c', 'arch/lanai/gnu/lanai-opc.c', 'arch/luac/lua_arch.c', diff --git a/librz/asm/p/asm_java.c b/librz/asm/p/asm_java.c index fafb4b2d64..43e840110f 100644 --- a/librz/asm/p/asm_java.c +++ b/librz/asm/p/asm_java.c @@ -8,6 +8,7 @@ #include #include "../arch/java/jvm.h" +#include "../arch/java/assembler.h" typedef struct java_analysis_context_t { LookupSwitch ls; @@ -121,6 +122,19 @@ static bool java_fini(void *user) { return true; } +static int java_assemble(RzAsm *a, RzAsmOp *ao, const char *str) { + ut8 buffer[128]; + st32 written = 0; + st32 slen = strlen(str); + + if (!java_assembler(str, slen, buffer, sizeof(buffer), a->pc, &written)) { + return -1; + } + + rz_strbuf_setbin(&ao->buf, (const ut8 *)&buffer, written); + return written; +} + RzAsmPlugin rz_asm_plugin_java = { .name = "java", .desc = "Java bytecode disassembler", @@ -132,6 +146,7 @@ RzAsmPlugin rz_asm_plugin_java = { .init = java_init, .fini = java_fini, .disassemble = &java_disassemble, + .assemble = &java_assemble, }; #ifndef RZ_PLUGIN_INCORE diff --git a/test/db/asm/java b/test/db/asm/java index 1136935849..7e2c6ee1bc 100644 --- a/test/db/asm/java +++ b/test/db/asm/java @@ -1,35 +1,240 @@ -d "aload_0" 2a -d "bipush 100" 1064 0x00000006 -d "bipush 32" 1020 -d "bipush 33" 1021 -d "goto 0x10" a70007 0x00000009 -d "goto 0x10" a70007 0x00000009 -d "goto 0x19" a70010 0x00000009 -d "goto 0x3b" a70037 0x00000004 -d "goto 0x10" a70007 0x00000009 -d "i2b" 91 -d "iadd" 60 -d "iconst_0" 03 -d "iconst_1" 04 -d "if_icmpeq 0xc" 9f0008 0x00000004 -d "if_icmpge 0xc" a20008 0x00000004 -d "if_icmpgt 0xc" a30008 0x00000004 -d "if_icmple 0xc" a40008 0x00000004 -d "if_icmplt 0xc" a10008 0x00000004 -d "if_icmplt 0xc" a1ffe7 0x00000025 -d "if_icmplt 0xc" a1fff1 0x0000001b -d "if_icmpne 0x1a" a00006 0x00000014 -d "if_icmpne 0xc" a00008 0x00000004 -d "iinc 0 1" 840001 -d "iload_0" 1a -d "iload_1" 1b -d "iload_2" 1c -d "irem" 70 -d "ireturn" ac -d "ireturn" ac 0x00000011 -d "istore_1" 3c -d "istore_2" 3d -d "ixor" 82 -d "nop" 00 -d "return" b1 -d "sipush 255" 1100ff +ad "aaload" 32 +ad "aastore" 53 +ad "aconst_null" 01 +ad "aload 10" 190a +ad "aload_0" 2a +ad "aload_1" 2b +ad "aload_2" 2c +ad "aload_3" 2d +ad "anewarray constant_pool.511" bd01ff +a "anewarray 511" bd01ff +ad "areturn" b0 +ad "arraylength" be +ad "astore 10" 3a0a +ad "astore_0" 4b +ad "astore_1" 4c +ad "astore_2" 4d +ad "astore_3" 4e +ad "athrow" bf +ad "baload" 33 +ad "bastore" 54 +ad "bipush 100" 1064 +ad "bipush 100" 1064 0x00000006 +ad "bipush 32" 1020 +ad "bipush 33" 1021 +ad "breakpoint" ca +ad "caload" 34 +ad "castore" 55 +ad "checkcast constant_pool.511" c001ff +a "checkcast 511" c001ff +ad "d2f" 90 +ad "d2i" 8e +ad "d2l" 8f +ad "dadd" 63 +ad "daload" 31 +ad "dastore" 52 +ad "dcmpg" 98 +ad "dcmpl" 97 +ad "dconst_0" 0e +ad "dconst_1" 0f +ad "ddiv" 6f +ad "dload 10" 180a +ad "dload_0" 26 +ad "dload_1" 27 +ad "dload_2" 28 +ad "dload_3" 29 +ad "dmul" 6b +ad "dneg" 77 +ad "drem" 73 +ad "dreturn" af +ad "dstore 10" 390a +ad "dstore_0" 47 +ad "dstore_1" 48 +ad "dstore_2" 49 +ad "dstore_3" 4a +ad "dsub" 67 +ad "dup" 59 +ad "dup2" 5c +ad "dup2_x1" 5d +ad "dup2_x2" 5e +ad "dup_x1" 5a +ad "dup_x2" 5b +ad "f2d" 8d +ad "f2i" 8b +ad "f2l" 8c +ad "fadd" 62 +ad "faload" 30 +ad "fastore" 51 +ad "fcmpg" 96 +ad "fcmpl" 95 +ad "fconst_0" 0b +ad "fconst_1" 0c +ad "fconst_2" 0d +ad "fdiv" 6e +ad "fload 10" 170a +ad "fload_0" 22 +ad "fload_1" 23 +ad "fload_2" 24 +ad "fload_3" 25 +ad "fmul" 6a +ad "fneg" 76 +ad "frem" 72 +ad "freturn" ae +ad "fstore 10" 380a +ad "fstore_0" 43 +ad "fstore_1" 44 +ad "fstore_2" 45 +ad "fstore_3" 46 +ad "fsub" 66 +ad "getfield constant_pool.511" b401ff +a "getfield 511" b401ff +ad "getstatic constant_pool.511" b201ff +a "getstatic 511" b201ff +ad "goto 0x10" a70007 0x00000009 +ad "goto 0x10" a70007 0x00000009 +ad "goto 0x19" a70010 0x00000009 +ad "goto 0x1ff" a701ff +ad "goto 0x3b" a70037 0x00000004 +ad "goto_w 0xa64" c800000a64 +ad "i2b" 91 +ad "i2c" 92 +ad "i2d" 87 +ad "i2f" 86 +ad "i2l" 85 +ad "i2s" 93 +ad "iadd" 60 +ad "iaload" 2e +ad "iand" 7e +ad "iastore" 4f +ad "iconst_0" 03 +ad "iconst_1" 04 +ad "iconst_2" 05 +ad "iconst_3" 06 +ad "iconst_4" 07 +ad "iconst_5" 08 +ad "iconst_m1" 02 +ad "idiv" 6c +ad "if_acmpeq 0xc" a50008 0x00000004 +ad "if_acmpne 0xc" a60008 0x00000004 +ad "if_icmpeq 0xc" 9f0008 0x00000004 +ad "if_icmpge 0xc" a20008 0x00000004 +ad "if_icmpgt 0xc" a30008 0x00000004 +ad "if_icmple 0xc" a40008 0x00000004 +ad "if_icmplt 0xc" a10008 0x00000004 +ad "if_icmplt 0xc" a1ffe7 0x00000025 +ad "if_icmplt 0xc" a1fff1 0x0000001b +ad "if_icmpne 0x1a" a00006 0x00000014 +ad "if_icmpne 0xc" a00008 0x00000004 +ad "ifeq 0xc" 990008 0x00000004 +ad "ifge 0xc" 9c0008 0x00000004 +ad "ifgt 0xc" 9d0008 0x00000004 +ad "ifle 0xc" 9e0008 0x00000004 +ad "iflt 0xc" 9b0008 0x00000004 +ad "ifne 0xc" 9a0008 0x00000004 +ad "ifnonnull 0xc" c70008 0x00000004 +ad "ifnull 0xc" c60008 0x00000004 +ad "iinc 0 1" 840001 +ad "iinc 5 15" 84050f +ad "iload 10" 150a +ad "iload_0" 1a +ad "iload_1" 1b +ad "iload_2" 1c +ad "iload_3" 1d +ad "impdep1" fe +ad "impdep2" ff +ad "imul" 68 +ad "ineg" 74 +ad "instanceof constant_pool.511" c101ff +a "instanceof 511" c101ff +ad "invokedynamic constant_pool.511 10" ba01ff0a +a "invokedynamic 511 10" ba01ff0a +ad "invokeinterface constant_pool.511 10" b901ff0a +a "invokeinterface 511 10" b901ff0a +ad "invokespecial constant_pool.511" b701ff +a "invokespecial 511" b701ff +ad "invokestatic constant_pool.511" b801ff +a "invokestatic 511" b801ff +ad "invokevirtual constant_pool.511" b601ff +a "invokevirtual 511" b601ff +ad "ior" 80 +ad "irem" 70 +ad "ireturn" ac +ad "ireturn" ac 0x00000011 +ad "ishl" 78 +ad "ishr" 7a +ad "istore 10" 360a +ad "istore_0" 3b +ad "istore_1" 3c +ad "istore_2" 3d +ad "istore_3" 3e +ad "isub" 64 +ad "iushr" 7c +ad "ixor" 82 +ad "jsr 0x1ff" a801ff +ad "jsr_w 0xa64" c900000a64 +ad "l2d" 8a +ad "l2f" 89 +ad "l2i" 88 +ad "ladd" 61 +ad "laload" 2f +ad "land" 7f +ad "lastore" 50 +ad "lcmp" 94 +ad "lconst_0" 09 +ad "lconst_1" 0a +ad "ldc constant_pool.255" 12ff +a "ldc 255" 12ff +ad "ldc2_w constant_pool.511" 1401ff +a "ldc2_w 511" 1401ff +ad "ldc_w constant_pool.511" 1301ff +a "ldc_w 511" 1301ff +ad "ldiv" 6d +ad "lload 10" 160a +ad "lload_0" 1e +ad "lload_1" 1f +ad "lload_2" 20 +ad "lload_3" 21 +ad "lmul" 69 +ad "lneg" 75 +ad "lor" 81 +ad "lrem" 71 +ad "lreturn" ad +ad "lshl" 79 +ad "lshr" 7b +ad "lstore 10" 370a +ad "lstore_0" 3f +ad "lstore_1" 40 +ad "lstore_2" 41 +ad "lstore_3" 42 +ad "lsub" 65 +ad "lushr" 7d +ad "lxor" 83 +ad "monitorenter" c2 +ad "monitorexit" c3 +ad "multianewarray constant_pool.10 100" c5000a64 +a "multianewarray 10 100" c5000a64 +ad "new constant_pool.511" bb01ff +a "new 511" bb01ff +ad "newarray bool" bc04 +ad "newarray byte" bc08 +ad "newarray char" bc05 +ad "newarray double" bc07 +ad "newarray float" bc06 +ad "newarray int" bc0a +ad "newarray long" bc0b +ad "newarray short" bc09 +ad "nop" 00 +ad "pop" 57 +ad "pop2" 58 +ad "putfield constant_pool.511" b501ff +a "putfield 511" b501ff +ad "putstatic constant_pool.511" b301ff +a "putstatic 511" b301ff +ad "ret 7" a907 +ad "return" b1 +ad "saload" 35 +ad "sastore" 56 +ad "sipush 100" 110064 +ad "sipush 511" 1101ff +ad "swap" 5f +ad "wide" c4 \ No newline at end of file