# This file is part of Metasm, the Ruby assembly manipulation suite # Copyright (C) 2006-2009 Yoann GUILLOT # # Licence is LGPL, see LICENCE in the top-level directory require 'metasm/cpu/bpf/opcodes' require 'metasm/decode' module Metasm class BPF def build_bin_lookaside opcode_list.inject({}) { |h, op| h.update op.bin => op } end # tries to find the opcode encoded at edata.ptr def decode_findopcode(edata) return if edata.ptr > edata.data.length-8 di = DecodedInstruction.new self code = edata.data[edata.ptr, 2].unpack('v')[0] return di if di.opcode = @bin_lookaside[code] end def decode_instr_op(edata, di) op = di.opcode di.instruction.opname = op.name di.bin_length = 8 code, jt, jf, k = edata.read(8).unpack('vCCV') op.args.each { |a| di.instruction.args << case a when :k; Expression[k] when :x; Reg.new(:x) when :a; Reg.new(:a) when :len; Reg.new(:len) when :p_k; Pktref.new(nil, Expression[k], op.props[:msz]) when :p_xk; Pktref.new(Reg.new(:x), Expression[k], op.props[:msz]) when :m_k; Memref.new(nil, Expression[4*k], 4) when :jt; Expression[jt] when :jf; Expression[jf] else raise "unhandled arg #{a}" end } # je a, x, 0, 12 -> jne a, x, 12 # je a, x, 12, 0 -> je a, x, 12 if op.args[2] == :jt and di.instruction.args[2] == Expression[0] di.opcode = op.dup di.opcode.props.delete :stopexec di.instruction.opname = { 'jg' => 'jle', 'jge' => 'jl', 'je' => 'jne', 'jtest' => 'jntest' }[di.instruction.opname] di.instruction.args.delete_at(2) elsif op.args[3] == :jf and di.instruction.args[3] == Expression[0] di.opcode = op.dup di.opcode.props.delete :stopexec di.instruction.args.delete_at(3) end di end 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] if di.instruction.args.length == 4 delta = di.instruction.args[2].reduce + 1 arg = Expression[addr, :+, 8*delta].reduce di.instruction.args[2] = Expression[arg] end end di end # populate the @backtrace_binding hash with default values def init_backtrace_binding @backtrace_binding ||= {} opcode_list.map { |ol| ol.basename }.uniq.sort.each { |op| binding = case op when 'mov'; lambda { |di, a0, a1| { a0 => Expression[a1] } } 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] } } when 'div'; lambda { |di, a0, a1| { a0 => Expression[a0, :/, a1] } } when 'shl'; lambda { |di, a0, a1| { a0 => Expression[a0, :<<, a1] } } when 'shr'; lambda { |di, a0, a1| { a0 => Expression[a0, :>>, a1] } } when 'neg'; lambda { |di, a0| { a0 => Expression[:-, a0] } } when 'msh'; lambda { |di, a0, a1| { a0 => Expression[[a1, :&, 0xf], :<<, 2] } } when 'jmp', 'jg', 'jge', 'je', 'jtest', 'ret'; lambda { |di, *a| { } } end @backtrace_binding[op] ||= binding if binding } @backtrace_binding end def get_xrefs_x(dasm, di) return [] if not di.opcode.props[:setip] if di.instruction.args.length == 4 di.instruction.args[-2, 2] else di.instruction.args[-1, 1] end end end end