class Metasm::Gui::DasmWindow

Attributes

dasm_widget[RW]
menu[RW]

Public Instance Methods

build_menu() click to toggle source
# File metasm/gui/dasm_main.rb, line 1043
def build_menu
        # TODO dynamic checkboxes (check $VERBOSE when opening the options menu to (un)set the mark)
        filemenu = new_menu

        # a fake unreferenced accel group, so that the shortcut keys appear in the menu, but the widget keypress is responsible
        # of handling them (otherwise this would take precedence and :hex couldn't get 'c' etc)
        # but ^o still works (must work even without DasmWidget loaded)

        addsubmenu(filemenu, 'OPEN', '^o') { promptopen }
        addsubmenu(filemenu, '_Debug') { promptdebug }
        addsubmenu(filemenu, 'SAVE', '^s') { promptsave }
        addsubmenu(filemenu, 'Save _as...') { promptsaveas }
        addsubmenu(filemenu, 'CLOSE') {
                if @dasm_widget
                        @dasm_widget.terminate
                        @dasm_widget = nil
                        self.widget = nil
                end
        }
        addsubmenu(filemenu)

        importmenu = new_menu
        addsubmenu(importmenu, 'Load _map') {
                openfile('choose map file') { |file|
                        @dasm_widget.dasm.load_map(File.read(file)) if @dasm_widget
                        @dasm_widget.gui_update if @dasm_widget
                } if @dasm_widget
        }
        addsubmenu(importmenu, 'Load _C') {
                openfile('choose C file') { |file|
                        @dasm_widget.dasm.parse_c(File.read(file)) if @dasm_widget
                } if @dasm_widget
        }
        addsubmenu(filemenu, '_Import', importmenu)

        exportmenu = new_menu
        addsubmenu(exportmenu, 'Save _map') {
                savefile('choose map file') { |file|
                        File.open(file, 'w') { |fd|
                                fd.puts @dasm_widget.dasm.save_map
                        } if @dasm_widget
                } if @dasm_widget
        }
        addsubmenu(exportmenu, 'Save _asm') {
                savefile('choose asm file') { |file|
                        File.open(file, 'w') { |fd|
                                fd.puts @dasm_widget.dasm
                        } if @dasm_widget
                } if @dasm_widget
        }
        addsubmenu(exportmenu, 'Save _C') {
                savefile('choose C file') { |file|
                        File.open(file, 'w') { |fd|
                                fd.puts @dasm_widget.dasm.c_parser
                        } if @dasm_widget
                } if @dasm_widget
        }
        addsubmenu(filemenu, '_Export', exportmenu)
        addsubmenu(filemenu)
        addsubmenu(filemenu, 'QUIT') { destroy } # post_quit_message ?

        addsubmenu(@menu, filemenu, '_File')

        actions = new_menu
        dasm = new_menu
        addsubmenu(dasm, '_Disassemble from here', 'c') { @dasm_widget.disassemble(@dasm_widget.curview.current_address) }
        addsubmenu(dasm, 'Disassemble _fast from here', 'C') { @dasm_widget.disassemble_fast(@dasm_widget.curview.current_address) }
        addsubmenu(dasm, 'Disassemble fast & dee_p from here', '^C') { @dasm_widget.disassemble_fast_deep(@dasm_widget.curview.current_address) }
        addsubmenu(actions, dasm, '_Disassemble')
        navigate = new_menu
        addsubmenu(navigate, 'Follow', '<enter>') { @dasm_widget.focus_addr @dasm_widget.curview.hl_word }    # XXX
        addsubmenu(navigate, 'Jmp back', '<esc>') { @dasm_widget.focus_addr_back }
        addsubmenu(navigate, 'Undo jmp back', '^<enter>') { @dasm_widget.focus_addr_redo }
        addsubmenu(navigate, 'Goto', 'g') { @dasm_widget.prompt_goto }
        addsubmenu(actions, navigate, 'Navigate')
        addsubmenu(actions, '_Backtrace', 'b') { @dasm_widget.prompt_backtrace }
        addsubmenu(actions, 'List functions', 'f') { @dasm_widget.list_functions }
        addsubmenu(actions, 'List labels', 'l') { @dasm_widget.list_labels }
        addsubmenu(actions, 'List xrefs', 'x') { @dasm_widget.list_xrefs }
        addsubmenu(actions, 'Find local vars', 'K') { @dasm_widget.name_local_vars(@dasm_widget.curview.current_address) }
        addsubmenu(actions, 'Rebase') { @dasm_widget.rebase }
        addsubmenu(actions, 'Rename label', 'n') { @dasm_widget.rename }
        addsubmenu(actions, 'Decompile', '<tab>') { @dasm_widget.decompile(@dasm_widget.curview.current_address) }
        addsubmenu(actions, 'Decompile finali_ze') { @dasm_widget.dasm.decompiler.finalize ; @dasm_widget.gui_update }
        addsubmenu(actions, 'Comment', ';') { @dasm_widget.add_comment(@dasm_widget.curview.current_address) }
        addsubmenu(actions, '_Undefine') { @dasm_widget.dasm.undefine_from(@dasm_widget.curview.current_address) ; @dasm_widget.gui_update }
        addsubmenu(actions, 'Unde_fine function') { @dasm_widget.undefine_function(@dasm_widget.curview.current_address) }
        addsubmenu(actions, 'Undefine function & _subfuncs') { @dasm_widget.undefine_function(@dasm_widget.curview.current_address, true) }
        addsubmenu(actions, 'Data', 'd') { @dasm_widget.toggle_data(@dasm_widget.curview.current_address) }
        addsubmenu(actions, 'Pause dasm', 'p', :check) { |ck| !@dasm_widget.playpause_dasm }
        addsubmenu(actions, 'Spawn EmuDb_g', 'G') { @dasm_widget.spawn_emudbg }
        addsubmenu(actions, 'Run ruby snippet', '^r') { promptruby }
        addsubmenu(actions, 'Run _ruby plugin') { @dasm_widget.prompt_run_ruby_plugin }

        addsubmenu(@menu, actions, '_Actions')

        options = new_menu
        addsubmenu(options, '_Verbose', :check, $VERBOSE, 'v') { |ck| $VERBOSE = ck }
        addsubmenu(options, 'Debu_g', :check, $DEBUG) { |ck| $DEBUG = ck }
        addsubmenu(options, 'Debug _backtrace', :check) { |ck| @dasm_widget.dasm.debug_backtrace = ck if @dasm_widget }
        addsubmenu(options, 'Backtrace li_mit') {
                inputbox('max blocks to backtrace', :text => @dasm_widget.dasm.backtrace_maxblocks) { |target|
                        @dasm_widget.dasm.backtrace_maxblocks = Integer(target) if not target.empty?
                } if @dasm_widget
        }
        addsubmenu(options, 'Backtrace _limit (data)') {
                inputbox('max blocks to backtrace data (-1 to never start)',
                                :text => @dasm_widget.dasm.backtrace_maxblocks_data) { |target|
                        @dasm_widget.dasm.backtrace_maxblocks_data = Integer(target) if not target.empty?
                } if @dasm_widget
        }
        addsubmenu(options)
        addsubmenu(options, 'Forbid a_ll optimizations', :check) { |ck| @dasm_widget.dasm.decompiler.forbid_all_optimizations = ck }
        addsubmenu(options, 'Forbid decompile _types', :check) { |ck| @dasm_widget.dasm.decompiler.forbid_decompile_types = ck }
        addsubmenu(options, 'Forbid decompile _if/while', :check) { |ck| @dasm_widget.dasm.decompiler.forbid_decompile_ifwhile = ck }
        addsubmenu(options, 'Forbid decomp _optimize', :check) { |ck| @dasm_widget.dasm.decompiler.forbid_optimize_code = ck }
        addsubmenu(options, 'Forbid decomp optim_data', :check) { |ck| @dasm_widget.dasm.decompiler.forbid_optimize_dataflow = ck }
        addsubmenu(options, 'Forbid decomp optimlab_els', :check) { |ck| @dasm_widget.dasm.decompiler.forbid_optimize_labels = ck }
        addsubmenu(options, 'Decompiler _recurse', :check, true) { |ck| @dasm_widget.dasm.decompiler.recurse = (ck ? 1/0.0 : 1) ; ck }        # XXX race if changed while decompiling
        # TODO CPU type, size, endian...
        # factorize headers

        addsubmenu(@menu, options, '_Options')

        views = new_menu
        addsubmenu(views, 'Dis_assembly') { @dasm_widget.focus_addr(@dasm_widget.curaddr, :listing) }
        addsubmenu(views, '_Graph') { @dasm_widget.focus_addr(@dasm_widget.curaddr, :graph) }
        addsubmenu(views, 'De_compiled') { @dasm_widget.focus_addr(@dasm_widget.curaddr, :decompile) }
        addsubmenu(views, 'Raw _opcodes') { @dasm_widget.focus_addr(@dasm_widget.curaddr, :opcodes) }
        addsubmenu(views, '_Hex') { @dasm_widget.focus_addr(@dasm_widget.curaddr, :hex) }
        addsubmenu(views, 'C S_truct') { @dasm_widget.focus_addr(@dasm_widget.curaddr, :cstruct) }
        addsubmenu(views, 'Co_verage') { @dasm_widget.focus_addr(@dasm_widget.curaddr, :coverage) }
        addsubmenu(views, '_Sections') { @dasm_widget.list_sections }
        addsubmenu(views, 'St_rings') { @dasm_widget.list_strings }

        funcgraph = new_menu
        addsubmenu(funcgraph, 'Fu_ll') { @dasm_widget.focus_addr(@dasm_widget.curaddr, :funcgraph, false, :full) }
        addsubmenu(funcgraph, '_From there') { @dasm_widget.focus_addr(@dasm_widget.curaddr, :funcgraph, false, :from) }
        addsubmenu(funcgraph, '_To there') { @dasm_widget.focus_addr(@dasm_widget.curaddr, :funcgraph, false, :to) }
        addsubmenu(funcgraph, '_Butterfly') { @dasm_widget.focus_addr(@dasm_widget.curaddr, :funcgraph, false, :both) }
        addsubmenu(views, '_Func graph', funcgraph)

        addsubmenu(@menu, views, '_Views')
end
destroy_window() click to toggle source
Calls superclass method
# File metasm/gui/dasm_main.rb, line 924
def destroy_window
        @dasm_widget.terminate if @dasm_widget
        super()
end
display(dasm, ep=[]) click to toggle source

sets up a DisasmWidget as main widget of the window, replaces the current if it exists returns the widget

# File metasm/gui/dasm_main.rb, line 931
def display(dasm, ep=[])
        @dasm_widget.terminate if @dasm_widget
        ep = [ep] if not ep.kind_of?(Array)
        ep0 = ep.first
        @dasm_widget = DisasmWidget.new(dasm, ep)
        self.widget = @dasm_widget
        @dasm_widget.focus_addr(ep0) if ep0
        @dasm_widget
end
initialize_window(*args) click to toggle source
# File metasm/gui/dasm_main.rb, line 906
def initialize_window(*args)
        dasm = args.grep(Disassembler).first
        args -= [dasm]
        title = args.find { |a| dasm ? !dasm.get_section_at(a) : a.kind_of?(::String) } || 'metasm disassembler'
        ep = args - [title]
        self.title = title
        @dasm_widget = nil
        if dasm
                display(dasm, ep)
        else
                self.widget = NoDasmWidget.new(self)
        end
end
loadfile(path, cpu='Ia32', exefmt=nil) click to toggle source
# File metasm/gui/dasm_main.rb, line 946
def loadfile(path, cpu='Ia32', exefmt=nil)
        if exefmt
                exefmt = Metasm.const_get(exefmt) if exefmt.kind_of? String
                if exefmt.kind_of?(::Class) and exefmt.name.split('::').last == 'Shellcode'
                        exefmt = Shellcode.withcpu(cpu)
                end
        else
                exefmt = AutoExe.orshellcode { cpu = Metasm.const_get(cpu) if cpu.kind_of? String ; cpu = cpu.new if cpu.kind_of?(::Class) ; cpu }
        end

        exe = exefmt.decode_file(path) { |type, str|
                # Disassembler save file will use this callback with unhandled sections / invalid binary file path
                case type
                when 'binarypath'
                        ret = nil
                        openfile("please locate #{str}", :blocking => true) { |f| ret = f }
                        return if not ret
                        ret
                end
        }
        tg_win = self
        tg_win = DasmWindow.new if @dasm_widget
        tg_win.display(exe.disassembler)
        tg_win.title = "#{File.basename(path)} - metasm disassembler"
        exe
end
promptdebug(caption='choose target', &b) click to toggle source
# File metasm/gui/dasm_main.rb, line 977
def promptdebug(caption='choose target', &b)
        l = nil
        i = inputbox(caption) { |name|
                i = nil ; l.destroy if l and not l.destroyed?
                if pr = OS.current.find_process(name)
                        target = pr.debugger
                elsif name =~ /^(udp|tcp|.*\d+.*):/i # don't match c:\kikoo, but allow 127.0.0.1 / [1:2::3]
                        target = GdbRemoteDebugger.new(name)
                elsif pr = OS.current.create_process(name)
                        target = pr.debugger
                else
                        messagebox('no such target')
                        next
                end
                DbgWindow.new(target)
                destroy if not @dasm_widget
                b.call(self) if b
        }

        # build process list in bg (exe name resolution takes a few seconds)
        list = [['pid', 'name']]
        list_pr = OS.current.list_processes
        Gui.idle_add {
                if pr = list_pr.shift
                        list << [pr.pid, pr.path] if pr.path
                        true
                elsif i
                        me = ::Process.pid.to_s
                        l = listwindow('running processes', list,
                                       :noshow => true,
                                       :color_callback => lambda { |le| [:grey, :palegrey] if le[0] == me }
                        ) { |e| i.text = e[0] }
                        l.x += l.width
                        l.show
                        false
                end
        } if not list_pr.empty?
end
promptopen(caption='choose target binary', &b) click to toggle source
# File metasm/gui/dasm_main.rb, line 973
def promptopen(caption='choose target binary', &b)
        openfile(caption) { |exename| loadfile(exename) ; b.call(self) if b }
end
promptruby() click to toggle source
# File metasm/gui/dasm_main.rb, line 1035
def promptruby
        if @dasm_widget
                @dasm_widget.prompt_run_ruby
        else
                inputbox('code to eval') { |c| messagebox eval(c).inspect[0, 512], 'eval' }
        end
end
promptsave() click to toggle source

reuse last @savefile to save dasm, prompt for file if undefined

# File metasm/gui/dasm_main.rb, line 1017
def promptsave
        return if not @dasm_widget
        if @savefile ||= nil
                @dasm_widget.dasm.save_file @savefile
                return
        end
        savefile('choose save file') { |file|
                @savefile = file
                @dasm_widget.dasm.save_file(file)
        }
end
promptsaveas() click to toggle source

same as promptsave, but always prompt

# File metasm/gui/dasm_main.rb, line 1030
def promptsaveas
        @savefile = nil
        promptsave
end
widget(idx=nil) click to toggle source

returns the specified widget from the @dasm_widget (idx in :hex, :listing, :graph etc)

# File metasm/gui/dasm_main.rb, line 942
def widget(idx=nil)
        idx && @dasm_widget ? @dasm_widget.view(idx) : @dasm_widget
end
widget=(w) click to toggle source
Calls superclass method
# File metasm/gui/dasm_main.rb, line 920
def widget=(w)
        super(w || NoDasmWidget.new(self))
end