class Metasm::Gui::CdecompListingWidget

Attributes

curaddr[RW]
dasm[RW]
tabwidth[RW]

Public Instance Methods

click(x, y) click to toggle source
# File metasm/gui/dasm_decomp.rb, line 30
def click(x, y)
        @caret_x = (x-1).to_i / @font_width + @view_x
        @caret_y = y.to_i / @font_height + @view_y
        update_caret
end
curfunc() click to toggle source
# File metasm/gui/dasm_decomp.rb, line 26
def curfunc
        @dasm.c_parser and (@dasm.c_parser.toplevel.symbol[@curaddr] or @dasm.c_parser.toplevel.struct[@curaddr])
end
current_address() click to toggle source

returns the address of the data under the cursor

# File metasm/gui/dasm_decomp.rb, line 292
def current_address
        @curaddr
end
doubleclick(x, y) click to toggle source
# File metasm/gui/dasm_decomp.rb, line 45
def doubleclick(x, y)
        click(x, y)
        @parent_widget.focus_addr(@hl_word)
end
focus_addr(addr) click to toggle source

focus on addr returns true on success (address exists & decompiled)

# File metasm/gui/dasm_decomp.rb, line 259
def focus_addr(addr)
        if @dasm.c_parser and (@dasm.c_parser.toplevel.symbol[addr] or @dasm.c_parser.toplevel.struct[addr].kind_of?(C::Union))
                @curaddr = addr
                @caret_x = @caret_y = 0
                gui_update
                return true
        end

        return if not addr = @parent_widget.normalize(addr)

        # scan up to func start/entrypoint
        todo = [addr]
        done = []
        ep = @dasm.entrypoints.to_a.inject({}) { |h, e| h.update @dasm.normalize(e) => true }
        while addr = todo.pop
                next if not di = @dasm.di_at(addr)
                addr = di.block.address
                next if done.include?(addr) or not @dasm.di_at(addr)
                done << addr
                break if @dasm.function[addr] or ep[addr]
                empty = true
                @dasm.decoded[addr].block.each_from_samefunc(@dasm) { |na| empty = false ; todo << na }
                break if empty
        end
        @dasm.auto_label_at(addr, 'loc') if @dasm.get_section_at(addr) and not @dasm.get_label_at(addr)
        return if not l = @dasm.get_label_at(addr)
        @curaddr = l
        @caret_x = @caret_y = 0
        gui_update
        true
end
get_cursor_pos() click to toggle source
# File metasm/gui/dasm_decomp.rb, line 235
def get_cursor_pos
        [@curaddr, @caret_x, @caret_y, @view_y]
end
gui_update() click to toggle source
# File metasm/gui/dasm_decomp.rb, line 339
def gui_update
        if not curfunc and not @decompiling ||= false
                @line_text = ['please wait']
                @line_text_col = [[[:text, 'please wait']]]
                redraw
                @decompiling = true
                @dasm.decompile_func(@curaddr)
                @decompiling = false
        end
        if curfunc
                update_line_text
                update_caret
        end
        redraw
end
initialize_widget(dasm, parent_widget) click to toggle source
# File metasm/gui/dasm_decomp.rb, line 11
def initialize_widget(dasm, parent_widget)
        @dasm = dasm
        @parent_widget = parent_widget

        @view_x = @view_y = 0 # coord of corner of view in characters
        @cwidth = @cheight = 1        # widget size in chars
        @line_text = []
        @line_text_col = []   # each line is [[:col, 'text'], [:col, 'text']]
        @curaddr = nil
        @tabwidth = 8

        @default_color_association = ColorTheme.merge :keyword => :blue, :localvar => :darkred,
                :globalvar => :darkgreen, :intrinsic => :darkyellow
end
keypress(key) click to toggle source
# File metasm/gui/dasm_decomp.rb, line 122
def keypress(key)
        case key
        when :left
                if @caret_x >= 1
                        @caret_x -= 1
                        update_caret
                end
        when :up
                if @caret_y > 0
                        @caret_y -= 1
                        update_caret
                end
        when :right
                if @caret_x < @line_text[@caret_y].to_s.length
                        @caret_x += 1
                        update_caret
                end
        when :down
                if @caret_y < @line_text.length
                        @caret_y += 1
                        update_caret
                end
        when :home
                @caret_x = @line_text[@caret_y].to_s[/^\s*/].length
                update_caret
        when :end
                @caret_x = @line_text[@caret_y].to_s.length
                update_caret
        when :pgup
                @caret_y -= @cheight/2
                @caret_y = 0 if @caret_y < 0
                update_caret
        when :pgdown
                @caret_y += @cheight/2
                @caret_y = @line_text.length if @caret_y > @line_text.length
                update_caret
        when n       # rename local/global variable
                f = curfunc.initializer if curfunc and curfunc.initializer.kind_of? C::Block
                n = @hl_word
                if (f and f.symbol[n]) or @dasm.c_parser.toplevel.symbol[n]
                        @parent_widget.inputbox("new name for #{n}", :text => n) { |v|
                                if v !~ /^[a-z_$][a-z_0-9$]*$/
                                        @parent_widget.messagebox("invalid name #{v.inspect} !")
                                        next
                                end
                                if f and f.symbol[n]
                                        # TODO add/update comment to the asm instrs
                                        s = f.symbol[v] = f.symbol.delete(n)
                                        s.name = v
                                        f.decompdata[:stackoff_name][s.stackoff] = v if s.stackoff
                                elsif @dasm.c_parser.toplevel.symbol[n]
                                        @dasm.rename_label(n, v)
                                        @curaddr = v if @curaddr == n
                                end
                                gui_update
                        }
                end
        when r # redecompile
                @parent_widget.decompile(@curaddr)
        when t       # change variable type (you'll want to redecompile after that)
                f = curfunc.initializer if curfunc.kind_of? C::Variable and curfunc.initializer.kind_of? C::Block
                n = @hl_word
                cp = @dasm.c_parser
                if (f and s = f.symbol[n]) or s = cp.toplevel.symbol[n] or s = cp.toplevel.symbol[@curaddr]
                        s_ = s.dup
                        s_.initializer = nil if s.kind_of? C::Variable      # for static var, avoid dumping the initializer in the textbox
                        s_.attributes &= C::Attributes::DECLSPECS if s_.attributes
                        @parent_widget.inputbox("new type for #{s.name}", :text => s_.dump_def(cp.toplevel)[0].join(' ')) { |t|
                                if t == ''
                                        if s.type.kind_of? C::Function and s.initializer and s.initializer.decompdata
                                                s.initializer.decompdata[:stackoff_type].clear
                                                s.initializer.decompdata.delete :return_type
                                        elsif s.kind_of? C::Variable and s.stackoff
                                                f.decompdata[:stackoff_type].delete s.stackoff
                                        end
                                        next
                                end
                                begin
                                        cp.lexer.feed(t)
                                        raise 'bad type' if not v = C::Variable.parse_type(cp, cp.toplevel, true)
                                        v.parse_declarator(cp, cp.toplevel)
                                        if s.type.kind_of? C::Function and s.initializer and s.initializer.decompdata
                                                # updated type of a decompiled func: update stack
                                                vt = v.type.untypedef
                                                vt = vt.type.untypedef if vt.kind_of? C::Pointer
                                                raise 'function forever !' if not vt.kind_of? C::Function
                                                # TODO _declspec
                                                ao = 1
                                                vt.args.to_a.each { |a|
                                                        next if a.has_attribute_var('register')
                                                        ao = (ao + [cp.sizeof(a), cp.typesize[:ptr]].max - 1) / cp.typesize[:ptr] * cp.typesize[:ptr]
                                                        s.initializer.decompdata[:stackoff_name][ao] = a.name if a.name
                                                        s.initializer.decompdata[:stackoff_type][ao] = a.type
                                                        ao += cp.sizeof(a)
                                                }
                                                s.initializer.decompdata[:return_type] = vt.type
                                                s.type = v.type
                                        else
                                                f.decompdata[:stackoff_type][s.stackoff] = v.type if f and s.kind_of? C::Variable and s.stackoff
                                                s.type = v.type
                                        end
                                        gui_update
                                rescue Object
                                        @parent_widget.messagebox([$!.message, $!.backtrace].join("\n"), "error")
                                end
                                cp.readtok until cp.eos?
                        }
                end
        else return false
        end
        true
end
mouse_wheel(dir, x, y) click to toggle source
# File metasm/gui/dasm_decomp.rb, line 50
def mouse_wheel(dir, x, y)
        case dir
        when :up
                if @caret_y > 0
                        @view_y -= 4
                        @caret_y -= 4
                        @caret_y = 0 if @caret_y < 0
                end
        when :down
                if @caret_y < @line_text.length - 1
                        @view_y += 4
                        @caret_y += 4
                        redraw
                end
        end
        redraw
end
paint() click to toggle source
# File metasm/gui/dasm_decomp.rb, line 68
def paint
        @cwidth = width/@font_width
        @cheight = height/@font_height

        # adjust viewport to cursor
        sz_x = @line_text.map { |l| l.length }.max.to_i + 1
        sz_y = @line_text.length.to_i + 1
        @view_x = @caret_x - @cwidth + 1 if @caret_x > @view_x + @cwidth - 1
        @view_x = @caret_x if @caret_x < @view_x
        @view_x = sz_x - @cwidth - 1 if @view_x >= sz_x - @cwidth
        @view_x = 0 if @view_x < 0

        @view_y = @caret_y - @cheight + 1 if @caret_y > @view_y + @cheight - 1
        @view_y = @caret_y if @caret_y < @view_y
        @view_y = sz_y - @cheight - 1 if @view_y >= sz_y - @cheight
        @view_y = 0 if @view_y < 0

        # current cursor position
        x = 1
        y = 0

        # renders a string at current cursor position with a color
        # must not include newline
        render = lambda { |str, color|
                # function ends when we write under the bottom of the listing
                draw_string_hl(color, x, y, str)
                x += str.length * @font_width
        }

        @line_text_col[@view_y, @cheight + 1].each { |l|
                cx = 0
                l.each { |c, t|
                        cx += t.length
                        if cx-t.length > @view_x + @cwidth + 1
                        elsif cx < @view_x
                        else
                                t = t[(@view_x - cx + t.length)..-1] if cx-t.length < @view_x
                                render[t, c]
                        end
                }
                x = 1
                y += @font_height
        }

        if focus?
                # draw caret
                cx = (@caret_x-@view_x)*@font_width+1
                cy = (@caret_y-@view_y)*@font_height
                draw_line_color(:caret, cx, cy, cx, cy+@font_height-1)
        end

        @oldcaret_x, @oldcaret_y = @caret_x, @caret_y
end
rightclick(x, y) click to toggle source
# File metasm/gui/dasm_decomp.rb, line 36
def rightclick(x, y)
        click(x, y)
        if @dasm.c_parser and @dasm.c_parser.toplevel.symbol[@hl_word]
                @parent_widget.clone_window(@hl_word, :decompile)
        elsif @hl_word
                @parent_widget.clone_window(@hl_word)
        end
end
set_cursor_pos(p) click to toggle source
# File metasm/gui/dasm_decomp.rb, line 239
def set_cursor_pos(p)
        focus_addr p[0]
        @caret_x, @caret_y, @view_y = p[1, 3]
        update_caret
end
update_caret() click to toggle source

hint that the caret moved redraws the caret, change the hilighted word, redraw if needed

# File metasm/gui/dasm_decomp.rb, line 247
def update_caret
        redraw if @caret_x < @view_x or @caret_x >= @view_x + @cwidth or @caret_y < @view_y or @caret_y >= @view_y + @cheight

        invalidate_caret(@oldcaret_x-@view_x, @oldcaret_y-@view_y)
        invalidate_caret(@caret_x-@view_x, @caret_y-@view_y)
        @oldcaret_x, @oldcaret_y = @caret_x, @caret_y

        redraw if update_hl_word(@line_text[@caret_y], @caret_x, :c)
end
update_line_text() click to toggle source
# File metasm/gui/dasm_decomp.rb, line 296
def update_line_text
        @line_text = curfunc.dump_def(@dasm.c_parser.toplevel)[0].map { |l| l.gsub("\t", ' '*@tabwidth) }
        @line_text_col = []

        if f = curfunc and f.kind_of? C::Variable and f.initializer.kind_of? C::Block
                keyword_re = /\b(#{C::Keyword.keys.join('|')})\b/
                intrinsic_re = /\b(intrinsic_\w+)\b/
                lv = f.initializer.symbol.keys
                lv << '00' if lv.empty?
                localvar_re = /\b(#{lv.join('|')})\b/
                globalvar_re = /\b(#{f.initializer.outer.symbol.keys.join('|')})\b/
        end

        @line_text.each { |l|
                lc = []
                if f
                        while l and l.length > 0
                                if (i_k = (l =~ keyword_re)) == 0
                                        m = $1.length
                                        col = :keyword
                                elsif (i_i = (l =~ intrinsic_re)) == 0
                                        m = $1.length
                                        col = :intrinsic
                                elsif (i_l = (l =~ localvar_re)) == 0
                                        m = $1.length
                                        col = :localvar
                                elsif (i_g = (l =~ globalvar_re)) == 0
                                        m = $1.length
                                        col = :globalvar
                                else
                                        m = ([i_k, i_i, i_l, i_g, l.length] - [nil, false]).min
                                        col = :text
                                end
                                lc << [col, l[0, m]]
                                l = l[m..-1]
                        end
                else
                        lc << [:text, l]
                end
                @line_text_col << lc
        }
end