| /* Capstone Disassembly Engine */ |
| /* BPF Backend by david942j <david942j@gmail.com>, 2019 */ |
| |
| #ifdef CAPSTONE_HAS_BPF |
| |
| #include <string.h> |
| #include <stddef.h> // offsetof macro |
| |
| #include "BPFConstants.h" |
| #include "BPFDisassembler.h" |
| #include "BPFMapping.h" |
| #include "../../cs_priv.h" |
| |
| static uint16_t read_u16(cs_struct *ud, const uint8_t *code) |
| { |
| if (MODE_IS_BIG_ENDIAN(ud->mode)) |
| return (((uint16_t)code[0] << 8) | code[1]); |
| else |
| return (((uint16_t)code[1] << 8) | code[0]); |
| } |
| |
| static uint32_t read_u32(cs_struct *ud, const uint8_t *code) |
| { |
| if (MODE_IS_BIG_ENDIAN(ud->mode)) |
| return ((uint32_t)read_u16(ud, code) << 16) | read_u16(ud, code + 2); |
| else |
| return ((uint32_t)read_u16(ud, code + 2) << 16) | read_u16(ud, code); |
| } |
| |
| ///< Malloc bpf_internal, also checks if code_len is large enough. |
| static bpf_internal *alloc_bpf_internal(size_t code_len) |
| { |
| bpf_internal *bpf; |
| |
| if (code_len < 8) |
| return NULL; |
| bpf = cs_mem_malloc(sizeof(bpf_internal)); |
| if (bpf == NULL) |
| return NULL; |
| /* default value */ |
| bpf->insn_size = 8; |
| return bpf; |
| } |
| |
| ///< Fetch a cBPF structure from code |
| static bpf_internal* fetch_cbpf(cs_struct *ud, const uint8_t *code, |
| size_t code_len) |
| { |
| bpf_internal *bpf; |
| |
| bpf = alloc_bpf_internal(code_len); |
| if (bpf == NULL) |
| return NULL; |
| |
| bpf->op = read_u16(ud, code); |
| bpf->jt = code[2]; |
| bpf->jf = code[3]; |
| bpf->k = read_u32(ud, code + 4); |
| return bpf; |
| } |
| |
| ///< Fetch an eBPF structure from code |
| static bpf_internal* fetch_ebpf(cs_struct *ud, const uint8_t *code, |
| size_t code_len) |
| { |
| bpf_internal *bpf; |
| |
| bpf = alloc_bpf_internal(code_len); |
| if (bpf == NULL) |
| return NULL; |
| |
| bpf->op = (uint16_t)code[0]; |
| bpf->dst = code[1] & 0xf; |
| bpf->src = (code[1] & 0xf0) >> 4; |
| |
| // eBPF has one 16-byte instruction: BPF_LD | BPF_DW | BPF_IMM, |
| // in this case imm is combined with the next block's imm. |
| if (bpf->op == (BPF_CLASS_LD | BPF_SIZE_DW | BPF_MODE_IMM)) { |
| if (code_len < 16) { |
| cs_mem_free(bpf); |
| return NULL; |
| } |
| bpf->k = read_u32(ud, code + 4) | (((uint64_t)read_u32(ud, code + 12)) << 32); |
| bpf->insn_size = 16; |
| } |
| else { |
| bpf->offset = read_u16(ud, code + 2); |
| bpf->k = read_u32(ud, code + 4); |
| } |
| return bpf; |
| } |
| |
| #define CHECK_READABLE_REG(ud, reg) do { \ |
| if (! ((reg) >= BPF_REG_R0 && (reg) <= BPF_REG_R10)) \ |
| return false; \ |
| } while (0) |
| |
| #define CHECK_WRITABLE_REG(ud, reg) do { \ |
| if (! ((reg) >= BPF_REG_R0 && (reg) < BPF_REG_R10)) \ |
| return false; \ |
| } while (0) |
| |
| #define CHECK_READABLE_AND_PUSH(ud, MI, r) do { \ |
| CHECK_READABLE_REG(ud, r + BPF_REG_R0); \ |
| MCOperand_CreateReg0(MI, r + BPF_REG_R0); \ |
| } while (0) |
| |
| #define CHECK_WRITABLE_AND_PUSH(ud, MI, r) do { \ |
| CHECK_WRITABLE_REG(ud, r + BPF_REG_R0); \ |
| MCOperand_CreateReg0(MI, r + BPF_REG_R0); \ |
| } while (0) |
| |
| static bool decodeLoad(cs_struct *ud, MCInst *MI, bpf_internal *bpf) |
| { |
| if (!EBPF_MODE(ud)) { |
| /* |
| * +-----+-----------+--------------------+ |
| * | ldb | [k] | [x+k] | |
| * | ldh | [k] | [x+k] | |
| * +-----+-----------+--------------------+ |
| */ |
| if (BPF_SIZE(bpf->op) == BPF_SIZE_DW) |
| return false; |
| if (BPF_SIZE(bpf->op) == BPF_SIZE_B || BPF_SIZE(bpf->op) == BPF_SIZE_H) { |
| /* no ldx */ |
| if (BPF_CLASS(bpf->op) != BPF_CLASS_LD) |
| return false; |
| /* can only be BPF_ABS and BPF_IND */ |
| if (BPF_MODE(bpf->op) == BPF_MODE_ABS) { |
| MCOperand_CreateImm0(MI, bpf->k); |
| return true; |
| } |
| else if (BPF_MODE(bpf->op) == BPF_MODE_IND) { |
| MCOperand_CreateReg0(MI, BPF_REG_X); |
| MCOperand_CreateImm0(MI, bpf->k); |
| return true; |
| } |
| return false; |
| } |
| /* |
| * +-----+----+------+------+-----+-------+ |
| * | ld | #k | #len | M[k] | [k] | [x+k] | |
| * +-----+----+------+------+-----+-------+ |
| * | ldx | #k | #len | M[k] | 4*([k]&0xf) | |
| * +-----+----+------+------+-------------+ |
| */ |
| switch (BPF_MODE(bpf->op)) { |
| default: |
| break; |
| case BPF_MODE_IMM: |
| MCOperand_CreateImm0(MI, bpf->k); |
| return true; |
| case BPF_MODE_LEN: |
| return true; |
| case BPF_MODE_MEM: |
| MCOperand_CreateImm0(MI, bpf->k); |
| return true; |
| } |
| if (BPF_CLASS(bpf->op) == BPF_CLASS_LD) { |
| if (BPF_MODE(bpf->op) == BPF_MODE_ABS) { |
| MCOperand_CreateImm0(MI, bpf->k); |
| return true; |
| } |
| else if (BPF_MODE(bpf->op) == BPF_MODE_IND) { |
| MCOperand_CreateReg0(MI, BPF_REG_X); |
| MCOperand_CreateImm0(MI, bpf->k); |
| return true; |
| } |
| } |
| else { /* LDX */ |
| if (BPF_MODE(bpf->op) == BPF_MODE_MSH) { |
| MCOperand_CreateImm0(MI, bpf->k); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /* eBPF mode */ |
| /* |
| * - IMM: lddw dst, imm64 |
| * - ABS: ld{w,h,b,dw} [k] |
| * - IND: ld{w,h,b,dw} [src+k] |
| * - MEM: ldx{w,h,b,dw} dst, [src+off] |
| */ |
| if (BPF_CLASS(bpf->op) == BPF_CLASS_LD) { |
| switch (BPF_MODE(bpf->op)) { |
| case BPF_MODE_IMM: |
| if (bpf->op != (BPF_CLASS_LD | BPF_SIZE_DW | BPF_MODE_IMM)) |
| return false; |
| CHECK_WRITABLE_AND_PUSH(ud, MI, bpf->dst); |
| MCOperand_CreateImm0(MI, bpf->k); |
| return true; |
| case BPF_MODE_ABS: |
| MCOperand_CreateImm0(MI, bpf->k); |
| return true; |
| case BPF_MODE_IND: |
| CHECK_READABLE_AND_PUSH(ud, MI, bpf->src); |
| MCOperand_CreateImm0(MI, bpf->k); |
| return true; |
| } |
| return false; |
| |
| } |
| /* LDX */ |
| if (BPF_MODE(bpf->op) == BPF_MODE_MEM) { |
| CHECK_WRITABLE_AND_PUSH(ud, MI, bpf->dst); |
| CHECK_READABLE_AND_PUSH(ud, MI, bpf->src); |
| MCOperand_CreateImm0(MI, bpf->offset); |
| return true; |
| } |
| return false; |
| } |
| |
| static bool decodeStore(cs_struct *ud, MCInst *MI, bpf_internal *bpf) |
| { |
| /* in cBPF, only BPF_ST* | BPF_MEM | BPF_W is valid |
| * while in eBPF: |
| * - BPF_STX | BPF_XADD | BPF_{W,DW} |
| * - BPF_ST* | BPF_MEM | BPF_{W,H,B,DW} |
| * are valid |
| */ |
| if (!EBPF_MODE(ud)) { |
| /* can only store to M[] */ |
| if (bpf->op != (BPF_CLASS(bpf->op) | BPF_MODE_MEM | BPF_SIZE_W)) |
| return false; |
| MCOperand_CreateImm0(MI, bpf->k); |
| return true; |
| } |
| |
| /* eBPF */ |
| |
| if (BPF_MODE(bpf->op) == BPF_MODE_XADD) { |
| if (BPF_CLASS(bpf->op) != BPF_CLASS_STX) |
| return false; |
| if (BPF_SIZE(bpf->op) != BPF_SIZE_W && BPF_SIZE(bpf->op) != BPF_SIZE_DW) |
| return false; |
| /* xadd [dst + off], src */ |
| CHECK_READABLE_AND_PUSH(ud, MI, bpf->dst); |
| MCOperand_CreateImm0(MI, bpf->offset); |
| CHECK_READABLE_AND_PUSH(ud, MI, bpf->src); |
| return true; |
| } |
| |
| if (BPF_MODE(bpf->op) != BPF_MODE_MEM) |
| return false; |
| |
| /* st [dst + off], src */ |
| CHECK_READABLE_AND_PUSH(ud, MI, bpf->dst); |
| MCOperand_CreateImm0(MI, bpf->offset); |
| if (BPF_CLASS(bpf->op) == BPF_CLASS_ST) |
| MCOperand_CreateImm0(MI, bpf->k); |
| else |
| CHECK_READABLE_AND_PUSH(ud, MI, bpf->src); |
| return true; |
| } |
| |
| static bool decodeALU(cs_struct *ud, MCInst *MI, bpf_internal *bpf) |
| { |
| /* Set MI->Operands */ |
| |
| /* cBPF */ |
| if (!EBPF_MODE(ud)) { |
| if (BPF_OP(bpf->op) > BPF_ALU_XOR) |
| return false; |
| /* cBPF's NEG has no operands */ |
| if (BPF_OP(bpf->op) == BPF_ALU_NEG) |
| return true; |
| if (BPF_SRC(bpf->op) == BPF_SRC_K) |
| MCOperand_CreateImm0(MI, bpf->k); |
| else /* BPF_SRC_X */ |
| MCOperand_CreateReg0(MI, BPF_REG_X); |
| return true; |
| } |
| |
| /* eBPF */ |
| |
| if (BPF_OP(bpf->op) > BPF_ALU_END) |
| return false; |
| /* ENDian's imm must be one of 16, 32, 64 */ |
| if (BPF_OP(bpf->op) == BPF_ALU_END) { |
| if (bpf->k != 16 && bpf->k != 32 && bpf->k != 64) |
| return false; |
| if (BPF_CLASS(bpf->op) == BPF_CLASS_ALU64 && BPF_SRC(bpf->op) != BPF_SRC_LITTLE) |
| return false; |
| } |
| |
| /* - op dst, imm |
| * - op dst, src |
| * - neg dst |
| * - le<imm> dst |
| */ |
| /* every ALU instructions have dst op */ |
| CHECK_WRITABLE_AND_PUSH(ud, MI, bpf->dst); |
| |
| /* special cases */ |
| if (BPF_OP(bpf->op) == BPF_ALU_NEG) |
| return true; |
| if (BPF_OP(bpf->op) == BPF_ALU_END) { |
| /* bpf->k must be one of 16, 32, 64 */ |
| MCInst_setOpcode(MI, MCInst_getOpcode(MI) | ((uint32_t)bpf->k << 4)); |
| return true; |
| } |
| |
| /* normal cases */ |
| if (BPF_SRC(bpf->op) == BPF_SRC_K) { |
| MCOperand_CreateImm0(MI, bpf->k); |
| } |
| else { /* BPF_SRC_X */ |
| CHECK_READABLE_AND_PUSH(ud, MI, bpf->src); |
| } |
| return true; |
| } |
| |
| static bool decodeJump(cs_struct *ud, MCInst *MI, bpf_internal *bpf) |
| { |
| /* cBPF and eBPF are very different in class jump */ |
| if (!EBPF_MODE(ud)) { |
| if (BPF_OP(bpf->op) > BPF_JUMP_JSET) |
| return false; |
| |
| /* ja is a special case of jumps */ |
| if (BPF_OP(bpf->op) == BPF_JUMP_JA) { |
| MCOperand_CreateImm0(MI, bpf->k); |
| return true; |
| } |
| |
| if (BPF_SRC(bpf->op) == BPF_SRC_K) |
| MCOperand_CreateImm0(MI, bpf->k); |
| else /* BPF_SRC_X */ |
| MCOperand_CreateReg0(MI, BPF_REG_X); |
| MCOperand_CreateImm0(MI, bpf->jt); |
| MCOperand_CreateImm0(MI, bpf->jf); |
| } |
| else { |
| if (BPF_OP(bpf->op) > BPF_JUMP_JSLE) |
| return false; |
| |
| /* No operands for exit */ |
| if (BPF_OP(bpf->op) == BPF_JUMP_EXIT) |
| return bpf->op == (BPF_CLASS_JMP | BPF_JUMP_EXIT); |
| if (BPF_OP(bpf->op) == BPF_JUMP_CALL) { |
| if (bpf->op == (BPF_CLASS_JMP | BPF_JUMP_CALL)) { |
| MCOperand_CreateImm0(MI, bpf->k); |
| return true; |
| } |
| if (bpf->op == (BPF_CLASS_JMP | BPF_JUMP_CALL | BPF_SRC_X)) { |
| CHECK_READABLE_AND_PUSH(ud, MI, bpf->k); |
| return true; |
| } |
| return false; |
| } |
| |
| /* ja is a special case of jumps */ |
| if (BPF_OP(bpf->op) == BPF_JUMP_JA) { |
| if (BPF_SRC(bpf->op) != BPF_SRC_K) |
| return false; |
| MCOperand_CreateImm0(MI, bpf->offset); |
| return true; |
| } |
| |
| /* <j> dst, src, +off */ |
| CHECK_READABLE_AND_PUSH(ud, MI, bpf->dst); |
| if (BPF_SRC(bpf->op) == BPF_SRC_K) |
| MCOperand_CreateImm0(MI, bpf->k); |
| else |
| CHECK_READABLE_AND_PUSH(ud, MI, bpf->src); |
| MCOperand_CreateImm0(MI, bpf->offset); |
| } |
| return true; |
| } |
| |
| static bool decodeReturn(cs_struct *ud, MCInst *MI, bpf_internal *bpf) |
| { |
| /* Here only handles the BPF_RET class in cBPF */ |
| switch (BPF_RVAL(bpf->op)) { |
| case BPF_SRC_K: |
| MCOperand_CreateImm0(MI, bpf->k); |
| return true; |
| case BPF_SRC_X: |
| MCOperand_CreateReg0(MI, BPF_REG_X); |
| return true; |
| case BPF_SRC_A: |
| MCOperand_CreateReg0(MI, BPF_REG_A); |
| return true; |
| } |
| return false; |
| } |
| |
| static bool decodeMISC(cs_struct *ud, MCInst *MI, bpf_internal *bpf) |
| { |
| uint16_t op = bpf->op ^ BPF_CLASS_MISC; |
| return op == BPF_MISCOP_TAX || op == BPF_MISCOP_TXA; |
| } |
| |
| ///< 1. Check if the instruction is valid |
| ///< 2. Set MI->opcode |
| ///< 3. Set MI->Operands |
| static bool getInstruction(cs_struct *ud, MCInst *MI, bpf_internal *bpf) |
| { |
| cs_detail *detail; |
| |
| detail = MI->flat_insn->detail; |
| // initialize detail |
| if (detail) { |
| memset(detail, 0, offsetof(cs_detail, bpf) + sizeof(cs_bpf)); |
| } |
| |
| MCInst_clear(MI); |
| MCInst_setOpcode(MI, bpf->op); |
| |
| switch (BPF_CLASS(bpf->op)) { |
| default: /* should never happen */ |
| return false; |
| case BPF_CLASS_LD: |
| case BPF_CLASS_LDX: |
| return decodeLoad(ud, MI, bpf); |
| case BPF_CLASS_ST: |
| case BPF_CLASS_STX: |
| return decodeStore(ud, MI, bpf); |
| case BPF_CLASS_ALU: |
| return decodeALU(ud, MI, bpf); |
| case BPF_CLASS_JMP: |
| return decodeJump(ud, MI, bpf); |
| case BPF_CLASS_RET: |
| /* eBPF doesn't have this class */ |
| if (EBPF_MODE(ud)) |
| return false; |
| return decodeReturn(ud, MI, bpf); |
| case BPF_CLASS_MISC: |
| /* case BPF_CLASS_ALU64: */ |
| if (EBPF_MODE(ud)) |
| return decodeALU(ud, MI, bpf); |
| else |
| return decodeMISC(ud, MI, bpf); |
| } |
| } |
| |
| bool BPF_getInstruction(csh ud, const uint8_t *code, size_t code_len, |
| MCInst *instr, uint16_t *size, uint64_t address, void *info) |
| { |
| cs_struct *cs; |
| bpf_internal *bpf; |
| |
| cs = (cs_struct*)ud; |
| if (EBPF_MODE(cs)) |
| bpf = fetch_ebpf(cs, code, code_len); |
| else |
| bpf = fetch_cbpf(cs, code, code_len); |
| if (bpf == NULL) |
| return false; |
| if (!getInstruction(cs, instr, bpf)) { |
| cs_mem_free(bpf); |
| return false; |
| } |
| |
| *size = bpf->insn_size; |
| cs_mem_free(bpf); |
| |
| return true; |
| } |
| |
| #endif |