class Object

searches an object in the attributes of another anyobj.scan_for() => “anyobj.someattr['blabla']”

Constants

TRACE_BUF_SZ

size of the eip buffer (in dwords)

Public Class Methods

autoexe_load(f, &b) click to toggle source

allows us to be AutoExe.loaded

# File metasm/disassemble.rb, line 2219
def self.autoexe_load(f, &b)
        d = load(f, &b)
        d.program
end
diff(o1, o2) click to toggle source

computes the difference beetween two ruby objects walks accessors, arrays and hashes

# File misc/objdiff.rb, line 10
def Object.diff(o1, o2)
        if o1.class == o2.class
                h = {}
                case o1
                when Array, Hash
                        if o1.kind_of? Array
                                keys = (0...[o1.length, o2.length].max).to_a
                        else
                                keys = o1.keys | o2.keys
                        end
                        keys.each { |k|
                                d = diff(o1[k], o2[k])
                                h["[#{k.inspect}]"] = d if not d.empty?
                        }
                else
                        a = ($diff_accessor_cache ||= {})[o1.class] ||= (
                                im = o1.class.public_instance_methods.map { |m| m.to_s }.grep(/^[a-z]/)
                                (im & im.map { |m| m+'=' }).map { |m| m.chop }.find_all { |m| o1.instance_variable_get('@'+m) }
                        )
                        if a.empty?
                                return o1 == o2 ? h : [o1, o2]
                        end
                        a.each { |k|
                                d = diff(o1.send(k), o2.send(k))
                                h['.' + k] = d if not d.empty?
                        }
                end

                # simplify tree
                h.keys.each { |k|
                        if h[k].kind_of? Hash and h[k].length == 1
                                v = h.delete k
                                h[k + v.keys.first] = v.values.first
                        end
                }

                h
        else
                [o1, o2]
        end
end

Public Instance Methods

_printadv(a) click to toggle source
# File samples/scan_pt_gnu_stack.rb, line 18
def _printadv(a)
        $stderr.print a.to_s.ljust(60)[-60, 60] + "\r"
end
_puts(a) click to toggle source
# File samples/scan_pt_gnu_stack.rb, line 15
def _puts(a)
        puts a.to_s.ljust(60)
end
addrtolabel() click to toggle source

metasm dasm plugin walks all disassembled instructions referencing an address if the address is a label, update the instruction to use the label esp. useful after a disassemble_fast, with a .map file

# File samples/dasm-plugins/imm2off.rb, line 12
def addrtolabel
        bp = prog_binding.invert
        @decoded.each_value { |di|
                next if not di.kind_of?(DecodedInstruction)
                di.each_expr { |e|
                        next unless e.kind_of?(Expression)
                        if l = bp[e.lexpr]
                                add_xref(e.lexpr, Xref.new(:addr, di.address))
                                e.lexpr = Expression[l]
                        end
                        if l = bp[e.rexpr]
                                add_xref(e.rexpr, Xref.new(:addr, di.address))
                                e.rexpr = (e.lexpr ? Expression[l] : l)
                        end
                }
        }
        nil
end
asm() click to toggle source

get in interactive assembler mode

# File samples/metasm-shell.rb, line 65
def asm
  puts "[+] Metasm assembly shell"
  puts "type help for usage..\n\n"

  Readline.completion_proc = lambda { |line| %w[help exit quit].find_all { |w| line.downcase == w[0, line.length] } }
  Readline.completion_append_character = ' '

  while line = Readline.readline('asm> ', true)
    case line
    when /^help(\W|$)/
      puts "",
           "Type in opcodes to see their binary form",
           "You can use ';' to type multi-line stuff",
           "e.g. 'nop nop' will display \"\\x90\\x90\"",
           "",
           "exit/quit    Quit the console",
           "help         Show this screen",
           ""
    when /^(quit|exit)(\W|$)/
      break
    else
      begin
        data = line.gsub(';', "\n")
        next if data.strip.empty?
        e_data = data.encode
        puts '"' + e_data.unpack('C*').map { |c| '\x%02x' % c }.join + '"'
      rescue Metasm::Exception => e
        puts "Error: #{e.class} #{e.message}"
      end
    end
  end

  puts
end
backtrace(expr, start_addr, nargs={}) click to toggle source

backtraces the value of an expression from start_addr updates blocks backtracked_for if type is set uses backtrace_walk all values returned are from #backtrace_check_found (which may generate xrefs, labels, addrs to dasm) unless :no_check is specified options:

:include_start => start backtracking including start_addr
:from_subfuncret =>
:origin => origin to set for xrefs when resolution is successful
:orig_expr => initial expression
:type => xref type (:r, :w, :x, :addr)  when :x, the results are added to #addrs_todo
:len => xref len (for :r/:w)
:snapshot_addr => addr (or array of) where the backtracker should stop
 if a snapshot_addr is given, values found are ignored if continuing the backtrace does not get to it (eg maxdepth/unk_addr/end)
:maxdepth => maximum number of blocks to backtrace
:detached => true if backtracking type :x and the result should not have from = origin set in @addrs_todo
:max_complexity{_data} => maximum complexity of the expression before aborting its backtrace
:log => Array, will be updated with the backtrace evolution
:only_upto => backtrace only to update bt_for for current block & previous ending at only_upto
:no_check => don't use backtrace_check_found (will not backtrace indirection static values)
:terminals => array of symbols with constant value (stop backtracking if all symbols in the expr are terminals) (only supported with no_check)
# File metasm/disassemble.rb, line 1469
        def backtrace(expr, start_addr, nargs={})
                include_start   = nargs.delete :include_start
                from_subfuncret = nargs.delete :from_subfuncret
                origin          = nargs.delete :origin
                origexpr        = nargs.delete :orig_expr
                type            = nargs.delete :type
                len             = nargs.delete :len
                snapshot_addr   = nargs.delete(:snapshot_addr) || nargs.delete(:stopaddr)
                maxdepth        = nargs.delete(:maxdepth) || @backtrace_maxblocks
                detached        = nargs.delete :detached
                max_complexity  = nargs.delete(:max_complexity) || @backtrace_maxcomplexity
                max_complexity_data = nargs.delete(:max_complexity) || @backtrace_maxcomplexity_data
                bt_log          = nargs.delete :log   # array to receive the ongoing backtrace info
                only_upto       = nargs.delete :only_upto
                no_check        = nargs.delete :no_check
                terminals       = nargs.delete(:terminals) || []
                raise ArgumentError, "invalid argument to backtrace #{nargs.keys.inspect}" if not nargs.empty?

                expr = Expression[expr]

                origexpr = expr if origin == start_addr

                start_addr = normalize(start_addr)
                di = @decoded[start_addr]

                if not snapshot_addr and @cpu.backtrace_is_stack_address(expr)
puts "  not backtracking stack address #{expr}" if debug_backtrace
                        return []
                end

                if type == :r or type == :w
                        max_complexity = max_complexity_data
                        maxdepth = @backtrace_maxblocks_data if backtrace_maxblocks_data and maxdepth > @backtrace_maxblocks_data
                end

                if vals = (no_check ? (!need_backtrace(expr, terminals) and [expr]) : backtrace_check_found(expr,
                                di, origin, type, len, maxdepth, detached, snapshot_addr))
                        # no need to update backtracked_for
                        return vals
                elsif maxdepth <= 0
                        return [Expression::Unknown]
                end

                # create initial backtracked_for
                if type and origin == start_addr and di
                        btt = BacktraceTrace.new(expr, origin, origexpr, type, len, maxdepth-1)
                        btt.address = di.address
                        btt.exclude_instr = true if not include_start
                        btt.from_subfuncret = true if from_subfuncret and include_start
                        btt.detached = true if detached
                        di.block.backtracked_for |= [btt]
                end

                @callback_prebacktrace[] if callback_prebacktrace

                # list of Expression/Integer
                result = []

puts "backtracking #{type} #{expr} from #{di || Expression[start_addr || 0]} for #{@decoded[origin]}" if debug_backtrace or $DEBUG
                bt_log << [:start, expr, start_addr] if bt_log
                backtrace_walk(expr, start_addr, include_start, from_subfuncret, snapshot_addr, maxdepth) { |ev, expr_, h|
                        expr = expr_
                        case ev
                        when :unknown_addr, :maxdepth
puts "  backtrace end #{ev} #{expr}" if debug_backtrace
                                result |= [expr] if not snapshot_addr
                                @addrs_todo << [expr, (detached ? nil : origin)] if not snapshot_addr and type == :x and origin
                        when :end
                                if not expr.kind_of? StoppedExpr
                                        oldexpr = expr
                                        expr = backtrace_emu_blockup(h[:addr], expr)
puts "  backtrace up #{Expression[h[:addr]]}  #{oldexpr}#{" => #{expr}" if expr != oldexpr}" if debug_backtrace
                                        bt_log << [:up, expr, oldexpr, h[:addr],  :end] if bt_log and expr != oldexpr
                                        if expr != oldexpr and not snapshot_addr and vals = (no_check ?
                                                        (!need_backtrace(expr, terminals) and [expr]) :
                                                        backtrace_check_found(expr, nil, origin, type, len,
                                                                maxdepth-h[:loopdetect].length, detached, snapshot_addr))
                                                result |= vals
                                                next
                                        end
                                end
puts "  backtrace end #{ev} #{expr}" if debug_backtrace
                                if not snapshot_addr
                                        result |= [expr]

                                        btt = BacktraceTrace.new(expr, origin, origexpr, type, len, maxdepth-h[:loopdetect].length-1)
                                        btt.detached = true if detached
                                        @decoded[h[:addr]].block.backtracked_for |= [btt] if @decoded[h[:addr]]
                                        @function[h[:addr]].backtracked_for |= [btt] if @function[h[:addr]] and h[:addr] != :default
                                        @addrs_todo << [expr, (detached ? nil : origin)] if type == :x and origin
                                end
                        when :stopaddr
                                if not expr.kind_of? StoppedExpr
                                        oldexpr = expr
                                        expr = backtrace_emu_blockup(h[:addr], expr)
puts "  backtrace up #{Expression[h[:addr]]}  #{oldexpr}#{" => #{expr}" if expr != oldexpr}" if debug_backtrace
                                        bt_log << [:up, expr, oldexpr, h[:addr], :end] if bt_log and expr != oldexpr
                                end
puts "  backtrace end #{ev} #{expr}" if debug_backtrace
                                result |= ((expr.kind_of?(StoppedExpr)) ? expr.exprs : [expr])
                        when :loop
                                next false if expr.kind_of? StoppedExpr
                                t = h[:looptrace]
                                oldexpr = t[0][0]
                                next false if expr == oldexpr               # unmodifying loop
puts "  bt loop at #{Expression[t[0][1]]}: #{oldexpr} => #{expr} (#{t.map { |z| Expression[z[1]] }.join(' <- ')})" if debug_backtrace
                                bt_log << [:loop, expr, oldexpr, t.map { |z| z[1] }] if bt_log
                                false
                        when :up
                                next false if only_upto and h[:to] != only_upto
                                next expr if expr.kind_of? StoppedExpr
                                oldexpr = expr
                                expr = backtrace_emu_blockup(h[:from], expr)
puts "  backtrace up #{Expression[h[:from]]}->#{Expression[h[:to]]}  #{oldexpr}#{" => #{expr}" if expr != oldexpr}" if debug_backtrace
                                bt_log << [:up, expr, oldexpr, h[:from], h[:to]] if bt_log

                                if expr != oldexpr and vals = (no_check ? (!need_backtrace(expr, terminals) and [expr]) :
                                                backtrace_check_found(expr, @decoded[h[:from]], origin, type, len,
                                                        maxdepth-h[:loopdetect].length, detached, snapshot_addr))
                                        if snapshot_addr
                                                expr = StoppedExpr.new vals
                                                next expr
                                        else
                                                result |= vals
                                                bt_log << [:found, vals, h[:from]] if bt_log
                                                next false
                                        end
                                end

                                if origin and type
                                        # update backtracked_for
                                        update_btf = lambda { |btf, new_btt|
                                                # returns true if btf was modified
                                                if i = btf.index(new_btt)
                                                        btf[i] = new_btt if btf[i].maxdepth < new_btt.maxdepth
                                                else
                                                        btf << new_btt
                                                end
                                        }

                                        btt = BacktraceTrace.new(expr, origin, origexpr, type, len, maxdepth-h[:loopdetect].length-1)
                                        btt.detached = true if detached
                                        if x = di_at(h[:from])
                                                update_btf[x.block.backtracked_for, btt]
                                        end
                                        if x = @function[h[:from]] and h[:from] != :default
                                                update_btf[x.backtracked_for, btt]
                                        end
                                        if x = di_at(h[:to])
                                                btt = btt.dup
                                                btt.address = x.address
                                                btt.from_subfuncret = true if h[:sfret] == :subfuncret
                                                if backtrace_check_funcret(btt, h[:from], h[:real_to] || h[:to])
puts "   function returns to caller" if debug_backtrace
                                                        next false
                                                end
                                                if not update_btf[x.block.backtracked_for, btt]
puts "   already backtraced" if debug_backtrace
                                                        next false
                                                end
                                        end
                                end
                                expr
                        when :di, :func
                                next if expr.kind_of? StoppedExpr
                                if not snapshot_addr and @cpu.backtrace_is_stack_address(expr)
puts "  not backtracking stack address #{expr}" if debug_backtrace
                                        next false
                                end

oldexpr = expr
                                case ev
                                when :di
                                        h[:addr] = h[:di].address
                                        expr = backtrace_emu_instr(h[:di], expr)
                                        bt_log << [ev, expr, oldexpr, h[:di], h[:addr]] if bt_log and expr != oldexpr
                                when :func
                                        expr = backtrace_emu_subfunc(h[:func], h[:funcaddr], h[:addr], expr, origin, maxdepth-h[:loopdetect].length)
                                        if snapshot_addr and snapshot_addr == h[:funcaddr]
                                                # XXX recursiveness detection needs to be fixed
puts "  backtrace: recursive function #{Expression[h[:funcaddr]]}" if debug_backtrace
                                                next false
                                        end
                                        bt_log << [ev, expr, oldexpr, h[:funcaddr], h[:addr]] if bt_log and expr != oldexpr
                                end
puts "  backtrace #{h[:di] || Expression[h[:funcaddr]]}  #{oldexpr} => #{expr}" if debug_backtrace and expr != oldexpr
                                if vals = (no_check ? (!need_backtrace(expr, terminals) and [expr]) : backtrace_check_found(expr,
                                                h[:di], origin, type, len, maxdepth-h[:loopdetect].length, detached, snapshot_addr))
                                        if snapshot_addr
                                                expr = StoppedExpr.new vals
                                        else
                                                result |= vals
                                                bt_log << [:found, vals, h[:addr]] if bt_log
                                                next false
                                        end
                                elsif expr.complexity > max_complexity
puts "  backtrace aborting, expr too complex" if debug_backtrace
                                        next false
                                end
                                expr
                        else raise ev.inspect
                        end
                }

puts '  backtrace result: ' + result.map { |r| Expression[r] }.join(', ') if debug_backtrace

                result
        end
backtrace_check_found(expr, di, origin, type, len, maxdepth, detached, snapshot_addr=nil) click to toggle source

returns an array of expressions, or nil if expr needs more backtrace it needs more backtrace if expr.externals include a Symbol != :unknown (symbol == register value) if it need no more backtrace, expr's indirections are recursively resolved xrefs are created, and di args are updated (immediate => label) if type is :x, addrs_todo is updated, and if di starts a block, expr is checked to see if it may be a subfunction return value

expr indirection are solved by first finding the value of the pointer, and then rebacktracking for write-type access detached is true if type is :x and from should not be set in addrs_todo (indirect call flow, eg external function callback) if the backtrace ends pre entrypoint, returns the value encoded in the raw binary XXX global variable (modified by another function), exported data, multithreaded app.. TODO handle memory aliasing (mov ebx, eax ; write [ebx] ; read [eax]) TODO trace expr evolution through backtrace, to modify immediates to an expr involving label names TODO mov [ptr], imm ; <…> ; jmp [ptr] => rename imm as loc_XX

eg. mov eax, 42 ; add eax, 4 ; jmp eax  =>  mov eax, some_label-4
# File metasm/disassemble.rb, line 1773
        def backtrace_check_found(expr, di, origin, type, len, maxdepth, detached, snapshot_addr=nil)
                # only entrypoints or block starts called by a :saveip are checked for being a function
                # want to execute [esp] from a block start
                if type == :x and di and di == di.block.list.first and @cpu.backtrace_is_function_return(expr, @decoded[origin]) and (
                        # which is an entrypoint..
                        (not di.block.from_normal and not di.block.from_subfuncret) or
                        # ..or called from a saveip
                        (bool = false ; di.block.each_from_normal { |fn| bool = true if @decoded[fn] and @decoded[fn].opcode.props[:saveip] } ; bool))

                        # now we can mark the current address a function start
                        # the actual return address will be found later (we tell the caller to continue the backtrace)
                        addr = di.address
                        l = auto_label_at(addr, 'sub', 'loc', 'xref')
                        if not f = @function[addr]
                                f = @function[addr] = DecodedFunction.new
                                puts "found new function #{l} at #{Expression[addr]}" if $VERBOSE
                        end
                        f.finalized = false

                        if @decoded[origin]
                                f.return_address ||= []
                                f.return_address |= [origin]
                                @decoded[origin].add_comment "endsub #{l}"
                                # TODO add_xref (to update the comment on rename_label)
                        end

                        f.backtracked_for |= @decoded[addr].block.backtracked_for.find_all { |btt| not btt.address }
                end

                return if need_backtrace(expr)
                if snapshot_addr
                        return if expr.expr_externals(true).find { |ee| ee.kind_of?(Indirection) }
                end

puts "backtrace #{type} found #{expr} from #{di} orig #{@decoded[origin] || Expression[origin] if origin}" if debug_backtrace
                result = backtrace_value(expr, maxdepth)
                # keep the ori pointer in the results to emulate volatile memory (eg decompiler prefers this)
                #result << expr if not type   # XXX returning multiple values for nothing is too confusing, TODO fix decompiler
                result.uniq!

                # create xrefs/labels
                result.each { |e|
                        backtrace_found_result(e, di, type, origin, len, detached)
                } if type and origin

                result
        end
backtrace_check_funcret(btt, funcaddr, instraddr) click to toggle source

checks if the BacktraceTrace is a call to a known subfunction returns true and updates self.addrs_todo

# File metasm/disassemble.rb, line 1680
        def backtrace_check_funcret(btt, funcaddr, instraddr)
                if di = @decoded[instraddr] and @function[funcaddr] and btt.type == :x and
                                not btt.from_subfuncret and
                                @cpu.backtrace_is_function_return(btt.expr, @decoded[btt.origin]) and
                                retaddr = backtrace_emu_instr(di, btt.expr) and
                                not need_backtrace(retaddr)
puts "  backtrace addrs_todo << #{Expression[retaddr]} from #{di} (funcret)" if debug_backtrace
                        di.block.add_to_subfuncret normalize(retaddr)
                        if @decoded[funcaddr].kind_of? DecodedInstruction
                                # check that all callers :saveip returns (eg recursive call that was resolved
                                # before we found funcaddr was a function)
                                @decoded[funcaddr].block.each_from_normal { |fm|
                                        if fdi = di_at(fm) and fdi.opcode.props[:saveip] and not fdi.block.to_subfuncret
                                                backtrace_check_funcret(btt, funcaddr, fm)
                                        end
                                }
                        end
                        if not @function[funcaddr].finalized
                                # the function is not fully disassembled: arrange for the retaddr to be
                                #  disassembled only after the subfunction is finished
                                # for that we walk the code from the call, mark each block start, and insert the sfret
                                #  just before the 1st function block address in @addrs_todo (which is pop()ed by dasm_step)
                                faddrlist = []
                                todo = []
                                di.block.each_to_normal { |t| todo << normalize(t) }
                                while a = todo.pop
                                        next if faddrlist.include? a or not get_section_at(a)
                                        faddrlist << a
                                        if @decoded[a].kind_of? DecodedInstruction
                                                @decoded[a].block.each_to_samefunc(self) { |t| todo << normalize(t) }
                                        end
                                end

                                idx = @addrs_todo.index(@addrs_todo.find { |r, i, sfr| faddrlist.include? normalize(r) }) || -1
                                @addrs_todo.insert(idx, [retaddr, instraddr, true])
                        else
                                @addrs_todo << [retaddr, instraddr, true]
                        end
                        true
                end
        end
backtrace_emu_blockup(addr, expr) click to toggle source

applies a location binding

# File metasm/disassemble.rb, line 1734
def backtrace_emu_blockup(addr, expr)
        (ab = @address_binding[addr]) ? Expression[expr.bind(ab).reduce] : expr
end
backtrace_emu_instr(di, expr) click to toggle source

applies one decodedinstruction to an expression

# File metasm/disassemble.rb, line 1723
def backtrace_emu_instr(di, expr)
        @cpu.backtrace_emu(di, expr)
end
backtrace_emu_subfunc(func, funcaddr, calladdr, expr, origin, maxdepth) click to toggle source

applies one subfunction to an expression

# File metasm/disassemble.rb, line 1728
def backtrace_emu_subfunc(func, funcaddr, calladdr, expr, origin, maxdepth)
        bind = func.get_backtrace_binding(self, funcaddr, calladdr, expr, origin, maxdepth)
        Expression[expr.bind(bind).reduce]
end
backtrace_found_result(expr, di, type, origin, len, detached) click to toggle source

creates xrefs, updates addrs_todo, updates instr args

# File metasm/disassemble.rb, line 1944
def backtrace_found_result(expr, di, type, origin, len, detached)
        n = normalize(expr)
        fallthrough = true if type == :x and o = di_at(origin) and not o.opcode.props[:stopexec] and n == o.block.list.last.next_addr # delay_slot
        add_xref(n, Xref.new(type, origin, len)) if origin != :default and origin != Expression::Unknown and not fallthrough
        unk = true if n == Expression::Unknown

        add_xref(n, Xref.new(:addr, di.address)) if di and di.address != origin and not unk
        base = { nil => 'loc', 1 => 'byte', 2 => 'word', 4 => 'dword', 8 => 'qword' }[len] || 'xref'
        base = 'sub' if @function[n]
        n = Expression[auto_label_at(n, base, 'xref') || n] if not fallthrough
        n = Expression[n]

        # update instr args
        # TODO trace expression evolution to allow handling of
        #  mov eax, 28 ; add eax, 4 ; jmp eax
        #  => mov eax, (loc_xx-4)
        if di and not unk and expr != n # and di.address == origin
                @cpu.replace_instr_arg_immediate(di.instruction, expr, n)
        end
        if @decoded[origin] and not unk
                 @cpu.backtrace_found_result(self, @decoded[origin], expr, type, len)
        end

        # add comment
        if type and @decoded[origin] # and not @decoded[origin].instruction.args.include? n
                @decoded[origin].add_comment "#{type}#{len}:#{n}" if not fallthrough
        end

        # check if target is a string
        if di and type == :r and (len == 1 or len == 2) and s = get_section_at(n)
                l = s[0].inv_export[s[0].ptr]
                case len
                when 1; str = s[0].read(32).unpack('C*')
                when 2; str = s[0].read(64).unpack('v*')
                end
                str = str.inject('') { |str_, c|
                        case c
                        when 0x20..0x7e, \n, \r, \t; str_ << c
                        else break str_
                        end
                }
                if str.length >= 4
                        di.add_comment "#{'L' if len == 2}#{str.inspect}"
                        str = 'a_' + str.downcase.delete('^a-z0-9')[0, 12]
                        if str.length >= 8 and l[0, 5] == 'byte_'
                                rename_label(l, @program.new_label(str))
                        end
                end
        end

        # XXX all this should be done in  backtrace() { <here> }
        if type == :x and origin
                if detached
                        o = @decoded[origin] ? origin : di ? di.address : nil       # lib function callback have origin == libfuncname, so we must find a block somewhere else
                        origin = nil
                        @decoded[o].block.add_to_indirect(normalize(n)) if @decoded[o] and not unk
                else
                        @decoded[origin].block.add_to_normal(normalize(n)) if @decoded[origin] and not unk
                end
                @addrs_todo << [n, origin]
        end
end
backtrace_indirection(ind, maxdepth) click to toggle source

returns the array of values pointed by the indirection at its invocation (ind.origin) first resolves the pointer using #backtrace_value, if it does not point in edata keep the original pointer then backtraces from ind.origin until it finds an :w xref origin if no :w access is found, returns the value encoded in the raw section data TODO handle unaligned (partial?) writes

# File metasm/disassemble.rb, line 1843
        def backtrace_indirection(ind, maxdepth)
                if not ind.origin
                        puts "backtrace_ind: no origin for #{ind}" if $VERBOSE
                        return [ind]
                end

                ret = []

                decode_imm = lambda { |addr, len|
                        edata = get_edata_at(addr)
                        if edata
                                Expression[ edata.decode_imm("u#{8*len}".to_sym, @cpu.endianness) ]
                        else
                                Expression::Unknown
                        end
                }

                # resolve pointers (they may include Indirections)
                backtrace_value(ind.target, maxdepth).each { |ptr|
                        # find write xrefs to the ptr
                        refs = []
                        each_xref(ptr, :w) { |x|
                                # XXX should be rebacktracked on new xref
                                next if not @decoded[x.origin]
                                refs |= [x.origin]
                        } if ptr != Expression::Unknown

                        if refs.empty?
                                if get_section_at(ptr)
                                        # static data, newer written : return encoded value
                                        ret |= [decode_imm[ptr, ind.len]]
                                        next
                                else
                                        # unknown pointer : backtrace the indirection, hope it solves itself
                                        initval = ind
                                end
                        else
                                # wait until we find a write xref, then backtrace the written value
                                initval = true
                        end

                        # wait until we arrive at an xref'ing instruction, then backtrace the written value
                        backtrace_walk(initval, ind.origin, true, false, nil, maxdepth-1) { |ev, expr, h|
                                case ev
                                when :unknown_addr, :maxdepth, :stopaddr
puts "   backtrace_indirection for #{ind.target} failed: #{ev}" if debug_backtrace
                                        ret |= [Expression::Unknown]
                                when :end
                                        if not refs.empty? and (expr == true or not need_backtrace(expr))
                                                if expr == true
                                                        # found a path avoiding the :w xrefs, read the encoded initial value
                                                        ret |= [decode_imm[ptr, ind.len]]
                                                else
                                                        bd = expr.expr_indirections.inject({}) { |h_, i| h_.update i => decode_imm[i.target, i.len] }
                                                        ret |= [Expression[expr.bind(bd).reduce]]
                                                end
                                        else
                                                # unknown pointer, backtrace did not resolve...
                                                ret |= [Expression::Unknown]
                                        end
                                when :di
                                        di = h[:di]
                                        if expr == true
                                                next true if not refs.include? di.address
                                                # find the expression to backtrace: assume this is the :w xref from this di
                                                writes = get_xrefs_rw(di)
                                                writes = writes.find_all { |x_type, x_ptr, x_len| x_type == :w and x_len == ind.len }
                                                if writes.length != 1
                                                        puts "backtrace_ind: incompatible xrefs to #{ptr} from #{di}" if $DEBUG
                                                        ret |= [Expression::Unknown]
                                                        next false
                                                end
                                                expr = Indirection.new(writes[0][1], ind.len, di.address)
                                        end
                                        expr = backtrace_emu_instr(di, expr)
                                        # may have new indirections... recall bt_value ?
                                        #if not need_backtrace(expr)
                                        if expr.expr_externals.all? { |e| @prog_binding[e] or @function[normalize(e)] } and expr.expr_indirections.empty?
                                                ret |= backtrace_value(expr, maxdepth-1-h[:loopdetect].length)
                                                false
                                        else
                                                expr
                                        end
                                when :func
                                        next true if expr == true  # XXX
                                        expr = backtrace_emu_subfunc(h[:func], h[:funcaddr], h[:addr], expr, ind.origin, maxdepth-h[:loopdetect].length)
                                        #if not need_backtrace(expr)
                                        if expr.expr_externals.all? { |e| @prog_binding[e] or @function[normalize(e)] } and expr.expr_indirections.empty?
                                                ret |= backtrace_value(expr, maxdepth-1-h[:loopdetect].length)
                                                false
                                        else
                                                expr
                                        end
                                end
                        }
                }

                ret
        end
backtrace_update_function_binding(addr, func=@function[addr], retaddrs=func.return_address) click to toggle source
# File metasm/disassemble.rb, line 1738
def backtrace_update_function_binding(addr, func=@function[addr], retaddrs=func.return_address)
        @cpu.backtrace_update_function_binding(self, addr, func, retaddrs)
end
backtrace_value(expr, maxdepth) click to toggle source

returns an array of expressions with Indirections resolved (recursive with #backtrace_indirection)

# File metasm/disassemble.rb, line 1822
def backtrace_value(expr, maxdepth)
        # array of expression with all indirections resolved
        result = [Expression[expr.reduce]]

        # solve each indirection sequentially, clone expr for each value (aka cross-product)
        result.first.expr_indirections.uniq.each { |i|
                next_result = []
                backtrace_indirection(i, maxdepth).each { |rr|
                        next_result |= result.map { |e| Expression[e.bind(i => rr).reduce] }
                }
                result = next_result
        }

        result.uniq
end
backup_program_file() click to toggle source

backup the executable file

# File samples/dasm-plugins/patch_file.rb, line 11
def backup_program_file
        f = @program.filename
        if File.exist?(f) and not File.exist?(f + '.bak')
                File.open(f + '.bak', 'wb') { |wfd|
                        File.open(f, 'rb') { |rfd|
                                while buf = rfd.read(1024*1024)
                                        wfd.write buf
                                end
                        }
                }
        end
end
bookmark_addrs(list, color) click to toggle source
# File samples/dasm-plugins/bookmark.rb, line 97
def bookmark_addrs(list, color)
        al = [list].flatten.uniq
        gui.session_append("dasm.bookmark_addrs(#{list.inspect}, #{color.inspect})")
        @bookmarklist |= [al.min]
        al.each { |a| @bookmarkcolor[a] = color }
        gui.gui_update
end
bookmark_delete(list) click to toggle source
# File samples/dasm-plugins/bookmark.rb, line 109
def bookmark_delete(list)
        @bookmarklist -= list
        list.each { |a| @bookmarkcolor.delete a }
end
bookmark_delete_color(col) click to toggle source
# File samples/dasm-plugins/bookmark.rb, line 113
def bookmark_delete_color(col)
        @bookmarkcolor.delete_if { |k, v| if v == col ; @bookmarklist.delete k ; true end }
end
bookmark_delete_function(addr) click to toggle source
# File samples/dasm-plugins/bookmark.rb, line 104
def bookmark_delete_function(addr)
        return if not fa = find_function_start(addr)
        list = function_blocks(fa).map { |k, v| block_at(k).list.map { |di| di.address } }.flatten
        bookmark_delete list
end
bookmark_function(addr, color) click to toggle source

an api to bookmark a function

# File samples/dasm-plugins/bookmark.rb, line 92
def bookmark_function(addr, color)
        return if not fa = find_function_start(addr)
        list = function_blocks(fa).map { |k, v| block_at(k).list.map { |di| di.address } }.flatten
        bookmark_addrs list, color
end
bouncepkt(clt, srv, timeout=nil) click to toggle source
# File misc/tcp_proxy_hex.rb, line 14
def bouncepkt(clt, srv, timeout=nil)
        s2c = ''
        c2s = ''
        loop do
                break if not IO.select([clt, srv], nil, nil, timeout)

                while srv and s2c.length < 1024*16 and IO.select([srv], nil, nil, 0)
                        str = (srv.read(1) rescue nil)
                        if not str or str.empty?
                                srv = false
                        else
                                s2c << str
                        end
                end

                while clt and c2s.length < 1024*16 and IO.select([clt], nil, nil, 0)
                        str = (clt.read(1) rescue nil)
                        if not str or str.empty?
                                clt = false
                        else
                                c2s << str
                        end
                end

                if clt and s2c.length > 0 and IO.select(nil, [clt], nil, 0)
                        puts Time.now.strftime('s -> c  %H:%M:%S')
                        s2c.hexdump(:fmt => ['c', 'a'])
                        clt.write s2c
                        s2c.replace ''
                end

                if srv and c2s.length > 0 and IO.select(nil, [srv], nil, 0)
                        puts Time.now.strftime('c -> s  %H:%M:%S')
                        c2s.hexdump(:fmt => ['c', 'a'])
                        srv.write c2s
                        c2s.replace ''
                end
                break if not clt or not srv
        end
end
compile_warn(tg) click to toggle source
# File misc/lint.rb, line 42
def compile_warn(tg)
        r, w = IO.pipe('binary')
        if !fork
                r.close
                $stderr.reopen w
                $stdout.reopen '/dev/null'
                exec 'ruby', '-v', '-c', tg
                exit!
        else
                w.close
        end
        r
end
create_sig(exe) click to toggle source

read a binary file, print a signature for all symbols found

# File samples/generate_libsigs.rb, line 24
def create_sig(exe)
        func = case exe
        when COFFArchive; :create_sig_arch
        when PE, COFF; :create_sig_coff
        when ELF; :create_sig_elf
        else raise 'unsupported file format'
        end
        send(func, exe) { |sym, edata|
                sig = edata.data.unpack('H*').first
                edata.reloc.each { |o, r|
# TODO if the reloc points to a known func (eg WinMain), keep the info
                        sz = r.length
                        sig[2*o, 2*sz] = '.' * sz * 2
                }

                next if sig.gsub('.', '').length < 2*$min_sigbytes

                puts sym
                sig.scan(/.{1,78}/) { |s| puts ' ' + s }
        }
end
create_sig_arch(exe) click to toggle source

handle coff archives

# File samples/generate_libsigs.rb, line 47
def create_sig_arch(exe)
        exe.members.each { |m|
                next if m.name == '/' or m.name == '//'
                obj = m.exe rescue next
                create_sig(obj)
        }
end
create_sig_coff(coff) { |name, data| ... } click to toggle source

scan a pe/coff file

# File samples/generate_libsigs.rb, line 80
def create_sig_coff(coff)
        if coff.kind_of? PE    # dll
                # dll
                # TODO
        else
                coff.symbols.to_a.compact.each { |sym|
                        next if sym.type != 'FUNCTION'
                        next if not sym.sec_nr.kind_of? Integer
                        data = coff.sections[sym.sec_nr-1].encoded
                        off = sym.value
                        len = data.export.find_all { |k, o| o > off and k !~ /_uuid/ }.transpose[1].to_a.min || data.length

                        yield sym.name, data[off, len]
                }
        end
end
create_sig_elf(elf) { |name, data| ... } click to toggle source

scan an elf file

# File samples/generate_libsigs.rb, line 56
def create_sig_elf(elf)
        elf.symbols.each { |sym|
                next if sym.type != 'FUNC' or sym.shndx == 'UNDEF'
                if elf.header.type == 'REL'
                        next if not data = elf.sections[sym.shndx].encoded
                        off = sym.value
                else
                        next if not seg = elf.segments.find { |s| s.type == 'LOAD' and sym.value >= s.vaddr and sym.value < s.vaddr+s.memsz }
                        next if not data = seg.encoded
                        off = sym.value - seg.vaddr
                end

                len = sym.size
                if len == 0
                        len = data.export.find_all { |k, o| o > off and k !~ /_uuid/ }.transpose[1].to_a.min || data.length
                        len -= off
                        len = 256 if len > 256
                end

                yield sym.name, data[off, len]
        }
end
dasm_all(addrstart, length, method=:disassemble_fast_deep) click to toggle source

metasm dasm plugin: retrieve a section section, and disassemble everything it can, skipping existing code and nops usage: load the plugin, then call (ruby snipped): dasm.dasm_all_section '.text'

# File samples/dasm-plugins/dasm_all.rb, line 9
def dasm_all(addrstart, length, method=:disassemble_fast_deep)
        s = get_section_at(addrstart)
        return if not s
        s = s[0]
        boff = s.ptr
        off = 0
        while off < length
                if di = di_at(addrstart + off)
                        off += di.bin_length
                elsif @decoded[addrstart+off]
                        off += 1
                else
                        s.ptr = boff+off
                        maydi = cpu.decode_instruction(s, 0)
                        if not maydi
                                off += 1
                        elsif maydi.instruction.to_s =~ /nop|lea (.*), \[\1(?:\+0)?\]|mov (.*), \2|int 3/
                                off += maydi.bin_length
                        else
                                puts "dasm_all: found #{Expression[addrstart+off]}" if $VERBOSE
                                send(method, addrstart+off)
                        end
                end
                Gui.main_iter if gui and off & 15 == 0
        end

        count = 0
        off = 0
        while off < length
                addr = addrstart+off
                if di = di_at(addr)
                        if di.block_head?
                                b = di.block
                                if not @function[addr] and b.from_subfuncret.to_a.empty? and b.from_normal.to_a.empty?
                                        l = auto_label_at(addr, 'sub_orph')
                                        puts "dasm_all: found orphan function #{l}"
                                        @function[addrstart+off] = DecodedFunction.new
                                        @function[addrstart+off].finalized = true
                                        detect_function_thunk(addr)
                                        count += 1
                                end
                        end
                        off += di.bin_length
                else
                        off += 1
                end
                Gui.main_iter if gui and off & 15 == 0
        end

        puts "found #{count} orphan functions" if $VERBOSE

        gui.gui_update if gui
end
dasm_all_section(name, method=:disassemble_fast_deep) click to toggle source
# File samples/dasm-plugins/dasm_all.rb, line 63
def dasm_all_section(name, method=:disassemble_fast_deep)
        section_info.each { |n, a, l, i|
                if name == n
                        dasm_all(Expression[a].reduce, l, method)
                end
        }
        true
end
decompile(*addr) click to toggle source
# File metasm/disassemble.rb, line 2211
def decompile(*addr)
        decompiler.decompile(*addr)
end
decompile_func(addr) click to toggle source
# File metasm/disassemble.rb, line 2214
def decompile_func(addr)
        decompiler.decompile_func(addr)
end
decompiler() click to toggle source
# File metasm/disassemble.rb, line 2204
def decompiler
        parse_c '' if not c_parser
        @decompiler ||= Decompiler.new(self)
end
decompiler=(dc) click to toggle source
# File metasm/disassemble.rb, line 2208
def decompiler=(dc)
        @decompiler = dc
end
demangle_all_cppnames() click to toggle source

metasm dasm plugin: try to demangle all labels as c++ names, add them as comment if successful

# File samples/dasm-plugins/demangle_cpp.rb, line 10
def demangle_all_cppnames
        cnt = 0
        prog_binding.each { |name, addr|
                cname = name.sub(/^thunk_/, '')
                if dname = demangle_cppname(cname)
                        cnt += 1
                        add_comment(addr, dname)
                        each_xref(addr, :x) { |xr|
                                if di = di_at(xr.origin)
                                        di.add_comment dname
                                        di.comment.delete "x:#{name}"
                                end
                        }
                end
        }
        cnt
end
dump(dump_data=true, &b) click to toggle source

dumps the source, optionnally including data yields (defaults puts) each line

# File metasm/disassemble.rb, line 2019
def dump(dump_data=true, &b)
        b ||= lambda { |l| puts l }
        @sections.sort_by { |addr, edata| addr.kind_of?(::Integer) ? addr : 0 }.each { |addr, edata|
                addr = Expression[addr] if addr.kind_of? ::String
                blockoffs = @decoded.values.grep(DecodedInstruction).map { |di| Expression[di.block.address, :-, addr].reduce if di.block_head? }.grep(::Integer).sort.reject { |o| o < 0 or o >= edata.length }
                b[@program.dump_section_header(addr, edata)]
                if not dump_data and edata.length > 16*1024 and blockoffs.empty?
                        b["// [#{edata.length} data bytes]"]
                        next
                end
                unk_off = 0  # last off displayed
                # blocks.sort_by { |b| b.addr }.each { |b|
                while unk_off < edata.length
                        if unk_off == blockoffs.first
                                blockoffs.shift
                                di = @decoded[addr+unk_off]
                                if unk_off != di.block.edata_ptr
                                        b["\n// ------ overlap (#{unk_off-di.block.edata_ptr}) ------"]
                                elsif di.block.from_normal.kind_of? ::Array
                                        b["\n"]
                                end
                                dump_block(di.block, &b)
                                unk_off += [di.block.bin_length, 1].max
                                unk_off = blockoffs.first if blockoffs.first and unk_off > blockoffs.first
                        else
                                next_off = blockoffs.first || edata.length
                                if dump_data or next_off - unk_off < 16
                                        unk_off = dump_data(addr + unk_off, edata, unk_off, &b)
                                else
                                        b["// [#{next_off - unk_off} data bytes]"]
                                        unk_off = next_off
                                end
                        end
                end
        }
end
dump_block(block, &b) click to toggle source

dumps a block of decoded instructions

# File metasm/disassemble.rb, line 2057
def dump_block(block, &b)
        b ||= lambda { |l| puts l }
        block = @decoded[block].block if @decoded[block]
        dump_block_header(block, &b)
        block.list.each { |di| b[di.show] }
end
dump_block_header(block, &b) click to toggle source

shows the xrefs/labels at block start

# File metasm/disassemble.rb, line 2065
def dump_block_header(block, &b)
        b ||= lambda { |l| puts l }
        xr = []
        each_xref(block.address) { |x|
                case x.type
                when :x; xr << Expression[x.origin]
                when :r, :w; xr << "#{x.type}#{x.len}:#{Expression[x.origin]}"
                end
        }
        if not xr.empty?
                b["\n// Xrefs: #{xr[0, 8].join(' ')}#{' ...' if xr.length > 8}"]
        end
        if block.edata.inv_export[block.edata_ptr] and label_alias[block.address]
                b["\n"] if xr.empty?
                label_alias[block.address].each { |name| b["#{name}:"] }
        end
        if c = @comment[block.address]
                c = c.join("\n") if c.kind_of? ::Array
                c.each_line { |l| b["// #{l}"] }
        end
end
dump_data(addr, edata, off, &b) click to toggle source

dumps data/labels, honours @xrefs.len if exists dumps one line only stops on end of edata/@decoded/@xref returns the next offset to display TODO array-style data access

# File metasm/disassemble.rb, line 2092
def dump_data(addr, edata, off, &b)
        b ||= lambda { |l| puts l }
        if l = edata.inv_export[off] and label_alias[addr]
                l_list = label_alias[addr].sort
                l = l_list.pop || l
                l_list.each { |ll|
                        b["#{ll}:"]
                }
                l = (l + ' ').ljust(16)
        else l = ''
        end
        elemlen = 1   # size of each element we dump (db by default)
        dumplen = -off % 16   # number of octets to dump
        dumplen = 16 if dumplen == 0
        cmt = []
        each_xref(addr) { |x|
                dumplen = elemlen = x.len if x.len == 2 or x.len == 4
                cmt << " #{x.type}#{x.len}:#{Expression[x.origin]}"
        }
        cmt = " ; @#{Expression[addr]}" + cmt.sort[0, 6].join
        if r = edata.reloc[off]
                dumplen = elemlen = r.type.to_s[1..-1].to_i/8
        end
        dataspec = { 1 => 'db ', 2 => 'dw ', 4 => 'dd ', 8 => 'dq ' }[elemlen]
        if not dataspec
                dataspec = 'db '
                elemlen = 1
        end
        l << dataspec

        # dup(?)
        if off >= edata.data.length
                dups = edata.virtsize - off
                @prog_binding.each_value { |a|
                        tmp = Expression[a, :-, addr].reduce
                        dups = tmp if tmp.kind_of? ::Integer and tmp > 0 and tmp < dups
                }
                @xrefs.each_key { |a|
                        tmp = Expression[a, :-, addr].reduce
                        dups = tmp if tmp.kind_of? ::Integer and tmp > 0 and tmp < dups
                }
                dups /= elemlen
                dups = 1 if dups < 1
                b[(l + "#{dups} dup(?)").ljust(48) << cmt]
                return off + dups*elemlen
        end

        vals = []
        edata.ptr = off
        dups = dumplen/elemlen
        elemsym = "u#{elemlen*8}".to_sym
        while edata.ptr < edata.data.length
                if vals.length > dups and vals.last != vals.first
                        # we have a dup(), unread the last element which is different
                        vals.pop
                        addr = Expression[addr, :-, elemlen].reduce
                        edata.ptr -= elemlen
                        break
                end
                break if vals.length == dups and vals.uniq.length > 1
                vals << edata.decode_imm(elemsym, @cpu.endianness)
                addr += elemlen
                if i = (1-elemlen..0).find { |i_|
                        t = addr + i_
                        @xrefs[t] or @decoded[t] or edata.reloc[edata.ptr+i_] or edata.inv_export[edata.ptr+i_]
                }
                        # i < 0
                        edata.ptr += i
                        addr += i
                        break
                end
                break if edata.reloc[edata.ptr-elemlen]
        end

        # line of repeated value => dup()
        if vals.length > 8 and vals.uniq.length == 1
                b[(l << "#{vals.length} dup(#{Expression[vals.first]})").ljust(48) << cmt]
                return edata.ptr
        end

        # recognize strings
        vals = vals.inject([]) { |vals_, value|
                if (elemlen == 1 or elemlen == 2)
                        case value
                        when 0x20..0x7e, 0x0a, 0x0d
                                if vals_.last.kind_of? ::String; vals_.last << value ; vals_
                                else vals_ << value.chr
                                end
                        else vals_ << value
                        end
                else vals_ << value
                end
        }

        vals.map! { |value|
                if value.kind_of? ::String
                        if value.length > 2 # or value == vals.first or value == vals.last # if there is no xref, don't care
                                value.inspect
                        else
                                value.unpack('C*').map { |c| Expression[c] }
                        end
                else
                        Expression[value]
                end
        }
        vals.flatten!

        b[(l << vals.join(', ')).ljust(48) << cmt]

        edata.ptr
end
epilog() click to toggle source
# File misc/ppc_pdf2oplist.rb, line 100
def epilog
        puts "\n\t@field_shift = {"
        puts $field_shift.sort_by { |k, v| k.to_s }.enum_slice(6).map { |slc|
                "\t\t" + slc.map { |k, v| "#{k.inspect} => #{v}" }.join(', ')
        }.join(",\n")
        puts "\t}"
        puts "\n\t@field_mask = {"
        puts $field_mask.sort_by { |k, v| k.to_s }.enum_slice(6).map { |slc|
                "\t\t" + slc.map { |k, v| "#{k.inspect} => #{v > 1000 ? '0x%X' % v : v}" }.join(', ')
        }.join(",\n")
        puts "\t}"
end
findgadget_asm(asm) click to toggle source

parse asm to a regexp, return the list of addresses matching

# File samples/dasm-plugins/findgadget.rb, line 47
def findgadget_asm(asm)
        pattern_scan(findgadget_asm_to_regex(asm))
end
findgadget_asm_to_regex(asm) click to toggle source

metasm dasm plugin scan for a given asm instruction sequence (all encodings) add the G dasm-gui shortcut, the input change ';' for line splits

# File samples/dasm-plugins/findgadget.rb, line 11
def findgadget_asm_to_regex(asm)
        fullre = ''
        asm = asm.gsub(';', "\n")

        sc = Shellcode.new(@cpu)
        sc.parse asm
        sc.source.each { |i|
                case i
                when Data
                        opts_edata = i.encode(@cpu.endianness)
                when Instruction
                        opts_edata = @cpu.encode_instruction(sc, i)
                else
                        raise "cant scan for #{i}"
                end

                opts_edata = [opts_edata] if opts_edata.kind_of?(EncodedData)

                opts_re = opts_edata.map { |ed|
                        # Regexp.escape ed.data, with relocs replaced with '.'
                        re = ''
                        off = 0
                        ed.reloc.sort.each { |o, rel|
                                re << Regexp.escape(ed.data[off...o])
                                re << ('.' * rel.length)
                                off = o + rel.length
                        }
                        re << Regexp.escape(ed.data[off..-1])
                }
                fullre << '(' << opts_re.join('|') << ')'
        }

        Regexp.new(fullre, Regexp::MULTILINE, 'n')
end
findgadget_prompt() click to toggle source
# File samples/dasm-plugins/findgadget.rb, line 51
def findgadget_prompt
        gui.inputbox("source for the gadget - separate with ;") { |asm|
                lst = findgadget_asm(asm)
                list = [['address', 'section']]
                sections = section_info
                list += lst.map { |addr|
                        # [name, addr, len, misc]
                        if s = sections.find { |s_| s_[1] <= addr and s_[1] + s_[2] > addr }
                                s = s[0]
                        else
                                s = '?'
                        end
                        [Expression[addr], s]
                }
                gui.listwindow("gadgetscan for #{asm}", list) { |args| gui.focus_addr(args[0]) }
        }
end
function_walk(addr_start, obj_start) { |:merge, addr, froms, values.first| ... } click to toggle source

iterates over all instructions of a function from a given entrypoint carries an object while walking, the object is yielded every instruction every block is walked only once, after all previous blocks are done (if possible) on a 'jz', a [:clone] event is yielded for every path beside the first on a juction (eg a -> b -> d, a -> c -> d), a [:merge] event occurs if froms have different objs event list:

[:di, <addr>, <decoded_instruction>, <object>]
[:clone, <newaddr>, <oldaddr>, <object>]
[:merge, <newaddr>, {<oldaddr1> => <object1>, <oldaddr2> => <object2>, ...}, <object1>]
[:subfunc, <subfunc_addr>, <call_addr>, <object>]

all events should return an object :merge has a copy of object1 at the end so that uninterested callers can always return args if an event returns false, the trace stops for the current branch

# File metasm/disassemble.rb, line 1371
def function_walk(addr_start, obj_start)
        # addresses of instrs already seen => obj
        done = {}
        todo = [[addr_start, obj_start]]

        while hop = todo.pop
                addr, obj = hop
                next if done.has_key?(done)

                di = di_at(addr)
                next if not di

                if done.empty?
                        dilist = di.block.list[di.block.list.index(di)..-1]
                else
                        # new block, check all 'from' have been seen
                        if not hop[2]
                                # may retry later
                                all_ok = true
                                di.block.each_from_samefunc(self) { |fa| all_ok = false unless done.has_key?(fa) }
                                if not all_ok
                                        todo.unshift([addr, obj, true])
                                        next
                                end
                        end

                        froms = {}
                        di.block.each_from_samefunc(self) { |fa| froms[fa] = done[fa] if done[fa] }
                        if froms.values.uniq.length > 1
                                obj = yield([:merge, addr, froms, froms.values.first])
                                next if obj == false
                        end

                        dilist = di.block.list
                end

                if dilist.each { |_di|
                                break if done.has_key?(_di.address)        # looped back into addr_start
                                done[_di.address] = obj
                                obj = yield([:di, _di.address, _di, obj])
                                break if obj == false      # also return false for the previous 'if'
                        }

                        from = dilist.last.address

                        if di.block.to_normal and di.block.to_normal[0] and
                                        di.block.to_subfuncret and di.block.to_subfuncret[0]
                                # current instruction block calls into a subfunction
                                obj = di.block.to_normal.map { |subf|
                                        yield([:subfunc, subf, from, obj])
                                }.first            # propagate 1st subfunc result
                                next if obj == false
                        end

                        wantclone = false
                        di.block.each_to_samefunc(self) { |ta|
                                if wantclone
                                        nobj = yield([:clone, ta, from, obj])
                                        next if obj == false
                                        todo << [ta, nobj]
                                else
                                        todo << [ta, obj]
                                        wantclone = true
                                end
                        }
                end
        end
end
gets() click to toggle source

shows the preprocessor path to find a specific line usage: ruby chdr-find.rb 'regex pattern' list of files.h

Calls superclass method
# File misc/cheader-findpppath.rb, line 11
def gets
        l = $ungets
        $ungets = nil
        l || super()
end
graph_to_svg() click to toggle source
# File samples/dasm-plugins/export_graph_svg.rb, line 13
def graph_to_svg
        gw = gui.curview.dup
        class << gw
                attr_accessor :svgbuf, :svgcol
                def draw_color(col)
                        col = @default_color_association.fetch(col, col)
                        col = BasicColor.fetch(col, col)
                        @svgcol = "##{col}"
                end

                def draw_line(x1, y1, x2, y2)
                        bb(x1, y1, x2, y2)
                        svgbuf << %Q{<line x1="#{x1}" y1="#{y1}" x2="#{x2}" y2="#{y2}" stroke="#{@svgcol}" />\n}
                end
                def draw_rectangle(x, y, w, h)
                        bb(x, y, x+w, y+h)
                        svgbuf << %Q{<rect x="#{x}" y="#{y}" width="#{w}" height="#{h}" fill="#{@svgcol}" />\n}
                end
                def draw_string(x, y, str)
                        bb(x, y, x+str.length*@font_width, y+@font_height)
                        stre = str.gsub('<', '&lt;').gsub('>', '&gt;')
                        svgbuf << %Q{<text x="#{(0...str.length).map { |i| x+i*@font_width }.join(',')}" y="#{y+@font_height*0.7}" stroke="#{@svgcol}">#{stre}</text>\n}
                end

                def draw_rectangle_color(c, *a)
                        draw_color(c)
                        draw_rectangle(*a)
                end
                def draw_line_color(c, *a)
                        draw_color(c)
                        draw_line(*a)
                end
                def draw_string_color(c, *a)
                        draw_color(c)
                        draw_string(*a)
                end

                def focus?; false; end
                def view_x; @svgvx ||= @curcontext.boundingbox[0]-20; end
                def view_y; @svgvy ||= @curcontext.boundingbox[1]-20; end
                def width;  @svgvw ||= (@curcontext ? (@curcontext.boundingbox[2]-@curcontext.boundingbox[0])*@zoom+20 : 800); end
                def height; @svgvh ||= (@curcontext ? (@curcontext.boundingbox[3]-@curcontext.boundingbox[1])*@zoom+20 : 600); end
                def svgcuraddr; @curcontext ? @curcontext.root_addrs.first : current_address; end

                # drawing bounding box (for the background rectangle)
                attr_accessor :bbx, :bby, :bbxm, :bbym
                def bb(x1, y1, x2, y2)
                        @bbx  = [x1, x2, @bbx].compact.min
                        @bbxm = [x1, x2, @bbxm].compact.max
                        @bby  = [y1, y2, @bby].compact.min
                        @bbym = [y1, y2, @bbym].compact.max
                end
        end
        ret = gw.svgbuf = ''
        gw.paint

        ret[0, 0] = <<EOS
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" 
  "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg xmlns="http://www.w3.org/2000/svg" font-family="courier, monospace">
<desc>Graph of #{get_label_at(gw.svgcuraddr) || Expression[gw.svgcuraddr]}</desc>
<rect x="#{gw.bbx-10}" y="#{gw.bby-10}" width="#{gw.bbxm-gw.bbx+20}" height="#{gw.bbym-gw.bby+20}" fill="#{gw.draw_color(:background)}" />"
EOS
        ret << %Q{</svg>}
end
gui_show_array(addr) click to toggle source
# File samples/dbg-plugins/heapscan.rb, line 166
def gui_show_array(addr)
        head = resolve(addr)
        e = @heap.xrchunksto[head].to_a.find { |ee| @heap.arrays[ee] and @heap.arrays[ee][head] }
        return if not e
        lst = @heap.arrays[e][head]

        if not st = @heap.chunk_struct[head]
                st = Metasm::C::Struct.new
                st.name = "array_#{'%x' % head}"
                st.members = []
                (@heap.chunks[head] / 4).times { |i|
                        n = "u#{i}"
                        v = @memory[head+4*i, 4].unpack('L').first
                        if @heap.chunks[v]
                                t = Metasm::C::Pointer.new(Metasm::C::BaseType.new(:void))
                        else
                                t = Metasm::C::BaseType.new(:int)
                        end
                        st.members << Metasm::C::Variable.new(n, t)
                }
                @heap.cp.toplevel.struct[st.name] ||= st
        end
        @heap.chunk_struct[head] = st

        $ghw.addr_struct = { head => @heap.cp.decode_c_struct(st.name, @memory, head) }

        if not st = lst.map { |l| @heap.chunk_struct[l] }.compact.first
                e = lst.first
                st = Metasm::C::Struct.new
                st.name = "elem_#{'%x' % head}"
                st.members = []
                (@heap.chunks[e] / 4).times { |i|
                        n = "u#{i}"
                        v = @memory[e+4*i, 4].unpack('L').first
                        if @heap.chunks[v]
                                t = Metasm::C::Pointer.new(Metasm::C::BaseType.new(:void))
                        else
                                t = Metasm::C::BaseType.new(:int)
                        end
                        st.members << Metasm::C::Variable.new(n, t)
                }
                @heap.cp.toplevel.struct[st.name] ||= st
        end
        lst.each { |l| @heap.chunk_struct[l] = st }

        lst.each { |aa|
                $ghw.addr_struct[aa] = @heap.cp.decode_c_struct(st.name, @memory, aa)
        }
        gui.parent_widget.mem.focus_addr(head, :graphheap)
end
gui_show_list(addr) click to toggle source
# File samples/dbg-plugins/heapscan.rb, line 135
def gui_show_list(addr)
        a = resolve(addr)
        #@heap.cp.parse("struct ptr { void *ptr; };") if not @heap.cp.toplevel.struct['ptr']
        h = @heap.linkedlists[a]
        off = h.keys.first
        lst = h[off]

        if not st = lst.map { |l| @heap.chunk_struct[l] }.compact.first
                st = Metasm::C::Struct.new
                st.name = "list_#{'%x' % lst.first}"
                st.members = []
                (@heap.chunks[lst.first] / 4).times { |i|
                        n = "u#{i}"
                        t = Metasm::C::BaseType.new(:int)
                        if i == off/4
                                n = "next"
                                t = Metasm::C::Pointer.new(st)
                        end
                        st.members << Metasm::C::Variable.new(n, t)
                }
                @heap.cp.toplevel.struct[st.name] = st
        end
        lst.each { |l| @heap.chunk_struct[l] = st }

        $ghw.addr_struct = {}
        lst.each { |aa|
                $ghw.addr_struct[aa] = @heap.cp.decode_c_struct(st.name, @memory, aa)
        }
        gui.parent_widget.mem.focus_addr(lst.first, :graphheap)
end
hash_name(sym) click to toggle source
# File samples/shellcode-dynlink.rb, line 104
def hash_name(sym)
        hash = 0
        sym.each_byte { |char|
                hash = (((hash >> 0xd) | (hash << (32-0xd))) + char) & 0xffff_ffff
        }
        hash
end
heap() click to toggle source
# File samples/dbg-plugins/heapscan.rb, line 49
def heap; @heap ; end
heap=(h) click to toggle source
# File samples/dbg-plugins/heapscan.rb, line 50
def heap=(h) ; @heap = h ; end
heapscan_graph() click to toggle source
# File samples/dbg-plugins/heapscan.rb, line 129
def heapscan_graph
        heapscan_time
        @heap.dump_graph
        heapscan_time 'graph.gv'
end
heapscan_kernels() click to toggle source
# File samples/dbg-plugins/heapscan.rb, line 117
def heapscan_kernels
        heapscan_time
        @heap.find_kernels
        heapscan_time "#{@heap.kernels.length} kernels"
end
heapscan_roots() click to toggle source
# File samples/dbg-plugins/heapscan.rb, line 123
def heapscan_roots
        heapscan_time
        @heap.find_roots
        heapscan_time "#{@heap.roots.length} roots"
end
heapscan_scan(xr=true) click to toggle source
# File samples/dbg-plugins/heapscan.rb, line 52
def heapscan_scan(xr=true)
        heaps = []
        mmaps = []
        libc = nil
        pr = os_process
        pr.mappings.each { |a, l, p, f|
                case f.to_s
                when /heap/
                        heaps << [a, l]
                when /libc[^a-zA-Z]/
                        libc ||= a if p == 'r-xp'
                when ''
                        mmaps << [a, l]
                end
        }

        heapscan_time ''
        @disassembler.parse_c ''
        if pr and OS.current.name =~ /winos/
                if OS.current.version[0] == 5
                        @heap = WindowsHeap.new(self)
                        @heap.cp = @disassembler.c_parser
                        @heap.cp.parse_file File.join($heapscan_dir, 'winheap.h') unless @heap.cp.toplevel.struct['_HEAP']
                else
                        @heap = Windows7Heap.new(self)
                        @heap.cp = @disassembler.c_parser
                        @heap.cp.parse_file File.join($heapscan_dir, 'winheap7.h') unless @heap.cp.toplevel.struct['_HEAP']
                end
                @heap.heaps = heaps
        else
                @heap = LinuxHeap.new(self)
                @heap.cp = @disassembler.c_parser
                @heap.mmaps = mmaps
                @heap.scan_libc(libc)
                heapscan_time "libc!main_arena #{'%x' % @heap.main_arena_ptr}"
        end

        hsz = 0
        (heaps + mmaps).each { |a, l|
                hsz += l
                @heap.range.update a => l
        }

        log "#{hsz/1024/1024}M heap"

        @heap.scan_chunks
        heapscan_time "#{@heap.chunks.length} chunks"
        return if not xr

        @heap.scan_chunks_xr
        heapscan_time "#{@heap.xrchunksto.length} src, #{@heap.xrchunksfrom.length} dst"
end
heapscan_structs() click to toggle source
# File samples/dbg-plugins/heapscan.rb, line 105
def heapscan_structs
        heapscan_time
        @heap.bucketize
        heapscan_time "#{@heap.buckets.length} buckets"

        @heap.find_arrays
        heapscan_time "#{@heap.allarrays.length} arrays (#{@heap.allarrays.flatten.length} elems)"

        @heap.find_linkedlists
        heapscan_time "#{@heap.alllists.length} lists (#{@heap.alllists.flatten.length} elems)"
end
heapscan_time(s='') click to toggle source
# File samples/dbg-plugins/heapscan.rb, line 41
def heapscan_time(s='')
        @heapscan_time ||= nil
        t = Time.now
        log s + ' %.2fs' % (t-@heapscan_time) if @heapscan_time and s != ''
        @heapscan_time = t
        Gui.main_iter if gui
end
imm_to_const(addr) { |i| ... } click to toggle source

find immediate exprs in the instruction at addr, yield them

# File samples/dasm-plugins/c_constants.rb, line 11
def imm_to_const(addr)
        return if not di = di_at(addr)
        # TODO enter into memrefs ?
        di.instruction.args.grep(Expression).each { |a|
                i = a.reduce
                next if not i.kind_of? Integer
                next if not cstbase = yield(i)
                if c = imm_to_const_decompose(i, cstbase)
                        di.add_comment c
                end
        }
end
imm_to_const_decompose(imm, cstbase) click to toggle source

find the bitwise decomposition of imm into constants whose name include cstbase

# File samples/dasm-plugins/c_constants.rb, line 25
def imm_to_const_decompose(imm, cstbase)
        cstbase = /#{cstbase}/ if not cstbase.kind_of? Regexp
        dict = {}
        c_parser.lexer.definition.keys.grep(cstbase).each { |cst|
                if i = c_parser.macro_numeric(cst)
                        dict[cst] = i
                end
        }
        c_parser.toplevel.symbol.each { |k, v|
                dict[k] = v if v.kind_of? Integer and k =~ cstbase
        }
        dict.delete_if { |k, v| imm & v != v }
        if cst = dict.index(imm)
                cst
        else
                # a => 1, b => 2, c => 4, all => 7: discard abc, keep 'all'
                dict.delete_if { |k, v| dict.find { |kk, vv| vv > v and vv & v == v } }
                dict.keys.join(' | ') if not dict.empty?
        end
end
inspect() click to toggle source
# File metasm/disassemble.rb, line 2007
def inspect
        "<Metasm::Disassembler @%x>" % object_id
end
lib_name(sym) click to toggle source
# File samples/shellcode-dynlink.rb, line 112
def lib_name(sym)
        raise "unknown libname for #{sym}" if not lib = Metasm::WindowsExports::EXPORT[sym]
        n = lib.downcase[0, 4].unpack('C*')
        n[0] + (n[1]<<8) + (n[2] << 16) + (n[3] << 24)
end
lint(tg) click to toggle source

this is a ruby code cleaner tool it passes its argument to ruby -v -c, which displays warnings (eg unused variable) it shows the incriminated line along the warning, to help identify false positives probably linux-only, and need ruby-1.9.1 or newer

# File misc/lint.rb, line 13
def lint(tg)
        if File.symlink?(tg)
                # nothing
        elsif File.directory?(tg)
                Dir.entries(tg).each { |ent|
                        next if ent == '.' or ent == '..'
                        ent = File.join(tg, ent)
                        lint(ent) if File.directory?(ent) or ent =~ /\.rb$/
                }
        else
                lint_file(tg)
        end
end
lint_file(tg) click to toggle source
# File misc/lint.rb, line 27
def lint_file(tg)
        flines = nil
        compile_warn(tg).each_line { |line|
                file, lineno, warn = line.split(/\s*:\s*/, 3)
                if file == tg
                        if not flines
                                puts "#{tg}:"
                                flines = File.readlines(file) #File.open(file, 'rb') { |fd| fd.readlines }
                        end
                        puts " l.#{lineno}: #{warn.strip}: #{flines[lineno.to_i-1].strip.inspect}"
                end
        }
        puts if flines
end
loadmod(mod=$drv) click to toggle source
# File samples/r0trace.rb, line 245
def loadmod(mod=$drv)
        sh = DynLdr.openscmanagera(0, 0, DynLdr::SC_MANAGER_ALL_ACCESS)
        raise "cannot openscm" if (sh == 0)
        rh = DynLdr.createservicea(sh, mod, mod, DynLdr::SERVICE_ALL_ACCESS, DynLdr::SERVICE_KERNEL_DRIVER, DynLdr::SERVICE_DEMAND_START, DynLdr::SERVICE_ERROR_NORMAL, File.expand_path(mod), 0, 0, 0, 0, 0)
        if (DynLdr.startservicea(rh, 0, 0) == 0)
                raise "cannot start service"
        end
        DynLdr.CloseServiceHandle(rh)
        DynLdr.CloseServiceHandle(sh)
end
log_caller(cls, meth, singleton=false, histlen=nil) click to toggle source

A script to help finding performance bottlenecks:

$ ruby-prof myscript.rb

=> String#+ gets called 50k times and takes 30s

$ LOGCALLER='String#+' ruby -r bottleneck myscript.rb

=> String#+ called 40k times from:
    stuff.rb:42 in Myclass#uglymethod from
    stuff.rb:32 in Myclass#initialize

now you know what to rewrite

# File misc/bottleneck.rb, line 20
def log_caller(cls, meth, singleton=false, histlen=nil)
        histlen ||= ENV.fetch('LOGCALLER_MAXHIST', 16).to_i
        dec_meth = 'm_' + meth.to_s.gsub(/[^\w]/) { |c| c.unpack('H*')[0] }
        malias = dec_meth + '_log_caller'
        mcntr = '$' + dec_meth + '_counter'
        eval <<EOS

#{cls.kind_of?(Class) ? 'class' : 'module'} #{cls}
#{'class << self' if singleton}
 alias #{malias} #{meth}

 def #{meth}(*a, &b)
  #{mcntr}[caller[0, #{histlen}]] += 1
  #{malias}(*a, &b)
 end

#{'end' if singleton}
end

#{mcntr} = Hash.new(0)

at_exit {
        total = #{mcntr}.inject(0) { |a, (k, v)| a+v } 
        puts "\#{total} callers of #{cls} #{meth}:"
        #{mcntr}.sort_by { |k, v|
                -v
        }[0, 4].each { |k, v|
                puts " \#{'%.2f%%' % (100.0*v/total)} - \#{v} times from", k, ''
        }
}

EOS

end
make_alias(newop, newargs, oldop, oldargs) click to toggle source

handle instruction aliases NOT WORKING should be implemented in the parser/displayer instead of opcode list manual work needed for eg conditionnal jumps

# File misc/ppc_pdf2oplist.rb, line 85
def make_alias(newop, newargs, oldop, oldargs)
        raise "unknown alias #{newop} => #{oldop}" if not op = $opcodes.reverse.find { |op_| op_[0] == oldop }
        op2 = op.dup
        op2[0] = newop
        oldargs.each_with_index { |oa, i|
                # XXX bcctr 4, 6  ->  bcctr 4, 6, 0 => not the work
                if oa =~ /^[0-9]+$/ or oa =~ /^0x[0-9a-f]+$/
                        fld = op[2][i]
                        op2[1] |= Integer(oa) << $field_shift[fld]
                end
        }
        puts "#\talias #{newop} #{newargs.join(', ')}  ->  #{oldop} #{oldargs.join(', ')}".downcase
end
make_instr(bins, bits, text) click to toggle source
# File misc/ppc_pdf2oplist.rb, line 14
def make_instr(bins, bits, text)
        # calc bitfields length from their offset
        last = 32
        bitlen = []
        bits.reverse_each { |bit|
                bitlen.unshift last-bit
                last = bit
        }

        # the opcode binary value (w/o fields)
        bin = 0
        fields = []

        # parse the data
        bins.zip(bits, bitlen).each { |val, off, len|
                off = 32-(off+len)
                msk = (1 << len) - 1
                case val
                when '/', '//', '///' # reserved field, value unspecified
                when /^\d+$/; bin |= val.to_i << off  # constant field
                when /^[A-Za-z]+$/
                        fld = val.downcase.to_sym
                        fld = "#{fld}_".to_sym while $field_mask[fld] and ($field_mask[fld] != msk or $field_shift[fld] != off)
                        fields << fld
                        $field_mask[fld] ||= msk
                        $field_shift[fld] ||= off
                end
        }

        text.each { |txt|
                # fnabs FRT,FRB (Rc=0)
                curbin = bin
                curfields = fields.dup
                txt.sub!('  Rc=1)', '  (Rc=1)') if txt.include? 'fdiv.'               # typo: fdiv. has no '('
                if txt =~ /(.*\S)\s*\((\w+=.*)\)/
                        txt = $1
                        $2.split.each { |e|
                                raise e if e !~ /(\w+)=(\d+)/
                                name, val = $1.downcase, $2.to_i
                                raise "bad bit #{name} in #{txt}" if not fld = curfields.find { |fld_| fld_.to_s.delete('_') == name }
                                curfields.delete fld
                                curbin |= val << $field_shift[fld]
                        }
                end
                opname, args = txt.split(/\s+/, 2)
                args = args.to_s.downcase.split(/\s*,\s*/).map { |arg| fld = curfields.find { |fld_| fld_.to_s.delete('_') == arg } ; curfields.delete fld ; fld }
                if args.include? nil and curfields.length == 2 and (curfields - [:ra, :d]).empty?
                        args[args.index(nil)] = :ra_i16
                        curfields.clear
                elsif args.include? nil and curfields.length == 2 and (curfields - [:ra, :ds]).empty?
                        args[args.index(nil)] = :ra_i16s
                        curfields.clear
                elsif args.include? nil and curfields.length == 2 and (curfields - [:ra, :dq]).empty?
                        args[args.index(nil)] = :ra_i16q
                        curfields.clear
                elsif args.include? nil and curfields.length == 1
                        args[args.index(nil)] = curfields.shift
                end
                raise "bad args #{args.inspect} (#{curfields.inspect}) in #{txt}" if args.include? nil
                $opcodes << [opname, curbin, args]

                n = (opname.inspect << ',').ljust(10) + '0x%08X' % curbin
                n << ', ' if not args.empty?
                puts "\taddop " + n + args.map { |e| e.inspect }.join(', ')
        }
end
match_libsigs(sigfile) click to toggle source
# File samples/dasm-plugins/match_libsigs.rb, line 82
def match_libsigs(sigfile)
        ls = LibSignature.new(sigfile)
        count = 0
        @sections.each { |b, s|
                count += ls.match(s.data) { |off, sym| set_label_at(b+off, sym) }
        }
        count
end
need_backtrace(expr, terminals=[]) click to toggle source

returns true if the expression needs more backtrace it checks for the presence of a symbol (not :unknown), which means it depends on some register value

# File metasm/disassemble.rb, line 1754
def need_backtrace(expr, terminals=[])
        return if expr.kind_of? ::Integer
        !(expr.externals.grep(::Symbol) - [:unknown] - terminals).empty?
end
parse(root=false) click to toggle source
# File misc/cheader-findpppath.rb, line 17
def parse(root=false)
        want = false
        ret = []
        while l = gets
                case l = l.strip
                when /^#if/
                        ret << l
                        r = parse(true)
                        if r.empty?
                                ret.pop
                        else
                                want = true
                                rr = r.pop
                                ret.concat r.map { |l_| (l_[0,3] == '#el' ? ' ' : '    ') << l_ }
                                ret << rr
                        end
                when /^#el/
                        if not root
                                $ungets = l
                                break
                        end
                        ret << l
                        r = parse
                        want = true if not r.empty?
                        ret.concat r
                when /^#endif/
                        if not root
                                $ungets = l
                                break
                        end
                        ret << l
                        break
                when /#$srch/ #, /^#include/
                        want = true
                        ret << l
                end
        end
        want ? ret : []
end
parse_page(lines) click to toggle source
# File misc/ppc_pdf2oplist.rb, line 114
def parse_page(lines)
        # all instr defining pages include this
        return unless lines.find { |l| l.str =~ /Special Registers Altered|Memory Barrier Instructions|Data Cache Instructions/  }     # sync L/dcbt

        ilist = [] # line buffer
        extended = false

        # concat lines with same y
        lines = lines.sort_by { |l| [-l.y, l.x] }
        lastline = nil
        lines.delete_if { |l|
                if lastline and lastline.y == l.y and ([lastline.fontx, lastline.fonty] == [l.fontx, l.fonty] or l.str =~ /^\s*$/)
                        lastline.str << ' ' << l.str
                        true
                else
                        lastline = l
                        false
                end
        }

        lines.each { |l|
                # search for the bit indices list
                if l.fonty < 7 and l.str =~ /^0 [\d ]+ 31\s*$/ and (ilist.last.str.split.length == l.str.split.length or ilist.last.str.split.length == l.str.split.length-1)
                        $foundop = true
                        bitindices = l.str.split.map { |i| i.to_i }
                        # previous line is the binary encoding
                        encoding = ilist.pop.str.split
                        bitindices.pop if encoding.length < bitindices.length
                        # previous line is the instruction text format
                        ilist.pop if ilist.last.str =~ /\[POWER2? mnemonics?: (.*)\]/
                        text = []
                        text.unshift l while l = ilist.pop and l = l.str and (l =~ /,|\)$/ or text.empty?)
                        ilist = []
                        make_instr(encoding, bitindices, text)
                elsif l.str.include? 'Special Registers Altered'
                        if not $foundop
                                puts ilist.map { |l_| "(#{l_.y}) #{l_.str}" }
                                puts lines.map { |l_| "(#{l_.y}) #{l_.str}" } if ilist.empty?
                                raise 'nofoundop'
                        else
                                $foundop = false
                        end
                elsif l.str =~ /Extended:\s+Equivalent to:/
                        extended = true
                elsif extended
                        if l.str.include? ',' and l.str =~ /^(\S+)\s+(\S+)\s+(\S+)\s+(.*)/ and $opcodes.find { |op| op[0] == $3 }
                                newop, newargs, exop, exargs = $1, $2, $3, $4
                                make_alias(newop, newargs.split(','), exop, exargs.split(','))
                        else extended = false
                        end
                else ilist << l
                end
        }
end
patch_instrs(addr, asmsrc) click to toggle source
# File samples/dasm-plugins/patch_file.rb, line 48
def patch_instrs(addr, asmsrc)
        sc = Metasm::Shellcode.new(cpu, addr)  # pfx needed for autorequire
        sc.assemble(asmsrc, cpu)
        sc.encoded.fixup! prog_binding # allow references to dasm labels in the shellcode
        raw = sc.encode_string

        if s = get_section_at(addr) and s[0].data.kind_of? VirtualFile
                s[0][s[0].ptr, raw.length] = raw
        elsif o = addr_to_fileoff(addr)        # section too small, not loaded as a VirtFile
                backup_program_file
                File.open(@program.filename, 'rb+') { |fd|
                        fd.pos = o
                        fd.write raw
                }
                s[0][s[0].ptr, raw.length] = raw if s
        else
                return
        end

        b = split_block(addr)

        # clear what we had in the rewritten space
        raw.length.times { |rawoff|
                next if not di = di_at(addr+rawoff)
                di.block.list.each { |ldi| @decoded.delete ldi.address }
        }

        disassemble_fast(addr) if b
        if b and @decoded[addr]
                nb = @decoded[addr].block
                nb.from_normal = b.from_normal
                nb.from_subfuncret = b.from_subfuncret
                nb.from_indirect = b.from_indirect
        end
        true
end
reopen_rw(addr=nil, edata=nil) click to toggle source

create a backup and reopen the backend VirtualFile RW

# File samples/dasm-plugins/patch_file.rb, line 25
def reopen_rw(addr=nil, edata=nil)
        if not edata
                sections.each { |k, v| reopen_rw(k, v) }
                return true
        end

        return if not File.writable?(@program.filename)
        backup_program_file
        if not edata.data.kind_of? VirtualFile
                # section too small, loaded as real String
                # force reopen as VFile (allow hexediting in gui)
                return if not off = addr_to_fileoff(addr)
                len = edata.data.length
                edata.data = VirtualFile.read(@program.filename, 'rb+').dup(off, len)
        else
                edata.data.fd.reopen @program.filename, 'rb+'
        end
end
resolve(expr) click to toggle source

static resolution of indirections

# File metasm/disassemble.rb, line 1743
def resolve(expr)
        binding = Expression[expr].expr_indirections.inject(@old_prog_binding) { |binding_, ind|
                e = get_edata_at(resolve(ind.target))
                return expr if not e
                binding_.merge ind => Expression[ e.decode_imm("u#{8*ind.len}".to_sym, @cpu.endianness) ]
        }
        Expression[expr].bind(binding).reduce
end
scan_for(targets, path='', done={}) click to toggle source

dumps to stdout the path to find some targets ( array of objects to match with == )

# File misc/objscan.rb, line 22
def scan_for(targets, path='', done={})
        done[object_id] = self if done.empty?
        if t = targets.find { |t_| self == t_ }
                puts "found #{t} at #{path}"
        end
        scan_iter { |v, p|
                case v
                when Integer, Symbol; next
                end
                p = path+p
                if done[v.object_id]
                        puts "loop #{p} -> #{done[v.object_id]}" if $VERBOSE
                else
                        done[v.object_id] = p
                        v.scan_for(targets, p, done)
                end
        }
end
scan_iter() { |self, "[| ... } click to toggle source
# File misc/objscan.rb, line 10
def scan_iter
        case self
        when ::Array
                length.times { |i| yield self[i], "[#{i}]" }
        when ::Hash
                each { |k, v| yield v, "[#{k.inspect}]" ; yield k, "(key)" }
        else
                instance_variables.each { |i| yield instance_variable_get(i), ".#{i[1..-1]}" }
        end
end
scanfuncstart(addr) click to toggle source

metasm dasm plugin: scan the memory for a 'ret' which could indicate the beginning of the current function (x86 only)

# File samples/dasm-plugins/scanfuncstart.rb, line 9
def scanfuncstart(addr)
        if o = (1..16).find { |off| @decoded[addr-off].kind_of? DecodedInstruction } and @decoded[addr-o].bin_length == o
                addr -= o
        end
        if @decoded[addr].kind_of? DecodedInstruction
                fs = find_function_start(addr)
                return fs if fs != addr
        end
        edata = get_edata_at(addr)
        if o = (1..1000).find { |off|
                @decoded[addr-off-1] or
                edata.data[edata.ptr-off-1] == \xcc or
                edata.data[edata.ptr-off-1] == \xc3 or
                edata.data[edata.ptr-off-3] == \xc2
        }
                o -= @decoded[addr-o-1].bin_length-1 if @decoded[addr-o-1].kind_of? DecodedInstruction
                addr-o
        end
end
scanxrefs(target) click to toggle source

metasm dasm plugin: scan for xrefs to the target address, incl. relative offsets (eg near call/jmp)

# File samples/dasm-plugins/scanxrefs.rb, line 8
def scanxrefs(target)
        ans = []
        msk = (1 << cpu.size) - 1
        sections.sort.each { |s_addr, edata|
                raw = edata.data.to_str
                (0..raw.length-4).each { |off|
                        r = raw[off, 4].unpack('V').first
                        ans << (s_addr + off) if (r + off+4 + s_addr) & msk == target or r == target
                }
        }
        ans
end
show(pfx, str) click to toggle source

prints the string in 80 cols with the first column filled with pfx

# File misc/hexdiff.rb, line 19
def show(pfx, str)
        loop do
                if str.length > 79
                        len = 79 - str[0...79][/\S+$/].to_s.length
                        len = 79 if len == 0
                        puts pfx + str[0...len]
                        str = str[len..-1]
                else
                        puts pfx + str
                        break
                end
        end
end
solve_indirect_call_set_struct(ptr, struct) click to toggle source
# File samples/dasm-plugins/cppobj_funcall.rb, line 14
def solve_indirect_call_set_struct(ptr, struct)
        struct = @c_parser.toplevel.struct[struct] if struct.kind_of? String
        raise 'no such struct' if not struct
        @indirect_call_struct[ptr] = struct
end
solve_indirect_calls() { |obj| ... } click to toggle source
# File samples/dasm-plugins/cppobj_funcall.rb, line 20
def solve_indirect_calls
        @decoded.values.grep(DecodedInstruction).each { |di|
                next if not di.opcode.props[:saveip]  # only calls
                fptr = get_xrefs_x(di)
                next if fptr.to_a.length != 1
                fptr = Expression[fptr.first].reduce_rec
                next if not fptr.kind_of? Indirection
                next if not fptr.pointer.lexpr.kind_of? Symbol
                next if not fptr.pointer.rexpr.kind_of? Integer
                obj = backtrace(fptr.pointer.lexpr, di.address)
                obj.delete Expression::Unknown
                next if obj.length != 1
                obj = obj.first
                obj = Expression[obj].reduce_rec
                next if not obj.kind_of? Indirection
                obj = obj.pointer     # vtable ptr -> object ptr

                if not struct = @indirect_call_struct[obj]
                        struct = yield obj if block_given?
                        solve_indirect_call_set_struct(obj, struct || :none)
                end

                if struct.kind_of? C::Struct and fld = struct.members.find { |m| struct.offsetof(c_parser, m) == fptr.pointer.rexpr } and fld.name
                        di.add_comment "#{struct.name || obj}->#{fld.name}"
                        di.comment.delete 'x:unknown'
                end
        }
end
stringsxrefs(maxsz = 32) click to toggle source

metasm dasm plugin walks all disassembled instructions referencing an address if this address points a C string, show that in the instruction comments esp. useful after a disassemble_fast

# File samples/dasm-plugins/stringsxrefs.rb, line 12
def stringsxrefs(maxsz = 32)
        @decoded.each_value { |di|
                next if not di.kind_of?(DecodedInstruction)
                di.instruction.args.grep(Expression).each { |e|
                        if str = decode_strz(e) and str.length >= 4 and str =~ /^[\x20-\x7e]*$/
                                di.add_comment str[0, maxsz].inspect
                                add_xref(normalize(e), Xref.new(:r, di.address, 1))
                        end
                }
        }
        nil
end
test_all() click to toggle source
# File tests/graph_layout.rb, line 34
def test_all
        test_layout <<EOS
line -> 2 -> 3 -> 4 -> 5 -> 6 -> 7;
EOS
        test_layout <<EOS
sep1 -> 1;
sep2 -> 2;
sep3 -> 3;
sep4 -> 4;
sep5 -> 5;
EOS
        test_layout <<EOS
fork -> 2 -> 3;
2 -> 4;
EOS
        test_layout <<EOS
diamond -> 2 -> 3 -> 5;
2 -> 4 -> 5;
EOS
        test_layout <<EOS
ifthen -> 2 -> 3 -> 4;
2 -> 4;
EOS
        test_layout <<EOS
ufork -> 2 -> 3;
1 -> 2;
EOS
        test_layout <<EOS
multidiamond -> 2 -> 31 -> 32 -> 34 -> 5 -> 6 -> 8;
2 -> 41 -> 42 -> 44 -> 5 -> 7 -> 8;
41 -> 43 -> 44;
31 -> 33 -> 34;
EOS
        test_layout <<EOS
dmdout -> 2 -> 3a -> 4;
2 -> 3b -> 4;
3a -> 4a;
3b -> 4b;
EOS
        test_layout <<EOS
ifthenthen -> 2 -> 8;
2 -> 3 -> 8;
2 -> 4 -> 5 -> 8;
2 -> 6 -> 7 -> 8;
EOS
        test_layout <<EOS
multipod -> 2 -> 3;
2 -> 4;
2 -> 5;
2 -> 6;
2 -> 7;
2 -> 8;
EOS
        test_layout <<EOS
mplarge -> 1 -> 2;
1 -> 3333333333333333333333333333333333;
EOS
        test_layout <<EOS
multif -> 1
1 -> a2 -> a3
a2 -> a222222222 -> a3
1 -> b2 -> b3
b2 -> b222222222 -> b3
EOS
        test_layout <<EOS
ifx -> 1 -> 2 -> 3 -> 4 -> 5
4 -> eeeeeeeeeeee -> 5
EOS
        test_layout <<EOS
llll -> 1 -> 22222222222222222222222222 -> e
1 -> 33333333333333333333333333 -> e
1 -> 4444444444444444444444 -> e
1 -> 5 -> e
5 -> 5t -> e
1 -> 6 -> e
6 -> 6t -> e
1 -> 7 -> e
7 -> 7t -> e
EOS
        test_layout <<EOS
dangling -> 2 -> 11 -> 12 -> 13 -> 4;
2 -> 21 -> 22 -> 23 -> 4;
2 -> 31 -> 32 -> 33 -> 4;
21 -> 2z;
31 -> 3z;
EOS
        test_layout <<EOS
dangin -> 2 -> 11 -> 12 -> 13;
2 -> 21 -> 22 -> 13;
2 -> 31 -> 32 -> 33;
22 -> 33;
21 -> z;
EOS
        test_layout <<EOS
cascadeclean -> 2 -> 3 -> 4 -> 5 -> 6 -> 62 -> 52 -> 42 -> 32 -> 22 -> e;
2 -> 21 -> 22;
3 -> 31 -> 32;
4 -> 41 -> 42;
5 -> 51 -> 52;
6 -> 61 -> 62;
EOS
        test_layout <<EOS
cascade -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> e;
2 -> 21 -> e;
3 -> 31 -> e;
4 -> 41 -> e;
5 -> 51 -> e;
6 -> 61 -> e;
EOS
        test_layout <<EOS
rstair -> 2 -> 3 -> 4 -> 5 -> 6;
2 -> 4;
2 -> 5;
2 -> 6;
EOS
        test_layout <<EOS
drstair -> 2a -> 3 -> 4 -> 5 -> 6;
drstair -> 2b -> 4;
2a -> 4;
2a -> 5;
2a -> 6;
2b -> 4;
2b -> 5;
2b -> 6;
EOS
        test_layout <<EOS
mrstair -> 2a -> 3a -> 4a -> 5a -> 6a;
mrstair -> 2b -> 4a;
2a -> 4a;
2a -> 5a;
2a -> 6a;
2b -> 4a;
2b -> 5a;
2b -> 6a;
2a -> 3b -> 4b -> 5b -> 6b;
2a -> 4b;
2a -> 5b;
2a -> 6b;
2b -> 3b;
2b -> 4b;
2b -> 5b;
2b -> 6b;
EOS
        test_layout <<EOS
loop -> 2 -> 3 -> 4;
3 -> 2;
EOS
        test_layout <<EOS
loopbreak -> 2 -> 3 -> e;
2 -> 4 -> 5 -> 6 -> 8 -> e;
5 -> 7 -> 4;
EOS
        test_layout <<EOS
loopbreak2 -> 2 -> 3 -> e;
2 -> 4 -> 5 -> 6 -> 8 -> e;
5 -> 7 -> 4;
7 -> 8;
EOS
        test_layout <<EOS
unbalance -> 2 -> 3 -> 4 -> 5 -> e;
2 -> 6 -> 7 -> 8 -> 9 -> 10 -> 11 -> 12 -> e;
EOS
        test_layout <<EOS
unbalance2 -> 2 -> 3 -> e;
2 -> 4 -> e;
2 -> 5 -> e;
2 -> 6 -> 7 -> 8 -> 9 -> 10 -> 11 -> 12 -> e;
EOS
        test_layout <<EOS
unbalance3 -> 2 -> 3 -> e;
2 -> 4 -> e;
2 -> 5 -> e;
2 -> 6 -> e;
8 -> 9 -> e;
2 -> 7 -> e;
EOS
        test_layout <<EOS
disjoint -> 1 -> 2 -> 3 -> 4 -> 5 -> 6;
l1 -> l2;
l1 -> l3;
l1 -> l4;
l1 -> l5;
l1 -> l6;
EOS

        test_layout <<EOS
lol -> 2 -> 31 -> 41 -> 5;
2 -> 32 -> 42 -> 5;
31 -> 42;
41 -> 32;
EOS
        test_layout <<EOS
nestloop -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> e;
6 -> 4;
7 -> 3;
EOS
        test_layout <<EOS
escape -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8;
2 -> 21;
4 -> 6;
EOS
        test_layout <<EOS
loophead -> 1 -> loophead;
2 -> 3 -> 2;
3 -> 4;
1 -> 4;
EOS
        test_layout <<EOS
1 -> e1;
l00pz -> 1 -> l00pz;
l2 -> 2 -> l2;
2 -> e1;
2 -> e2;
l3 -> 3 -> l3;
3 -> e2;
EOS
        test_layout <<EOS
3loop -> 1 -> 3loop;
1 -> 2 -> 3  -> 2;
0 -> 00 -> 0 -> 2;
EOS
        test_layout <<EOS
foo -> 0 -> 1
0 -> 2 -> 3 -> 4 -> 5
4 -> 6
4 -> 7 -> 5
4 -> 8 -> 6
2 -> 1 -> 7
3 -> 1 -> 8
EOS
        test_layout <<EOS
dang -> 2 -> 3 -> 4 -> 5 -> 6 -> 4;
2 -> 9;
5 -> 9;
EOS
        test_layout <<EOS
dang2 -> 2 -> 3 -> 4 -> 5 -> 6 -> 4
2 -> 9
5 -> 9
9 -> a -> 9
EOS
        test_layout <<EOS
onlyloop -> onlyloop
EOS

rescue Interrupt
end
test_layout(lo) click to toggle source
# File tests/graph_layout.rb, line 13
def test_layout(lo)
        $cur ||= 0
        $cur += 1
        if $target.to_i != 0
                return if $cur != $target
        else
                return if not lo.include? $target
        end if $target
        puts $cur, lo, '' if $VERBOSE
        w = Gui::Window.new
        ww = w.widget = Gui::GraphViewWidget.new(nil, nil)
        ww.grab_focus
        Gui.idle_add {
                ww.load_dot(lo)
                ww.curcontext.auto_arrange_boxes
                ww.zoom_all
                false
        }
        Gui.main
end
to_s() click to toggle source
# File metasm/disassemble.rb, line 2011
def to_s
        a = ''
        dump { |l| a << l << "\n" }
        a
end
trace() click to toggle source

start tracing now, and stop only when @trace_terminate is set

# File samples/dbg-plugins/trace_func.rb, line 31
def trace
        @trace_subfuncs = true
        @trace_terminate = false
        id = [pc, 0, 0]
        trace_func_newtrace(id)
        trace_func_block(id)
        continue
end
trace_func(addr, oneshot = false) click to toggle source

setup the initial breakpoint at func start

# File samples/dbg-plugins/trace_func.rb, line 13
def trace_func(addr, oneshot = false)
        @trace_terminate = false
        # distinguish different hits on the same function entry
        counter = 0
        bp = bpx(addr, oneshot) {
                counter += 1
                id = [disassembler.normalize(addr), counter, func_retaddr]
                trace_func_newtrace(id)
                trace_func_block(id)
                continue
        }
        if addr == pc
                del_bp bp if oneshot
                bp.action.call
        end
end
trace_func_add_block(id, blockaddr) click to toggle source

a new block is added to a trace

# File samples/dbg-plugins/trace_func.rb, line 151
def trace_func_add_block(id, blockaddr)
        @trace_func_counter[id] += 1
        if di = disassembler.di_at(blockaddr)
                di.add_comment "functrace #{@trace_func_counter[id]}"
        end
end
trace_func_block(id) click to toggle source

we hit the beginning of a block we want to trace

# File samples/dbg-plugins/trace_func.rb, line 41
def trace_func_block(id)
        blockaddr = pc
        if b = trace_get_block(blockaddr)
                trace_func_add_block(id, blockaddr)
                if b.list.length == 1
                        trace_func_blockend(id, blockaddr)
                else
                        bpx(b.list.last.address, true) {
                                finished = trace_func_blockend(id, blockaddr)
                                continue if not finished
                        }
                end
        else
                # invalid opcode ?
                trace_func_blockend(id, blockaddr)
        end
end
trace_func_blockend(id, blockaddr) click to toggle source

we are at the end of a traced block, find whats next

# File samples/dbg-plugins/trace_func.rb, line 60
def trace_func_blockend(id, blockaddr)
        if di = disassembler.di_at(pc)
                if end_stepout(di) and trace_func_istraceend(id, di)
                        # trace ends there
                        trace_func_finish(id)
                        return true
                elsif di.opcode.props[:saveip] and not trace_func_entersubfunc(id, di)
                        # call to a subfunction
                        bpx(di.next_addr, true) {
                                trace_func_block(id)
                                continue
                        }
                        continue
                else
                        singlestep {
                                newaddr = pc
                                trace_func_block(id)

                                trace_func_linkdasm(di.address, newaddr)
                                continue
                        }
                end
        else
                # XXX should link in the dasm somehow
                singlestep {
                        trace_func_block(id)
                        continue
                }
        end
        false
end
trace_func_entersubfunc(id, di) click to toggle source

the tracer is on a subfunction call instruction, should it trace into or stepover ?

# File samples/dbg-plugins/trace_func.rb, line 182
def trace_func_entersubfunc(id, di)
        if trace_subfuncs
                @trace_func_subfunccache ||= {}
                if not target = @trace_func_subfunccache[di.address]
                        # even if the target is dynamic, its module should be static
                        if target = disassembler.get_xrefs_x(di)[0]
                                @trace_func_subfunccache[di.address] =
                                target = resolve(disassembler.normalize(target))
                        end
                end
                # check if the target subfunction is in the same module as the main
                # XXX should check against the list of loaded modules etc
                # XXX call thunk_foo -> jmp [other_module]
                true if target.kind_of? Integer and target & 0xffc0_0000 == id[0] & 0xffc0_0000
        else
                false
        end
end
trace_func_finish(id) click to toggle source

the trace is finished

# File samples/dbg-plugins/trace_func.rb, line 159
def trace_func_finish(id)
        puts "finished tracing #{Expression[id[0]]}"
end
trace_func_istraceend(id, di) click to toggle source

the tracer is on a end-of-func instruction, should the trace end ?

# File samples/dbg-plugins/trace_func.rb, line 167
def trace_func_istraceend(id, di)
        if trace_subfuncs
                if @trace_terminate
                        true
                elsif id[2] == 0      # trace VS func_trace
                elsif target = disassembler.get_xrefs_x(di)[0]
                        # check the current return address against the one saved at trace start
                        resolve(disassembler.normalize(target)) == id[2]
                end
        else
                true
        end
end
trace_func_linkdasm(from_addr, new_addr) click to toggle source

update the blocks links in the disassembler

# File samples/dbg-plugins/trace_func.rb, line 102
def trace_func_linkdasm(from_addr, new_addr)
        di = disassembler.di_at(from_addr)
        ndi = disassembler.di_at(new_addr)

        return if not di

        # is it a subfunction return ?
        if end_stepout(di) and cdi = (1..8).map { |i|
                                disassembler.di_at(new_addr - i)
                        }.compact.find { |cdi_|
                                cdi_.opcode.props[:saveip] and cdi_.next_addr == new_addr
                        }
                cdi.block.add_to_subfuncret new_addr
                ndi.block.add_from_subfuncret cdi.address if ndi
                cdi.block.each_to_normal { |f|
                        disassembler.function[f] ||= DecodedFunction.new if disassembler.di_at(f)
                }
        else
                di.block.add_to_normal new_addr
                ndi.block.add_from_normal from_addr if ndi
        end
end
trace_func_newtrace(id) click to toggle source

a new trace is about to begin

# File samples/dbg-plugins/trace_func.rb, line 129
def trace_func_newtrace(id)
        @trace_func_counter ||= {}
        @trace_func_counter[id] = 0

        puts "start tracing #{Expression[id[0]]}"

        # setup a bg_color_callback on the disassembler
        if not defined? @trace_func_dasmcolor
                @trace_func_dasmcolor = true
                return if not disassembler.gui
                oldcb = disassembler.gui.bg_color_callback
                disassembler.gui.bg_color_callback = lambda { |addr|
                        if oldcb and c = oldcb[addr]
                                c
                        elsif di = disassembler.di_at(addr) and di.block.list.first.comment.to_s =~ /functrace/
                                'ff0'
                        end
                }
        end
end
trace_get_block(addr) click to toggle source

retrieve an instructionblock, disassemble if needed

# File samples/dbg-plugins/trace_func.rb, line 93
def trace_get_block(addr)
        # TODO trace all blocks from addr for which we know the target, stop on call / jmp [foo]
        disassembler.disassemble_fast_block(addr)
        if di = disassembler.di_at(addr)
                di.block
        end
end
trace_subfuncs() click to toggle source
# File samples/dbg-plugins/trace_func.rb, line 164
def trace_subfuncs; @trace_subfuncs ||= false end
trace_subfuncs=(v) click to toggle source
# File samples/dbg-plugins/trace_func.rb, line 163
def trace_subfuncs=(v) @trace_subfuncs = v end
unloadmod(mod=$drv) click to toggle source
# File samples/r0trace.rb, line 256
def unloadmod(mod=$drv)
        sh = DynLdr.openscmanagera(0, 0, DynLdr::SC_MANAGER_ALL_ACCESS)
        raise "cannot openscm" if (sh == 0)
        rh = DynLdr.openservicea(sh, mod, DynLdr::SERVICE_ALL_ACCESS)
        DynLdr.controlservice(rh, DynLdr::SERVICE_CONTROL_STOP, 0.chr*4*32)
        DynLdr.deleteservice(rh)
        DynLdr.CloseServiceHandle(rh)
        DynLdr.CloseServiceHandle(sh)
end