Beispiel #1
0
    def initUI(self):
        self.locationStack = list()

        self.app.title("PyDA")

        # Set up the Menu Bar
        self.menu_bar = MenuBar(self.app)
        self.menu_bar.addMenu('File')
        self.menu_bar.addMenuItem('File', 'Disassemble File',
                                  self.onDisassembleFile)
        self.menu_bar.addMenuItem('File', 'Load PyDA Save', self.onLoad)
        self.menu_bar.addMenuItem('File', 'Save', self.onSave)
        self.menu_bar.addMenuSeparator('File')
        self.menu_bar.addMenuItem('File', 'Exit', self.onExit)

        # Set up the Tool Bar
        # TODO: Add images to buttons with mouseover text
        self.toolbar = ToolBar(self.app, 'top')
        self.toolbar.addButton('Back', self.onBack, 'left')
        self.toolbar.addVertSeperator('left')
        self.toolbar.addButton('Disassemble File', self.onDisassembleFile,
                               'left')
        self.toolbar.addButton('Load', self.onLoad, 'left')
        self.toolbar.addButton('Save', self.onSave, 'left')
        self.toolbar.addVertSeperator('left')
        self.toolbar.addButton('Share', self.onShare, 'left')

        # Set up the status bar ##
        self.status_bar = ToolBar(self.app,
                                  'bottom',
                                  relief='sunken',
                                  borderwidth=2)
        self.status_bar.addLabel('Status:', 'left')
        self.status_label = self.status_bar.addLabel('Ready', 'left')
        self.progress_bar = self.status_bar.addProgressBar(
            'right', length=200, mode='indeterminate')

        # Set up the vertical paned window
        self.tl_v_window = PanedWindow(self.app,
                                       'top',
                                       borderwidth=1,
                                       relief="sunken",
                                       sashwidth=4,
                                       orient="vertical")

        # Set up the horizontal paned window and add to the vertical window
        self.tl_h_window = self.tl_v_window.addElement(
            PanedWindow(self.tl_v_window,
                        borderwidth=1,
                        relief="sunken",
                        sashwidth=4))

        # Set up the two notebooks
        self.left_notebook = self.tl_h_window.addNotebook()
        self.right_notebook = self.tl_h_window.addNotebook()
        self.bottom_notebook = self.tl_v_window.addNotebook()

        # Set up the functions listbox
        self.functions_listbox = self.left_notebook.addListboxWithScrollbar(
            'Functions',
            background='white',
            borderwidth=1,
            highlightthickness=1,
            relief='sunken')

        self.functions_listbox.bind('<Double-Button-1>',
                                    self.onFunctionDoubleClick)

        # Set up the strings listbox
        self.strings_listbox = self.left_notebook.addListboxWithScrollbar(
            'Strings',
            background='white',
            borderwidth=1,
            highlightthickness=1,
            relief='sunken')

        self.strings_listbox.bind('<Double-Button-1>',
                                  self.onStringDoubleClick)

        # Set up the disassembly textbox
        self.disassembly_textbox = self.right_notebook.addTextboxWithScrollbar(
            'Disassembly',
            tcl_buffer_size=self.TEXTBOX_BUFFER_SIZE,
            tcl_buffer_low_cutoff=self.TEXTBOX_BUFFER_LOW_CUTOFF,
            tcl_buffer_high_cutoff=self.TEXTBOX_BUFFER_HIGH_CUTOFF,
            tcl_moveto_yview=self.TEXTBOX_MOVETO_YVIEW,
            max_lines_jump=self.TEXTBOX_MAX_LINES_JUMP,
            background="white",
            borderwidth=1,
            highlightthickness=1,
            relief='sunken')

        # Set up the data section textbox
        # self.data_sections_textbox = self.right_notebook.addTextboxWithScrollbar(
        #         'Data Sections', background="white", borderwidth=1,
        #         highlightthickness=1, relief='sunken')
        self.data_sections_textbox = self.right_notebook.addTextboxWithScrollbar(
            'Data Sections',
            tcl_buffer_size=self.TEXTBOX_BUFFER_SIZE,
            tcl_buffer_low_cutoff=self.TEXTBOX_BUFFER_LOW_CUTOFF,
            tcl_buffer_high_cutoff=self.TEXTBOX_BUFFER_HIGH_CUTOFF,
            tcl_moveto_yview=self.TEXTBOX_MOVETO_YVIEW,
            max_lines_jump=self.TEXTBOX_MAX_LINES_JUMP,
            background="white",
            borderwidth=1,
            highlightthickness=1,
            relief='sunken')

        # Set up the output window
        debug_frame = self.bottom_notebook.addFrame('Debug')
        debug_frame_2 = debug_frame.addFrame('bottom',
                                             'x',
                                             False,
                                             borderwidth=1)
        debug_frame_1 = debug_frame.addFrame('top',
                                             'both',
                                             True,
                                             borderwidth=1)
        self.debug_textbox = debug_frame_1.addTextboxWithScrollbar(
            background='white',
            borderwidth=1,
            highlightthickness=1,
            relief='sunken')
        self.debug_entry = debug_frame_2.addEntryWithLabel(
            'Command:',
            'bottom',
            'x',
            True,
            background='white',
            borderwidth=1,
            highlightthickness=1,
            relief='sunken')

        self.debug_data_model = TextModel()
        self.debug_textbox.setDataModel(self.debug_data_model)

        # Set up the chat window
        chat_frame = self.bottom_notebook.addFrame('Chat')
        chat_frame_2 = chat_frame.addFrame('bottom', 'x', False, borderwidth=1)
        chat_frame_1 = chat_frame.addFrame('top', 'both', True, borderwidth=1)
        self.chat_textbox = chat_frame_1.addTextboxWithScrollbar(
            background='white',
            borderwidth=1,
            highlightthickness=1,
            relief='sunken')
        self.chat_entry = chat_frame_2.addEntryWithLabel('Send:',
                                                         'bottom',
                                                         'x',
                                                         True,
                                                         background='white',
                                                         borderwidth=1,
                                                         highlightthickness=1,
                                                         relief='sunken')

        self.chat_data_model = TextModel()
        self.chat_textbox.setDataModel(self.chat_data_model)

        # Set up the context menus
        self.section_context_menu = ContextMenu([('Copy', self.onCopyString)])
        self.address_context_menu = ContextMenu([
            ('Copy String', self.onCopyString),
            ('Copy Value', self.onCopyValue)
        ])
        self.disass_comment_context_menu = ContextMenu([(';  Comment',
                                                         self.disassComment)])
        self.data_comment_context_menu = ContextMenu([(';  Comment',
                                                       self.dataComment)])
        self.disass_label_context_menu = ContextMenu([
            ('n  Rename Label', self.disassRenameLabel)
        ])
        self.data_label_context_menu = ContextMenu([('n  Rename Label',
                                                     self.dataRenameLabel)])

        # Force the mouse to always have focus
        self.tk_focusFollowsMouse()

        # Get the appropriate button number based on system
        right_click_button = "<Button-2>" if system(
        ) == "Darwin" else "<Button-3>"

        dis_textbox_context_queue = self.app.createCallbackQueue()
        # Create a context manager for the disassembly textbox
        self.disassembly_textbox_context_manager = WidgetContextManager(
            self.app,
            dis_textbox_context_queue,
            self.disassembly_textbox,
            self.PYDA_SEP,
            self.PYDA_BEGL,
            right_click_button,
            [(self.PYDA_SECTION, 'darkgreen', self.section_context_menu),
             (self.PYDA_ADDRESS, 'black', self.address_context_menu),
             (self.PYDA_MNEMONIC, 'blue', None),
             (self.PYDA_OP_STR, 'darkblue', None),
             (self.PYDA_COMMENT, 'darkgreen',
              self.disass_comment_context_menu),
             (self.PYDA_LABEL, 'saddle brown', self.disass_label_context_menu),
             (self.PYDA_BYTES, 'dark gray', None),
             (self.PYDA_GENERIC, 'black', None),
             (self.PYDA_ENDL, 'black', self.disass_comment_context_menu)],
        )

        self.disassembly_textbox.context_manager = self.disassembly_textbox_context_manager

        self.disassembly_textbox.bind('<Key>', self.keyHandler)

        data_textbox_context_queue = self.app.createCallbackQueue()
        # Create a context manager for the data sections textbox
        self.data_textbox_context_manager = WidgetContextManager(
            self.app, data_textbox_context_queue, self.data_sections_textbox,
            self.PYDA_SEP, self.PYDA_BEGL, right_click_button,
            [(self.PYDA_SECTION, 'darkgreen', None),
             (self.PYDA_MNEMONIC, 'blue', None),
             (self.PYDA_OP_STR, 'darkblue', None),
             (self.PYDA_COMMENT, 'darkgreen', self.data_comment_context_menu),
             (self.PYDA_LABEL, 'saddle brown', self.data_label_context_menu),
             (self.PYDA_BYTES, 'dark gray', None),
             (self.PYDA_GENERIC, 'black', None),
             (self.PYDA_ENDL, 'black', self.data_comment_context_menu)])

        self.data_sections_textbox.context_manager = self.data_textbox_context_manager

        self.data_sections_textbox.bind('<Key>', self.keyHandler)

        # Redirect stdout to the debug window
        if self.REDIR_STDOUT:
            sys.stdout = StdoutRedirector(self.stdoutMessage)
            print "Stdout is being redirected to here"
Beispiel #2
0
    def initUI(self):
        self.locationStack = list()

        self.app.title("PyDA")

        # Set up the Menu Bar
        self.menu_bar = MenuBar(self.app)
        self.menu_bar.addMenu('File')
        self.menu_bar.addMenuItem('File', 'Disassemble File', self.onDisassembleFile)
        self.menu_bar.addMenuItem('File', 'Load PyDA Save', self.onLoad)
        self.menu_bar.addMenuItem('File', 'Save', self.onSave)
        self.menu_bar.addMenuSeparator('File')
        self.menu_bar.addMenuItem('File', 'Exit', self.onExit)

        # Set up the Tool Bar
        # TODO: Add images to buttons with mouseover text
        self.toolbar = ToolBar(self.app, 'top')
        self.toolbar.addButton('Back', self.onBack, 'left')
        self.toolbar.addVertSeperator('left')
        self.toolbar.addButton('Disassemble File', self.onDisassembleFile, 'left')
        self.toolbar.addButton('Load', self.onLoad, 'left')
        self.toolbar.addButton('Save', self.onSave, 'left')
        self.toolbar.addVertSeperator('left')
        self.toolbar.addButton('Share', self.onShare, 'left')

        # Set up the status bar ##
        self.status_bar = ToolBar(self.app, 'bottom', relief='sunken', borderwidth=2)
        self.status_bar.addLabel('Status:', 'left')
        self.status_label = self.status_bar.addLabel('Ready', 'left')
        self.progress_bar = self.status_bar.addProgressBar('right', length=200, mode='indeterminate')

        # Set up the vertical paned window
        self.tl_v_window = PanedWindow(self.app, 'top', borderwidth=1,
                relief="sunken", sashwidth=4, orient="vertical")

        # Set up the horizontal paned window and add to the vertical window
        self.tl_h_window = self.tl_v_window.addElement(
                PanedWindow(self.tl_v_window, borderwidth=1,
                    relief="sunken", sashwidth=4))

        # Set up the two notebooks
        self.left_notebook = self.tl_h_window.addNotebook()
        self.right_notebook = self.tl_h_window.addNotebook()
        self.bottom_notebook = self.tl_v_window.addNotebook()

        # Set up the functions listbox
        self.functions_listbox = self.left_notebook.addListboxWithScrollbar(
                'Functions', background='white', borderwidth=1,
                highlightthickness=1, relief='sunken')

        self.functions_listbox.bind('<Double-Button-1>', self.onFunctionDoubleClick)

        # Set up the strings listbox
        self.strings_listbox = self.left_notebook.addListboxWithScrollbar(
                'Strings', background='white', borderwidth=1,
                highlightthickness=1, relief='sunken')

        self.strings_listbox.bind('<Double-Button-1>', self.onStringDoubleClick)

        # Set up the disassembly textbox
        self.disassembly_textbox = self.right_notebook.addTextboxWithScrollbar(
                'Disassembly', tcl_buffer_size=self.TEXTBOX_BUFFER_SIZE,
                tcl_buffer_low_cutoff=self.TEXTBOX_BUFFER_LOW_CUTOFF,
                tcl_buffer_high_cutoff=self.TEXTBOX_BUFFER_HIGH_CUTOFF,
                tcl_moveto_yview=self.TEXTBOX_MOVETO_YVIEW,
                max_lines_jump=self.TEXTBOX_MAX_LINES_JUMP,
                background="white", borderwidth=1, highlightthickness=1, relief='sunken')

        # Set up the data section textbox
        # self.data_sections_textbox = self.right_notebook.addTextboxWithScrollbar(
        #         'Data Sections', background="white", borderwidth=1,
        #         highlightthickness=1, relief='sunken')
        self.data_sections_textbox = self.right_notebook.addTextboxWithScrollbar(
                'Data Sections', tcl_buffer_size=self.TEXTBOX_BUFFER_SIZE,
                tcl_buffer_low_cutoff=self.TEXTBOX_BUFFER_LOW_CUTOFF,
                tcl_buffer_high_cutoff=self.TEXTBOX_BUFFER_HIGH_CUTOFF,
                tcl_moveto_yview=self.TEXTBOX_MOVETO_YVIEW,
                max_lines_jump=self.TEXTBOX_MAX_LINES_JUMP,
                background="white", borderwidth=1, highlightthickness=1, relief='sunken')

        # Set up the output window
        debug_frame = self.bottom_notebook.addFrame('Debug')
        debug_frame_2 = debug_frame.addFrame('bottom', 'x', False, borderwidth=1)
        debug_frame_1 = debug_frame.addFrame('top', 'both', True, borderwidth=1)
        self.debug_textbox = debug_frame_1.addTextboxWithScrollbar(
                background='white', borderwidth=1, highlightthickness=1,
                relief='sunken')
        self.debug_entry = debug_frame_2.addEntryWithLabel(
                'Command:', 'bottom', 'x', True, background='white',
                borderwidth=1, highlightthickness=1, relief='sunken')

        self.debug_data_model = TextModel()
        self.debug_textbox.setDataModel(self.debug_data_model)

        # Set up the chat window
        chat_frame = self.bottom_notebook.addFrame('Chat')
        chat_frame_2 = chat_frame.addFrame('bottom', 'x', False, borderwidth=1)
        chat_frame_1 = chat_frame.addFrame('top', 'both', True, borderwidth=1)
        self.chat_textbox = chat_frame_1.addTextboxWithScrollbar(
                background='white', borderwidth=1, highlightthickness=1,
                relief='sunken')
        self.chat_entry = chat_frame_2.addEntryWithLabel(
                'Send:', 'bottom', 'x', True, background='white',
                borderwidth=1, highlightthickness=1, relief='sunken')

        self.chat_data_model = TextModel()
        self.chat_textbox.setDataModel(self.chat_data_model)

        # Set up the context menus
        self.section_context_menu = ContextMenu([('Copy', self.onCopyString)])
        self.address_context_menu = ContextMenu([('Copy String', self.onCopyString), ('Copy Value', self.onCopyValue)])
        self.disass_comment_context_menu = ContextMenu([(';  Comment', self.disassComment)])
        self.data_comment_context_menu = ContextMenu([(';  Comment', self.dataComment)])
        self.disass_label_context_menu   = ContextMenu([('n  Rename Label', self.disassRenameLabel)])
        self.data_label_context_menu   = ContextMenu([('n  Rename Label', self.dataRenameLabel)])

        # Force the mouse to always have focus
        self.tk_focusFollowsMouse()

        # Get the appropriate button number based on system
        right_click_button = "<Button-2>" if system() == "Darwin" else "<Button-3>"

        dis_textbox_context_queue = self.app.createCallbackQueue()
        # Create a context manager for the disassembly textbox
        self.disassembly_textbox_context_manager = WidgetContextManager(
                self.app, dis_textbox_context_queue, self.disassembly_textbox, self.PYDA_SEP,
                self.PYDA_BEGL, right_click_button, [
                    (self.PYDA_SECTION, 'darkgreen', self.section_context_menu),
                    (self.PYDA_ADDRESS, 'black', self.address_context_menu),
                    (self.PYDA_MNEMONIC, 'blue', None),
                    (self.PYDA_OP_STR, 'darkblue', None),
                    (self.PYDA_COMMENT, 'darkgreen', self.disass_comment_context_menu),
                    (self.PYDA_LABEL, 'saddle brown', self.disass_label_context_menu),
                    (self.PYDA_BYTES, 'dark gray', None),
                    (self.PYDA_GENERIC, 'black', None),
                    (self.PYDA_ENDL, 'black', self.disass_comment_context_menu)], )

        self.disassembly_textbox.context_manager = self.disassembly_textbox_context_manager

        self.disassembly_textbox.bind('<Key>', self.keyHandler)

        data_textbox_context_queue = self.app.createCallbackQueue()
        # Create a context manager for the data sections textbox
        self.data_textbox_context_manager = WidgetContextManager(
                self.app, data_textbox_context_queue, self.data_sections_textbox, self.PYDA_SEP,
                self.PYDA_BEGL, right_click_button, [
                    (self.PYDA_SECTION, 'darkgreen', None),
                    (self.PYDA_MNEMONIC, 'blue', None),
                    (self.PYDA_OP_STR, 'darkblue', None),
                    (self.PYDA_COMMENT, 'darkgreen', self.data_comment_context_menu),
                    (self.PYDA_LABEL, 'saddle brown', self.data_label_context_menu),
                    (self.PYDA_BYTES, 'dark gray', None),
                    (self.PYDA_GENERIC, 'black', None),
                    (self.PYDA_ENDL, 'black', self.data_comment_context_menu)])

        self.data_sections_textbox.context_manager = self.data_textbox_context_manager

        self.data_sections_textbox.bind('<Key>', self.keyHandler)

        # Redirect stdout to the debug window
        if self.REDIR_STDOUT:
            sys.stdout = StdoutRedirector(self.stdoutMessage)
            print "Stdout is being redirected to here"
Beispiel #3
0
class PyDAInterface(Frame):
    def __init__(self, app):
        Frame.__init__(self, app)
        self.app = app
        self.disassembly = None
        self.main_queue = self.app.createCallbackQueue()
        self.initVars()
        self.initUI()
        self.centerWindow()

    def initVars(self):
        self.PYDA_SEP = self.app.settings_manager.get('context', 'pyda-sep')
        self.PYDA_SECTION = self.app.settings_manager.get(
            'context', 'pyda-section')
        self.PYDA_ADDRESS = self.app.settings_manager.get(
            'context', 'pyda-address')
        self.PYDA_MNEMONIC = self.app.settings_manager.get(
            'context', 'pyda-mnemonic')
        self.PYDA_OP_STR = self.app.settings_manager.get(
            'context', 'pyda-op-str')
        self.PYDA_COMMENT = self.app.settings_manager.get(
            'context', 'pyda-comment')
        self.PYDA_LABEL = self.app.settings_manager.get(
            'context', 'pyda-label')
        self.PYDA_BYTES = self.app.settings_manager.get(
            'context', 'pyda-bytes')
        self.PYDA_GENERIC = self.app.settings_manager.get(
            'context', 'pyda-generic')
        self.PYDA_BEGL = self.app.settings_manager.get('context', 'pyda-begl')
        self.PYDA_ENDL = self.app.settings_manager.get('context', 'pyda-endl')
        self.REDIR_STDOUT = self.app.settings_manager.getint(
            'debugging', 'redirect-stdout')
        self.DEBUG = self.app.settings_manager.getint('debugging', 'debug-on')
        self.PROFILE = self.app.settings_manager.getint(
            'debugging', 'profiler-on')
        self.TEXTBOX_BUFFER_SIZE = self.app.settings_manager.getint(
            'gui', 'textbox-buffer-size')
        self.TEXTBOX_BUFFER_LOW_CUTOFF = self.app.settings_manager.getfloat(
            'gui', 'textbox-buffer-low-cutoff')
        self.TEXTBOX_BUFFER_HIGH_CUTOFF = self.app.settings_manager.getfloat(
            'gui', 'textbox-buffer-high-cutoff')
        self.TEXTBOX_MOVETO_YVIEW = self.app.settings_manager.getfloat(
            'gui', 'moveto-yview')
        self.TEXTBOX_MAX_LINES_JUMP = self.app.settings_manager.getint(
            'gui', 'max-lines-jump')
        self.NUM_OPCODE_BYTES_SHOWN = self.app.settings_manager.getint(
            'disassembly', 'num-opcode-bytes-shown')
        self.MIN_STRING_SIZE = self.app.settings_manager.getint(
            'disassembly', 'min-string-size')
        self.SAVE_PATH = self.app.save_manager.save_path

    def initUI(self):
        self.locationStack = list()

        self.app.title("PyDA")

        # Set up the Menu Bar
        self.menu_bar = MenuBar(self.app)
        self.menu_bar.addMenu('File')
        self.menu_bar.addMenuItem('File', 'Disassemble File',
                                  self.onDisassembleFile)
        self.menu_bar.addMenuItem('File', 'Load PyDA Save', self.onLoad)
        self.menu_bar.addMenuItem('File', 'Save', self.onSave)
        self.menu_bar.addMenuSeparator('File')
        self.menu_bar.addMenuItem('File', 'Exit', self.onExit)

        # Set up the Tool Bar
        # TODO: Add images to buttons with mouseover text
        self.toolbar = ToolBar(self.app, 'top')
        self.toolbar.addButton('Back', self.onBack, 'left')
        self.toolbar.addVertSeperator('left')
        self.toolbar.addButton('Disassemble File', self.onDisassembleFile,
                               'left')
        self.toolbar.addButton('Load', self.onLoad, 'left')
        self.toolbar.addButton('Save', self.onSave, 'left')
        self.toolbar.addVertSeperator('left')
        self.toolbar.addButton('Share', self.onShare, 'left')

        # Set up the status bar ##
        self.status_bar = ToolBar(self.app,
                                  'bottom',
                                  relief='sunken',
                                  borderwidth=2)
        self.status_bar.addLabel('Status:', 'left')
        self.status_label = self.status_bar.addLabel('Ready', 'left')
        self.progress_bar = self.status_bar.addProgressBar(
            'right', length=200, mode='indeterminate')

        # Set up the vertical paned window
        self.tl_v_window = PanedWindow(self.app,
                                       'top',
                                       borderwidth=1,
                                       relief="sunken",
                                       sashwidth=4,
                                       orient="vertical")

        # Set up the horizontal paned window and add to the vertical window
        self.tl_h_window = self.tl_v_window.addElement(
            PanedWindow(self.tl_v_window,
                        borderwidth=1,
                        relief="sunken",
                        sashwidth=4))

        # Set up the two notebooks
        self.left_notebook = self.tl_h_window.addNotebook()
        self.right_notebook = self.tl_h_window.addNotebook()
        self.bottom_notebook = self.tl_v_window.addNotebook()

        # Set up the functions listbox
        self.functions_listbox = self.left_notebook.addListboxWithScrollbar(
            'Functions',
            background='white',
            borderwidth=1,
            highlightthickness=1,
            relief='sunken')

        self.functions_listbox.bind('<Double-Button-1>',
                                    self.onFunctionDoubleClick)

        # Set up the strings listbox
        self.strings_listbox = self.left_notebook.addListboxWithScrollbar(
            'Strings',
            background='white',
            borderwidth=1,
            highlightthickness=1,
            relief='sunken')

        self.strings_listbox.bind('<Double-Button-1>',
                                  self.onStringDoubleClick)

        # Set up the disassembly textbox
        self.disassembly_textbox = self.right_notebook.addTextboxWithScrollbar(
            'Disassembly',
            tcl_buffer_size=self.TEXTBOX_BUFFER_SIZE,
            tcl_buffer_low_cutoff=self.TEXTBOX_BUFFER_LOW_CUTOFF,
            tcl_buffer_high_cutoff=self.TEXTBOX_BUFFER_HIGH_CUTOFF,
            tcl_moveto_yview=self.TEXTBOX_MOVETO_YVIEW,
            max_lines_jump=self.TEXTBOX_MAX_LINES_JUMP,
            background="white",
            borderwidth=1,
            highlightthickness=1,
            relief='sunken')

        # Set up the data section textbox
        # self.data_sections_textbox = self.right_notebook.addTextboxWithScrollbar(
        #         'Data Sections', background="white", borderwidth=1,
        #         highlightthickness=1, relief='sunken')
        self.data_sections_textbox = self.right_notebook.addTextboxWithScrollbar(
            'Data Sections',
            tcl_buffer_size=self.TEXTBOX_BUFFER_SIZE,
            tcl_buffer_low_cutoff=self.TEXTBOX_BUFFER_LOW_CUTOFF,
            tcl_buffer_high_cutoff=self.TEXTBOX_BUFFER_HIGH_CUTOFF,
            tcl_moveto_yview=self.TEXTBOX_MOVETO_YVIEW,
            max_lines_jump=self.TEXTBOX_MAX_LINES_JUMP,
            background="white",
            borderwidth=1,
            highlightthickness=1,
            relief='sunken')

        # Set up the output window
        debug_frame = self.bottom_notebook.addFrame('Debug')
        debug_frame_2 = debug_frame.addFrame('bottom',
                                             'x',
                                             False,
                                             borderwidth=1)
        debug_frame_1 = debug_frame.addFrame('top',
                                             'both',
                                             True,
                                             borderwidth=1)
        self.debug_textbox = debug_frame_1.addTextboxWithScrollbar(
            background='white',
            borderwidth=1,
            highlightthickness=1,
            relief='sunken')
        self.debug_entry = debug_frame_2.addEntryWithLabel(
            'Command:',
            'bottom',
            'x',
            True,
            background='white',
            borderwidth=1,
            highlightthickness=1,
            relief='sunken')

        self.debug_data_model = TextModel()
        self.debug_textbox.setDataModel(self.debug_data_model)

        # Set up the chat window
        chat_frame = self.bottom_notebook.addFrame('Chat')
        chat_frame_2 = chat_frame.addFrame('bottom', 'x', False, borderwidth=1)
        chat_frame_1 = chat_frame.addFrame('top', 'both', True, borderwidth=1)
        self.chat_textbox = chat_frame_1.addTextboxWithScrollbar(
            background='white',
            borderwidth=1,
            highlightthickness=1,
            relief='sunken')
        self.chat_entry = chat_frame_2.addEntryWithLabel('Send:',
                                                         'bottom',
                                                         'x',
                                                         True,
                                                         background='white',
                                                         borderwidth=1,
                                                         highlightthickness=1,
                                                         relief='sunken')

        self.chat_data_model = TextModel()
        self.chat_textbox.setDataModel(self.chat_data_model)

        # Set up the context menus
        self.section_context_menu = ContextMenu([('Copy', self.onCopyString)])
        self.address_context_menu = ContextMenu([
            ('Copy String', self.onCopyString),
            ('Copy Value', self.onCopyValue)
        ])
        self.disass_comment_context_menu = ContextMenu([(';  Comment',
                                                         self.disassComment)])
        self.data_comment_context_menu = ContextMenu([(';  Comment',
                                                       self.dataComment)])
        self.disass_label_context_menu = ContextMenu([
            ('n  Rename Label', self.disassRenameLabel)
        ])
        self.data_label_context_menu = ContextMenu([('n  Rename Label',
                                                     self.dataRenameLabel)])

        # Force the mouse to always have focus
        self.tk_focusFollowsMouse()

        # Get the appropriate button number based on system
        right_click_button = "<Button-2>" if system(
        ) == "Darwin" else "<Button-3>"

        dis_textbox_context_queue = self.app.createCallbackQueue()
        # Create a context manager for the disassembly textbox
        self.disassembly_textbox_context_manager = WidgetContextManager(
            self.app,
            dis_textbox_context_queue,
            self.disassembly_textbox,
            self.PYDA_SEP,
            self.PYDA_BEGL,
            right_click_button,
            [(self.PYDA_SECTION, 'darkgreen', self.section_context_menu),
             (self.PYDA_ADDRESS, 'black', self.address_context_menu),
             (self.PYDA_MNEMONIC, 'blue', None),
             (self.PYDA_OP_STR, 'darkblue', None),
             (self.PYDA_COMMENT, 'darkgreen',
              self.disass_comment_context_menu),
             (self.PYDA_LABEL, 'saddle brown', self.disass_label_context_menu),
             (self.PYDA_BYTES, 'dark gray', None),
             (self.PYDA_GENERIC, 'black', None),
             (self.PYDA_ENDL, 'black', self.disass_comment_context_menu)],
        )

        self.disassembly_textbox.context_manager = self.disassembly_textbox_context_manager

        self.disassembly_textbox.bind('<Key>', self.keyHandler)

        data_textbox_context_queue = self.app.createCallbackQueue()
        # Create a context manager for the data sections textbox
        self.data_textbox_context_manager = WidgetContextManager(
            self.app, data_textbox_context_queue, self.data_sections_textbox,
            self.PYDA_SEP, self.PYDA_BEGL, right_click_button,
            [(self.PYDA_SECTION, 'darkgreen', None),
             (self.PYDA_MNEMONIC, 'blue', None),
             (self.PYDA_OP_STR, 'darkblue', None),
             (self.PYDA_COMMENT, 'darkgreen', self.data_comment_context_menu),
             (self.PYDA_LABEL, 'saddle brown', self.data_label_context_menu),
             (self.PYDA_BYTES, 'dark gray', None),
             (self.PYDA_GENERIC, 'black', None),
             (self.PYDA_ENDL, 'black', self.data_comment_context_menu)])

        self.data_sections_textbox.context_manager = self.data_textbox_context_manager

        self.data_sections_textbox.bind('<Key>', self.keyHandler)

        # Redirect stdout to the debug window
        if self.REDIR_STDOUT:
            sys.stdout = StdoutRedirector(self.stdoutMessage)
            print "Stdout is being redirected to here"

    ### START OF GUI FUNCTION CALLBACKS ###
    def onCopyString(self, *args):
        print 'Copy String Selected', args

    def onCopyValue(self, *args):
        print 'Copy Value Selected', args

    def onFunctionDoubleClick(self, event):
        widget = event.widget
        selection = widget.curselection()
        name = widget.get(selection[0])
        index = self.disassembly.getLabelIndex(name, key="exe")
        self.pushCurrentLocation()
        self.goto(index, self.disassembly_textbox)

    def onStringDoubleClick(self, event):
        widget = event.widget
        selection = widget.curselection()
        name = widget.get(selection[0])
        index = self.disassembly.getLabelIndex(name, key="data")
        self.pushCurrentLocation()
        self.goto(index, self.data_sections_textbox)

    def onLoad(self):
        file_types = [] if system() == "Darwin" else [('PyDA Saves', '*.pyda'),
                                                      ('All Files', '*')]
        dialog = tkFileDialog.Open(self,
                                   initialdir=self.SAVE_PATH,
                                   filetypes=file_types)
        file_name = dialog.show()
        if file_name:
            print 'Loading %s' % file_name
            self.disassembly = self.app.load(file_name)

            # TODO: Is this really necessary? Will it do what I want?
            self.initVars()  # Reinitialize the settings per the load

            if isinstance(self.disassembly, CommonProgramDisassemblyFormat):
                self.clearWindows()
                print 'Load successful! Putting data now.'
                self.processDisassembly()
            else:
                print 'Load did not successfully return a CommonProgramDisassemblyFormat'

    def onSave(self):
        dialog = tkFileDialog.SaveAs(self,
                                     initialdir=self.SAVE_PATH,
                                     initialfile='.pyda')
        file_name = dialog.show()
        if file_name:
            print 'Saving to %s' % file_name
            self.app.save(file_name, self.disassembly)

    def onBack(self):
        self.popLastLocation()

    def onDisassembleFile(self):
        self.progress_bar.start()
        dialog = tkFileDialog.Open(self, initialdir=os.getcwd())
        file_name = dialog.show()
        if file_name:
            self.clearWindows()
            self.app.executor.submit(self.disassembleFile, file_name)
        else:
            self.progress_bar.stop()

    def onShare(self):
        self.server.start()

    def onError(self):
        tkMessageBox.showerror(
            "Error", "Could not determine file type from magic header.")

    def destroy(self):
        self.app.shutdown()

    def onExit(self):
        print 'Shutting down'
        self.app.shutdown()

    ### END OF GUI FUNCTION CALLBACKS ###

    def clearWindows(self):
        self.disassembly_textbox.clear()
        self.data_sections_textbox.clear()
        self.strings_listbox.delete(0, 'end')
        self.functions_listbox.delete(0, 'end')

    def centerWindow(self):
        height = self.app.winfo_screenheight() * 5 / 6
        width = height * 16 / 9
        x = (self.app.winfo_screenwidth() - width) / 2
        y = (self.app.winfo_screenheight() - height) / 2
        self.app.geometry('%dx%d+%d+%d' % (width, height, x, y))

    def stdoutMessage(self, message):
        self.debug_textbox.appendData(message, True)

    def status(self, message):
        self.app.addCallback(self.main_queue, self._status, (message, ))

    def _status(self, message):
        self.status_label['text'] = message

    def debug(self, message):
        if self.DEBUG:
            print message + '\n',

    def getCurrentRowIndex(self, textbox):
        line, _ = textbox.index('insert').split('.')
        return line + '.0'

    def getCurrentLine(self, textbox):
        index = self.getCurrentRowIndex(textbox)
        contents = textbox.get(index, index + " lineend")
        return contents.splitlines()[0]

    def disassComment(self, event):
        self.comment(self.disassembly_textbox)

    def dataComment(self, event):
        self.comment(self.data_sections_textbox)

    def comment(self, event):
        if self.disassembly is None:
            return

        textbox = None
        if isinstance(event, Textbox):
            textbox = event
        else:
            textbox = event.widget

        tl = Toplevel(master=self.app)
        tl.title("Insert Comment")

        frame1 = Frame(tl)
        frame1.pack(side=TOP)
        frame2 = Frame(tl)
        frame2.pack()
        frame3 = Frame(tl)
        frame3.pack(side=BOTTOM)

        msg = Label(frame1, text="Please enter a comment:", height=0, width=50)
        msg.pack()

        e = Entry(frame2)
        e.pack(side=LEFT)

        def addComment(*args):
            # print 'Comment:', e.get()
            comment = e.get()
            contents = self.getCurrentLine(textbox)
            result = self.disassembly.setCommentForLine(contents, comment)
            if result:
                index = self.getCurrentRowIndex(textbox)
                textbox.redrawLine(index)

            tl.destroy()

        button1 = Button(frame2, text="Add Comment", command=addComment)
        button1.pack(side=RIGHT)

        e.focus()
        e.bind('<Return>', addComment)  # Bind return to submit

        tl.grab_set()  # Keeps this toplevel on top

    def disassRenameLabel(self, event):
        self.renameLabel(self.disassembly_textbox)

    def dataRenameLabel(self, event):
        self.dataRenameLabel(self.data_sections_textbox)

    def renameLabel(self, event):
        if self.disassembly is None:
            return

        textbox = None
        if isinstance(event, Textbox):
            textbox = event
        else:
            textbox = event.widget

        tl = Toplevel(master=self.app)
        tl.title('Rename Label')

        frame1 = Frame(tl)
        frame1.pack(side=TOP)
        frame2 = Frame(tl)
        frame2.pack()
        frame3 = Frame(tl)
        frame3.pack(side=BOTTOM)

        msg = Label(frame1,
                    text='Please enter a new name:',
                    height=0,
                    width=50)
        msg.pack()

        e = Entry(frame2)
        e.pack(side=LEFT)

        def rename(*args):
            new_name = e.get()
            contents = self.getCurrentLine(textbox)
            result = self.disassembly.renameLabel(contents, new_name)
            if result:
                textbox.redrawLine(self.getCurrentRowIndex(textbox))
                self.functions_listbox.delete(0, 'end')
                self.strings_listbox.delete(0, 'end')
                self.populateFunctions()
                self.populateStrings()

            tl.destroy()

        button1 = Button(frame2, text="Rename", command=rename)
        button1.pack(side=RIGHT)

        e.focus()
        e.bind('<Return>', rename)  # Bind return to submit

        tl.grab_set()  # Keeps this toplevel on top

    def controlKeyHeld(self, event):
        return event.state & 0x004  # see here: http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/event-handlers.html

    def keyHandler(self, event):
        # print event.keysym

        c = event.char
        if c == ';':
            self.comment(event)
        elif c == 'n' or c == 'N':
            self.renameLabel(event)
        elif event.keysym in [
                'Up', 'Down', 'Left', 'Right', 'Next', 'Prior', 'End', 'Home'
        ]:
            return
        elif event.keysym == 'Escape':
            self.popLastLocation()
        elif self.controlKeyHeld(event):
            if event.keysym == 'c':
                self.app.clipboard_clear()
                self.app.clipboard_append(event.widget.selection_get())
            elif event.keysym == 'a':
                textbox = event.widget
                textbox.tag_add('sel', '1.0', 'end')
            elif event.keysym == 'Tab':
                return

        # Otherwise, don't let this key go to the screen
        return 'break'

    def pushCurrentLocation(self):
        textbox = self.getSelectedTextbox()
        current = int(float(self.getCurrentRowIndex(textbox)))
        if current >= textbox.TCL_BUFFER_SIZE:
            current = 10
        index = current + textbox.current_data_offset
        # print "pushing:",index
        self.locationStack.append(Location(index, textbox))

    def popLastLocation(self):
        if len(self.locationStack) == 0:
            return
        location = self.locationStack.pop()
        index = location.index
        textbox = location.textbox
        # print "popping:",index
        self.goto(index, textbox)

    def goto(self, index, textbox):
        # TODO: Move this functionality out of the interface into the data structure
        # print "going to:",index
        is_disassembly = textbox == self.disassembly_textbox
        key = "exe" if is_disassembly else "data"
        fraction = index / float(self.disassembly.length(key=key))
        fraction -= .0001  # enough so that the text isn't cut off
        textbox.changeView("moveto", fraction)
        textbox.setCursor(str(index) + ".0")
        tab_index = 0 if is_disassembly else 1
        self.selectTab(tab_index)

    def selectTab(self, index):
        # TODO: make this less hacky if possible
        tab = self.right_notebook.tabs()[index]
        self.right_notebook.select(tab)

    def getSelectedTextbox(self):
        box = self.right_notebook.tabs().index(self.right_notebook.select())
        # TODO: make this less hacky if possible
        return self.disassembly_textbox if box == 0 else self.data_sections_textbox

    def disassembleFile(self, file_name):
        self.debug('Reading %s' % file_name)
        self.status('Reading %s' % file_name)
        binary = open(file_name, 'rb').read()
        self.debug('Loading binary')
        self.status('Loading binary')
        self.app.disassembler.load(binary, filename=file_name)
        self.debug('Disassembling as %s' % self.app.disassembler.getFileType())
        self.status('Disassembling as %s' %
                    self.app.disassembler.getFileType())
        self.disassembly = self.app.disassembler.disassemble()
        self.debug('Finished disassembling')
        self.status('Finished disassembling')
        self.processDisassembly()

    def populateFunctions(self):
        funcs = self.disassembly.getFuncs()
        for func in funcs:
            self.app.addCallback(self.main_queue,
                                 self.functions_listbox.insert,
                                 ('end', func.name))

    def populateStrings(self):
        strings = self.disassembly.getStrings()
        for string in strings:
            self.app.addCallback(self.main_queue, self.strings_listbox.insert,
                                 ('end', string.name))

    def processDisassembly(self):
        if isinstance(self.disassembly, CommonProgramDisassemblyFormat):
            self.status('Processing Data')

            self.debug('Processing Functions')
            self.populateFunctions()
            self.debug('Processing Strings')
            self.populateStrings()

            self.app.addCallback(self.main_queue,
                                 self.disassembly_textbox.setDataModel,
                                 (self.disassembly, 'exe'))
            self.app.addCallback(self.main_queue,
                                 self.data_sections_textbox.setDataModel,
                                 (self.disassembly, 'data', self.progress_bar))

            self.debug('Done')
            self.status('Done')
            self.progress_bar.stop()

    def printStats(self):
        stats = self.app.executor.getProfileStats()
        stats.sort_stats('cumulative').print_stats()
Beispiel #4
0
class PyDAInterface(Frame):
    def __init__(self, app):
        Frame.__init__(self, app)
        self.app = app
        self.disassembly = None
        self.main_queue = self.app.createCallbackQueue()
        self.initVars()
        self.initUI()
        self.centerWindow()

    def initVars(self):
        self.PYDA_SEP = self.app.settings_manager.get('context', 'pyda-sep')
        self.PYDA_SECTION = self.app.settings_manager.get('context', 'pyda-section')
        self.PYDA_ADDRESS = self.app.settings_manager.get('context', 'pyda-address')
        self.PYDA_MNEMONIC = self.app.settings_manager.get('context', 'pyda-mnemonic')
        self.PYDA_OP_STR = self.app.settings_manager.get('context', 'pyda-op-str')
        self.PYDA_COMMENT = self.app.settings_manager.get('context', 'pyda-comment')
        self.PYDA_LABEL = self.app.settings_manager.get('context', 'pyda-label')
        self.PYDA_BYTES = self.app.settings_manager.get('context', 'pyda-bytes')
        self.PYDA_GENERIC = self.app.settings_manager.get('context', 'pyda-generic')
        self.PYDA_BEGL = self.app.settings_manager.get('context', 'pyda-begl')
        self.PYDA_ENDL = self.app.settings_manager.get('context', 'pyda-endl')
        self.REDIR_STDOUT = self.app.settings_manager.getint('debugging', 'redirect-stdout')
        self.DEBUG = self.app.settings_manager.getint('debugging', 'debug-on')
        self.PROFILE = self.app.settings_manager.getint('debugging', 'profiler-on')
        self.TEXTBOX_BUFFER_SIZE = self.app.settings_manager.getint('gui', 'textbox-buffer-size')
        self.TEXTBOX_BUFFER_LOW_CUTOFF = self.app.settings_manager.getfloat('gui', 'textbox-buffer-low-cutoff')
        self.TEXTBOX_BUFFER_HIGH_CUTOFF = self.app.settings_manager.getfloat('gui', 'textbox-buffer-high-cutoff')
        self.TEXTBOX_MOVETO_YVIEW = self.app.settings_manager.getfloat('gui', 'moveto-yview')
        self.TEXTBOX_MAX_LINES_JUMP = self.app.settings_manager.getint('gui', 'max-lines-jump')
        self.NUM_OPCODE_BYTES_SHOWN = self.app.settings_manager.getint('disassembly','num-opcode-bytes-shown')
        self.MIN_STRING_SIZE = self.app.settings_manager.getint('disassembly','min-string-size')
        self.SAVE_PATH = self.app.save_manager.save_path

    def initUI(self):
        self.locationStack = list()

        self.app.title("PyDA")

        # Set up the Menu Bar
        self.menu_bar = MenuBar(self.app)
        self.menu_bar.addMenu('File')
        self.menu_bar.addMenuItem('File', 'Disassemble File', self.onDisassembleFile)
        self.menu_bar.addMenuItem('File', 'Load PyDA Save', self.onLoad)
        self.menu_bar.addMenuItem('File', 'Save', self.onSave)
        self.menu_bar.addMenuSeparator('File')
        self.menu_bar.addMenuItem('File', 'Exit', self.onExit)

        # Set up the Tool Bar
        # TODO: Add images to buttons with mouseover text
        self.toolbar = ToolBar(self.app, 'top')
        self.toolbar.addButton('Back', self.onBack, 'left')
        self.toolbar.addVertSeperator('left')
        self.toolbar.addButton('Disassemble File', self.onDisassembleFile, 'left')
        self.toolbar.addButton('Load', self.onLoad, 'left')
        self.toolbar.addButton('Save', self.onSave, 'left')
        self.toolbar.addVertSeperator('left')
        self.toolbar.addButton('Share', self.onShare, 'left')

        # Set up the status bar ##
        self.status_bar = ToolBar(self.app, 'bottom', relief='sunken', borderwidth=2)
        self.status_bar.addLabel('Status:', 'left')
        self.status_label = self.status_bar.addLabel('Ready', 'left')
        self.progress_bar = self.status_bar.addProgressBar('right', length=200, mode='indeterminate')

        # Set up the vertical paned window
        self.tl_v_window = PanedWindow(self.app, 'top', borderwidth=1,
                relief="sunken", sashwidth=4, orient="vertical")

        # Set up the horizontal paned window and add to the vertical window
        self.tl_h_window = self.tl_v_window.addElement(
                PanedWindow(self.tl_v_window, borderwidth=1,
                    relief="sunken", sashwidth=4))

        # Set up the two notebooks
        self.left_notebook = self.tl_h_window.addNotebook()
        self.right_notebook = self.tl_h_window.addNotebook()
        self.bottom_notebook = self.tl_v_window.addNotebook()

        # Set up the functions listbox
        self.functions_listbox = self.left_notebook.addListboxWithScrollbar(
                'Functions', background='white', borderwidth=1,
                highlightthickness=1, relief='sunken')

        self.functions_listbox.bind('<Double-Button-1>', self.onFunctionDoubleClick)

        # Set up the strings listbox
        self.strings_listbox = self.left_notebook.addListboxWithScrollbar(
                'Strings', background='white', borderwidth=1,
                highlightthickness=1, relief='sunken')

        self.strings_listbox.bind('<Double-Button-1>', self.onStringDoubleClick)

        # Set up the disassembly textbox
        self.disassembly_textbox = self.right_notebook.addTextboxWithScrollbar(
                'Disassembly', tcl_buffer_size=self.TEXTBOX_BUFFER_SIZE,
                tcl_buffer_low_cutoff=self.TEXTBOX_BUFFER_LOW_CUTOFF,
                tcl_buffer_high_cutoff=self.TEXTBOX_BUFFER_HIGH_CUTOFF,
                tcl_moveto_yview=self.TEXTBOX_MOVETO_YVIEW,
                max_lines_jump=self.TEXTBOX_MAX_LINES_JUMP,
                background="white", borderwidth=1, highlightthickness=1, relief='sunken')

        # Set up the data section textbox
        # self.data_sections_textbox = self.right_notebook.addTextboxWithScrollbar(
        #         'Data Sections', background="white", borderwidth=1,
        #         highlightthickness=1, relief='sunken')
        self.data_sections_textbox = self.right_notebook.addTextboxWithScrollbar(
                'Data Sections', tcl_buffer_size=self.TEXTBOX_BUFFER_SIZE,
                tcl_buffer_low_cutoff=self.TEXTBOX_BUFFER_LOW_CUTOFF,
                tcl_buffer_high_cutoff=self.TEXTBOX_BUFFER_HIGH_CUTOFF,
                tcl_moveto_yview=self.TEXTBOX_MOVETO_YVIEW,
                max_lines_jump=self.TEXTBOX_MAX_LINES_JUMP,
                background="white", borderwidth=1, highlightthickness=1, relief='sunken')

        # Set up the output window
        debug_frame = self.bottom_notebook.addFrame('Debug')
        debug_frame_2 = debug_frame.addFrame('bottom', 'x', False, borderwidth=1)
        debug_frame_1 = debug_frame.addFrame('top', 'both', True, borderwidth=1)
        self.debug_textbox = debug_frame_1.addTextboxWithScrollbar(
                background='white', borderwidth=1, highlightthickness=1,
                relief='sunken')
        self.debug_entry = debug_frame_2.addEntryWithLabel(
                'Command:', 'bottom', 'x', True, background='white',
                borderwidth=1, highlightthickness=1, relief='sunken')

        self.debug_data_model = TextModel()
        self.debug_textbox.setDataModel(self.debug_data_model)

        # Set up the chat window
        chat_frame = self.bottom_notebook.addFrame('Chat')
        chat_frame_2 = chat_frame.addFrame('bottom', 'x', False, borderwidth=1)
        chat_frame_1 = chat_frame.addFrame('top', 'both', True, borderwidth=1)
        self.chat_textbox = chat_frame_1.addTextboxWithScrollbar(
                background='white', borderwidth=1, highlightthickness=1,
                relief='sunken')
        self.chat_entry = chat_frame_2.addEntryWithLabel(
                'Send:', 'bottom', 'x', True, background='white',
                borderwidth=1, highlightthickness=1, relief='sunken')

        self.chat_data_model = TextModel()
        self.chat_textbox.setDataModel(self.chat_data_model)

        # Set up the context menus
        self.section_context_menu = ContextMenu([('Copy', self.onCopyString)])
        self.address_context_menu = ContextMenu([('Copy String', self.onCopyString), ('Copy Value', self.onCopyValue)])
        self.disass_comment_context_menu = ContextMenu([(';  Comment', self.disassComment)])
        self.data_comment_context_menu = ContextMenu([(';  Comment', self.dataComment)])
        self.disass_label_context_menu   = ContextMenu([('n  Rename Label', self.disassRenameLabel)])
        self.data_label_context_menu   = ContextMenu([('n  Rename Label', self.dataRenameLabel)])

        # Force the mouse to always have focus
        self.tk_focusFollowsMouse()

        # Get the appropriate button number based on system
        right_click_button = "<Button-2>" if system() == "Darwin" else "<Button-3>"

        dis_textbox_context_queue = self.app.createCallbackQueue()
        # Create a context manager for the disassembly textbox
        self.disassembly_textbox_context_manager = WidgetContextManager(
                self.app, dis_textbox_context_queue, self.disassembly_textbox, self.PYDA_SEP,
                self.PYDA_BEGL, right_click_button, [
                    (self.PYDA_SECTION, 'darkgreen', self.section_context_menu),
                    (self.PYDA_ADDRESS, 'black', self.address_context_menu),
                    (self.PYDA_MNEMONIC, 'blue', None),
                    (self.PYDA_OP_STR, 'darkblue', None),
                    (self.PYDA_COMMENT, 'darkgreen', self.disass_comment_context_menu),
                    (self.PYDA_LABEL, 'saddle brown', self.disass_label_context_menu),
                    (self.PYDA_BYTES, 'dark gray', None),
                    (self.PYDA_GENERIC, 'black', None),
                    (self.PYDA_ENDL, 'black', self.disass_comment_context_menu)], )

        self.disassembly_textbox.context_manager = self.disassembly_textbox_context_manager

        self.disassembly_textbox.bind('<Key>', self.keyHandler)

        data_textbox_context_queue = self.app.createCallbackQueue()
        # Create a context manager for the data sections textbox
        self.data_textbox_context_manager = WidgetContextManager(
                self.app, data_textbox_context_queue, self.data_sections_textbox, self.PYDA_SEP,
                self.PYDA_BEGL, right_click_button, [
                    (self.PYDA_SECTION, 'darkgreen', None),
                    (self.PYDA_MNEMONIC, 'blue', None),
                    (self.PYDA_OP_STR, 'darkblue', None),
                    (self.PYDA_COMMENT, 'darkgreen', self.data_comment_context_menu),
                    (self.PYDA_LABEL, 'saddle brown', self.data_label_context_menu),
                    (self.PYDA_BYTES, 'dark gray', None),
                    (self.PYDA_GENERIC, 'black', None),
                    (self.PYDA_ENDL, 'black', self.data_comment_context_menu)])

        self.data_sections_textbox.context_manager = self.data_textbox_context_manager

        self.data_sections_textbox.bind('<Key>', self.keyHandler)

        # Redirect stdout to the debug window
        if self.REDIR_STDOUT:
            sys.stdout = StdoutRedirector(self.stdoutMessage)
            print "Stdout is being redirected to here"
    
    ### START OF GUI FUNCTION CALLBACKS ###
    def onCopyString(self, *args):
        print 'Copy String Selected', args

    def onCopyValue(self, *args):
        print 'Copy Value Selected', args

    def onFunctionDoubleClick(self, event):
        widget = event.widget
        selection = widget.curselection()
        name = widget.get(selection[0])
        index = self.disassembly.getLabelIndex(name, key="exe")
        self.pushCurrentLocation()
        self.goto(index, self.disassembly_textbox)

    def onStringDoubleClick(self, event):
        widget = event.widget
        selection = widget.curselection()
        name = widget.get(selection[0])
        index = self.disassembly.getLabelIndex(name, key="data")
        self.pushCurrentLocation()
        self.goto(index, self.data_sections_textbox)

    def onLoad(self):
        file_types = [] if system() == "Darwin" else [('PyDA Saves', '*.pyda'), ('All Files', '*')]
        dialog = tkFileDialog.Open(self, initialdir=self.SAVE_PATH, filetypes=file_types)
        file_name = dialog.show()
        if file_name:
            print 'Loading %s' % file_name
            self.disassembly = self.app.load(file_name)

            # TODO: Is this really necessary? Will it do what I want?
            self.initVars() # Reinitialize the settings per the load

            if isinstance(self.disassembly, CommonProgramDisassemblyFormat):
                self.clearWindows()
                print 'Load successful! Putting data now.'
                self.processDisassembly()
            else:
                print 'Load did not successfully return a CommonProgramDisassemblyFormat'

    def onSave(self):
        dialog = tkFileDialog.SaveAs(self, initialdir=self.SAVE_PATH, initialfile='.pyda')
        file_name = dialog.show()
        if file_name:
            print 'Saving to %s' % file_name
            self.app.save(file_name, self.disassembly)

    def onBack(self):
        self.popLastLocation()

    def onDisassembleFile(self):
        self.progress_bar.start()
        dialog = tkFileDialog.Open(self, initialdir=os.getcwd())
        file_name = dialog.show()
        if file_name:
            self.clearWindows()
            self.app.executor.submit(self.disassembleFile, file_name)
        else:
            self.progress_bar.stop()

    def onShare(self):
        self.server.start()

    def onError(self):
        tkMessageBox.showerror("Error", "Could not determine file type from magic header.")

    def destroy(self):
        self.app.shutdown()

    def onExit(self):
        print 'Shutting down'
        self.app.shutdown()
    ### END OF GUI FUNCTION CALLBACKS ###

    def clearWindows(self):
        self.disassembly_textbox.clear()
        self.data_sections_textbox.clear()
        self.strings_listbox.delete(0,'end')
        self.functions_listbox.delete(0,'end')

    def centerWindow(self):
        height = self.app.winfo_screenheight() * 5/6
        width = height * 16/9
        x = (self.app.winfo_screenwidth() - width)/2
        y = (self.app.winfo_screenheight() - height)/2
        self.app.geometry('%dx%d+%d+%d' % (width, height, x, y))

    def stdoutMessage(self, message):
        self.debug_textbox.appendData(message, True)

    def status(self, message):
        self.app.addCallback(self.main_queue, self._status, (message,))

    def _status(self, message):
        self.status_label['text'] = message

    def debug(self, message):
        if self.DEBUG:
            print message + '\n',

    def getCurrentRowIndex(self, textbox):
        line, _ = textbox.index('insert').split('.')
        return line + '.0'

    def getCurrentLine(self, textbox):
        index = self.getCurrentRowIndex(textbox)
        contents = textbox.get(index, index + " lineend")
        return contents.splitlines()[0]

    def disassComment(self, event):
        self.comment(self.disassembly_textbox)

    def dataComment(self, event):
        self.comment(self.data_sections_textbox)

    def comment(self, event):
        if self.disassembly is None:
            return

        textbox = None
        if isinstance(event, Textbox):
            textbox = event
        else:
            textbox = event.widget

        tl = Toplevel(master=self.app)
        tl.title("Insert Comment")

        frame1 = Frame(tl)
        frame1.pack(side=TOP)
        frame2 = Frame(tl)
        frame2.pack()
        frame3 = Frame(tl)
        frame3.pack(side=BOTTOM)

        msg = Label(frame1, text="Please enter a comment:", height=0, width=50)
        msg.pack()

        e = Entry(frame2)
        e.pack(side=LEFT)

        def addComment(*args):
            # print 'Comment:', e.get()
            comment = e.get()
            contents = self.getCurrentLine(textbox)
            result = self.disassembly.setCommentForLine(contents, comment)
            if result:
                index = self.getCurrentRowIndex(textbox)
                textbox.redrawLine(index)

            tl.destroy()

        button1 = Button(frame2, text="Add Comment", command=addComment)
        button1.pack(side=RIGHT)

        e.focus()
        e.bind('<Return>', addComment) # Bind return to submit

        tl.grab_set() # Keeps this toplevel on top

    def disassRenameLabel(self, event):
        self.renameLabel(self.disassembly_textbox)

    def dataRenameLabel(self, event):
        self.dataRenameLabel(self.data_sections_textbox)

    def renameLabel(self, event):
        if self.disassembly is None:
            return

        textbox = None
        if isinstance(event, Textbox):
            textbox = event
        else:
            textbox = event.widget

        tl = Toplevel(master=self.app)
        tl.title('Rename Label')

        frame1 = Frame(tl)
        frame1.pack(side=TOP)
        frame2 = Frame(tl)
        frame2.pack()
        frame3 = Frame(tl)
        frame3.pack(side=BOTTOM)

        msg = Label(frame1, text='Please enter a new name:', height=0, width=50)
        msg.pack()

        e = Entry(frame2)
        e.pack(side=LEFT)

        def rename(*args):
            new_name = e.get()
            contents = self.getCurrentLine(textbox)
            result = self.disassembly.renameLabel(contents, new_name)
            if result:
                textbox.redrawLine(self.getCurrentRowIndex(textbox))
                self.functions_listbox.delete(0, 'end')
                self.strings_listbox.delete(0, 'end')
                self.populateFunctions()
                self.populateStrings()

            tl.destroy()

        button1 = Button(frame2, text="Rename", command=rename)
        button1.pack(side=RIGHT)

        e.focus()
        e.bind('<Return>', rename) # Bind return to submit

        tl.grab_set() # Keeps this toplevel on top

    def controlKeyHeld(self, event):
        return event.state & 0x004 # see here: http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/event-handlers.html

    def keyHandler(self, event):
        # print event.keysym

        c = event.char
        if c == ';':
            self.comment(event)
        elif c == 'n' or c == 'N':
            self.renameLabel(event)
        elif event.keysym in ['Up', 'Down', 'Left', 'Right', 'Next', 'Prior', 'End', 'Home']:
            return
        elif event.keysym == 'Escape':
            self.popLastLocation()
        elif self.controlKeyHeld(event):
            if event.keysym == 'c':
                self.app.clipboard_clear()
                self.app.clipboard_append(event.widget.selection_get())
            elif event.keysym == 'a':
                textbox = event.widget
                textbox.tag_add('sel', '1.0', 'end')
            elif event.keysym == 'Tab':
                return

        # Otherwise, don't let this key go to the screen
        return 'break'

    def pushCurrentLocation(self):
        textbox = self.getSelectedTextbox()
        current = int(float(self.getCurrentRowIndex(textbox)))
        if current >= textbox.TCL_BUFFER_SIZE:
            current = 10
        index = current + textbox.current_data_offset
        # print "pushing:",index
        self.locationStack.append(Location(index, textbox))

    def popLastLocation(self):
        if len(self.locationStack) == 0:
            return
        location = self.locationStack.pop()
        index = location.index
        textbox = location.textbox
        # print "popping:",index
        self.goto(index, textbox)

    def goto(self, index, textbox):
        # TODO: Move this functionality out of the interface into the data structure
        # print "going to:",index
        is_disassembly = textbox == self.disassembly_textbox
        key = "exe" if is_disassembly else "data"
        fraction = index / float(self.disassembly.length(key=key))
        fraction -= .0001 # enough so that the text isn't cut off
        textbox.changeView("moveto", fraction)
        textbox.setCursor(str(index) + ".0")
        tab_index = 0 if is_disassembly else 1
        self.selectTab(tab_index)

    def selectTab(self, index):
        # TODO: make this less hacky if possible
        tab = self.right_notebook.tabs()[index]
        self.right_notebook.select(tab)

    def getSelectedTextbox(self):
        box = self.right_notebook.tabs().index(self.right_notebook.select())
        # TODO: make this less hacky if possible
        return self.disassembly_textbox if box == 0 else self.data_sections_textbox

    def disassembleFile(self, file_name):
        self.debug('Reading %s' % file_name)
        self.status('Reading %s' % file_name)
        binary = open(file_name, 'rb').read()
        self.debug('Loading binary')
        self.status('Loading binary')
        self.app.disassembler.load(binary, filename=file_name)
        self.debug('Disassembling as %s' % self.app.disassembler.getFileType())
        self.status('Disassembling as %s' % self.app.disassembler.getFileType())
        self.disassembly = self.app.disassembler.disassemble()
        self.debug('Finished disassembling')
        self.status('Finished disassembling')
        self.processDisassembly()

    def populateFunctions(self):
        funcs = self.disassembly.getFuncs()
        for func in funcs:
            self.app.addCallback(self.main_queue, self.functions_listbox.insert, ('end',func.name))

    def populateStrings(self):
        strings = self.disassembly.getStrings()
        for string in strings:
            self.app.addCallback(self.main_queue, self.strings_listbox.insert, ('end',string.name))

    def processDisassembly(self):
        if isinstance(self.disassembly, CommonProgramDisassemblyFormat):
            self.status('Processing Data')

            self.debug('Processing Functions')
            self.populateFunctions()
            self.debug('Processing Strings')
            self.populateStrings()

            self.app.addCallback(self.main_queue, self.disassembly_textbox.setDataModel, (self.disassembly, 'exe'))
            self.app.addCallback(self.main_queue, self.data_sections_textbox.setDataModel, (self.disassembly, 'data', self.progress_bar))

            self.debug('Done')
            self.status('Done')
            self.progress_bar.stop()

    def printStats(self):
        stats = self.app.executor.getProfileStats()
        stats.sort_stats('cumulative').print_stats()