class Metasm::EBPF
Public Class Methods
new(family = :latest, endianness = :big)
click to toggle source
Calls superclass method
# File metasm/cpu/ebpf/main.rb, line 45 def initialize(family = :latest, endianness = :big) super() @endianness = endianness @size = 64 @family = family end
Public Instance Methods
addop(name, bin, *args)
click to toggle source
# File metasm/cpu/ebpf/opcodes.rb, line 12 def addop(name, bin, *args) o = Opcode.new name, bin args.each { |a| o.args << a if @valid_args[a] o.props.update a if a.kind_of?(::Hash) } @opcode_list << o end
addop_alu(name, bin)
click to toggle source
# File metasm/cpu/ebpf/opcodes.rb, line 21 def addop_alu(name, bin) addop name, bin | 0x07, :rd, :i addop name, bin | 0x0F, :rd, :rs addop name+'32', bin | 0x04, :rd, :i addop name+'32', bin | 0x0C, :rd, :rs end
addop_j(name, bin)
click to toggle source
# File metasm/cpu/ebpf/opcodes.rb, line 44 def addop_j(name, bin) addop name, bin | 0x00, :rd, :i, :off, :setip => true addop name, bin | 0x08, :rd, :rs, :off, :setip => true end
addop_sz(name, bin, dst, src)
click to toggle source
# File metasm/cpu/ebpf/opcodes.rb, line 39 def addop_sz(name, bin, dst, src) addop_sz32(name, bin, dst, src) addop name + 'dw', bin | 0x18, dst, src, :msz => 8 end
addop_sz32(name, bin, dst, src)
click to toggle source
# File metasm/cpu/ebpf/opcodes.rb, line 28 def addop_sz32(name, bin, dst, src) addop name + 'w', bin | 0x00, dst, src, :msz => 4 addop name + 'h', bin | 0x08, dst, src, :msz => 2 addop name + 'b', bin | 0x10, dst, src, :msz => 1 end
addop_sz64(name, bin, dst, src)
click to toggle source
# File metasm/cpu/ebpf/opcodes.rb, line 34 def addop_sz64(name, bin, dst, src) addop name + 'w', bin | 0x00, dst, src, :msz => 4 addop name + 'dw', bin | 0x18, dst, src, :msz => 8 end
build_bin_lookaside()
click to toggle source
# File metasm/cpu/ebpf/decode.rb, line 12 def build_bin_lookaside opcode_list.inject({}) { |h, op| h.update op.bin => op } end
dbg_disable_bp(dbg, bp)
click to toggle source
# File metasm/cpu/ebpf/debug.rb, line 58 def dbg_disable_bp(dbg, bp) end
dbg_enable_bp(dbg, bp)
click to toggle source
# File metasm/cpu/ebpf/debug.rb, line 55 def dbg_enable_bp(dbg, bp) end
dbg_flag_list()
click to toggle source
# File metasm/cpu/ebpf/debug.rb, line 22 def dbg_flag_list @dbg_flag_list ||= [] end
dbg_need_stepover(dbg, addr, di)
click to toggle source
# File metasm/cpu/ebpf/debug.rb, line 30 def dbg_need_stepover(dbg, addr, di) false end
dbg_register_list()
click to toggle source
# File metasm/cpu/ebpf/debug.rb, line 18 def dbg_register_list @dbg_register_list ||= [:r0, :r1, :r2, :r3, :r4, :r5, :r6, :r7, :r8, :r9, :r10, :pc] end
dbg_register_pc()
click to toggle source
# File metasm/cpu/ebpf/debug.rb, line 11 def dbg_register_pc @dbg_register_pc ||= :pc end
dbg_register_size()
click to toggle source
# File metasm/cpu/ebpf/debug.rb, line 26 def dbg_register_size @dbg_register_size ||= Hash.new(64) end
dbg_register_sp()
click to toggle source
# File metasm/cpu/ebpf/debug.rb, line 14 def dbg_register_sp @dbg_register_sp ||= :r10 end
dbg_resolve_pc(di, fbd, pc_reg, dbg_ctx)
click to toggle source
Calls superclass method
# File metasm/cpu/ebpf/debug.rb, line 34 def dbg_resolve_pc(di, fbd, pc_reg, dbg_ctx) a = di.instruction.args.map { |aa| symbolic(aa) } cond = case di.opcode.name when 'jeq'; dbg_ctx.resolve(a[0]) == dbg_ctx.resolve(a[1]) when 'jgt'; dbg_ctx.resolve(a[0]) > dbg_ctx.resolve(a[1]) when 'jge'; dbg_ctx.resolve(a[0]) >= dbg_ctx.resolve(a[1]) when 'jset'; dbg_ctx.resolve(a[0]) & dbg_ctx.resolve(a[1]) > 0 when 'jne'; dbg_ctx.resolve(a[0]) != dbg_ctx.resolve(a[1]) when 'jsgt'; Expression.make_signed(dbg_ctx.resolve(a[0]), 64) > Expression.make_signed(dbg_ctx.resolve(a[1]), 64) when 'jsge'; Expression.make_signed(dbg_ctx.resolve(a[0]), 64) >= Expression.make_signed(dbg_ctx.resolve(a[1]), 64) else return super(di, fbd, pc_reg, dbg_ctx) end if cond fbd[pc_reg] = a.last else fbd[pc_reg] = di.next_addr end end
decode_findopcode(edata)
click to toggle source
tries to find the opcode encoded at edata.ptr
# File metasm/cpu/ebpf/decode.rb, line 17 def decode_findopcode(edata) return if edata.ptr > edata.data.length-8 di = DecodedInstruction.new self code_off = (@endianness == :little ? 0 : 7) code = edata.data[edata.ptr+code_off, 1].unpack('C')[0] return di if di.opcode = @bin_lookaside[code] end
decode_instr_interpret(di, addr)
click to toggle source
# File metasm/cpu/ebpf/decode.rb, line 61 def decode_instr_interpret(di, addr) if di.opcode.props[:setip] delta = di.instruction.args[-1].reduce + 1 arg = Expression[addr, :+, 8*delta].reduce di.instruction.args[-1] = Expression[arg] end di end
decode_instr_op(edata, di)
click to toggle source
# File metasm/cpu/ebpf/decode.rb, line 25 def decode_instr_op(edata, di) op = di.opcode di.instruction.opname = op.name di.bin_length = 8 blob = edata.decode_imm(:u64, @endianness) imm = (blob >> 32) & 0xffff_ffff imm = Expression.make_signed(imm, 32) off = (blob >> 16) & 0xffff off = Expression.make_signed(off, 16) src = (blob >> 12) & 0xf dst = (blob >> 8) & 0xf #code = blob & 0xff if di.opcode.props[:imm64] imm = (imm & 0xffff_ffff) | (edata.decode_imm(:u64, @endianness) & 0xffff_ffff_0000_0000) # next_imm << 32 di.bin_length += 8 end op.args.each { |a| di.instruction.args << case a when :i; Expression[imm] when :r0; Reg.new(0) when :rs; Reg.new(src) when :rd; Reg.new(dst) when :off; Expression[off] when :p_rs_o; Memref.new(Reg.new(src), Expression[off], op.props[:msz]) when :p_rd_o; Memref.new(Reg.new(dst), Expression[off], op.props[:msz]) when :p_pkt_i; Pktref.new(nil, Expression[imm], op.props[:msz]) when :p_pkt_rs_i; Pktref.new(Reg.new(src), Expression[imm], op.props[:msz]) else raise "unhandled arg #{a}" end } di end
init_backtrace_binding()
click to toggle source
populate the @backtrace_binding hash with default values
# File metasm/cpu/ebpf/decode.rb, line 72 def init_backtrace_binding @backtrace_binding ||= {} bswap = lambda { |val, nbytes| case nbytes when 1; val when 2; Expression[[[val, :&, 0xff], :<<, 8], :|, [[val, :&, 0xff00], :>>, 8]] when 4; Expression[[bswap[Expression[val, :&, 0xffff], 2], :<<, 16], :|, bswap[Expression[[val, :>>, 16], :&, 0xffff], 2]] when 8; Expression[[bswap[Expression[val, :&, 0xffffffff], 4], :<<, 32], :|, bswap[Expression[[val, :>>, 32], :&, 0xffffffff], 4]] end } opcode_list.map { |ol| ol.basename }.uniq.sort.each { |op| binding = case op when 'add'; lambda { |di, a0, a1| { a0 => Expression[a0, :+, a1] } } when 'sub'; lambda { |di, a0, a1| { a0 => Expression[a0, :-, a1] } } when 'mul'; lambda { |di, a0, a1| { a0 => Expression[[a0, :*, a1], :&, 0xffff_ffff_ffff_ffff] } } when 'div'; lambda { |di, a0, a1| { a0 => Expression[a0, :/, a1] } } when 'or'; lambda { |di, a0, a1| { a0 => Expression[a0, :|, a1] } } when 'and'; lambda { |di, a0, a1| { a0 => Expression[a0, :&, a1] } } when 'shl'; lambda { |di, a0, a1| { a0 => Expression[[a0, :<<, a1], :&, 0xffff_ffff_ffff_ffff] } } when 'shr'; lambda { |di, a0, a1| { a0 => Expression[a0, :>>, a1] } } # XXX sign when 'neg'; lambda { |di, a0| { a0 => Expression[:-, a0] } } when 'mod'; lambda { |di, a0, a1| { a0 => Expression[a0, :%, a1] } } when 'xor'; lambda { |di, a0, a1| { a0 => Expression[a0, :^, a1] } } when 'mov'; lambda { |di, a0, a1| { a0 => Expression[a1] } } when 'sar'; lambda { |di, a0, a1| { a0 => Expression[a0, :>>, a1] } } when 'add32'; lambda { |di, a0, a1| { a0 => Expression[[a0, :+, a1], :&, 0xffff_ffff] } } when 'sub32'; lambda { |di, a0, a1| { a0 => Expression[[a0, :-, a1], :&, 0xffff_ffff] } } when 'mul32'; lambda { |di, a0, a1| { a0 => Expression[[a0, :*, a1], :&, 0xffff_ffff] } } when 'div32'; lambda { |di, a0, a1| { a0 => Expression[[a0, :/, a1], :&, 0xffff_ffff] } } when 'or32'; lambda { |di, a0, a1| { a0 => Expression[[a0, :|, a1], :&, 0xffff_ffff] } } when 'and32'; lambda { |di, a0, a1| { a0 => Expression[[a0, :&, a1], :&, 0xffff_ffff] } } when 'shl32'; lambda { |di, a0, a1| { a0 => Expression[[a0, :<<, a1], :&, 0xffff_ffff] } } when 'shr32'; lambda { |di, a0, a1| { a0 => Expression[[[a0, :&, 0xffff_ffff], :>>, a1], :&, 0xffff_ffff] } } # XXX sign when 'neg32'; lambda { |di, a0| { a0 => Expression[:-, [a0, :&, 0xffff_ffff]] } } when 'mod32'; lambda { |di, a0, a1| { a0 => Expression[[a0, :%, a1], :&, 0xffff_ffff] } } when 'xor32'; lambda { |di, a0, a1| { a0 => Expression[[a0, :^, a1], :&, 0xffff_ffff] } } when 'mov32'; lambda { |di, a0, a1| { a0 => Expression[a1, :&, 0xffff_ffff] } } when 'sar32'; lambda { |di, a0, a1| { a0 => Expression[[[a0, :&, 0xffff_ffff], :>>, a1], :&, 0xffff_ffff] } } when 'be', 'le'; lambda { |di, a0, a1| if @endianness.to_s[0] == di.opcode.name[0] {} else { a1 => bswap[a1, Expression[a0].reduce] } end } when /^ldind|^ldabs|^stind|^stabs/; lambda { |di, a0, a1| if @endianness == :big { a0 => Expression[a1] } else { a0 => bswap[a1, di.opcode.props[:msz]] } end } when /^ld|^st/; lambda { |di, a0, a1| { a0 => Expression[a1] } } when /^xadd/; lambda { |di, a0, a1| { a0 => Expression[a0, :+, a1] } } # XXX bswap ? when 'call'; lambda { |di, *a| { :r0 => Expression::Unknown } } when 'jmp', 'jeq', 'jgt', 'jge', 'jset', 'jne', 'jsgt', 'jsge'; lambda { |di, *a| { } } end @backtrace_binding[op] ||= binding if binding } @backtrace_binding end
init_ebpf()
click to toggle source
# File metasm/cpu/ebpf/opcodes.rb, line 49 def init_ebpf @opcode_list = [] [:i, :rs, :rd, :off, :p_rs_o, :p_rd_o, :r0, :p_pkt_i, :p_pkt_rs_i].each { |a| @valid_args[a] = true } # ALU addop_alu 'add', 0x00 addop_alu 'sub', 0x10 addop_alu 'mul', 0x20 addop_alu 'div', 0x30 addop_alu 'or', 0x40 addop_alu 'and', 0x50 addop_alu 'shl', 0x60 addop_alu 'shr', 0x70 addop 'neg', 0x87, :rd addop 'neg32', 0x84, :rd addop_alu 'mod', 0x90 addop_alu 'xor', 0xa0 addop_alu 'mov', 0xb0 addop_alu 'sar', 0xc0 addop 'le', 0xd4, :i, :rd # native to little endian (short if i==16, word if i==32, quad if imm==64) addop 'be', 0xdC, :i, :rd # native to big endian # LD/ST addop 'lddw', 0x18, :rd, :i, :imm64 => true # next insns serves only to store high 32bits of imm64 addop_sz32 'ldabs', 0x20, :r0, :p_pkt_i addop_sz32 'ldind', 0x40, :r0, :p_pkt_rs_i addop_sz 'ldx', 0x61, :rd, :p_rs_o addop_sz 'st', 0x62, :p_rd_o, :i addop_sz 'stx', 0x63, :p_rd_o, :rs addop_sz64 'xadd', 0xC3, :p_rd_o, :rs # BRANCH addop 'jmp', 0x05, :off, :setip => true, :stopexec => true # 'ja' addop_j 'jeq', 0x15 addop_j 'jgt', 0x25 addop_j 'jge', 0x35 addop_j 'jset', 0x45 addop_j 'jne', 0x55 addop_j 'jsgt', 0x65 addop_j 'jsge', 0x75 addop 'call', 0x85, :i # native call, doesn't interfere with bpf code flow addop 'tailcall', 0x8D, :i, :stopexec => true # tail call: r2 is a map of bpf programs, r3 is an index, pass control to r2[r3] (no return) addop 'exit', 0x95, :stopexec => true end
Also aliased as: init_latest
init_opcode_list()
click to toggle source
# File metasm/cpu/ebpf/main.rb, line 52 def init_opcode_list send("init_#@family") @opcode_list end