def add(self, parent_id, row, values, **kwargs): if row % 2 == 1: kwargs['tag'] = self.LIGHT_BLUE_TAG_NAME TreeTable.add(self, parent_id, row, values, **kwargs)
class StringWindow(WindowTab): TITLE = 'Strings' LIGHT_BLUE_TAG_NAME = 'light_blue_background' LIGHT_BLUE = '#e0e8f0' MACH_O_TABLE_COLUMNS = ('Description', 'Offset', '# Strings', '# Matched') def __init__(self, parent): WindowTab.__init__(self, parent) self.search_bar = ttk.Labelframe(self, text='Filter', borderwidth=5) self.search_bar.pack(side=Tk.TOP, fill=Tk.X, expand=False) self.search_label = Tk.Label(self.search_bar, text='Pattern') self.search_label.pack(side=Tk.LEFT) self.gap = ttk.Frame(self, height=15) self.gap.pack(side=Tk.TOP, fill=Tk.X, expand=False) self.search_entry = Tk.Entry(self.search_bar) self.search_entry.pack(side=Tk.LEFT, fill=Tk.X, expand=True) self.search_entry.bind('<Return>', self.search) self.panedwindow = ttk.Panedwindow(self, orient=Tk.VERTICAL) self.panedwindow.pack(side=Tk.BOTTOM, fill=Tk.BOTH, expand=True) self.mach_o_table = TreeTable(self, 'Mach-O / Sections', columns=self.MACH_O_TABLE_COLUMNS) self.mach_o_table.tree.configure(selectmode='browse') self.mach_o_table.tree.column(self.MACH_O_TABLE_COLUMNS[1], anchor=Tk.E) self.mach_o_table.tree.column(self.MACH_O_TABLE_COLUMNS[2], anchor=Tk.E) self.mach_o_table.tree.column(self.MACH_O_TABLE_COLUMNS[3], anchor=Tk.E) self.mach_o_table.select_callback = self._mach_o_selected self.panedwindow.add(self.mach_o_table) self.string_table = StringTableView(self) self.string_table.widget.tag_configure(self.LIGHT_BLUE_TAG_NAME, background=self.LIGHT_BLUE) self.panedwindow.add(self.string_table) self._mach_o_info = list() self._filter_mapping = None self._current_segemnt = None # only used by _parse() def clear(self): self.clear_ui() self.clear_states() def clear_states(self): self.byte_range = None self._mach_o_info = list() self._filter_mapping = None def clear_ui(self): self.mach_o_table.clear() self.string_table.clear_widget() def load(self, byte_range, bytes_): assert isinstance(byte_range, ByteRange) self.clear_states() byte_range.iterate(self._parse) self._current_segemnt = None self.byte_range = byte_range self.display() def _parse(self, br, start, stop, level): assert start is not None and stop is not None and level is not None if isinstance(br.data, (MachHeader, MachHeader64)): mach_o_hdr = br.data cpu_type = mach_o_hdr.FIELDS[1].display(mach_o_hdr) self._mach_o_info.append(StringMachOInfo('Mach-O: ' + cpu_type, br.abs_start())) elif isinstance(br.data, SegmentBlock): self._current_segemnt = br.data.seg_name elif isinstance(br.data, (CstringSection, ObjCMethodNameSection)): assert len(self._mach_o_info) > 0 section = br.data self._mach_o_info[-1].add_section(self._current_segemnt, section, br.abs_start()) def display(self): filter_pattern = self.search_entry.get() # Update Mach-O table self._filter_mapping = dict() first_matched_id = None mach_o_idx = 0 for mach_o_info in self._mach_o_info: # TODO - Add graphical progress indicator as this operation can be time consuming num_matches = mach_o_info.filter(filter_pattern) if num_matches == 0: continue mach_o_id = '.' + str(mach_o_idx) self.mach_o_table.add('', mach_o_idx, (mach_o_info.desc, commafy(mach_o_info.offset), commafy(mach_o_info.num_strings), commafy(mach_o_info.num_matched))) mach_o_idx += 1 if first_matched_id is None: first_matched_id = mach_o_id self._filter_mapping[mach_o_id] = mach_o_info.string_sections section_idx = 0 for sect_info in mach_o_info.string_sections: if sect_info.num_matched == 0: continue sect_id = self.mach_o_table.add(mach_o_id, section_idx, (sect_info.desc, commafy(sect_info.offset), commafy(sect_info.num_strings), commafy(sect_info.num_matched))) section_idx += 1 self._filter_mapping[sect_id] = [sect_info] self.mach_o_table.tree.item(mach_o_id, open=True) # Update symbol table if first_matched_id is not None: self.mach_o_table.tree.selection_set(first_matched_id) def search(self, event): assert event is not None # for getting rid of pycharm warning self.clear_ui() self.display() def _mach_o_selected(self, path): """ When an item in the Mach-O table is clicked, we set one of a list of section to the string table for display. :param path: :return: """ id_ = '.' + '.'.join([str(x) for x in path]) sections = self._filter_mapping[id_] self.string_table.set_sections(sections) self.string_table.refresh()
class SymbolWindow(WindowTab): TITLE = 'Symbols' LIGHT_BLUE_TAG_NAME = 'light_blue_background' LIGHT_BLUE = '#e0e8f0' MACH_O_TABLE_COLUMNS = ('CPU Type', '# Symbols', '# Matched') def __init__(self, parent): WindowTab.__init__(self, parent) self.search_bar = ttk.Labelframe(self, text='Filter', borderwidth=5) self.search_bar.pack(side=Tk.TOP, fill=Tk.X, expand=False) self.search_label = Tk.Label(self.search_bar, text='Pattern') self.search_label.pack(side=Tk.LEFT) self.gap = ttk.Frame(self, height=15) self.gap.pack(side=Tk.TOP, fill=Tk.X, expand=False) self.search_entry = Tk.Entry(self.search_bar) self.search_entry.pack(side=Tk.LEFT, fill=Tk.X, expand=True) self.search_entry.bind('<Return>', self.search) self.panedwindow = ttk.Panedwindow(self, orient=Tk.VERTICAL) self.panedwindow.pack(side=Tk.BOTTOM, fill=Tk.BOTH, expand=True) self.mach_o_table = TreeTable(self, 'Mach-O', columns=self.MACH_O_TABLE_COLUMNS) self.mach_o_table.tree.configure(selectmode='browse') self.mach_o_table.tree.column(self.MACH_O_TABLE_COLUMNS[1], width=75, stretch=False, anchor=Tk.E) self.mach_o_table.tree.column(self.MACH_O_TABLE_COLUMNS[2], width=75, stretch=False, anchor=Tk.E) self.mach_o_table.tree.tag_configure(self.LIGHT_BLUE_TAG_NAME, background=self.LIGHT_BLUE) self.mach_o_table.select_callback = self._mach_o_selected self.panedwindow.add(self.mach_o_table) self.symbol_table = SymbolTableView(self) self.panedwindow.add(self.symbol_table) self._mach_o_info = list() self._filter_mapping = None # map table index to mach-o info index when an entry in mach-o table is clicked def clear(self): self.clear_ui() self.clear_states() def clear_ui(self): self.mach_o_table.clear() self.symbol_table.clear_widget() def clear_states(self): self.byte_range = None self._mach_o_info = list() self._filter_mapping = None def load(self, byte_range, bytes_): assert isinstance(byte_range, ByteRange) byte_range.iterate(self._parse) self.byte_range = byte_range self.display() def _parse(self, br, start, stop, level): assert start is not None and stop is not None and level is not None # get rid of pycharm warnings if isinstance(br.data, (MachHeader, MachHeader64)): mach_o_hdr = br.data cpu_type = mach_o_hdr.FIELDS[1].display(mach_o_hdr) mach_o_info = SymbolMachOInfo(cpu_type) self._mach_o_info.append(mach_o_info) elif isinstance(br.data, SymbolTable): self._mach_o_info[-1].add_symbol_table(br.data) elif isinstance(br.data, (Section, Section64)): self._mach_o_info[-1].add_section(br.data) def display(self): filter_pattern = self.search_entry.get() # Update Mach-O table self._filter_mapping = list() for (mach_o_idx, mach_o_info) in enumerate(self._mach_o_info): # TODO - Add graphical progress indicator as this operation can be time consuming num_matches = mach_o_info.filter(filter_pattern) if num_matches == 0: continue self.mach_o_table.add( '', len(self._filter_mapping), (mach_o_info.desc, commafy(mach_o_info.num_symbols()), commafy(mach_o_info.num_matched))) self._filter_mapping.append(mach_o_idx) # Update symbol table if len(self._filter_mapping) > 0: self.mach_o_table.tree.selection_set('.0') def search(self, event): assert event is not None self.clear_ui() self.display() def _mach_o_selected(self, path): assert len(path) == 1 # a flat list should only return a length-1 list mach_o = self._mach_o_info[path[0]] self.symbol_table.set_mach_o_info(mach_o) self.symbol_table.refresh()
class StringWindow(WindowTab): TITLE = 'Strings' LIGHT_BLUE_TAG_NAME = 'light_blue_background' LIGHT_BLUE = '#e0e8f0' MACH_O_TABLE_COLUMNS = ('Description', 'Offset', '# Strings', '# Matched') def __init__(self, parent): WindowTab.__init__(self, parent) self.search_bar = ttk.Labelframe(self, text='Filter', borderwidth=5) self.search_bar.pack(side=Tk.TOP, fill=Tk.X, expand=False) self.search_label = Tk.Label(self.search_bar, text='Pattern') self.search_label.pack(side=Tk.LEFT) self.gap = ttk.Frame(self, height=15) self.gap.pack(side=Tk.TOP, fill=Tk.X, expand=False) self.search_entry = Tk.Entry(self.search_bar) self.search_entry.pack(side=Tk.LEFT, fill=Tk.X, expand=True) self.search_entry.bind('<Return>', self.search) self.panedwindow = ttk.Panedwindow(self, orient=Tk.VERTICAL) self.panedwindow.pack(side=Tk.BOTTOM, fill=Tk.BOTH, expand=True) self.mach_o_table = TreeTable(self, 'Mach-O / Sections', columns=self.MACH_O_TABLE_COLUMNS) self.mach_o_table.tree.configure(selectmode='browse') self.mach_o_table.tree.column(self.MACH_O_TABLE_COLUMNS[1], anchor=Tk.E) self.mach_o_table.tree.column(self.MACH_O_TABLE_COLUMNS[2], anchor=Tk.E) self.mach_o_table.tree.column(self.MACH_O_TABLE_COLUMNS[3], anchor=Tk.E) self.mach_o_table.select_callback = self._mach_o_selected self.panedwindow.add(self.mach_o_table) self.string_table = StringTableView(self) self.string_table.widget.tag_configure(self.LIGHT_BLUE_TAG_NAME, background=self.LIGHT_BLUE) self.panedwindow.add(self.string_table) self._mach_o_info = list() self._filter_mapping = None self._current_segemnt = None # only used by _parse() def clear(self): self.clear_ui() self.clear_states() def clear_states(self): self.byte_range = None self._mach_o_info = list() self._filter_mapping = None def clear_ui(self): self.mach_o_table.clear() self.string_table.clear_widget() def load(self, byte_range, bytes_): assert isinstance(byte_range, ByteRange) self.clear_states() byte_range.iterate(self._parse) self._current_segemnt = None self.byte_range = byte_range self.display() def _parse(self, br, start, stop, level): assert start is not None and stop is not None and level is not None if isinstance(br.data, (MachHeader, MachHeader64)): mach_o_hdr = br.data cpu_type = mach_o_hdr.FIELDS[1].display(mach_o_hdr) self._mach_o_info.append( StringMachOInfo('Mach-O: ' + cpu_type, br.abs_start())) elif isinstance(br.data, SegmentBlock): self._current_segemnt = br.data.seg_name elif isinstance(br.data, (CstringSection, ObjCMethodNameSection)): assert len(self._mach_o_info) > 0 section = br.data self._mach_o_info[-1].add_section(self._current_segemnt, section, br.abs_start()) def display(self): filter_pattern = self.search_entry.get() # Update Mach-O table self._filter_mapping = dict() first_matched_id = None mach_o_idx = 0 for mach_o_info in self._mach_o_info: # TODO - Add graphical progress indicator as this operation can be time consuming num_matches = mach_o_info.filter(filter_pattern) if num_matches == 0: continue mach_o_id = '.' + str(mach_o_idx) self.mach_o_table.add( '', mach_o_idx, (mach_o_info.desc, commafy( mach_o_info.offset), commafy(mach_o_info.num_strings), commafy(mach_o_info.num_matched))) mach_o_idx += 1 if first_matched_id is None: first_matched_id = mach_o_id self._filter_mapping[mach_o_id] = mach_o_info.string_sections section_idx = 0 for sect_info in mach_o_info.string_sections: if sect_info.num_matched == 0: continue sect_id = self.mach_o_table.add( mach_o_id, section_idx, (sect_info.desc, commafy( sect_info.offset), commafy(sect_info.num_strings), commafy(sect_info.num_matched))) section_idx += 1 self._filter_mapping[sect_id] = [sect_info] self.mach_o_table.tree.item(mach_o_id, open=True) # Update symbol table if first_matched_id is not None: self.mach_o_table.tree.selection_set(first_matched_id) def search(self, event): assert event is not None # for getting rid of pycharm warning self.clear_ui() self.display() def _mach_o_selected(self, path): """ When an item in the Mach-O table is clicked, we set one of a list of section to the string table for display. :param path: :return: """ id_ = '.' + '.'.join([str(x) for x in path]) sections = self._filter_mapping[id_] self.string_table.set_sections(sections) self.string_table.refresh()