diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bdd86af0aaa..e9586cf392c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -59,7 +59,7 @@ jobs: linux-clang-tests-asan, linux-gcc-tests-codecov, capstone-v3, - capstone-next, + capstone-v4, ] include: - name: linux-meson-clang-tests @@ -135,11 +135,11 @@ jobs: timeout: 45 cflags: "-Wno-cpp" allow_failure: false - - name: capstone-next + - name: capstone-v4 os: ubuntu-22.04 build_system: meson compiler: gcc - meson_options: -Dbuildtype=release -Duse_capstone_version=next --werror + meson_options: -Dbuildtype=release -Duse_capstone_version=v4 --werror run_tests: false enabled: ${{ (github.event_name != 'pull_request' || contains(github.head_ref, 'capstone')) && needs.changes.outputs.edited == 'true' }} timeout: 45 diff --git a/doc/PACKAGERS.md b/doc/PACKAGERS.md index 28d0bb49591..b1e71a3ca56 100644 --- a/doc/PACKAGERS.md +++ b/doc/PACKAGERS.md @@ -60,7 +60,7 @@ directories used by Rizin, have a look at options `rizin_sdb`, `rizin_zigns`, etc. in [meson_options.txt][]. Rizin uses the Capstone disassembly engine and supports versions 3, 4, and 5. -By default we use a custom version of Capstone based on v4 and statically link +By default we use a custom version of Capstone based on v5 and statically link it into the Rizin executables. Some distributions might prefer that a system version of Capstone be dynamically linked at runtime. To do this, use the `-Duse_sys_capstone=enabled` command line option when running `meson`. diff --git a/librz/analysis/meson.build b/librz/analysis/meson.build index c57cd8199e8..856f716a45d 100644 --- a/librz/analysis/meson.build +++ b/librz/analysis/meson.build @@ -10,6 +10,7 @@ analysis_plugins_list = [ 'cr16', 'dalvik', 'ebc', + 'evm_cs', 'gb', 'h8300', 'hexagon', @@ -121,6 +122,7 @@ rz_analysis_sources = [ 'p/analysis_cr16.c', 'p/analysis_dalvik.c', 'p/analysis_ebc.c', + 'p/analysis_evm_cs.c', 'p/analysis_gb.c', 'p/analysis_h8300.c', 'p/analysis_hexagon.c', diff --git a/librz/analysis/p/analysis_evm_cs.c b/librz/analysis/p/analysis_evm_cs.c new file mode 100644 index 00000000000..1b7e70e043f --- /dev/null +++ b/librz/analysis/p/analysis_evm_cs.c @@ -0,0 +1,341 @@ +// SPDX-FileCopyrightText: 2023 Yaroslav Yashin +// SPDX-License-Identifier: LGPL-3.0-only + +#include "rz_util/rz_log.h" +#include +#include +#include +#include +#include + +#if CS_API_MAJOR < 5 +#error Old Capstone not supported +#endif + +#define INSOP(n) insn->detail->sparc.operands[n] +#define INSCC insn->detail->sparc.cc + +static int parse_reg_name(RzRegItem *reg, csh handle, cs_insn *insn, int reg_num) { + if (!reg) { + return -1; + } + switch (INSOP(reg_num).type) { + case SPARC_OP_REG: + reg->name = (char *)cs_reg_name(handle, INSOP(reg_num).reg); + break; + case SPARC_OP_MEM: + if (INSOP(reg_num).mem.base != SPARC_REG_INVALID) { + reg->name = (char *)cs_reg_name(handle, INSOP(reg_num).mem.base); + break; + } + default: + break; + } + return 0; +} + +static void op_fillval(RzAnalysisOp *op, csh handle, cs_insn *insn) { + static RzRegItem reg; + switch (op->type & RZ_ANALYSIS_OP_TYPE_MASK) { + case RZ_ANALYSIS_OP_TYPE_LOAD: + if (INSOP(0).type == SPARC_OP_MEM) { + ZERO_FILL(reg); + op->src[0] = rz_analysis_value_new(); + op->src[0]->type = RZ_ANALYSIS_VAL_MEM; + op->src[0]->reg = ® + parse_reg_name(op->src[0]->reg, handle, insn, 0); + op->src[0]->delta = INSOP(0).mem.disp; + } + break; + case RZ_ANALYSIS_OP_TYPE_STORE: + if (INSOP(1).type == SPARC_OP_MEM) { + ZERO_FILL(reg); + op->dst = rz_analysis_value_new(); + op->dst->type = RZ_ANALYSIS_VAL_MEM; + op->dst->reg = ® + parse_reg_name(op->dst->reg, handle, insn, 1); + op->dst->delta = INSOP(1).mem.disp; + } + break; + } +} + +static int analop(RzAnalysis *a, RzAnalysisOp *op, ut64 addr, const ut8 *buf, int len, RzAnalysisOpMask mask) { + static csh handle = 0; + static int omode; + cs_insn *insn; + int mode, n, ret; + + if (mode != omode) { + cs_close(&handle); + handle = 0; + omode = mode; + } + if (handle == 0) { + ret = cs_open(CS_ARCH_EVM, CS_MODE_LITTLE_ENDIAN, &handle); + RZ_LOG_DEBUG("analysis_evm_cs.c:cs_open: %i\n", ret) + if (ret != CS_ERR_OK) { + return -1; + } + cs_option(handle, CS_OPT_DETAIL, CS_OPT_ON); + } + // capstone-next + n = cs_disasm(handle, (const ut8 *)buf, len, addr, 1, &insn); + if (n < 1) { + RZ_LOG_DEBUG("analysis_evm_cs.c:cs_disasm: %s\n", "RZ_ANALYSIS_OP_TYPE_ILL") + op->type = RZ_ANALYSIS_OP_TYPE_ILL; + } else { + // RZ_LOG_DEBUG("analysis_evm_cs.c:cs_disasm: %s\n", "ELSE") + op->size = insn->size; + op->id = insn->id; + RZ_LOG_DEBUG("analysis_evm_cs.c:cs_disasm:op->size(%#x), op->id(%#x)\n", insn->size, insn->id) + switch (insn->id) { + case EVM_INS_STOP: + op->type = RZ_ANALYSIS_OP_TYPE_NULL; + break; + case EVM_INS_ADD: + op->type = RZ_ANALYSIS_OP_TYPE_ADD; + break; + case EVM_INS_MUL: + op->type = RZ_ANALYSIS_OP_TYPE_MUL; + break; + case EVM_INS_SUB: + op->type = RZ_ANALYSIS_OP_TYPE_SUB; + break; + case EVM_INS_DIV: + case EVM_INS_SDIV: + op->type = RZ_ANALYSIS_OP_TYPE_DIV; + break; + case EVM_INS_MOD: + case EVM_INS_ADDMOD: + case EVM_INS_MULMOD: + case EVM_INS_SMOD: + op->type = RZ_ANALYSIS_OP_TYPE_MOD; + break; + case EVM_INS_AND: + op->type = RZ_ANALYSIS_OP_TYPE_AND; + break; + case EVM_INS_OR: + op->type = RZ_ANALYSIS_OP_TYPE_OR; + break; + case EVM_INS_XOR: + op->type = RZ_ANALYSIS_OP_TYPE_XOR; + break; + case EVM_INS_NOT: + op->type = RZ_ANALYSIS_OP_TYPE_NOT; + break; + case EVM_INS_POP: + op->type = RZ_ANALYSIS_OP_TYPE_POP; + break; + case EVM_INS_MLOAD: + op->type = RZ_ANALYSIS_OP_TYPE_LOAD; + break; + case EVM_INS_MSTORE: + case EVM_INS_MSTORE8: + op->type = RZ_ANALYSIS_OP_TYPE_STORE; + break; + case EVM_INS_JUMP: + op->type = RZ_ANALYSIS_ADDR_HINT_TYPE_JUMP; + break; + case EVM_INS_JUMPI: + op->type = RZ_ANALYSIS_OP_TYPE_CJMP; + break; + + case EVM_INS_PUSH1: + case EVM_INS_PUSH2: + case EVM_INS_PUSH3: + case EVM_INS_PUSH4: + case EVM_INS_PUSH5: + case EVM_INS_PUSH6: + case EVM_INS_PUSH7: + case EVM_INS_PUSH8: + case EVM_INS_PUSH9: + case EVM_INS_PUSH10: + case EVM_INS_PUSH11: + case EVM_INS_PUSH12: + case EVM_INS_PUSH13: + case EVM_INS_PUSH14: + case EVM_INS_PUSH15: + case EVM_INS_PUSH16: + case EVM_INS_PUSH17: + case EVM_INS_PUSH18: + case EVM_INS_PUSH19: + case EVM_INS_PUSH20: + case EVM_INS_PUSH21: + case EVM_INS_PUSH22: + case EVM_INS_PUSH23: + case EVM_INS_PUSH24: + case EVM_INS_PUSH25: + case EVM_INS_PUSH26: + case EVM_INS_PUSH27: + case EVM_INS_PUSH28: + case EVM_INS_PUSH29: + case EVM_INS_PUSH30: + case EVM_INS_PUSH31: + case EVM_INS_PUSH32: + op->type = RZ_ANALYSIS_OP_TYPE_PUSH; + break; + case EVM_INS_DUP1: + case EVM_INS_DUP2: + case EVM_INS_DUP3: + case EVM_INS_DUP4: + case EVM_INS_DUP5: + case EVM_INS_DUP6: + case EVM_INS_DUP7: + case EVM_INS_DUP8: + case EVM_INS_DUP9: + case EVM_INS_DUP10: + case EVM_INS_DUP11: + case EVM_INS_DUP12: + case EVM_INS_DUP13: + case EVM_INS_DUP14: + case EVM_INS_DUP15: + case EVM_INS_DUP16: + RZ_LOG_DEBUG("NOT IMPLEMENTED: op->id(%#x)\n", insn->id); + break; + case EVM_INS_SWAP1: + case EVM_INS_SWAP2: + case EVM_INS_SWAP3: + case EVM_INS_SWAP4: + case EVM_INS_SWAP5: + case EVM_INS_SWAP6: + case EVM_INS_SWAP7: + case EVM_INS_SWAP8: + case EVM_INS_SWAP9: + case EVM_INS_SWAP10: + case EVM_INS_SWAP11: + case EVM_INS_SWAP12: + case EVM_INS_SWAP13: + case EVM_INS_SWAP14: + case EVM_INS_SWAP15: + case EVM_INS_SWAP16: + RZ_LOG_DEBUG("NOT IMPLEMENTED: op->id(%#x)\n", insn->id); + break; + case EVM_INS_LOG0: + case EVM_INS_LOG1: + case EVM_INS_LOG2: + case EVM_INS_LOG3: + case EVM_INS_LOG4: + RZ_LOG_DEBUG("NOT IMPLEMENTED: op->id(%#x)\n", insn->id); + break; + + case EVM_INS_CALL: + op->type = RZ_ANALYSIS_OP_TYPE_CALL; + break; + case EVM_INS_RETURN: + op->type = RZ_ANALYSIS_OP_TYPE_RET; + break; + + case EVM_INS_INVALID: + op->type = RZ_ANALYSIS_OP_TYPE_ILL; + break; + + // Not implemented yet + case EVM_INS_SIGNEXTEND: + case EVM_INS_EXP: + case EVM_INS_LT: + case EVM_INS_GT: + case EVM_INS_SLT: + case EVM_INS_SGT: + case EVM_INS_EQ: + case EVM_INS_ISZERO: + case EVM_INS_BYTE: + // case EVM_INS_SHL: // logical shift left + // case EVM_INS_SHR: // logical shift right + // case EVM_INS_SAR: // arithmetic shigt right + case EVM_INS_SHA3: // kessac256 hash + case EVM_INS_ADDRESS: + case EVM_INS_BALANCE: + case EVM_INS_ORIGIN: + case EVM_INS_CALLER: + case EVM_INS_CALLVALUE: + case EVM_INS_CALLDATALOAD: + case EVM_INS_CALLDATASIZE: + case EVM_INS_CALLDATACOPY: + case EVM_INS_CODESIZE: + case EVM_INS_CODECOPY: + case EVM_INS_GASPRICE: + case EVM_INS_EXTCODESIZE: + case EVM_INS_EXTCODECOPY: + case EVM_INS_RETURNDATASIZE: + case EVM_INS_RETURNDATACOPY: + // case EVM_INS_EXTCODEHASH: + case EVM_INS_BLOCKHASH: + case EVM_INS_COINBASE: + case EVM_INS_TIMESTAMP: + case EVM_INS_NUMBER: // Number of a current block + case EVM_INS_DIFFICULTY: + case EVM_INS_GASLIMIT: + // case EVM_INS_CHAINID: + // case EVM_INS_SELFBALANCE: + // case EVM_INS_BASEFEE: + case EVM_INS_PC: + case EVM_INS_MSIZE: + case EVM_INS_GAS: + case EVM_INS_JUMPDEST: + case EVM_INS_CREATE: + case EVM_INS_CALLCODE: + case EVM_INS_DELEGATECALL: + // case EVM_INS_CREATE2: + case EVM_INS_STATICCALL: + // case EVM_INS_SELFDESTRUCT: + RZ_LOG_DEBUG("NOT IMPLEMENTED: op->id(%#x)\n", insn->id); + break; + + if (mask & RZ_ANALYSIS_OP_MASK_VAL) { + op_fillval(op, handle, insn); + } + cs_free(insn, n); + } + } + return op->size; +} + +static char *get_reg_profile(RzAnalysis *analysis) { + return strdup( + "=PC pc\n" + "=BP bp\n" + "=SP sp\n" + "=A0 r0\n" + "gpr sp .256 0 0\n" // stack pointer + "gpr pc .32 256 0\n" // program counter + "gpr bp .32 288 0\n" // base pointer // unused + ); +} + +static int archinfo(RzAnalysis *a, RzAnalysisInfoType query) { + switch (query) { + case RZ_ANALYSIS_ARCHINFO_MIN_OP_SIZE: + return 1; + case RZ_ANALYSIS_ARCHINFO_MAX_OP_SIZE: + return 33; + case RZ_ANALYSIS_ARCHINFO_CAN_USE_POINTERS: + return true; + case RZ_ANALYSIS_ARCHINFO_TEXT_ALIGN: + /* fall-thru */ + case RZ_ANALYSIS_ARCHINFO_DATA_ALIGN: + /* fall-thru */ + default: + return 0; + } +} + +RzAnalysisPlugin rz_analysis_plugin_evm_cs = { + .name = "evm", + .desc = "Capstone EVM analysis", + .esil = false, + .license = "BSD", + .arch = "evm", + .bits = 256, + .archinfo = archinfo, + .op = &analop, + .get_reg_profile = &get_reg_profile, +}; + +#ifndef RZ_PLUGIN_INCORE +RZ_API RzLibStruct rizin_plugin = { + .type = RZ_LIB_TYPE_ANALYSIS, + .data = &rz_analysis_plugin_evm_cs, + .version = RZ_VERSION +}; +#endif diff --git a/librz/asm/meson.build b/librz/asm/meson.build index 3c55a8f5ad3..13c9b0b70c1 100644 --- a/librz/asm/meson.build +++ b/librz/asm/meson.build @@ -12,6 +12,7 @@ asm_plugins_list = [ 'dalvik', 'dcpu16', 'ebc', + 'evm_cs', 'gb', 'h8300', 'hexagon', @@ -98,6 +99,7 @@ rz_asm_sources = [ 'p/asm_dalvik.c', 'p/asm_dcpu16.c', 'p/asm_ebc.c', + 'p/asm_evm_cs.c', 'p/asm_gb.c', 'p/asm_h8300.c', 'p/asm_hexagon.c', diff --git a/librz/asm/p/asm_evm_cs.c b/librz/asm/p/asm_evm_cs.c new file mode 100644 index 00000000000..89c999a5233 --- /dev/null +++ b/librz/asm/p/asm_evm_cs.c @@ -0,0 +1,75 @@ +// SPDX-FileCopyrightText: 2023 Yaroslav Yashin +// SPDX-License-Identifier: LGPL-3.0-only + +#include +#include +#include +static csh cd = 0; +#include "cs_mnemonics.c" + +static int disassemble(RzAsm *asm_context, RzAsmOp *op, const ut8 *buf, int len) { + RZ_LOG_DEBUG("%s\n", "asm_evm_cs.c:disassemble") + cs_insn *insn; + int n = -1, ret = -1; + int mode = CS_MODE_LITTLE_ENDIAN; + if (op) { + memset(op, 0, sizeof(RzAsmOp)); + op->size = 4; + } + if (cd != 0) { + cs_close(&cd); + } + ret = cs_open(CS_ARCH_EVM, mode, &cd); + if (ret) { + goto fin; + } + cs_option(cd, CS_OPT_DETAIL, CS_OPT_OFF); + if (!op) { + return 0; + } + if (asm_context->big_endian) { + n = cs_disasm(cd, buf, len, asm_context->pc, 1, &insn); + } + if (n < 1) { + rz_asm_op_set_asm(op, "invalid"); + op->size = 4; + ret = -1; + goto beach; + } else { + ret = 4; + } + if (insn->size < 1) { + goto beach; + } + op->size = insn->size; + char *buf_asm = sdb_fmt("%s%s%s", + insn->mnemonic, insn->op_str[0] ? " " : "", + insn->op_str); + rz_str_replace_char(buf_asm, '%', 0); + rz_asm_op_set_asm(op, buf_asm); + // TODO: remove the '$' in the string + cs_free(insn, n); +beach: + cs_close(&cd); +fin: + return ret; +} + +RzAsmPlugin rz_asm_plugin_evm_cs = { + .name = "evm.cs", + .desc = "Capstone EVM disassembler", + .license = "BSD", + .arch = "evm", + .bits = 32, + .endian = RZ_SYS_ENDIAN_BIG, + .disassemble = &disassemble, + // .mnemonics = mnemonics +}; + +#ifndef RZ_PLUGIN_INCORE +RZ_API RzLibStruct rizin_plugin = { + .type = RZ_LIB_TYPE_ASM, + .data = &rz_asm_plugin_evm_cs, + .version = RZ_VERSION +}; +#endif diff --git a/librz/include/rz_analysis.h b/librz/include/rz_analysis.h index 4222aa7b09d..7044c21d6c8 100644 --- a/librz/include/rz_analysis.h +++ b/librz/include/rz_analysis.h @@ -2214,6 +2214,7 @@ extern RzAnalysisPlugin rz_analysis_plugin_cr16; extern RzAnalysisPlugin rz_analysis_plugin_cris; extern RzAnalysisPlugin rz_analysis_plugin_dalvik; extern RzAnalysisPlugin rz_analysis_plugin_ebc; +extern RzAnalysisPlugin rz_analysis_plugin_evm_cs; extern RzAnalysisPlugin rz_analysis_plugin_gb; extern RzAnalysisPlugin rz_analysis_plugin_h8300; extern RzAnalysisPlugin rz_analysis_plugin_hexagon; diff --git a/librz/include/rz_asm.h b/librz/include/rz_asm.h index 783fb536173..8a5633d86a4 100644 --- a/librz/include/rz_asm.h +++ b/librz/include/rz_asm.h @@ -229,6 +229,7 @@ extern RzAsmPlugin rz_asm_plugin_cris_gnu; extern RzAsmPlugin rz_asm_plugin_dalvik; extern RzAsmPlugin rz_asm_plugin_dcpu16; extern RzAsmPlugin rz_asm_plugin_ebc; +extern RzAsmPlugin rz_asm_plugin_evm_cs; extern RzAsmPlugin rz_asm_plugin_gb; extern RzAsmPlugin rz_asm_plugin_h8300; extern RzAsmPlugin rz_asm_plugin_hexagon; diff --git a/meson_options.txt b/meson_options.txt index 8f78217ab62..fc18fb7fb19 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -19,7 +19,7 @@ option('rizin_bindings', type: 'string', value: '', description: 'Path where riz option('checks_level', type: 'integer', value: 9999, description: 'Value between 0 and 3 to enable different level of assert (see RZ_CHECKS_LEVEL). By default its value depends on buildtype (2 on debug, 1 on release).') option('use_sys_capstone', type: 'feature', value: 'disabled') -option('use_capstone_version', type: 'combo', choices: ['v3', 'v4', 'next', 'bundled'], value: 'bundled', description: 'Specify which version of capstone to use') +option('use_capstone_version', type: 'combo', choices: ['v3', 'v4', 'next', 'bundled'], value: 'next', description: 'Specify which version of capstone to use') option('use_sys_magic', type: 'feature', value: 'disabled') option('use_sys_libzip', type: 'feature', value: 'disabled') option('use_sys_libzip_openssl', type: 'boolean', value: false, description: 'Whether to use or not system openssl dependency to build libzip') diff --git a/subprojects/capstone-next.wrap b/subprojects/capstone-next.wrap index b675a3166cc..0d132e6af65 100644 --- a/subprojects/capstone-next.wrap +++ b/subprojects/capstone-next.wrap @@ -1,5 +1,5 @@ [wrap-git] url = https://github.com/capstone-engine/capstone.git -revision = a4ae97c08fe8d1489d20d462c370cdeb3231cea5 +revision = d5141c04785678535c7792eddc21f146186e639f directory = capstone-next patch_directory = capstone-next diff --git a/subprojects/capstone-v3.wrap b/subprojects/capstone-v3.wrap index 2ff2ed530fe..e99a2565532 100644 --- a/subprojects/capstone-v3.wrap +++ b/subprojects/capstone-v3.wrap @@ -1,6 +1,6 @@ [wrap-file] -source_url = https://github.com/aquynh/capstone/archive/3.0.5.tar.gz +source_url = https://github.com/capstone-engine/capstone/archive/3.0.5.tar.gz source_filename = 3.0.5.tar.gz source_hash = 913dd695e7c5a2b972a6f427cb31f2e93677ec1c38f39dda37d18a91c70b6df1 patch_directory = capstone-3.0.5 -directory = capstone-3.0.5 \ No newline at end of file +directory = capstone-3.0.5 diff --git a/subprojects/capstone-v4.wrap b/subprojects/capstone-v4.wrap index befd952ce06..a1e260fa8ba 100644 --- a/subprojects/capstone-v4.wrap +++ b/subprojects/capstone-v4.wrap @@ -1,6 +1,6 @@ [wrap-file] -source_url = https://github.com/aquynh/capstone/archive/4.0.2.tar.gz +source_url = https://github.com/capstone-engine/capstone/archive/4.0.2.tar.gz source_filename = 4.0.2.tar.gz source_hash = 7c81d798022f81e7507f1a60d6817f63aa76e489aa4e7055255f21a22f5e526a patch_directory = capstone-4.0.2 -directory = capstone-4.0.2 \ No newline at end of file +directory = capstone-4.0.2 diff --git a/subprojects/packagefiles/capstone-next/meson.build b/subprojects/packagefiles/capstone-next/meson.build index c154f0cea76..3ddb1d90185 100644 --- a/subprojects/packagefiles/capstone-next/meson.build +++ b/subprojects/packagefiles/capstone-next/meson.build @@ -44,6 +44,10 @@ cs_files = [ 'arch/X86/X86Mapping.c', 'arch/X86/X86Module.c', 'arch/X86/X86InstPrinterCommon.c', + 'arch/EVM/EVMDisassembler.c', + 'arch/EVM/EVMInstPrinter.c', + 'arch/EVM/EVMMapping.c', + 'arch/EVM/EVMModule.c', 'arch/XCore/XCoreDisassembler.c', 'arch/XCore/XCoreInstPrinter.c', 'arch/XCore/XCoreMapping.c', @@ -74,6 +78,7 @@ libcapstone_c_args = [ '-DCAPSTONE_HAS_X86', '-DCAPSTONE_HAS_XCORE', '-DCAPSTONE_HAS_TMS320C64X', + '-DCAPSTONE_HAS_EVM', ] libcapstone = library('capstone', cs_files, diff --git a/test/db/analysis/arm b/test/db/analysis/arm index 88dd532ee21..a4dccc0d38c 100644 --- a/test/db/analysis/arm +++ b/test/db/analysis/arm @@ -912,7 +912,7 @@ pseudo: push (r3, lr) mnemonic: push mask: ffffffff prefix: 0 -id: 424 +id: 128 bytes: 08402de9 refptr: 0 size: 4 diff --git a/test/db/analysis/ppc b/test/db/analysis/ppc index 3ad53f7adb7..1a98f9a71fb 100644 --- a/test/db/analysis/ppc +++ b/test/db/analysis/ppc @@ -681,7 +681,7 @@ EXPECT=<