BPF指令集入门

BPF指令集入门

struct bpf_insn {
      u_short code;
      u_char  jt;
      u_char  jf;
      u_long k;
 };

操作码(指令集)

#define BPF_LD 0x00 // 从某个位置加载数据到寄存器A
#define BPF_LDX 0x01 // 从摸个位置加载到寄存器X
#define BPF_ST 0x02
#define BPF_STX 0x03
#define BPF_ALU 0x04
#define BPF_JMP 0x05
#define BPF_RET 0x06
#define BPF_MISC 0x07
#define BPF_SIZE(code) ((code) & 0x18)
#define BPF_W 0x00 // 4字节的玩意
#define BPF_H 0x08 // 2字节的玩意
#define BPF_B 0x10 // 1字节的玩意
#define BPF_MODE(code) ((code) & 0xe0)
#define BPF_IMM 0x00 // 表示数据来源是立即数
#define BPF_ABS 0x20 // 表示数据来源的绝对地址
#define BPF_IND 0x40
#define BPF_MEM 0x60
#define BPF_LEN 0x80
#define BPF_MSH 0xa0
#define BPF_OP(code) ((code) & 0xf0)
#define BPF_ADD 0x00
#define BPF_SUB 0x10
#define BPF_MUL 0x20
#define BPF_DIV 0x30
#define BPF_OR 0x40
#define BPF_AND 0x50
#define BPF_LSH 0x60
#define BPF_RSH 0x70
#define BPF_NEG 0x80
#define BPF_MOD 0x90
#define BPF_XOR 0xa0

#define BPF_JA 0x00 // 无条件跳转
#define BPF_JEQ 0x10 // 寄存器A和某个玩意相等
#define BPF_JGT 0x20  // 大于
#define BPF_JGE 0x30 // 大于等于
#define BPF_JSET 0x40

#define BPF_SRC(code) ((code) & 0x08)
#define BPF_K 0x00  // 表示源操作数是立即数
#define BPF_X 0x08 // 表示X寄存器数据

操作宏

#define BPF_RVAL(code) ((code) & 0x18)
#define BPF_A 0x10
#define BPF_MISCOP(code) ((code) & 0xf8)
#define BPF_TAX 0x00
#define BPF_TXA 0x80
#ifndef BPF_STMT
#define BPF_STMT(code,k) { (unsigned short) (code), 0, 0, k } // 执行BPF操作
#endif
#ifndef BPF_JUMP
#define BPF_JUMP(code,k,jt,jf) { (unsigned short) (code), jt, jf, k } // 
#endif

解析

说理念和说所谓的定义是非常弱智的,在提供定义之后整几个示例就简单多了。

BPF_STMT

加载数值 BPF_LD

BPF_STMT(BPF_LD | BPF_IMM, 114514)

上面这坨垃圾是把一个立即数保存到寄存器A里面去。

刚开始挺好奇有没有寄存器B的,如何发现BPF只有寄存器A和寄存器X。

上点难度,这行代码将会把struct seccomp_data结构体中nr成员的绝对地址加载到寄存器A中。

BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(struct seccomp_data, nr)))

跳转控制 BPF_JMP

BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(struct seccomp_data, nr))),
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_write, 0, 1),
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), // line3
// ...

这里的代码将通过绝对地址取出nr保存寄存器A里面,然后JMP指令,

  • 如果寄存器A的值等于BPF_K(__NR_write),则跳过0条指令,也就是执行line3的指令。
  • 反之,跳过BPF_JUMP下面的指令。

莫名其妙的地方

这个玩意我都使用BPF_JUMP宏了,理论上,这个就是一个朴实无华的跳转,我怎么还要写个BPF_JUMP(BPF_JMP....这个丑陋的开头,我也不知道,但是不写就会爆炸,hhh。