This commit is contained in:
2026-03-23 12:11:07 +01:00
commit e64eb40b38
4573 changed files with 3117439 additions and 0 deletions

View File

@@ -0,0 +1,103 @@
/* Capstone Disassembly Engine */
/* BPF Backend by david942j <david942j@gmail.com>, 2019 */
/* SPDX-FileCopyrightText: 2024 Roee Toledano <roeetoledano10@gmail.com> */
/* SPDX-License-Identifier: BSD-3 */
/* This file defines constants and macros used for parsing a BPF instruction */
#ifndef CS_BPF_CONSTANTS_H
#define CS_BPF_CONSTANTS_H
#define BPF_CLASS(code) ((code) & 0x7)
///< Instruction classes
#define BPF_CLASS_LD 0x00
#define BPF_CLASS_LDX 0x01
#define BPF_CLASS_ST 0x02
#define BPF_CLASS_STX 0x03
#define BPF_CLASS_ALU 0x04
#define BPF_CLASS_JMP 0x05
#define BPF_CLASS_RET 0x06 ///< cBPF only
#define BPF_CLASS_JMP32 0x06 ///< eBPF only
#define BPF_CLASS_MISC 0x07 ///< cBPF only
#define BPF_CLASS_ALU64 0x07 ///< eBPF only
#define BPF_OP(code) ((code) & 0xf0)
///< Types of ALU instruction
#define BPF_ALU_ADD 0x00
#define BPF_ALU_SUB 0x10
#define BPF_ALU_MUL 0x20
#define BPF_ALU_DIV 0x30
#define BPF_ALU_OR 0x40
#define BPF_ALU_AND 0x50
#define BPF_ALU_LSH 0x60
#define BPF_ALU_RSH 0x70
#define BPF_ALU_NEG 0x80
#define BPF_ALU_MOD 0x90
#define BPF_ALU_XOR 0xa0
#define BPF_ALU_MOV 0xb0 ///< eBPF only: mov reg to reg
#define BPF_ALU_ARSH 0xc0 ///< eBPF only: sign extending shift right
#define BPF_ALU_END 0xd0 ///< eBPF only: endianness conversion
///< Types of jmp instruction
#define BPF_JUMP_JA 0x00 ///< goto
#define BPF_JUMP_JEQ 0x10 ///< '=='
#define BPF_JUMP_JGT 0x20 ///< unsigned '>'
#define BPF_JUMP_JGE 0x30 ///< unsigned '>='
#define BPF_JUMP_JSET 0x40 ///< '&'
#define BPF_JUMP_JNE 0x50 ///< eBPF only: '!=' */
#define BPF_JUMP_JSGT 0x60 ///< eBPF only: signed '>'
#define BPF_JUMP_JSGE 0x70 ///< eBPF only: signed '>='
#define BPF_JUMP_CALL 0x80 ///< eBPF only: function call
#define BPF_JUMP_EXIT 0x90 ///< eBPF only: exit
#define BPF_JUMP_JLT 0xa0 ///< eBPF only: unsigned '<'
#define BPF_JUMP_JLE 0xb0 ///< eBPF only: unsigned '<='
#define BPF_JUMP_JSLT 0xc0 ///< eBPF only: signed '<'
#define BPF_JUMP_JSLE 0xd0 ///< eBPF only: signed '<='
///< Types of complex atomic instructions
#define BPF_ATOMIC_XCHG 0xe0 ///< eBPF only: atomic exchange
#define BPF_ATOMIC_CMPXCHG 0xf0 ///< eBPF only: atomic compare and exchange
#define BPF_SRC(code) ((code) & 0x08)
#define BPF_RVAL(code) ((code) & 0x18) /* cBPF only: for return types */
///< Source operand
#define BPF_SRC_K 0x00
#define BPF_SRC_X 0x08
#define BPF_SRC_A 0x10 /* cBPF only */
#define BPF_SRC_LITTLE BPF_SRC_K
#define BPF_SRC_BIG BPF_SRC_X
#define BPF_SIZE(code) ((code) & 0x18)
///< Size modifier
#define BPF_SIZE_W 0x00 ///< word
#define BPF_SIZE_H 0x08 ///< half word
#define BPF_SIZE_B 0x10 ///< byte
#define BPF_SIZE_DW 0x18 ///< eBPF only: double word
#define BPF_MODE(code) ((code) & 0xe0)
///< Mode modifier
#define BPF_MODE_IMM 0x00 ///< used for 32-bit mov in cBPF and 64-bit in eBPF
#define BPF_MODE_ABS \
0x20 ///< absolute indexing of socket buffer. eBPF only, but deprecated in new versions
#define BPF_MODE_IND \
0x40 ///< indirect indexing of socket buffer. eBPF only, but deprecated in new versions
#define BPF_MODE_MEM 0x60
#define BPF_MODE_LEN 0x80 ///< cBPF only, reserved in eBPF
#define BPF_MODE_MSH 0xa0 ///< cBPF only, reserved in eBPF
#define BPF_MODE_ATOMIC \
0xc0 ///< eBPF only: atomic operations. Originally BPF_MODE_XADD
#define BPF_MODE_FETCH \
0x01 /* eBPF only: overwrite 'src' with what was in the modified mem address before it was modified.
NOTE: in contrast to regular modifiers, this one is encoded in the 'imm' field, not opcode!
Must be used for BPF_XCHG and BPF_CMPXCHG. Optional for the other atomic operations. */
#define BPF_MISCOP(code) ((code) & 0x80)
///< Operation of misc
#define BPF_MISCOP_TAX 0x00
#define BPF_MISCOP_TXA 0x80
#endif

View File

@@ -0,0 +1,867 @@
/* Capstone Disassembly Engine */
/* BPF Backend by david942j <david942j@gmail.com>, 2019 */
/* SPDX-FileCopyrightText: 2024 Roee Toledano <roeetoledano10@gmail.com> */
/* SPDX-License-Identifier: BSD-3 */
#ifdef CAPSTONE_HAS_BPF
#include <string.h>
#include <stddef.h> // offsetof macro
#include "BPFConstants.h"
#include "BPFDisassembler.h"
#include "BPFMapping.h"
#include "../../Mapping.h"
#include "../../cs_priv.h"
#include "../../utils.h"
///< Malloc bpf_internal, also checks if code_len is large enough.
static bpf_internal *alloc_bpf_internal(const 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(MCInst *instr, const uint8_t *code,
const size_t code_len)
{
bpf_internal *bpf;
bpf = alloc_bpf_internal(code_len);
if (bpf == NULL)
return NULL;
bpf->op = readBytes16(instr, code);
bpf->jt = code[2];
bpf->jf = code[3];
bpf->k = readBytes32(instr, code + 4);
return bpf;
}
///< Fetch an eBPF structure from code
static bpf_internal *fetch_ebpf(MCInst *instr, const uint8_t *code,
const 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 = readBytes32(instr, code + 4) |
(((uint64_t)readBytes32(instr, code + 12)) << 32);
bpf->insn_size = 16;
} else {
bpf->offset = readBytes16(instr, code + 2);
bpf->k = readBytes32(instr, 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_WRITEABLE_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_WRITEABLE_REG(ud, r + BPF_REG_R0); \
MCOperand_CreateReg0(MI, r + BPF_REG_R0); \
} while (0)
static bool decodeLoad(MCInst *MI, bpf_internal *bpf)
{
if (!EBPF_MODE(MI->csh->mode)) {
/*
* +-----+-----------+--------------------+
* | 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} [k]
* - IND: ld{w,h,b} [src]
* - 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);
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(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(MI->csh->mode)) {
/* 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_ATOMIC) {
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(MCInst *MI, bpf_internal *bpf)
{
/* Set MI->Operands */
/* cBPF */
if (!EBPF_MODE(MI->csh->mode)) {
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 */
bpf->op |= ((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(MCInst *MI, bpf_internal *bpf)
{
/* cBPF and eBPF are very different in class jump */
if (!EBPF_MODE(MI->csh->mode)) {
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;
/* JMP32 has no CALL/EXIT instruction */
/* 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;
if (BPF_CLASS(bpf->op) == BPF_CLASS_JMP)
MCOperand_CreateImm0(MI, bpf->offset);
else
MCOperand_CreateImm0(MI, bpf->k);
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(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(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(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);
switch (BPF_CLASS(bpf->op)) {
default: /* should never happen */
return false;
case BPF_CLASS_LD:
case BPF_CLASS_LDX:
return decodeLoad(MI, bpf);
case BPF_CLASS_ST:
case BPF_CLASS_STX:
return decodeStore(MI, bpf);
case BPF_CLASS_ALU:
return decodeALU(MI, bpf);
case BPF_CLASS_JMP:
return decodeJump(MI, bpf);
case BPF_CLASS_RET:
/* case BPF_CLASS_JMP32: */
if (EBPF_MODE(MI->csh->mode))
return decodeJump(MI, bpf);
else
return decodeReturn(MI, bpf);
case BPF_CLASS_MISC:
/* case BPF_CLASS_ALU64: */
if (EBPF_MODE(MI->csh->mode))
return decodeALU(MI, bpf);
else
return decodeMISC(MI, bpf);
}
}
// Check for regular load instructions
#define REG_LOAD_CASE(c) \
case BPF_SIZE_##c: \
if (BPF_CLASS(opcode) == BPF_CLASS_LD) \
return BPF_INS_LD##c; \
else \
return BPF_INS_LDX##c;
static bpf_insn op2insn_ld_cbpf(unsigned opcode)
{
switch (BPF_SIZE(opcode)) {
REG_LOAD_CASE(W);
REG_LOAD_CASE(H);
REG_LOAD_CASE(B);
REG_LOAD_CASE(DW);
}
return BPF_INS_INVALID;
}
#undef REG_LOAD_CASE
// Check for packet load instructions
#define PACKET_LOAD_CASE(c) \
case BPF_SIZE_##c: \
if (BPF_MODE(opcode) == BPF_MODE_ABS) \
return BPF_INS_LDABS##c; \
else if (BPF_MODE(opcode) == BPF_MODE_IND) \
return BPF_INS_LDIND##c; \
else \
return BPF_INS_INVALID;
static bpf_insn op2insn_ld_ebpf(unsigned opcode)
{
if (BPF_CLASS(opcode) == BPF_CLASS_LD) {
switch (BPF_SIZE(opcode)) {
PACKET_LOAD_CASE(W);
PACKET_LOAD_CASE(H);
PACKET_LOAD_CASE(B);
}
}
// If it's not a packet load instruction, it must be a regular load instruction
return op2insn_ld_cbpf(opcode);
}
#undef PACKET_LOAD_CASE
/* During parsing we already checked to make sure the size is D/DW and
* mode is STX and not ST, so we don't need to check again*/
#define ALU_CASE_REG(c) \
case BPF_ALU_##c: \
if (BPF_SIZE(opcode) == BPF_SIZE_W) \
return BPF_INS_A##c; \
else \
return BPF_INS_A##c##64;
#define ALU_CASE_FETCH(c) \
case BPF_ALU_##c | BPF_MODE_FETCH: \
if (BPF_SIZE(opcode) == BPF_SIZE_W) \
return BPF_INS_AF##c; \
else \
return BPF_INS_AF##c##64;
#define COMPLEX_CASE(c) \
case BPF_ATOMIC_##c | BPF_MODE_FETCH: \
if (BPF_SIZE(opcode) == BPF_SIZE_DW) \
return BPF_INS_A##c##64; \
else \
return BPF_INS_INVALID;
#define CASE(c) \
case BPF_SIZE_##c: \
if (BPF_CLASS(opcode) == BPF_CLASS_ST) \
return BPF_INS_ST##c; \
else \
return BPF_INS_STX##c;
static bpf_insn op2insn_st(unsigned opcode, const uint32_t imm)
{
/*
* - BPF_STX | ALU atomic operations | BPF_{W,DW}
* - BPF_STX | Complex atomic operations | BPF_{DW}
* - BPF_ST* | BPF_MEM | BPF_{W,H,B,DW}
*/
if (BPF_MODE(opcode) == BPF_MODE_ATOMIC) {
switch (imm) {
ALU_CASE_REG(ADD);
ALU_CASE_REG(OR);
ALU_CASE_REG(AND);
ALU_CASE_REG(XOR);
ALU_CASE_FETCH(ADD);
ALU_CASE_FETCH(OR);
ALU_CASE_FETCH(AND);
ALU_CASE_FETCH(XOR);
COMPLEX_CASE(XCHG);
COMPLEX_CASE(CMPXCHG);
default: // Reached if complex atomic operation is used without fetch modifier
return BPF_INS_INVALID;
}
}
/* should be BPF_MEM */
switch (BPF_SIZE(opcode)) {
CASE(W);
CASE(H);
CASE(B);
CASE(DW);
}
return BPF_INS_INVALID;
}
#undef CASE
#define CASE(c) \
case BPF_ALU_##c: \
CASE_IF(c)
#define CASE_IF(c) \
do { \
if (BPF_CLASS(opcode) == BPF_CLASS_ALU) \
return BPF_INS_##c; \
else \
return BPF_INS_##c##64; \
} while (0)
static bpf_insn op2insn_alu(unsigned opcode, const uint16_t off,
const bool is_ebpf)
{
/* Endian is a special case */
if (BPF_OP(opcode) == BPF_ALU_END) {
if (BPF_CLASS(opcode) == BPF_CLASS_ALU64) {
switch (opcode ^ BPF_CLASS_ALU64 ^ BPF_ALU_END ^
BPF_SRC_LITTLE) {
case (16 << 4):
return BPF_INS_BSWAP16;
case (32 << 4):
return BPF_INS_BSWAP32;
case (64 << 4):
return BPF_INS_BSWAP64;
default:
return BPF_INS_INVALID;
}
}
switch (opcode ^ BPF_CLASS_ALU ^ BPF_ALU_END) {
case BPF_SRC_LITTLE | (16 << 4):
return BPF_INS_LE16;
case BPF_SRC_LITTLE | (32 << 4):
return BPF_INS_LE32;
case BPF_SRC_LITTLE | (64 << 4):
return BPF_INS_LE64;
case BPF_SRC_BIG | (16 << 4):
return BPF_INS_BE16;
case BPF_SRC_BIG | (32 << 4):
return BPF_INS_BE32;
case BPF_SRC_BIG | (64 << 4):
return BPF_INS_BE64;
}
return BPF_INS_INVALID;
}
switch (BPF_OP(opcode)) {
CASE(ADD);
CASE(SUB);
CASE(MUL);
CASE(OR);
CASE(AND);
CASE(LSH);
CASE(RSH);
CASE(NEG);
CASE(XOR);
CASE(ARSH);
case BPF_ALU_DIV:
if (!is_ebpf || off == 0)
CASE_IF(DIV);
else if (off == 1)
CASE_IF(SDIV);
else
return BPF_INS_INVALID;
case BPF_ALU_MOD:
if (!is_ebpf || off == 0)
CASE_IF(MOD);
else if (off == 1)
CASE_IF(SMOD);
else
return BPF_INS_INVALID;
case BPF_ALU_MOV:
/* BPF_CLASS_ALU can have: mov, mov8s, mov16s
* BPF_CLASS_ALU64 can have: mov, mov8s, mov16s, mov32s
* */
if (off == 0)
CASE_IF(MOV);
else if (off == 8)
CASE_IF(MOVSB);
else if (off == 16)
CASE_IF(MOVSH);
else if (off == 32 && BPF_CLASS(opcode) == BPF_CLASS_ALU64)
return BPF_INS_MOVSW64;
else
return BPF_INS_INVALID;
}
return BPF_INS_INVALID;
}
#undef CASE_IF
#undef CASE
#define BPF_CALLX (BPF_CLASS_JMP | BPF_JUMP_CALL | BPF_SRC_X)
#define CASE(c) \
case BPF_JUMP_##c: \
if (BPF_CLASS(opcode) == BPF_CLASS_JMP) \
return BPF_INS_##c; \
else \
return BPF_INS_##c##32;
#define SPEC_CASE(c) \
case BPF_JUMP_##c: \
if (BPF_CLASS(opcode) == BPF_CLASS_JMP) \
return BPF_INS_##c; \
else \
return BPF_INS_INVALID;
static bpf_insn op2insn_jmp(unsigned opcode)
{
if (opcode == BPF_CALLX) {
return BPF_INS_CALLX;
}
switch (BPF_OP(opcode)) {
case BPF_JUMP_JA:
if (BPF_CLASS(opcode) == BPF_CLASS_JMP)
return BPF_INS_JA;
else
return BPF_INS_JAL;
CASE(JEQ);
CASE(JGT);
CASE(JGE);
CASE(JSET);
CASE(JNE);
CASE(JSGT);
CASE(JSGE);
SPEC_CASE(CALL);
SPEC_CASE(EXIT);
CASE(JLT);
CASE(JLE);
CASE(JSLT);
CASE(JSLE);
}
return BPF_INS_INVALID;
}
#undef SPEC_CASE
#undef CASE
#undef BPF_CALLX
#ifndef CAPSTONE_DIET
static void update_regs_access(MCInst *MI, cs_detail *detail,
bpf_insn insn_id, unsigned int opcode)
{
if (insn_id == BPF_INS_INVALID)
return;
/*
* In eBPF mode, only these instructions have implicit registers access:
* - legacy ld{w,h,b,dw} * // w: r0
* - exit // r: r0
*/
if (EBPF_MODE(MI->csh->mode)) {
switch (insn_id) {
default:
break;
case BPF_INS_LDABSW:
case BPF_INS_LDABSH:
case BPF_INS_LDABSB:
case BPF_INS_LDINDW:
case BPF_INS_LDINDH:
case BPF_INS_LDINDB:
case BPF_INS_LDDW:
if (BPF_MODE(opcode) == BPF_MODE_ABS ||
BPF_MODE(opcode) == BPF_MODE_IND)
map_add_implicit_write(MI, BPF_REG_R0);
break;
case BPF_INS_EXIT:
map_add_implicit_read(MI, BPF_REG_R0);
break;
}
return;
}
/* cBPF mode */
switch (BPF_CLASS(opcode)) {
default:
break;
case BPF_CLASS_LD:
map_add_implicit_write(MI, BPF_REG_A);
break;
case BPF_CLASS_LDX:
map_add_implicit_write(MI, BPF_REG_X);
break;
case BPF_CLASS_ST:
map_add_implicit_read(MI, BPF_REG_A);
break;
case BPF_CLASS_STX:
map_add_implicit_read(MI, BPF_REG_X);
break;
case BPF_CLASS_ALU:
map_add_implicit_read(MI, BPF_REG_A);
map_add_implicit_write(MI, BPF_REG_A);
break;
case BPF_CLASS_JMP:
if (insn_id != BPF_INS_JA) // except the unconditional jump
map_add_implicit_read(MI, BPF_REG_A);
break;
/* case BPF_CLASS_RET: */
case BPF_CLASS_MISC:
if (insn_id == BPF_INS_TAX) {
map_add_implicit_read(MI, BPF_REG_A);
map_add_implicit_write(MI, BPF_REG_X);
} else {
map_add_implicit_read(MI, BPF_REG_X);
map_add_implicit_write(MI, BPF_REG_A);
}
break;
}
}
#endif
static bool setFinalOpcode(MCInst *MI, const bpf_internal *bpf)
{
bpf_insn id = BPF_INS_INVALID;
#ifndef CAPSTONE_DIET
cs_detail *detail;
detail = get_detail(MI);
#endif
const uint16_t opcode = bpf->op;
switch (BPF_CLASS(opcode)) {
default: // will never happen
break;
case BPF_CLASS_LD:
case BPF_CLASS_LDX:
if (EBPF_MODE(MI->csh->mode))
id = op2insn_ld_ebpf(opcode);
else
id = op2insn_ld_cbpf(opcode);
add_group(MI, BPF_GRP_LOAD);
break;
case BPF_CLASS_ST:
case BPF_CLASS_STX:
id = op2insn_st(opcode, bpf->k);
add_group(MI, BPF_GRP_STORE);
break;
case BPF_CLASS_ALU:
id = op2insn_alu(opcode, bpf->offset, EBPF_MODE(MI->csh->mode));
add_group(MI, BPF_GRP_ALU);
break;
case BPF_CLASS_JMP:
id = op2insn_jmp(opcode);
#ifndef CAPSTONE_DIET
if (id == BPF_INS_CALL || id == BPF_INS_CALLX)
add_group(MI, BPF_GRP_CALL);
else if (id == BPF_INS_EXIT)
add_group(MI, BPF_GRP_RETURN);
else
add_group(MI, BPF_GRP_JUMP);
#endif
break;
case BPF_CLASS_RET:
/* case BPF_CLASS_JMP32: */
if (EBPF_MODE(MI->csh->mode)) {
id = op2insn_jmp(opcode);
add_group(MI, BPF_GRP_JUMP);
} else {
id = BPF_INS_RET;
add_group(MI, BPF_GRP_RETURN);
}
break;
// BPF_CLASS_MISC and BPF_CLASS_ALU64 have exactly same value
case BPF_CLASS_MISC:
/* case BPF_CLASS_ALU64: */
if (EBPF_MODE(MI->csh->mode)) {
// ALU64 in eBPF
id = op2insn_alu(opcode, bpf->offset, true);
add_group(MI, BPF_GRP_ALU);
} else {
if (BPF_MISCOP(opcode) == BPF_MISCOP_TXA)
id = BPF_INS_TXA;
else
id = BPF_INS_TAX;
add_group(MI, BPF_GRP_MISC);
}
break;
}
if (id == BPF_INS_INVALID)
return false;
MCInst_setOpcodePub(MI, id);
#undef PUSH_GROUP
#ifndef CAPSTONE_DIET
if (detail) {
update_regs_access(MI, detail, id, opcode);
}
#endif
return true;
}
bool BPF_getInstruction(csh ud, const uint8_t *code, size_t code_len,
MCInst *instr, uint16_t *size, uint64_t address,
void *info)
{
bpf_internal *bpf;
if (EBPF_MODE(instr->csh->mode))
bpf = fetch_ebpf(instr, code, code_len);
else
bpf = fetch_cbpf(instr, code, code_len);
if (bpf == NULL)
return false;
if (!getInstruction(instr, bpf) || !setFinalOpcode(instr, bpf)) {
cs_mem_free(bpf);
return false;
}
MCInst_setOpcode(instr, bpf->op);
*size = bpf->insn_size;
cs_mem_free(bpf);
return true;
}
#endif

View File

@@ -0,0 +1,28 @@
/* Capstone Disassembly Engine */
/* BPF Backend by david942j <david942j@gmail.com>, 2019 */
#ifndef CS_BPF_DISASSEMBLER_H
#define CS_BPF_DISASSEMBLER_H
#include "../../MCInst.h"
typedef struct bpf_internal {
uint16_t op;
uint64_t k;
/* for cBPF */
uint8_t jt;
uint8_t jf;
/* for eBPF */
uint8_t dst;
uint8_t src;
uint16_t offset;
/* length of this bpf instruction */
uint8_t insn_size;
} bpf_internal;
bool BPF_getInstruction(csh ud, const uint8_t *code, size_t code_len,
MCInst *instr, uint16_t *size, uint64_t address,
void *info);
#endif

View File

@@ -0,0 +1,370 @@
/* Capstone Disassembly Engine */
/* BPF Backend by david942j <david942j@gmail.com>, 2019 */
/* SPDX-FileCopyrightText: 2024 Roee Toledano <roeetoledano10@gmail.com> */
/* SPDX-License-Identifier: BSD-3 */
#include <capstone/platform.h>
#include "BPFConstants.h"
#include "BPFInstPrinter.h"
#include "BPFMapping.h"
#include "../../Mapping.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, const bool is_signed)
{
cs_bpf_op *op = expand_bpf_operands(bpf);
op->type = BPF_OP_IMM;
op->imm = val;
op->is_signed = is_signed;
}
static void push_op_off(cs_bpf *bpf, uint32_t val, const bool is_signed)
{
cs_bpf_op *op = expand_bpf_operands(bpf);
op->type = BPF_OP_OFF;
op->off = val;
op->is_signed = is_signed;
}
static void push_op_mem(cs_bpf *bpf, bpf_reg reg, uint32_t val,
const bool is_signed, const bool is_pkt)
{
cs_bpf_op *op = expand_bpf_operands(bpf);
op->type = BPF_OP_MEM;
op->mem.base = reg;
op->mem.disp = val;
op->is_signed = is_signed;
op->is_pkt = is_pkt;
}
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;
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:
if (EBPF_MODE(MI->csh->mode)) {
push_op_reg(bpf,
MCOperand_getReg(
MCInst_getOperand(MI, 0)),
CS_AC_WRITE);
push_op_imm(bpf,
MCOperand_getImm(
MCInst_getOperand(MI, 1)),
false);
} else {
push_op_imm(bpf,
MCOperand_getImm(
MCInst_getOperand(MI, 0)),
false);
}
break;
case BPF_MODE_ABS:
op = MCInst_getOperand(MI, 0);
push_op_mem(bpf, BPF_REG_INVALID,
(uint32_t)MCOperand_getImm(op), EBPF_MODE(MI->csh->mode), true);
break;
case BPF_MODE_IND:
op = MCInst_getOperand(MI, 0);
if (EBPF_MODE(MI->csh->mode))
push_op_mem(bpf, MCOperand_getReg(op), 0x0,
true, true);
else {
op2 = MCInst_getOperand(MI, 1);
push_op_mem(bpf, MCOperand_getReg(op),
(uint32_t)MCOperand_getImm(op2),
false, true);
}
break;
case BPF_MODE_MEM:
if (EBPF_MODE(MI->csh->mode)) {
/* 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),
true, false);
} 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->mode)) {
// 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), true, false);
op = MCInst_getOperand(MI, 2);
if (MCOperand_isImm(op))
push_op_imm(bpf, MCOperand_getImm(op), false);
else if (MCOperand_isReg(op))
push_op_reg(bpf, MCOperand_getReg(op), CS_AC_READ);
return;
}
{
const bool is_jmp32 = EBPF_MODE(MI->csh->mode) &&
(BPF_CLASS(opcode) == BPF_CLASS_JMP32);
if (BPF_CLASS(opcode) == BPF_CLASS_JMP || is_jmp32) {
for (size_t i = 0; i < mc_op_count; i++) {
op = MCInst_getOperand(MI, i);
if (MCOperand_isImm(op)) {
/* Decide if we're using IMM or OFF here (and if OFF, then signed or unsigned):
*
* 1. any jump/jump32 + signed off (not including exit/call and ja on jump32) // eBPF
* 2. exit/call/ja + k // eBPF
* 3. ja + unsigned off // cBPF (cBPF programs can only jump forwards)
* 4. any jump {x,k}, +jt, +jf // cBPF
* */
if ((BPF_OP(opcode) == BPF_JUMP_JA &&
!is_jmp32) ||
(!EBPF_MODE(MI->csh->mode) &&
i >= 1) ||
(EBPF_MODE(MI->csh->mode) &&
i == 2))
push_op_off(
bpf,
MCOperand_getImm(op),
EBPF_MODE(
MI->csh->mode));
else
push_op_imm(
bpf,
MCOperand_getImm(op),
true);
} else if (MCOperand_isReg(op)) {
push_op_reg(bpf, MCOperand_getReg(op),
CS_AC_READ);
}
}
return;
}
}
if (!EBPF_MODE(MI->csh->mode)) {
/* In cBPF mode, all registers in operands are accessed as read */
for (size_t i = 0; i < mc_op_count; i++) {
op = MCInst_getOperand(MI, i);
if (MCOperand_isImm(op))
push_op_imm(bpf, MCOperand_getImm(op), false);
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), false);
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:
if (op->is_signed)
printInt32Hex(O, op->imm);
else
SStream_concat(O, "0x%" PRIx64, op->imm);
break;
case BPF_OP_OFF:
if (op->is_signed)
printInt16HexOffset(O, op->off);
else
SStream_concat(O, "+0x%" PRIx32, op->off);
break;
case BPF_OP_MEM:
SStream_concat(O, "[");
if (op->is_pkt && EBPF_MODE(MI->csh->mode)) {
SStream_concat(O, "skb");
if (op->mem.base != BPF_REG_INVALID)
SStream_concat(O, "+%s",
BPF_reg_name((csh)MI->csh,
op->mem.base));
else {
if (op->is_signed)
printInt32HexOffset(O, op->mem.disp);
else
SStream_concat(O, "+0x%" PRIx32,
op->mem.disp);
}
} else {
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) {
// if operation is signed, then it always uses off, not k
if (op->is_signed)
printInt16HexOffset(
O, op->mem.disp);
else if (op->is_pkt)
SStream_concat(O, "+0x%" PRIx32,
op->mem.disp);
else
SStream_concat(O, "+0x%" PRIx16,
op->mem.disp);
} else
SStream_concat(O, "0x%" PRIx32,
op->mem.disp);
}
if (op->mem.base == BPF_REG_INVALID &&
op->mem.disp == 0)
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)
{
cs_bpf bpf = { 0 };
/* set pubOpcode as instruction id */
SStream_concat(O, BPF_insn_name((csh)MI->csh, MCInst_getOpcodePub(MI)));
convert_operands(MI, &bpf);
for (size_t 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 (detail_is_set(MI)) {
MI->flat_insn->detail->bpf = bpf;
}
#endif
}

View File

@@ -0,0 +1,16 @@
/* Capstone Disassembly Engine */
/* BPF Backend by david942j <david942j@gmail.com>, 2019 */
#ifndef CS_BPFINSTPRINTER_H
#define CS_BPFINSTPRINTER_H
#include <capstone/capstone.h>
#include "../../MCInst.h"
#include "../../SStream.h"
struct SStream;
void BPF_printInst(MCInst *MI, struct SStream *O, void *Info);
#endif

238
external/capstone/arch/BPF/BPFMapping.c vendored Normal file
View File

@@ -0,0 +1,238 @@
/* Capstone Disassembly Engine */
/* BPF Backend by david942j <david942j@gmail.com>, 2019 */
/* SPDX-FileCopyrightText: 2024 Roee Toledano <roeetoledano10@gmail.com> */
/* SPDX-License-Identifier: BSD-3 */
#include <string.h>
#include "BPFConstants.h"
#include "BPFMapping.h"
#include "../../Mapping.h"
#include "../../utils.h"
#ifndef CAPSTONE_DIET
static const name_map group_name_maps[] = {
{ BPF_GRP_INVALID, NULL },
{ BPF_GRP_LOAD, "load" }, { BPF_GRP_STORE, "store" },
{ BPF_GRP_ALU, "alu" }, { BPF_GRP_JUMP, "jump" },
{ BPF_GRP_CALL, "call" }, { BPF_GRP_RETURN, "return" },
{ BPF_GRP_MISC, "misc" },
};
#endif
const char *BPF_group_name(csh handle, unsigned int id)
{
#ifndef CAPSTONE_DIET
return id2name(group_name_maps, ARR_SIZE(group_name_maps), id);
#else
return NULL;
#endif
}
#ifndef CAPSTONE_DIET
static const name_map insn_name_maps[BPF_INS_ENDING] = {
{ BPF_INS_INVALID, NULL },
{ BPF_INS_ADD, "add" }, { BPF_INS_SUB, "sub" },
{ BPF_INS_MUL, "mul" }, { BPF_INS_DIV, "div" },
{ BPF_INS_SDIV, "sdiv" }, { BPF_INS_OR, "or" },
{ BPF_INS_AND, "and" }, { BPF_INS_LSH, "lsh" },
{ BPF_INS_RSH, "rsh" }, { BPF_INS_NEG, "neg" },
{ BPF_INS_MOD, "mod" }, { BPF_INS_SMOD, "smod" },
{ BPF_INS_XOR, "xor" }, { BPF_INS_MOV, "mov" },
{ BPF_INS_MOVSB, "movsb" }, { BPF_INS_MOVSH, "movsh" },
{ BPF_INS_ARSH, "arsh" },
{ BPF_INS_ADD64, "add64" }, { BPF_INS_SUB64, "sub64" },
{ BPF_INS_MUL64, "mul64" }, { BPF_INS_DIV64, "div64" },
{ BPF_INS_SDIV64, "sdiv64" }, { BPF_INS_OR64, "or64" },
{ BPF_INS_AND64, "and64" }, { BPF_INS_LSH64, "lsh64" },
{ BPF_INS_RSH64, "rsh64" }, { BPF_INS_NEG64, "neg64" },
{ BPF_INS_MOD64, "mod64" }, { BPF_INS_SMOD64, "smod64" },
{ BPF_INS_XOR64, "xor64" }, { BPF_INS_MOV64, "mov64" },
{ BPF_INS_MOVSB64, "movsb64" }, { BPF_INS_MOVSH64, "movsh64" },
{ BPF_INS_MOVSW64, "movsw64" }, { BPF_INS_ARSH64, "arsh64" },
{ BPF_INS_LE16, "le16" }, { BPF_INS_LE32, "le32" },
{ BPF_INS_LE64, "le64" }, { BPF_INS_BE16, "be16" },
{ BPF_INS_BE32, "be32" }, { BPF_INS_BE64, "be64" },
{ BPF_INS_BSWAP16, "bswap16" }, { BPF_INS_BSWAP32, "bswap32" },
{ BPF_INS_BSWAP64, "bswap64" },
{ BPF_INS_LDW, "ldw" }, { BPF_INS_LDH, "ldh" },
{ BPF_INS_LDB, "ldb" }, { BPF_INS_LDDW, "lddw" },
{ BPF_INS_LDXW, "ldxw" }, { BPF_INS_LDXH, "ldxh" },
{ BPF_INS_LDXB, "ldxb" }, { BPF_INS_LDXDW, "ldxdw" },
{ BPF_INS_LDABSW, "ldabsw" }, { BPF_INS_LDABSH, "ldabsh" },
{ BPF_INS_LDABSB, "ldabsb" }, { BPF_INS_LDINDW, "ldindw" },
{ BPF_INS_LDINDH, "ldindh" }, { BPF_INS_LDINDB, "ldindb" },
{ BPF_INS_STW, "stw" }, { BPF_INS_STH, "sth" },
{ BPF_INS_STB, "stb" }, { BPF_INS_STDW, "stdw" },
{ BPF_INS_STXW, "stxw" }, { BPF_INS_STXH, "stxh" },
{ BPF_INS_STXB, "stxb" }, { BPF_INS_STXDW, "stxdw" },
{ BPF_INS_XADDW, "xaddw" }, { BPF_INS_XADDDW, "xadddw" },
{ BPF_INS_JA, "ja" }, { BPF_INS_JEQ, "jeq" },
{ BPF_INS_JGT, "jgt" }, { BPF_INS_JGE, "jge" },
{ BPF_INS_JSET, "jset" }, { BPF_INS_JNE, "jne" },
{ BPF_INS_JSGT, "jsgt" }, { BPF_INS_JSGE, "jsge" },
{ BPF_INS_CALL, "call" }, { BPF_INS_CALLX, "callx" },
{ BPF_INS_EXIT, "exit" }, { BPF_INS_JLT, "jlt" },
{ BPF_INS_JLE, "jle" }, { BPF_INS_JSLT, "jslt" },
{ BPF_INS_JSLE, "jsle" },
{ BPF_INS_JAL, "jal" }, { BPF_INS_JEQ32, "jeq32" },
{ BPF_INS_JGT32, "jgt32" }, { BPF_INS_JGE32, "jge32" },
{ BPF_INS_JSET32, "jset32" }, { BPF_INS_JNE32, "jne32" },
{ BPF_INS_JSGT32, "jsgt32" }, { BPF_INS_JSGE32, "jsge32" },
{ BPF_INS_JLT32, "jlt32" }, { BPF_INS_JLE32, "jle32" },
{ BPF_INS_JSLT32, "jslt32" }, { BPF_INS_JSLE32, "jsle32" },
{ BPF_INS_RET, "ret" },
{ BPF_INS_AADD, "aadd" }, { BPF_INS_AOR, "aor" },
{ BPF_INS_AAND, "aand" }, { BPF_INS_AXOR, "axor" },
{ BPF_INS_AFADD, "afadd" }, { BPF_INS_AFOR, "afor" },
{ BPF_INS_AFAND, "afand" }, { BPF_INS_AFXOR, "afxor" },
{ BPF_INS_AXCHG64, "axchg64" }, { BPF_INS_ACMPXCHG64, "acmpxchg64" },
{ BPF_INS_AADD64, "aadd64" }, { BPF_INS_AOR64, "aor64" },
{ BPF_INS_AAND64, "aand64" }, { BPF_INS_AXOR64, "axor64" },
{ BPF_INS_AFADD64, "afadd64" }, { BPF_INS_AFOR64, "afor64" },
{ BPF_INS_AFAND64, "afand64" }, { BPF_INS_AFXOR64, "afxor64" },
{ BPF_INS_TAX, "tax" }, { BPF_INS_TXA, "txa" },
};
#endif
bool BPF_getFeature(const cs_mode mode, const cs_mode feature)
{
return (mode & feature);
}
const char *BPF_insn_name(csh handle, unsigned int id)
{
#ifndef CAPSTONE_DIET
/* We have some special cases because 'ld' in cBPF is equivalent to 'ldw'
* in eBPF, and we don't want to see 'ldw' appears in cBPF mode.
*/
if (!EBPF_MODE(((cs_struct *)handle)->mode)) {
switch (id) {
case BPF_INS_LD:
return "ld";
case BPF_INS_LDX:
return "ldx";
case BPF_INS_ST:
return "st";
case BPF_INS_STX:
return "stx";
}
}
return id2name(insn_name_maps, ARR_SIZE(insn_name_maps), id);
#else
return NULL;
#endif
}
const char *BPF_reg_name(csh handle, unsigned int reg)
{
#ifndef CAPSTONE_DIET
if (EBPF_MODE(((cs_struct *)handle)->mode)) {
if (reg < BPF_REG_R0 || reg > BPF_REG_R10)
return NULL;
static const char reg_names[11][4] = { "r0", "r1", "r2", "r3",
"r4", "r5", "r6", "r7",
"r8", "r9", "r10" };
return reg_names[reg - BPF_REG_R0];
}
/* cBPF mode */
if (reg == BPF_REG_A)
return "a";
else if (reg == BPF_REG_X)
return "x";
else
return NULL;
#else
return NULL;
#endif
}
void BPF_get_insn_id(cs_struct *h, cs_insn *insn, unsigned int id)
{
// Not used by BPF. Information is set after disassembly.
}
static void sort_and_uniq(cs_regs arr, uint8_t n, uint8_t *new_n)
{
/* arr is always a tiny (usually n < 3) array,
* a simple O(n^2) sort is efficient enough. */
size_t iMin;
size_t tmp;
/* a modified selection sort for sorting and making unique */
for (size_t j = 0; j < n; j++) {
/* arr[iMin] will be min(arr[j .. n-1]) */
iMin = j;
for (size_t i = j + 1; i < n; i++) {
if (arr[i] < arr[iMin])
iMin = i;
}
if (j != 0 && arr[iMin] == arr[j - 1]) { // duplicate ele found
arr[iMin] = arr[n - 1];
--n;
} else {
tmp = arr[iMin];
arr[iMin] = arr[j];
arr[j] = tmp;
}
}
*new_n = n;
}
void BPF_reg_access(const cs_insn *insn, cs_regs regs_read,
uint8_t *regs_read_count, cs_regs regs_write,
uint8_t *regs_write_count)
{
unsigned i;
uint8_t read_count, write_count;
const cs_bpf *bpf = &(insn->detail->bpf);
read_count = insn->detail->regs_read_count;
write_count = insn->detail->regs_write_count;
// implicit registers
memcpy(regs_read, insn->detail->regs_read,
read_count * sizeof(insn->detail->regs_read[0]));
memcpy(regs_write, insn->detail->regs_write,
write_count * sizeof(insn->detail->regs_write[0]));
for (i = 0; i < bpf->op_count; i++) {
const cs_bpf_op *op = &(bpf->operands[i]);
switch (op->type) {
default:
break;
case BPF_OP_REG:
if (op->access & CS_AC_READ) {
regs_read[read_count] = op->reg;
read_count++;
}
if (op->access & CS_AC_WRITE) {
regs_write[write_count] = op->reg;
write_count++;
}
break;
case BPF_OP_MEM:
if (op->mem.base != BPF_REG_INVALID) {
regs_read[read_count] = op->mem.base;
read_count++;
}
break;
}
}
sort_and_uniq(regs_read, read_count, regs_read_count);
sort_and_uniq(regs_write, write_count, regs_write_count);
}

23
external/capstone/arch/BPF/BPFMapping.h vendored Normal file
View File

@@ -0,0 +1,23 @@
/* Capstone Disassembly Engine */
/* BPF Backend by david942j <david942j@gmail.com>, 2019 */
#ifndef CS_BPFMAPPING_H
#define CS_BPFMAPPING_H
#include <capstone/capstone.h>
#include "../../cs_priv.h"
bool BPF_getFeature(const cs_mode mode, const cs_mode feature);
#define EBPF_MODE(mode) BPF_getFeature(mode, CS_MODE_BPF_EXTENDED)
const char *BPF_group_name(csh handle, unsigned int id);
const char *BPF_insn_name(csh handle, unsigned int id);
const char *BPF_reg_name(csh handle, unsigned int reg);
void BPF_get_insn_id(cs_struct *h, cs_insn *insn, unsigned int id);
void BPF_reg_access(const cs_insn *insn, cs_regs regs_read,
uint8_t *regs_read_count, cs_regs regs_write,
uint8_t *regs_write_count);
#endif

34
external/capstone/arch/BPF/BPFModule.c vendored Normal file
View File

@@ -0,0 +1,34 @@
/* Capstone Disassembly Engine */
/* BPF Backend by david942j <david942j@gmail.com>, 2019 */
#ifdef CAPSTONE_HAS_BPF
#include "BPFDisassembler.h"
#include "BPFInstPrinter.h"
#include "BPFMapping.h"
#include "BPFModule.h"
cs_err BPF_global_init(cs_struct *ud)
{
ud->printer = BPF_printInst;
ud->reg_name = BPF_reg_name;
ud->insn_id = BPF_get_insn_id;
ud->insn_name = BPF_insn_name;
ud->group_name = BPF_group_name;
#ifndef CAPSTONE_DIET
ud->reg_access = BPF_reg_access;
#endif
ud->disasm = BPF_getInstruction;
return CS_ERR_OK;
}
cs_err BPF_option(cs_struct *handle, cs_opt_type type, size_t value)
{
if (type == CS_OPT_MODE)
handle->mode = (cs_mode)value;
return CS_ERR_OK;
}
#endif

12
external/capstone/arch/BPF/BPFModule.h vendored Normal file
View File

@@ -0,0 +1,12 @@
/* Capstone Disassembly Engine */
/* BPF Backend by david942j <david942j@gmail.com>, 2019 */
#ifndef CS_BPF_MODULE_H
#define CS_BPF_MODULE_H
#include "../../utils.h"
cs_err BPF_global_init(cs_struct *ud);
cs_err BPF_option(cs_struct *handle, cs_opt_type type, size_t value);
#endif