diff options
Diffstat (limited to 'capstone/arch/BPF/BPFInstPrinter.c')
-rw-r--r-- | capstone/arch/BPF/BPFInstPrinter.c | 280 |
1 files changed, 280 insertions, 0 deletions
diff --git a/capstone/arch/BPF/BPFInstPrinter.c b/capstone/arch/BPF/BPFInstPrinter.c new file mode 100644 index 000000000..782d8cbc0 --- /dev/null +++ b/capstone/arch/BPF/BPFInstPrinter.c @@ -0,0 +1,280 @@ +/* Capstone Disassembly Engine */ +/* BPF Backend by david942j <david942j@gmail.com>, 2019 */ + +#include <capstone/platform.h> + +#include "BPFConstants.h" +#include "BPFInstPrinter.h" +#include "BPFMapping.h" + +static cs_bpf_op *expand_bpf_operands(cs_bpf *bpf) +{ + /* assert(bpf->op_count < 3); */ + return &bpf->operands[bpf->op_count++]; +} + +static void push_op_reg(cs_bpf *bpf, bpf_op_type val, uint8_t ac_mode) +{ + cs_bpf_op *op = expand_bpf_operands(bpf); + + op->type = BPF_OP_REG; + op->reg = val; + op->access = ac_mode; +} + +static void push_op_imm(cs_bpf *bpf, uint64_t val) +{ + cs_bpf_op *op = expand_bpf_operands(bpf); + + op->type = BPF_OP_IMM; + op->imm = val; +} + +static void push_op_off(cs_bpf *bpf, uint32_t val) +{ + cs_bpf_op *op = expand_bpf_operands(bpf); + + op->type = BPF_OP_OFF; + op->off = val; +} + +static void push_op_mem(cs_bpf *bpf, bpf_reg reg, uint32_t val) +{ + cs_bpf_op *op = expand_bpf_operands(bpf); + + op->type = BPF_OP_MEM; + op->mem.base = reg; + op->mem.disp = val; +} + +static void push_op_mmem(cs_bpf *bpf, uint32_t val) +{ + cs_bpf_op *op = expand_bpf_operands(bpf); + + op->type = BPF_OP_MMEM; + op->mmem = val; +} + +static void push_op_msh(cs_bpf *bpf, uint32_t val) +{ + cs_bpf_op *op = expand_bpf_operands(bpf); + + op->type = BPF_OP_MSH; + op->msh = val; +} + +static void push_op_ext(cs_bpf *bpf, bpf_ext_type val) +{ + cs_bpf_op *op = expand_bpf_operands(bpf); + + op->type = BPF_OP_EXT; + op->ext = val; +} + +static void convert_operands(MCInst *MI, cs_bpf *bpf) +{ + unsigned opcode = MCInst_getOpcode(MI); + unsigned mc_op_count = MCInst_getNumOperands(MI); + MCOperand *op; + MCOperand *op2; + unsigned i; + + bpf->op_count = 0; + if (BPF_CLASS(opcode) == BPF_CLASS_LD || BPF_CLASS(opcode) == BPF_CLASS_LDX) { + switch (BPF_MODE(opcode)) { + case BPF_MODE_IMM: + push_op_imm(bpf, MCOperand_getImm(MCInst_getOperand(MI, 0))); + break; + case BPF_MODE_ABS: + op = MCInst_getOperand(MI, 0); + push_op_mem(bpf, BPF_REG_INVALID, (uint32_t)MCOperand_getImm(op)); + break; + case BPF_MODE_IND: + op = MCInst_getOperand(MI, 0); + op2 = MCInst_getOperand(MI, 1); + push_op_mem(bpf, MCOperand_getReg(op), (uint32_t)MCOperand_getImm(op2)); + break; + case BPF_MODE_MEM: + if (EBPF_MODE(MI->csh)) { + /* ldx{w,h,b,dw} dst, [src+off] */ + push_op_reg(bpf, MCOperand_getReg(MCInst_getOperand(MI, 0)), CS_AC_WRITE); + op = MCInst_getOperand(MI, 1); + op2 = MCInst_getOperand(MI, 2); + push_op_mem(bpf, MCOperand_getReg(op), (uint32_t)MCOperand_getImm(op2)); + } + else { + push_op_mmem(bpf, (uint32_t)MCOperand_getImm(MCInst_getOperand(MI, 0))); + } + break; + case BPF_MODE_LEN: + push_op_ext(bpf, BPF_EXT_LEN); + break; + case BPF_MODE_MSH: + op = MCInst_getOperand(MI, 0); + push_op_msh(bpf, (uint32_t)MCOperand_getImm(op)); + break; + /* case BPF_MODE_XADD: // not exists */ + } + return; + } + if (BPF_CLASS(opcode) == BPF_CLASS_ST || BPF_CLASS(opcode) == BPF_CLASS_STX) { + if (!EBPF_MODE(MI->csh)) { + // cBPF has only one case - st* M[k] + push_op_mmem(bpf, (uint32_t)MCOperand_getImm(MCInst_getOperand(MI, 0))); + return; + } + /* eBPF has two cases: + * - st [dst + off], src + * - xadd [dst + off], src + * they have same form of operands. + */ + op = MCInst_getOperand(MI, 0); + op2 = MCInst_getOperand(MI, 1); + push_op_mem(bpf, MCOperand_getReg(op), (uint32_t)MCOperand_getImm(op2)); + op = MCInst_getOperand(MI, 2); + if (MCOperand_isImm(op)) + push_op_imm(bpf, MCOperand_getImm(op)); + else if (MCOperand_isReg(op)) + push_op_reg(bpf, MCOperand_getReg(op), CS_AC_READ); + return; + } + + if (BPF_CLASS(opcode) == BPF_CLASS_JMP) { + for (i = 0; i < mc_op_count; i++) { + op = MCInst_getOperand(MI, i); + if (MCOperand_isImm(op)) { + /* decide the imm is BPF_OP_IMM or BPF_OP_OFF type here */ + /* + * 1. ja +off + * 2. j {x,k}, +jt, +jf // cBPF + * 3. j dst_reg, {src_reg, k}, +off // eBPF + */ + if (BPF_OP(opcode) == BPF_JUMP_JA || + (!EBPF_MODE(MI->csh) && i >= 1) || + (EBPF_MODE(MI->csh) && i == 2)) + push_op_off(bpf, (uint32_t)MCOperand_getImm(op)); + else + push_op_imm(bpf, MCOperand_getImm(op)); + } + else if (MCOperand_isReg(op)) { + push_op_reg(bpf, MCOperand_getReg(op), CS_AC_READ); + } + } + return; + } + + if (!EBPF_MODE(MI->csh)) { + /* In cBPF mode, all registers in operands are accessed as read */ + for (i = 0; i < mc_op_count; i++) { + op = MCInst_getOperand(MI, i); + if (MCOperand_isImm(op)) + push_op_imm(bpf, MCOperand_getImm(op)); + else if (MCOperand_isReg(op)) + push_op_reg(bpf, MCOperand_getReg(op), CS_AC_READ); + } + return; + } + + /* remain cases are: eBPF mode && ALU */ + /* if (BPF_CLASS(opcode) == BPF_CLASS_ALU || BPF_CLASS(opcode) == BPF_CLASS_ALU64) */ + + /* We have three types: + * 1. {l,b}e dst // dst = byteswap(dst) + * 2. neg dst // dst = -dst + * 3. <op> dst, {src_reg, imm} // dst = dst <op> src + * so we can simply check the number of operands, + * exactly one operand means we are in case 1. and 2., + * otherwise in case 3. + */ + if (mc_op_count == 1) { + op = MCInst_getOperand(MI, 0); + push_op_reg(bpf, MCOperand_getReg(op), CS_AC_READ | CS_AC_WRITE); + } + else { // if (mc_op_count == 2) + op = MCInst_getOperand(MI, 0); + push_op_reg(bpf, MCOperand_getReg(op), CS_AC_READ | CS_AC_WRITE); + + op = MCInst_getOperand(MI, 1); + if (MCOperand_isImm(op)) + push_op_imm(bpf, MCOperand_getImm(op)); + else if (MCOperand_isReg(op)) + push_op_reg(bpf, MCOperand_getReg(op), CS_AC_READ); + } +} + +static void print_operand(MCInst *MI, struct SStream *O, const cs_bpf_op *op) +{ + switch (op->type) { + case BPF_OP_INVALID: + SStream_concat(O, "invalid"); + break; + case BPF_OP_REG: + SStream_concat(O, BPF_reg_name((csh)MI->csh, op->reg)); + break; + case BPF_OP_IMM: + SStream_concat(O, "0x%" PRIx64, op->imm); + break; + case BPF_OP_OFF: + SStream_concat(O, "+0x%x", op->off); + break; + case BPF_OP_MEM: + SStream_concat(O, "["); + if (op->mem.base != BPF_REG_INVALID) + SStream_concat(O, BPF_reg_name((csh)MI->csh, op->mem.base)); + if (op->mem.disp != 0) { + if (op->mem.base != BPF_REG_INVALID) + SStream_concat(O, "+"); + SStream_concat(O, "0x%x", op->mem.disp); + } + if (op->mem.base == BPF_REG_INVALID && op->mem.disp == 0) // special case + SStream_concat(O, "0x0"); + SStream_concat(O, "]"); + break; + case BPF_OP_MMEM: + SStream_concat(O, "m[0x%x]", op->mmem); + break; + case BPF_OP_MSH: + SStream_concat(O, "4*([0x%x]&0xf)", op->msh); + break; + case BPF_OP_EXT: + switch (op->ext) { + case BPF_EXT_LEN: + SStream_concat(O, "#len"); + break; + } + break; + } +} + +/* + * 1. human readable mnemonic + * 2. set pubOpcode (BPF_INSN_*) + * 3. set detail->bpf.operands + * */ +void BPF_printInst(MCInst *MI, struct SStream *O, void *PrinterInfo) +{ + int i; + cs_insn insn; + cs_bpf bpf; + + insn.detail = NULL; + /* set pubOpcode as instruction id */ + BPF_get_insn_id((cs_struct*)MI->csh, &insn, MCInst_getOpcode(MI)); + MCInst_setOpcodePub(MI, insn.id); + + SStream_concat(O, BPF_insn_name((csh)MI->csh, insn.id)); + convert_operands(MI, &bpf); + for (i = 0; i < bpf.op_count; i++) { + if (i == 0) + SStream_concat(O, "\t"); + else + SStream_concat(O, ", "); + print_operand(MI, O, &bpf.operands[i]); + } + +#ifndef CAPSTONE_DIET + if (MI->flat_insn->detail) { + MI->flat_insn->detail->bpf = bpf; + } +#endif +} |