class Metasm::Dalvik

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

Constants

OPCODES

Public Class Methods

new(*args) click to toggle source
Calls superclass method Metasm::CPU.new
# File metasm/cpu/dalvik/main.rb, line 96
def initialize(*args)
        super()
        @size = args.grep(Integer).first || 32
        @dex = args.grep(ExeFormat).first
        @endianness = args.delete(:little) || args.delete(:big) || (@dex ? @dex.endianness : :little)
end

Public Instance Methods

addop_args(op) click to toggle source
# File metasm/cpu/dalvik/opcodes.rb, line 82
def addop_args(op)
        fmt = case op.name
        when 'goto'
                :fmt10t
        when 'nop', 'return_void'
                :fmt10x
        when 'const_4'
                :fmt11n
        when 'const_high16'
                :fmt21h
        when 'const_wide_high16'
                :fmt21hh
        when 'move_result', 'move_result_wide', 'move_result_object',
                'move_exception', 'return', 'return_wide',
                'return_object', 'monitor_enter', 'monitor_exit',
                'throw'
                :fmt11x
        when 'move', 'move_wide', 'move_object', 'array_length',
                'neg_int', 'not_int', 'neg_long', 'not_long',
                'neg_float', 'neg_double', 'int_to_long',
                'int_to_float', 'int_to_double', 'long_to_int',
                'long_to_float', 'long_to_double', 'float_to_int',
                'float_to_long', 'float_to_double', 'double_to_int',
                'double_to_long', 'double_to_float', 'int_to_byte',
                'int_to_char', 'int_to_short', 'add_int_2addr',
                'sub_int_2addr', 'mul_int_2addr', 'div_int_2addr',
                'rem_int_2addr', 'and_int_2addr', 'or_int_2addr',
                'xor_int_2addr', 'shl_int_2addr', 'shr_int_2addr',
                'ushr_int_2addr', 'add_long_2addr', 'sub_long_2addr',
                'mul_long_2addr', 'div_long_2addr', 'rem_long_2addr',
                'and_long_2addr', 'or_long_2addr', 'xor_long_2addr',
                'shl_long_2addr', 'shr_long_2addr', 'ushr_long_2addr',
                'add_float_2addr', 'sub_float_2addr', 'mul_float_2addr',
                'div_float_2addr', 'rem_float_2addr',
                'add_double_2addr', 'sub_double_2addr',
                'mul_double_2addr', 'div_double_2addr',
                'rem_double_2addr'
                :fmt12x
        when 'goto_16'
                :fmt20t
        when 'goto_32'
                :fmt30t
        when 'const_string'
                :fmt21c_str
        when 'const_class', 'check_cast',
                'new_instance'
                :fmt21c_typ
        when 'sget', 'sget_wide', 'sget_object',
                'sget_boolean', 'sget_byte', 'sget_char', 'sget_short',
                'sput', 'sput_wide', 'sput_object', 'sput_boolean',
                'sput_byte', 'sput_char', 'sput_short'
                :fmt21c_fld
        when 'const_16', 'const_wide_16'
                :fmt21s
        when 'if_eqz', 'if_nez', 'if_ltz', 'if_gez', 'if_gtz', 'if_lez'
                :fmt21t
        when 'fill_array_data', 'packed_switch', 'sparse_switch'
                :fmt31t
        when 'add_int_lit8', 'rsub_int_lit8', 'mul_int_lit8',
                'div_int_lit8', 'rem_int_lit8', 'and_int_lit8',
                'or_int_lit8', 'xor_int_lit8', 'shl_int_lit8',
                'shr_int_lit8', 'ushr_int_lit8'
                :fmt22b
        when 'instance_of', 'new_array', 'iget', 'iget_wide',
                'iget_object', 'iget_boolean', 'iget_byte',
                'iget_char', 'iget_short', 'iput', 'iput_wide',
                'iput_object', 'iput_boolean', 'iput_byte',
                'iput_char', 'iput_short'
                :fmt22c
        when 'add_int_lit16', 'rsub_int', 'mul_int_lit16',
                'div_int_lit16', 'rem_int_lit16', 'and_int_lit16',
                'or_int_lit16', 'xor_int_lit16'
                :fmt22s
        when 'if_eq', 'if_ne', 'if_lt', 'if_ge', 'if_gt', 'if_le'
                :fmt22t
        when 'move_from16', 'move_wide_from16', 'move_object_from16'
                :fmt22x
        when 'cmpl_float', 'cmpg_float', 'cmpl_double', 'cmpg_double',
                'cmp_long', 'aget', 'aget_wide', 'aget_object',
                'aget_boolean', 'aget_byte', 'aget_char', 'aget_short',
                'aput', 'aput_wide', 'aput_object', 'aput_boolean',
                'aput_byte', 'aput_char', 'aput_short', 'add_int',
                'sub_int', 'mul_int', 'div_int', 'rem_int', 'and_int',
                'or_int', 'xor_int', 'shl_int', 'shr_int', 'ushr_int',
                'add_long', 'sub_long', 'mul_long', 'div_long',
                'rem_long', 'and_long', 'or_long', 'xor_long',
                'shl_long', 'shr_long', 'ushr_long', 'add_float',
                'sub_float', 'mul_float', 'div_float', 'rem_float',
                'add_double', 'sub_double', 'mul_double', 'div_double',
                'rem_double'
                :fmt23x
        when 'const', 'const_wide_32'
                :fmt31i
        when 'const_string_jumbo'
                :fmt31c
        when 'move_16', 'move_wide_16', 'move_object_16'
                :fmt32x
        when 'filled_new_array'
                :fmt35ca
        when 'invoke_virtual', 'invoke_super',
                'invoke_direct', 'invoke_static', 'invoke_interface'
                :fmt35c
        when 'filled_new_array_range', 'invoke_virtual_range',
                'invoke_super_range', 'invoke_direct_range',
                'invoke_static_range', 'invoke_interface_range'
                :fmt3rc
        when 'const_wide'
                :fmt51l
        when 'throw_verification_error'
                :fmt20bc
        when 'iget_quick', 'iget_wide_quick', 'iget_object_quick',
                'iput_quick', 'iput_wide_quick', 'iput_object_quick'
                :fmt22cs
        when 'invoke_virtual_quick', 'invoke_super_quick'
                :fmt35ms
        when 'invoke_virtual_quick_range', 'invoke_super_quick_range'
                :fmt3rms
        when 'execute_inline'
                :fmt3inline
        when 'invoke_direct_empty'
                :fmt35c
        when 'unused_3e', 'unused_3f', 'unused_40', 'unused_41',
                'unused_42', 'unused_43', 'unused_73', 'unused_79',
                'unused_7a', 'unused_e3', 'unused_e4', 'unused_e5',
                'unused_e6', 'unused_e7', 'unused_e8', 'unused_e9',
                'unused_ea', 'unused_eb', 'unused_ec', 'unused_ef',
                'unused_f1', 'unused_fc', 'unused_fd', 'unused_fe',
                'unused_ff'
                :fmtUnknown
        else
                raise "Internal error #{op.name}"
        end

        case fmt
        when :fmt10x; op.args << :iaa
        when :fmt12x; op.args << :ra << :rb
        when :fmt11n; op.args << :ra << :ib
        when :fmt11x; op.args << :raa
        when :fmt10t; op.args << :iaa
        when :fmt20t; op.args << :i16
        when :fmt20bc; op.args << :iaa << :u16
        when :fmt21c_str; op.args << :raa << :str16
        when :fmt21c_typ; op.args << :raa << :typ16
        when :fmt21c_fld; op.args << :raa << :fld16
        when :fmt22x; op.args << :raa << :r16
        when :fmt21s, :fmt21t; op.args << :raa << :i16
        when :fmt21h; op.args << :raa << :i16_32hi
        when :fmt21hh; op.args << :raa << :i16_64hi
        when :fmt23x; op.args << :raa << :rbb << :rcc
        when :fmt22b; op.args << :raa << :rbb << :icc
        when :fmt22s, :fmt22t; op.args << :ra << :rb << :i16
        when :fmt22c, :fmt22cs; op.args << :ra << :rb << :fld16
        when :fmt30t; op.args << :i32
        when :fmt31t, :fmt31c; op.args << :raa << :u32
        when :fmt32x; op.args << :r16 << :r16
        when :fmt31i; op.args << :raa << :i32
        when :fmt35ca
                op.args << :r16 << :rlist5
        when :fmt35c, :fmt35ms
                # rlist:
                #  nr of regs in :ib (max 5)
                #  regs: :ib.times { reg :i16 & 0xf ; :i16 >>= 4 }
                #  reg :ra if :ib == 5
                op.args << :m16 << :rlist5
        when :fmt3inline
                op.args << :r16 << :rlist4
        when :fmt3rc, :fmt3rms
                # rlist = :r16, :r16+1, :r16+2, ..., :r16+:iaa-1
                op.args << :r16 << :rlist16
        when :fmt51l
                # u64 = u16 | (u16 << 16) | ...
                op.args << :raa << :u64
        when :fmtUnknown
                op.args << :iaa
        else
                raise "Internal error #{fmt.inspect}"
        end
end
addop_props(op) click to toggle source
# File metasm/cpu/dalvik/opcodes.rb, line 261
def addop_props(op)
        case op.name
        when 'nop', 'move', 'move_from16', 'move_16', 'move_wide',
                'move_wide_from16', 'move_wide_16', 'move_object',
                'move_object_from16', 'move_object_16', 'move_result',
                'move_result_wide', 'move_result_object',
                'move_exception', 'const_4', 'const_16', 'const',
                'const_high16', 'const_wide_16', 'const_wide_32',
                'const_wide', 'const_wide_high16', 'fill_array_data',
                'cmpl_float', 'cmpg_float', 'cmpl_double',
                'cmpg_double', 'cmp_long', 'neg_int', 'not_int',
                'neg_long', 'not_long', 'neg_float', 'neg_double',
                'int_to_long', 'int_to_float', 'int_to_double',
                'long_to_int', 'long_to_float', 'long_to_double',
                'float_to_int', 'float_to_long', 'float_to_double',
                'double_to_int', 'double_to_long', 'double_to_float',
                'int_to_byte', 'int_to_char', 'int_to_short', 'add_int',
                'sub_int', 'mul_int', 'and_int', 'or_int', 'xor_int',
                'shl_int', 'shr_int', 'ushr_int', 'add_long',
                'sub_long', 'mul_long', 'and_long', 'or_long',
                'xor_long', 'shl_long', 'shr_long', 'ushr_long',
                'add_float', 'sub_float', 'mul_float', 'div_float',
                'rem_float', 'add_double', 'sub_double', 'mul_double',
                'div_double', 'rem_double', 'add_int_2addr',
                'sub_int_2addr', 'mul_int_2addr', 'and_int_2addr',
                'or_int_2addr', 'xor_int_2addr', 'shl_int_2addr',
                'shr_int_2addr', 'ushr_int_2addr', 'add_long_2addr',
                'sub_long_2addr', 'mul_long_2addr', 'and_long_2addr',
                'or_long_2addr', 'xor_long_2addr', 'shl_long_2addr',
                'shr_long_2addr', 'ushr_long_2addr', 'add_float_2addr',
                'sub_float_2addr', 'mul_float_2addr', 'div_float_2addr',
                'rem_float_2addr', 'add_double_2addr',
                'sub_double_2addr', 'mul_double_2addr',
                'div_double_2addr', 'rem_double_2addr', 'add_int_lit16',
                'rsub_int', 'mul_int_lit16', 'and_int_lit16',
                'or_int_lit16', 'xor_int_lit16', 'add_int_lit8',
                'rsub_int_lit8', 'mul_int_lit8', 'and_int_lit8',
                'or_int_lit8', 'xor_int_lit8', 'shl_int_lit8',
                'shr_int_lit8', 'ushr_int_lit8'
                # normal opcode, continues to next, nothing raised
        when 'const_string', 'const_string_jumbo', 'const_class',
                'monitor_enter', 'monitor_exit', 'check_cast',
                'instance_of', 'array_length', 'new_instance',
                'new_array', 'filled_new_array',
                'filled_new_array_range', 'aget', 'aget_boolean',
                'aget_byte', 'aget_char', 'aget_short', 'aget_wide',
                'aget_object', 'aput', 'aput_boolean', 'aput_byte',
                'aput_char', 'aput_short', 'aput_wide', 'aput_object',
                'iget', 'iget_boolean', 'iget_byte', 'iget_char',
                'iget_short', 'iget_wide', 'iget_object', 'iput',
                'iput_boolean', 'iput_byte', 'iput_char', 'iput_short',
                'iput_wide', 'iput_object', 'sget', 'sget_boolean',
                'sget_byte', 'sget_char', 'sget_short', 'sget_wide',
                'sget_object', 'sput', 'sput_boolean', 'sput_byte',
                'sput_char', 'sput_short', 'sput_wide', 'sput_object',
                'div_int', 'rem_int', 'div_long', 'rem_long',
                'div_int_2addr', 'rem_int_2addr', 'div_long_2addr',
                'rem_long_2addr', 'div_int_lit16', 'rem_int_lit16',
                'div_int_lit8', 'rem_int_lit8'
                op.props[:canthrow] = true
        when 'invoke_virtual', 'invoke_virtual_range', 'invoke_super',
                'invoke_super_range', 'invoke_direct',
                'invoke_direct_range', 'invoke_static',
                'invoke_static_range', 'invoke_interface',
                'invoke_interface_range'
                op.props[:canthrow] = true
                op.props[:saveip] = true
                op.props[:setip] = true
                op.props[:stopexec] = true
        when 'return_void', 'return', 'return_wide', 'return_object'
                op.props[:setip] = true
                op.props[:stopexec] = true
        when 'throw'
                op.props[:canthrow] = true
                op.props[:stopexec] = true
        when 'goto', 'goto_16', 'goto_32'
                op.props[:setip] = true
                op.props[:stopexec] = true
        when 'if_eq', 'if_ne', 'if_lt', 'if_ge', 'if_gt', 'if_le',
                'if_eqz', 'if_nez', 'if_ltz', 'if_gez', 'if_gtz',
                'if_lez'
                op.props[:setip] = true
        when 'packed_switch', 'sparse_switch'
                op.props[:setip] = true      # if no table match, nostopexec
                op.props[:setip] = true
        when 'throw_verification_error'
                op.props[:canthrow] = true
                op.props[:stopexec] = true
        when 'execute_inline'
        when 'iget_quick', 'iget_wide_quick', 'iget_object_quick',
                'iput_quick', 'iput_wide_quick', 'iput_object_quick'
                op.props[:canthrow] = true
        when 'invoke_virtual_quick', 'invoke_virtual_quick_range',
                'invoke_super_quick', 'invoke_super_quick_range',
                'invoke_direct_empty'
                op.props[:canthrow] = true
                op.props[:saveip] = true
                op.props[:setip] = true
                op.props[:stopexec] = true
        when 'unused_3e', 'unused_3f', 'unused_40', 'unused_41',
                'unused_42', 'unused_43', 'unused_73', 'unused_79',
                'unused_7a', 'unused_e3', 'unused_e4', 'unused_e5',
                'unused_e6', 'unused_e7', 'unused_e8', 'unused_e9',
                'unused_ea', 'unused_eb', 'unused_ec', 'unused_ef',
                'unused_f1', 'unused_fc', 'unused_fd', 'unused_fe',
                'unused_ff'
                op.props[:stopexec] = true
        else
                raise "Internal error #{op.name}"
        end
end
backtrace_binding() click to toggle source
# File metasm/cpu/dalvik/decode.rb, line 129
def backtrace_binding
        @backtrace_binding ||= init_backtrace_binding
end
backtrace_is_function_return(expr, di=nil) click to toggle source
# File metasm/cpu/dalvik/decode.rb, line 214
def backtrace_is_function_return(expr, di=nil)
        expr and Expression[expr] == Expression[Indirection[:callstack, @size/8]]
end
build_bin_lookaside() click to toggle source
# File metasm/cpu/dalvik/decode.rb, line 11
def build_bin_lookaside
end
decode_findopcode(edata) click to toggle source
# File metasm/cpu/dalvik/decode.rb, line 14
def decode_findopcode(edata)
        return if edata.ptr+2 > edata.length
        di = DecodedInstruction.new(self)
        di.opcode = opcode_list[edata.decode_imm(:u16, @endianness) & 0xff]
        edata.ptr -= 2
        di
end
decode_instr_interpret(di, addr) click to toggle source
# File metasm/cpu/dalvik/decode.rb, line 120
def decode_instr_interpret(di, addr)
        if di.opcode.props[:setip] and di.instruction.args.last.kind_of? Expression and di.instruction.opname =~ /^if|^goto/
                arg = Expression[addr, :+, [di.instruction.args.last, :*, 2]].reduce
                di.instruction.args[-1] = Expression[arg]
        end

        di
end
decode_instr_op(edata, di) click to toggle source
# File metasm/cpu/dalvik/decode.rb, line 22
def decode_instr_op(edata, di)
        op = di.opcode
        di.instruction.opname = op.name

        val = [edata.decode_imm(:u16, @endianness)]

        op.args.each { |a|
                di.instruction.args << case a
                when :i16
                        val << edata.decode_imm(:i16, @endianness)
                        Expression[val.last]
                when :u16
                        val << edata.decode_imm(:u16, @endianness)
                        Expression[val.last]
                when :r16
                        val << edata.decode_imm(:u16, @endianness)
                        Reg.new(val.last)
                when :i16_32hi
                        val << edata.decode_imm(:i16, @endianness)
                        Expression[val.last << 16]
                when :i16_64hi
                        val << edata.decode_imm(:i16, @endianness)
                        Expression[val.last << 48]
                when :i32
                        val << edata.decode_imm(:u16, @endianness)
                        val << edata.decode_imm(:i16, @endianness)
                        Expression[val[-2] | (val[-1] << 16)]
                when :u32
                        val << edata.decode_imm(:u16, @endianness)
                        val << edata.decode_imm(:u16, @endianness)
                        Expression[val[-2] | (val[-1] << 16)]
                when :u64
                        val << edata.decode_imm(:u16, @endianness)
                        val << edata.decode_imm(:u16, @endianness)
                        val << edata.decode_imm(:u16, @endianness)
                        val << edata.decode_imm(:u16, @endianness)
                        Expression[val[-4] | (val[-3] << 16) | (val[-2] << 32) | (val[-1] << 48)]
                when :ra
                        Reg.new((val[0] >> 8) & 0xf)
                when :rb
                        Reg.new((val[0] >> 12) & 0xf)
                when :ib
                        Expression[Expression.make_signed((val[0] >> 12) & 0xf, 4)]
                when :raa
                        Reg.new((val[0] >> 8) & 0xff)
                when :iaa
                        Expression[Expression.make_signed((val[0] >> 8) & 0xff, 8)]
                when :rbb
                        val[1] ||= edata.decode_imm(:u16, @endianness)
                        Reg.new(val[1] & 0xff)
                when :ibb
                        val[1] ||= edata.decode_imm(:u16, @endianness)
                        Expression[Expression.make_signed(val[1] & 0xff, 8)]
                when :rcc
                        val[1] ||= edata.decode_imm(:u16, @endianness)
                        Reg.new((val[1] >> 8) & 0xff)
                when :icc
                        val[1] ||= edata.decode_imm(:u16, @endianness)
                        Expression[Expression.make_signed((val[1] >> 8) & 0xff, 8)]
                when :rlist4, :rlist5
                        cnt = (val[0] >> 12) & 0xf
                        val << edata.decode_imm(:u16, @endianness)
                        [cnt, 4].min.times {
                                di.instruction.args << Reg.new(val[-1] & 0xf)
                                val[-1] >>= 4
                        }
                        di.instruction.args << Reg.new((val[0] >> 8) & 0xf) if cnt > 4
                        next
                when :rlist16
                        cnt = (val[0] >> 8) & 0xff
                        val << edata.decode_imm(:u16, @endianness)
                        cnt.times { |c|
                                di.instruction.args << Reg.new(val[-1] + c)
                        }
                        next
                when :m16
                        val << edata.decode_imm(:u16, @endianness)
                        DexMethod.new(@dex, val.last)
                when :fld16
                        val << edata.decode_imm(:u16, @endianness)
                        DexField.new(@dex, val.last)
                when :typ16
                        val << edata.decode_imm(:u16, @endianness)
                        DexType.new(@dex, val.last)
                when :str16
                        val << edata.decode_imm(:u16, @endianness)
                        DexString.new(@dex, val.last)
                else raise SyntaxError, "Internal error: invalid argument #{a} in #{op.name}"
                end
        }

        di.bin_length = val.length*2

        return if edata.ptr > edata.length

        di
end
disassembler_default_func() click to toggle source

returns a DecodedFunction suitable for :default uses disassembler_default_bt{for/bind}_callback

# File metasm/cpu/dalvik/decode.rb, line 197
def disassembler_default_func
        df = DecodedFunction.new
        ra = Indirection[:callstack, @size/8]
        df.backtracked_for << BacktraceTrace.new(ra, :default, ra, :x, nil)
        df.backtrace_binding[:callstack] = Expression[:callstack, :+, @size/8]
        df.btfor_callback = lambda { |dasm, btfor, funcaddr, calladdr|
                if funcaddr != :default
                        btfor
                elsif di = dasm.decoded[calladdr] and di.opcode.props[:saveip]
                        btfor
                else []
                end
        }

        df
end
get_backtrace_binding(di) click to toggle source
# File metasm/cpu/dalvik/decode.rb, line 152
def get_backtrace_binding(di)
        a = di.instruction.args.map { |arg|
                case arg
                when Reg; arg.symbolic
                else arg
                end
        }

        if binding = backtrace_binding[di.opcode.name]
                binding[di, *a]
        else
                puts "unhandled instruction to backtrace: #{di}" if $VERBOSE
                # assume nothing except the 1st arg is modified
                case a[0]
                when Indirection, Symbol; { a[0] => Expression::Unknown }
                when Expression; (x = a[0].externals.first) ? { x => Expression::Unknown } : {}
                else {}
                end.update(:incomplete_binding => Expression[1])
        end

end
get_xrefs_x(dasm, di) click to toggle source
# File metasm/cpu/dalvik/decode.rb, line 174
def get_xrefs_x(dasm, di)
        if di.opcode.props[:saveip]
                m = di.instruction.args.first
                if m.kind_of?(DexMethod) and m.off
                        [m.off]
                else
                        [:default]
                end
        elsif di.opcode.props[:setip]
                if di.opcode.name =~ /^return/
                        [Indirection[:callstack, @size/8]]
                elsif di.opcode.name =~ /^if|^goto/
                        [di.instruction.args.last]
                else
                        []  # [di.instruction.args.last]
                end
        else
                []
        end
end
init_backtrace_binding() click to toggle source
# File metasm/cpu/dalvik/decode.rb, line 133
def init_backtrace_binding
        @backtrace_binding ||= {}
        sz = @size/8
        @opcode_list.each { |op|
                case op.name
                when /invoke/
                        @backtrace_binding[op.name] = lambda { |di, *args| {
                                :callstack => Expression[:callstack, :-, sz],
                                Indirection[:callstack, sz] => Expression[di.next_addr]
                        } }
                when /return/
                        @backtrace_binding[op.name] = lambda { |di, *args| {
                                :callstack => Expression[:callstack, :+, sz]
                        } }
                end
        }
        @backtrace_binding
end
init_dalvik() click to toggle source
# File metasm/cpu/dalvik/opcodes.rb, line 63
def init_dalvik
        @valid_props[:canthrow] = true
        [:i16, :i16_32hi, :i16_64hi, :i32, :iaa, :ib, :icc, :u16, :u32, :u64,
         :r16, :ra, :raa, :rb, :rbb, :rcc, :rlist16, :rlist4, :rlist5,
         :m16, :fld16, :typ16, :str16
        ].each { |a| @valid_args[a] = true }
        @opcode_list = []

        OPCODES.each_with_index { |n, b|
                op = Opcode.new(n, b)
                addop_args(op)
                addop_props(op)
                @opcode_list << op
        }

        raise "Internal error #{@opcode_list.length}" if @opcode_list.length != 256
end
Also aliased as: init_latest
init_latest()
Alias for: init_dalvik
init_opcode_list() click to toggle source
# File metasm/cpu/dalvik/main.rb, line 103
def init_opcode_list
        init_latest
        @opcode_list
end