diff options
Diffstat (limited to 'capstone/arch/BPF/BPFDisassembler.c')
-rw-r--r-- | capstone/arch/BPF/BPFDisassembler.c | 458 |
1 files changed, 458 insertions, 0 deletions
diff --git a/capstone/arch/BPF/BPFDisassembler.c b/capstone/arch/BPF/BPFDisassembler.c new file mode 100644 index 000000000..cea475236 --- /dev/null +++ b/capstone/arch/BPF/BPFDisassembler.c @@ -0,0 +1,458 @@ +/* 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]; + + // 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->dst = code[1] & 0xf; + bpf->src = (code[1] & 0xf0) >> 4; + 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 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; + 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; + /* ALU64 class doesn't have ENDian */ + /* ENDian's imm must be one of 16, 32, 64 */ + if (BPF_OP(bpf->op) == BPF_ALU_END) { + if (BPF_CLASS(bpf->op) == BPF_CLASS_ALU64) + return false; + if (bpf->k != 16 && bpf->k != 32 && bpf->k != 64) + 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)) + return false; + MCOperand_CreateImm0(MI, bpf->k); + return true; + } + + /* 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 |