mirror of
https://github.com/capstone-engine/capstone.git
synced 2024-11-23 13:39:46 +00:00
464 lines
11 KiB
C
464 lines
11 KiB
C
/* 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
|