# This file is part of Metasm, the Ruby assembly manipulation suite # Copyright (C) 2006-2010 Yoann GUILLOT # # Licence is LGPL, see LICENCE in the top-level directory require 'metasm/cpu/python/opcodes' require 'metasm/decode' module Metasm class Python def build_bin_lookaside opcode_list.inject({}) { |la, op| la.update op.bin => op } end def decode_findopcode(edata) di = DecodedInstruction.new(self) byte = edata.decode_imm(:u8, :little) di if di.opcode = @bin_lookaside[byte] end def decode_instr_op(edata, di) di.bin_length = 1 di.instruction.opname = di.opcode.name di.opcode.args.each { |a| case a when :cmp di.bin_length += 2 v = edata.decode_imm(:i16, @endianness) di.instruction.args << (CMP_OP[v] || Expression[v]) when :i16 di.bin_length += 2 di.instruction.args << Expression[edata.decode_imm(:i16, @endianness)] when :u8 di.bin_length += 1 di.instruction.args << Expression[edata.decode_imm(:u8, @endianness)] else raise "unsupported arg #{a.inspect}" end } return if edata.ptr > edata.length di end def decode_instr_interpret(di, addr) case di.opcode.name when 'LOAD_CONST' if c = prog_code(addr) cst = c[:consts][di.instruction.args.first.reduce] if cst.kind_of? Hash and cst[:type] == :code di.add_comment "lambda #{Expression[cst[:fileoff]]}" else di.add_comment cst.inspect end end when 'LOAD_NAME', 'LOAD_ATTR', 'LOAD_GLOBAL', 'STORE_NAME', 'IMPORT_NAME', 'LOAD_FAST' if c = prog_code(addr) di.add_comment c[:names][di.instruction.args.first.reduce].inspect end end di end def backtrace_binding @backtrace_binding ||= init_backtrace_binding end def init_backtrace_binding @backtrace_binding ||= {} opcode_list.each { |op| binding = case op when 'nop'; lambda { |*a| {} } end @backtrace_binding[op] ||= binding if binding } @backtrace_binding end def get_backtrace_binding(di) a = di.instruction.args.map { |arg| case arg when Var; arg.symbolic else arg end } if binding = backtrace_binding[di.opcode.basename] binding[di, *a] else puts "unhandled instruction to backtrace: #{di}" if $VERBOSE { :incomplete_binding => Expression[1] } end end def get_xrefs_x(dasm, di) return [] if not di.opcode.props[:setip] arg = case di.opcode.name when 'JUMP_FORWARD', 'FOR_ITER' # relative offset di.instruction.args.last.reduce + di.next_addr when 'CALL_FUNCTION_VAR' 'lol' when /CALL/ :unknown else # absolute offset from :code start off = di.instruction.args.last.reduce if c = prog_code(di) off += c[:fileoff] end off end [Expression[(arg.kind_of?(Var) ? arg.symbolic : arg)]] end def prog_code(addr) addr = addr.address if addr.kind_of? DecodedInstruction @last_prog_code ||= nil return @last_prog_code if @last_prog_code and @last_prog_code[:fileoff] <= addr and @last_prog_code[:fileoff] + @last_prog_code[:code].length > addr @last_prog_code = @program.code_at_off(addr) if @program end def backtrace_is_function_return(expr, di=nil) #Expression[expr].reduce == Expression['wtf'] end end end