Skip to content

Commit

Permalink
Add Support for BPF arch (#144)
Browse files Browse the repository at this point in the history
  • Loading branch information
deepaksirone committed Apr 13, 2024
1 parent 752d24d commit 0fbf3c1
Show file tree
Hide file tree
Showing 10 changed files with 593 additions and 83 deletions.
102 changes: 102 additions & 0 deletions capstone-rs/src/arch/bpf.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
//! Contains bpf specific types

use core::convert::From;
use core::{cmp, fmt, slice};

pub use capstone_sys::bpf_insn_group as BpfInsnGroup;
pub use capstone_sys::bpf_insn as BpfInsn;
pub use capstone_sys::bpf_reg as BpfReg;
use capstone_sys::{cs_bpf, cs_bpf_op, bpf_op_mem, bpf_op_type};

pub use crate::arch::arch_builder::bpf::*;
use crate::arch::DetailsArchInsn;
use crate::instruction::{RegId, RegIdInt};

/// Contains BPF-specific details for an instruction
pub struct BpfInsnDetail<'a>(pub(crate) &'a cs_bpf);

impl_PartialEq_repr_fields!(BpfInsnDetail<'a> [ 'a ];
operands
);

/// BPF operand
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum BpfOperand {
/// Register
Reg(RegId),

/// Immediate
Imm(u64),

/// Memory
Mem(BpfOpMem),

/// Offset
Off(u32),

/// Mmem
Mmem(u32),

/// Msh
Msh(u32),

/// Ext
Ext(u32),

/// Invalid
Invalid,
}

impl Default for BpfOperand {
fn default() -> Self {
BpfOperand::Invalid
}
}


/// Bpf memory operand
#[derive(Debug, Copy, Clone)]
pub struct BpfOpMem(pub(crate) bpf_op_mem);

impl BpfOpMem {
/// Base register
pub fn base(&self) -> RegId {
RegId(self.0.base as RegIdInt)
}

/// Disp value
pub fn disp(&self) -> u32 {
self.0.disp
}
}

impl_PartialEq_repr_fields!(BpfOpMem;
base, disp
);

impl cmp::Eq for BpfOpMem {}

impl<'a> From<&'a cs_bpf_op> for BpfOperand {
fn from(insn: &cs_bpf_op) -> BpfOperand {
match insn.type_ {
bpf_op_type::BPF_OP_EXT => BpfOperand::Ext(unsafe { insn.__bindgen_anon_1.ext }),
bpf_op_type::BPF_OP_INVALID => BpfOperand::Invalid,
bpf_op_type::BPF_OP_REG => BpfOperand::Reg(RegId(unsafe {insn.__bindgen_anon_1.reg} as RegIdInt)),
bpf_op_type::BPF_OP_IMM => BpfOperand::Imm(unsafe { insn.__bindgen_anon_1.imm }),
bpf_op_type::BPF_OP_MEM => BpfOperand::Mem(BpfOpMem(unsafe { insn.__bindgen_anon_1.mem})),
bpf_op_type::BPF_OP_OFF => BpfOperand::Off(unsafe { insn.__bindgen_anon_1.off }),
bpf_op_type::BPF_OP_MMEM => BpfOperand::Mmem(unsafe { insn.__bindgen_anon_1.mmem }),
bpf_op_type::BPF_OP_MSH => BpfOperand::Msh(unsafe { insn.__bindgen_anon_1.msh }),
}
}
}

def_arch_details_struct!(
InsnDetail = BpfInsnDetail;
Operand = BpfOperand;
OperandIterator = BpfOperandIterator;
OperandIteratorLife = BpfOperandIterator<'a>;
[ pub struct BpfOperandIterator<'a>(slice::Iter<'a, cs_bpf_op>); ]
cs_arch_op = cs_bpf_op;
cs_arch = cs_bpf;
);
17 changes: 17 additions & 0 deletions capstone-rs/src/arch/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,16 @@ macro_rules! arch_info_base {
( syntax: )
( both_endian: false )
]
[
( bpf, BPF )
( mode:
Cbpf,
Ebpf,
)
( extra_modes: )
( syntax: )
( both_endian: true )
]
);
};
}
Expand Down Expand Up @@ -534,6 +544,13 @@ macro_rules! detail_arch_base {
/// Returns the XCore details, if any
=> arch_name = xcore,
]
[
detail = BpfDetail,
insn_detail = BpfInsnDetail<'a>,
op = BpfOperand,
/// Returns the XCore details, if any
=> arch_name = bpf,
]
);
};
}
Expand Down
6 changes: 6 additions & 0 deletions capstone-rs/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,8 @@ define_cs_enum_wrapper!(
=> EVM = CS_ARCH_EVM;
/// RISC-V
=> RISCV = CS_ARCH_RISCV;
/// BPF
=> BPF = CS_ARCH_BPF;
);

define_cs_enum_wrapper!(
Expand Down Expand Up @@ -286,6 +288,10 @@ define_cs_enum_wrapper!(
=> RiscV32 = CS_MODE_RISCV32;
/// RISC-V 64-bit mode
=> RiscV64 = CS_MODE_RISCV64;
/// Classic BPF mode
=> Cbpf = CS_MODE_BPF_CLASSIC;
/// Extended BPF mode
=> Ebpf = CS_MODE_BPF_EXTENDED;
/// Default mode for little-endian
=> Default = CS_MODE_LITTLE_ENDIAN;
);
Expand Down
1 change: 1 addition & 0 deletions capstone-rs/src/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,7 @@ impl<'a> InsnDetail<'a> {
[TMS320C64X, Tms320c64xDetail, Tms320c64xInsnDetail, tms320c64x]
[X86, X86Detail, X86InsnDetail, x86]
[XCORE, XcoreDetail, XcoreInsnDetail, xcore]
[BPF, BpfDetail, BpfInsnDetail, bpf]
);
}
}
Expand Down
193 changes: 193 additions & 0 deletions capstone-rs/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,17 @@ use super::*;

const X86_CODE: &[u8] = b"\x55\x48\x8b\x05\xb8\x13\x00\x00";
const ARM_CODE: &[u8] = b"\x55\x48\x8b\x05\xb8\x13\x00\x00";
const CBPF_CODE: &[u8] = b"\x94\x09\x00\x00\x37\x13\x03\x00\
\x87\x00\x00\x00\x00\x00\x00\x00\
\x07\x00\x00\x00\x00\x00\x00\x00\
\x16\x00\x00\x00\x00\x00\x00\x00\
\x80\x00\x00\x00\x00\x00\x00\x00";
const EBPF_CODE: &[u8] = b"\x97\x09\x00\x00\x37\x13\x03\x00\
\xdc\x02\x00\x00\x20\x00\x00\x00\
\x30\x00\x00\x00\x00\x00\x00\x00\
\xdb\x3a\x00\x01\x00\x00\x00\x00\
\x84\x02\x00\x00\x00\x00\x00\x00\
\x6d\x33\x17\x02\x00\x00\x00\x00";

// Aliases for group types
const JUMP: cs_group_type::Type = cs_group_type::CS_GRP_JUMP;
Expand Down Expand Up @@ -3244,3 +3255,185 @@ fn test_owned_insn() {
assert_eq!(format!("{:?}", insn), format!("{:?}", owned));
}
}

/// Print register names
fn reg_names(cs: &Capstone, regs: &[RegId]) -> String {
let names: Vec<String> = regs.iter().map(|&x| cs.reg_name(x).unwrap()).collect();
names.join(", ")
}

/// Print instruction group names
fn group_names(cs: &Capstone, regs: &[InsnGroupId]) -> String {
let names: Vec<String> = regs.iter().map(|&x| cs.group_name(x).unwrap()).collect();
names.join(", ")
}

#[test]
fn test_cbpf() {
let cs = Capstone::new()
.bpf()
.mode(bpf::ArchMode::Cbpf)
.endian(Endian::Little)
.detail(true)
.build()
.unwrap();
let insns = cs.disasm_all(CBPF_CODE, 0x1000);
match insns {
Ok(ins) => {
for i in ins.as_ref() {
println!();
eprintln!("{}", i);

let detail: InsnDetail = cs.insn_detail(&i).expect("Failed to get insn detail");
let arch_detail: ArchDetail = detail.arch_detail();
let ops = arch_detail.operands();

let output: &[(&str, String)] = &[
("insn id:", format!("{:?}", i.id().0)),
("bytes:", format!("{:?}", i.bytes())),
("read regs:", reg_names(&cs, detail.regs_read())),
("write regs:", reg_names(&cs, detail.regs_write())),
("insn groups:", group_names(&cs, detail.groups())),
];

for &(ref name, ref message) in output.iter() {
eprintln!("{:4}{:12} {}", "", name, message);
}

println!("{:4}operands: {}", "", ops.len());
for op in ops {
eprintln!("{:8}{:?}", "", op);
}
}
}

Err(e) => {
eprintln!("{:?}", e);
assert!(false);
}
}
}

#[test]
fn test_ebpf() {
let cs = Capstone::new()
.bpf()
.mode(bpf::ArchMode::Ebpf)
.endian(Endian::Little)
.detail(true)
.build()
.unwrap();
let insns = cs.disasm_all(EBPF_CODE, 0x1000);
match insns {
Ok(ins) => {
for i in ins.as_ref() {
println!();
eprintln!("{}", i);

let detail: InsnDetail = cs.insn_detail(&i).expect("Failed to get insn detail");
let arch_detail: ArchDetail = detail.arch_detail();
let ops = arch_detail.operands();

let output: &[(&str, String)] = &[
("insn id:", format!("{:?}", i.id().0)),
("bytes:", format!("{:?}", i.bytes())),
("read regs:", reg_names(&cs, detail.regs_read())),
("write regs:", reg_names(&cs, detail.regs_write())),
("insn groups:", group_names(&cs, detail.groups())),
];

for &(ref name, ref message) in output.iter() {
eprintln!("{:4}{:12} {}", "", name, message);
}

println!("{:4}operands: {}", "", ops.len());
for op in ops {
eprintln!("{:8}{:?}", "", op);
}
}
}

Err(e) => {
eprintln!("{:?}", e);
assert!(false);
}
}
}

#[test]
fn test_arch_bpf_detail() {
use crate::arch::bpf::BpfOperand::*;
use crate::arch::bpf::BpfReg::*;
use crate::arch::bpf::*;
use capstone_sys::*;

test_arch_mode_endian_insns_detail(
&mut Capstone::new()
.bpf()
.mode(bpf::ArchMode::Ebpf)
.endian(Endian::Little)
.detail(true)
.build()
.unwrap(),
Arch::BPF,
Mode::Ebpf,
None,
&[],
&[
// r1 = 0x1
DII::new(
"mov64",
b"\xb7\x01\x00\x00\x01\x00\x00\x00",
&[Reg(RegId(BPF_REG_R1 as RegIdInt)), Imm(1)],
),
// r0 = *(u32 *)(r10 - 0xc)
DII::new(
"ldxw",
b"\x61\xa0\xf4\xff\x00\x00\x00\x00",
&[
Reg(RegId(BPF_REG_R0 as RegIdInt)),
Mem(BpfOpMem(bpf_op_mem {
base: BPF_REG_R10,
disp: 0xfff4,
})),
],
),
// *(u32 *)(r10 - 0xc) = r1
DII::new(
"stxw",
b"\x63\x1a\xf4\xff\x00\x00\x00\x00",
&[
Mem(BpfOpMem(bpf_op_mem {
base: BPF_REG_R10,
disp: 0xfff4,
})),
Reg(RegId(BPF_REG_R1 as RegIdInt)),
],
),
// exit
DII::new("exit", b"\x95\x00\x00\x00\x00\x00\x00\x00", &[]),
],
);

test_arch_mode_endian_insns_detail(
&mut Capstone::new()
.bpf()
.mode(bpf::ArchMode::Cbpf)
.endian(Endian::Little)
.detail(true)
.build()
.unwrap(),
Arch::BPF,
Mode::Cbpf,
None,
&[],
&[
DII::new("txa", b"\x87\x00\x00\x00\x00\x00\x00\x00", &[]),
DII::new(
"ret",
b"\x16\x00\x00\x00\x00\x00\x00\x00",
&[Reg(RegId(BPF_REG_A as RegIdInt))],
),
],
);
}
2 changes: 1 addition & 1 deletion capstone-sys/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ travis-ci = { repository = "capstone-rust/capstone-sys" }
libc = { version = "0.2.59", default-features = false }

[build-dependencies]
bindgen = { optional = true, version = "0.59.1" }
bindgen = { optional = true, version = "0.62.0" }
regex = { optional = true, version = "1.3.1" }
cc = "1.0"

Expand Down
1 change: 1 addition & 0 deletions capstone-sys/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ fn build_capstone_cc() {
.define("CAPSTONE_HAS_WASM", None)
.define("CAPSTONE_HAS_X86", None)
.define("CAPSTONE_HAS_XCORE", None)
.define("CAPSTONE_HAS_BPF", None)
// No need to display any warnings from the C library
.flag_if_supported("-w")
.static_crt(use_static_crt);
Expand Down
4 changes: 4 additions & 0 deletions capstone-sys/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ pub static ARCH_INCLUDES: &[CapstoneArchInfo<'static>] = &[
header_name: "xcore.h",
cs_name: "xcore",
},
CapstoneArchInfo {
header_name: "bpf.h",
cs_name: "bpf"
}
];

pub static BINDINGS_FILE: &str = "capstone.rs";
Expand Down
Loading

0 comments on commit 0fbf3c1

Please sign in to comment.