############################################################################### # eBPF Processor Specification for Ghidra ############################################################################### #eBPF is a RISC register machine with a total of 11 64-bit registers, a program counter and a 512 byte fixed-size stack. #9 registers are general purpouse read-write, one is a read-only stack pointer and the program counter is implicit, #i.e. we can only jump to a certain offset from it. The eBPF registers are always 64-bit wide. define space ram type=ram_space size=8 default; define space syscall type=ram_space size=2; define space register type=register_space size=4; define register offset=0 size=8 [ R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 PC ]; # Instruction encoding: Insop:8, dst_reg:4, src_reg:4, off:16, imm:32 - from lsb to msb define token instr(64) imm=(32, 63) signed off=(16, 31) signed src=(12, 15) dst=(8, 11) op_alu_jmp_opcode=(4, 7) op_alu_jmp_source=(3, 3) op_ld_st_mode=(5, 7) op_ld_st_size=(3, 4) op_insn_class=(0, 2) ; #We'll need this token to operate with LDDW instruction, which has 64 bit imm value define token immtoken(64) imm2=(32, 63) ; #To operate with registers attach variables [ src dst ] [ R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 _ _ _ _ _ ]; #Definitions for blackbox in-kernel functions (eBPF helpers) define pcodeop bpf_unspec; define pcodeop bpf_map_lookup_elem; define pcodeop bpf_map_update_elem; define pcodeop bpf_map_delete_elem; define pcodeop bpf_probe_read; define pcodeop bpf_ktime_get_ns; define pcodeop bpf_trace_printk; define pcodeop bpf_get_prandom_u32; define pcodeop bpf_get_smp_processor_id; define pcodeop bpf_skb_store_bytes; define pcodeop bpf_l3_csum_replace; define pcodeop bpf_l4_csum_replace; define pcodeop bpf_tail_call; define pcodeop bpf_clone_redirect; define pcodeop bpf_get_current_pid_tgid; define pcodeop bpf_get_current_uid_gid; define pcodeop bpf_get_current_comm; define pcodeop bpf_get_cgroup_classid; define pcodeop bpf_skb_vlan_push; define pcodeop bpf_skb_vlan_pop; define pcodeop bpf_skb_get_tunnel_key; define pcodeop bpf_skb_set_tunnel_key; define pcodeop bpf_perf_event_read; define pcodeop bpf_redirect; define pcodeop bpf_get_route_realm; define pcodeop bpf_perf_event_output; define pcodeop bpf_skb_load_bytes; define pcodeop bpf_get_stackid; define pcodeop bpf_csum_diff; define pcodeop bpf_skb_get_tunnel_opt; define pcodeop bpf_skb_set_tunnel_opt; define pcodeop bpf_skb_change_proto; define pcodeop bpf_skb_change_type; define pcodeop bpf_skb_under_cgroup; define pcodeop bpf_get_hash_recalc; define pcodeop bpf_get_current_task; define pcodeop bpf_probe_write_user; define pcodeop bpf_undef; #Arithmetic instructions ############################################################################### :MOV dst, src is src & dst & op_alu_jmp_opcode=0xb & op_alu_jmp_source=1 & op_insn_class=0x7 { dst=src; } :MOV dst, imm is imm & dst & op_alu_jmp_opcode=0xb & op_alu_jmp_source=0 & op_insn_class=0x7 { dst=imm; } :ADD dst, src is src & dst & op_alu_jmp_opcode=0x0 & op_alu_jmp_source=1 & op_insn_class=0x7 { dst=dst + src; } :ADD dst, imm is imm & dst & op_alu_jmp_opcode=0x0 & op_alu_jmp_source=0 & op_insn_class=0x7 { dst=dst + imm; } :SUB dst, src is src & dst & op_alu_jmp_opcode=0x1 & op_alu_jmp_source=1 & op_insn_class=0x7 { dst=dst - src; } :SUB dst, imm is imm & dst & op_alu_jmp_opcode=0x1 & op_alu_jmp_source=0 & op_insn_class=0x7 { dst=dst - imm; } :MUL dst, src is src & dst & op_alu_jmp_opcode=0x2 & op_alu_jmp_source=1 & op_insn_class=0x7 { dst=dst * src; } :MUL dst, imm is imm & dst & op_alu_jmp_opcode=0x2 & op_alu_jmp_source=0 & op_insn_class=0x7 { dst=dst * imm; } :DIV dst, src is src & dst & op_alu_jmp_opcode=0x3 & op_alu_jmp_source=1 & op_insn_class=0x7 { dst=dst / src; } :DIV dst, imm is imm & dst & op_alu_jmp_opcode=0x3 & op_alu_jmp_source=0 & op_insn_class=0x7 { dst=dst / imm; } :OR dst, src is src & dst & op_alu_jmp_opcode=0x4 & op_alu_jmp_source=1 & op_insn_class=0x7 { dst=dst | src; } :OR dst, imm is imm & dst & op_alu_jmp_opcode=0x4 & op_alu_jmp_source=0 & op_insn_class=0x7 { dst=dst | imm; } :AND dst, src is src & dst & op_alu_jmp_opcode=0x5 & op_alu_jmp_source=1 & op_insn_class=0x7 { dst=dst & src; } :AND dst, imm is imm & dst & op_alu_jmp_opcode=0x5 & op_alu_jmp_source=0 & op_insn_class=0x7 { dst=dst & imm; } :LSH dst, src is src & dst & op_alu_jmp_opcode=0x6 & op_alu_jmp_source=1 & op_insn_class=0x7 { dst=dst << src; } :LSH dst, imm is imm & dst & op_alu_jmp_opcode=0x6 & op_alu_jmp_source=0 & op_insn_class=0x7 { dst=dst << imm; } :RSH dst, src is src & dst & op_alu_jmp_opcode=0x7 & op_alu_jmp_source=1 & op_insn_class=0x7 { dst=dst >> src; } :RSH dst, imm is imm & dst & op_alu_jmp_opcode=0x7 & op_alu_jmp_source=0 & op_insn_class=0x7 { dst=dst >> imm; } :NEG dst is dst & op_alu_jmp_opcode=0x8 & op_alu_jmp_source=0 & op_insn_class=0x7 { dst=-dst; } :MOD dst, src is src & dst & op_alu_jmp_opcode=0x9 & op_alu_jmp_source=1 & op_insn_class=0x7 { dst=dst % src; } :MOD dst, imm is imm & dst & op_alu_jmp_opcode=0x9 & op_alu_jmp_source=0 & op_insn_class=0x7 { dst=dst % imm; } :XOR dst, src is src & dst & op_alu_jmp_opcode=0xa & op_alu_jmp_source=1 & op_insn_class=0x7 { dst=dst ^ src; } :XOR dst, imm is imm & dst & op_alu_jmp_opcode=0xa & op_alu_jmp_source=0 & op_insn_class=0x7 { dst=dst ^ imm; } :ARSH dst, src is src & dst & op_alu_jmp_opcode=0xc & op_alu_jmp_source=1 & op_insn_class=0x7 { dst=dst s>> src; } :ARSH dst, imm is imm & dst & op_alu_jmp_opcode=0xc & op_alu_jmp_source=0 & op_insn_class=0x7 { dst=dst s>> imm; } #Bytewasp instructions ############################################################################### :LE16 dst is imm=0x10 & dst & op_alu_jmp_opcode=0xd & op_alu_jmp_source=0 & op_insn_class=0x4 { dst=((dst) >> 8) | ((dst) << 8); } :LE32 dst is imm=0x20 & dst & op_alu_jmp_opcode=0xd & op_alu_jmp_source=0 & op_insn_class=0x4 { dst=((dst) >> 24) | (((dst) & 0x00FF0000) >> 8) | (((dst) & 0x0000FF00) << 8) | ((dst) << 24); } :LE64 dst is imm=0x40 & dst & op_alu_jmp_opcode=0xd & op_alu_jmp_source=0 & op_insn_class=0x4 { dst=( (dst << 56) & 0xff00000000000000 ) | ( (dst << 40) & 0x00ff000000000000 ) | ( (dst << 24) & 0x0000ff0000000000 ) | ( (dst << 8) & 0x000000ff00000000 ) | ( (dst >> 8) & 0x00000000ff000000 ) | ( (dst >> 24) & 0x0000000000ff0000 ) | ( (dst >> 40) & 0x000000000000ff00 ) | ( (dst >> 56) & 0x00000000000000ff ); } :BE16 dst is imm=0x10 & dst & op_alu_jmp_opcode=0xd & op_alu_jmp_source=1 & op_insn_class=0x4 { dst=((dst) >> 8) | ((dst) << 8); } :BE32 dst is imm=0x20 & dst & op_alu_jmp_opcode=0xd & op_alu_jmp_source=1 & op_insn_class=0x4 { dst=((dst) >> 24) | (((dst) & 0x00FF0000) >> 8) | (((dst) & 0x0000FF00) << 8) | ((dst) << 24); } :BE64 dst is imm=0x40 & dst & op_alu_jmp_opcode=0xd & op_alu_jmp_source=1 & op_insn_class=0x4 { dst=( (dst << 56) & 0xff00000000000000 ) | ( (dst << 40) & 0x00ff000000000000 ) | ( (dst << 24) & 0x0000ff0000000000 ) | ( (dst << 8) & 0x000000ff00000000 ) | ( (dst >> 8) & 0x00000000ff000000 ) | ( (dst >> 24) & 0x0000000000ff0000 ) | ( (dst >> 40) & 0x000000000000ff00 ) | ( (dst >> 56) & 0x00000000000000ff ); } #Memory instructions- Load and Store ############################################################################### #LDDW is the only 16-byte eBPF instruction which consists of two consecutive 8-byte blocks ('struct bpf_insn') #and interpreted as single instruction which loads 64-bit imm value into dst. Encoding of LDDW: #LSR MSR # opcode src dst offset Low 8-byte imm zero-block High 8-byte imm #bits 8 4 4 16 32 32 32 # So, imm64 consists of concatination of high 8-byte imm and low 8-byte imm. :LDDW dst, concat is imm & dst & op_ld_st_mode=0x0 & op_ld_st_size=0x3 & op_insn_class=0x0; imm2 [ concat= (imm2 << 32) | imm; ] { dst = concat; } #BPF_LD_MAP_FD(DST, MAP_FD) -> second LDDW = pseudo LDDW insn used to refer to process-local map_fd #For each instruction which needs relocation, it inject corresponding file descriptor to imm field. #As a part of protocol, src_reg is set to BPF_PSEUDO_MAP_FD (which defined as 1) to notify kernel this is a map loading instruction. :LDDW dst, imm is imm & src=1 & dst & op_ld_st_mode=0x0 & op_ld_st_size=0x3 & op_insn_class=0x0; imm2 { dst = *:8 imm:8; } :LDABSW dst, imm is imm & dst & op_ld_st_mode=0x1 & op_ld_st_size=0x0 & op_insn_class=0x0 { dst=*:4 imm:8; } :LDABSH dst, imm is imm & dst & op_ld_st_mode=0x1 & op_ld_st_size=0x1 & op_insn_class=0x0 { dst=*:2 imm:8; } :LDABSB dst, imm is imm & dst & op_ld_st_mode=0x1 & op_ld_st_size=0x2 & op_insn_class=0x0 { dst=*:1 imm:8; } :LDABSDW dst, imm is imm & dst & op_ld_st_mode=0x1 & op_ld_st_size=0x3 & op_insn_class=0x0 { dst=*:8 imm:8; } :LDINDW src, dst, imm is imm & src & dst & op_ld_st_mode=0x2 & op_ld_st_size=0x0 & op_insn_class=0x0 { dst=*:4 (src + imm); } :LDINDH src, dst, imm is imm & src & dst & op_ld_st_mode=0x2 & op_ld_st_size=0x1 & op_insn_class=0x0 { dst=*:2 (src + imm); } :LDINDB src, dst, imm is imm & src & dst & op_ld_st_mode=0x2 & op_ld_st_size=0x2 & op_insn_class=0x0 { dst=*:1 (src + imm); } :LDINDDW src, dst, imm is imm & src & dst & op_ld_st_mode=0x2 & op_ld_st_size=0x3 & op_insn_class=0x0 { dst=*:8 (src + imm); } :LDXW dst, [src + off] is off & src & dst & op_ld_st_mode=0x3 & op_ld_st_size=0x0 & op_insn_class=0x1 { dst=*:4 (src + off); } :LDXH dst, [src + off] is off & src & dst & op_ld_st_mode=0x3 & op_ld_st_size=0x1 & op_insn_class=0x1 { dst=*:2 (src + off); } :LDXB dst, [src + off] is off & src & dst & op_ld_st_mode=0x3 & op_ld_st_size=0x2 & op_insn_class=0x1 { dst=*:1 (src + off); } :LDXDW dst, [src + off] is off & src & dst & op_ld_st_mode=0x3 & op_ld_st_size=0x3 & op_insn_class=0x1 { dst=*:8 (src + off); } :STW [dst + off], imm is imm & off & dst & op_ld_st_mode=0x3 & op_ld_st_size=0x0 & op_insn_class=0x2 { *:4 (dst + off)=imm:4; } :STH [dst + off], imm is imm & off & dst & op_ld_st_mode=0x3 & op_ld_st_size=0x1 & op_insn_class=0x2 { *:2 (dst + off)=imm:2; } :STB [dst + off], imm is imm & off & dst & op_ld_st_mode=0x3 & op_ld_st_size=0x2 & op_insn_class=0x2 { *:1 (dst + off)=imm:1; } :STDW [dst + off], imm is imm & off & dst & op_ld_st_mode=0x3 & op_ld_st_size=0x3 & op_insn_class=0x2 { *:8 (dst + off)=imm:8; } :STXW [dst + off], src is off & src & dst & op_ld_st_mode=0x3 & op_ld_st_size=0x0 & op_insn_class=0x3 { *:4 (dst + off)=src:4; } :STXH [dst + off], src is off & src & dst & op_ld_st_mode=0x3 & op_ld_st_size=0x1 & op_insn_class=0x3 { *:2 (dst + off)=src:2; } :STXB [dst + off], src is off & src & dst & op_ld_st_mode=0x3 & op_ld_st_size=0x2 & op_insn_class=0x3 { *:1 (dst + off)=src:1; } :STXDW [dst + off], src is off & src & dst & op_ld_st_mode=0x3 & op_ld_st_size=0x3 & op_insn_class=0x3 { *:8 (dst + off)=src:8; } :XADDStSrc dst, src is src & dst & op_ld_st_mode=0x6 & op_ld_st_size=0x3 & op_insn_class=0x3 { *:8 dst=*:8 dst + src; } #Branch instructions ############################################################################### joff: reloc is off [ reloc = inst_next + off * 8; ] { export *:8 reloc; } :JA joff is joff & op_alu_jmp_opcode=0x0 & op_alu_jmp_source=0 & op_insn_class=0x5 { goto joff; } :JEQ dst, imm, joff is imm & joff & dst & op_alu_jmp_opcode=0x1 & op_alu_jmp_source=0 & op_insn_class=0x5 { if (dst==imm) goto joff; } :JEQ dst, src, joff is joff & src & dst & op_alu_jmp_opcode=0x1 & op_alu_jmp_source=1 & op_insn_class=0x5 { if (dst==src) goto joff; } :JGT dst, imm, joff is imm & joff & dst & op_alu_jmp_opcode=0x2 & op_alu_jmp_source=0 & op_insn_class=0x5 { if (dst>imm) goto joff; } :JGT dst, src, joff is joff & src & dst & op_alu_jmp_opcode=0x2 & op_alu_jmp_source=1 & op_insn_class=0x5 { if (dst>src) goto joff; } :JGE dst, imm, joff is imm & joff & dst & op_alu_jmp_opcode=0x3 & op_alu_jmp_source=0 & op_insn_class=0x5 { if (dst>=imm) goto joff; } :JGE dst, src, joff is joff & src & dst & op_alu_jmp_opcode=0x3 & op_alu_jmp_source=1 & op_insn_class=0x5 { if (dst>=src) goto joff; } :JLT dst, imm, joff is imm & joff & dst & op_alu_jmp_opcode=0xa & op_alu_jmp_source=0 & op_insn_class=0x5 { if (dstimm) goto joff; } :JSGT dst, src, joff is joff & src & dst & op_alu_jmp_opcode=0x6 & op_alu_jmp_source=1 & op_insn_class=0x5 { if (dst s>src) goto joff; } :JSGE dst, imm, joff is imm & joff & dst & op_alu_jmp_opcode=0x7 & op_alu_jmp_source=0 & op_insn_class=0x5 { if (dst s>=imm) goto joff; } :JSGE dst, src, joff is joff & src & dst & op_alu_jmp_opcode=0x7 & op_alu_jmp_source=1 & op_insn_class=0x5 { if (dst s>=src) goto joff; } :JSLT dst, imm, joff is imm & joff & dst & op_alu_jmp_opcode=0xc & op_alu_jmp_source=0 & op_insn_class=0x5 { if (dst s