class JavaExplorerPanel(QWidget): def __init__(self, parent=None): super().__init__(parent=parent) self._app_window = parent self._handle_history = [] self._setup_ui() self._setup_models() def _setup_ui(self): self.setContentsMargins(0, 0, 0, 0) top_font = QFont() top_font.setBold(True) top_font.setPixelSize(19) # main wrapper main_wrapper = QVBoxLayout() main_wrapper.setContentsMargins(1, 1, 1, 1) # wrapwdgt wrap_wdgt = QWidget() self._top_class_name = QLabel(wrap_wdgt) self._top_class_name.setContentsMargins(10, 10, 10, 10) self._top_class_name.setAttribute(Qt.WA_TranslucentBackground, True) # keep this self._top_class_name.setFont(top_font) self._top_class_name.setStyleSheet('color: #ef5350;') wrap_wdgt.setMaximumHeight(self._top_class_name.height() + 20) main_wrapper.addWidget(wrap_wdgt) # left list left_wrap_wdgt = QWidget() left_v_box = QVBoxLayout(left_wrap_wdgt) left_v_box.setContentsMargins(0, 0, 0, 0) methods_label = QLabel('METHODS') font = methods_label.font() font.setBold(True) methods_label.setFont(font) methods_label.setContentsMargins(10, 0, 10, 2) methods_label.setAttribute(Qt.WA_TranslucentBackground, True) # keep this left_v_box.addWidget(methods_label) self._methods_list = DwarfListView() left_v_box.addWidget(self._methods_list) # center list center_wrap_wdgt = QWidget() center_v_box = QVBoxLayout(center_wrap_wdgt) center_v_box.setContentsMargins(0, 0, 0, 0) methods_label = QLabel('NATIVE FIELDS') methods_label.setFont(font) methods_label.setContentsMargins(10, 0, 10, 2) methods_label.setAttribute(Qt.WA_TranslucentBackground, True) # keep this center_v_box.addWidget(methods_label) self._native_fields_list = DwarfListView() self._native_fields_list.doubleClicked.connect( self._on_native_field_dblclicked) center_v_box.addWidget(self._native_fields_list) # right list right_wrap_wdgt = QWidget() right_v_box = QVBoxLayout(right_wrap_wdgt) right_v_box.setContentsMargins(0, 0, 0, 0) methods_label = QLabel('FIELDS') methods_label.setFont(font) methods_label.setContentsMargins(10, 0, 10, 2) methods_label.setAttribute(Qt.WA_TranslucentBackground, True) # keep this right_v_box.addWidget(methods_label) self._fields_list = DwarfListView() self._fields_list.doubleClicked.connect(self._on_field_dblclicked) right_v_box.addWidget(self._fields_list) # main splitter main_splitter = QSplitter(Qt.Horizontal) main_splitter.setContentsMargins(0, 0, 0, 0) main_splitter.addWidget(left_wrap_wdgt) main_splitter.addWidget(center_wrap_wdgt) main_splitter.addWidget(right_wrap_wdgt) main_splitter.setSizes([250, 100, 100]) main_wrapper.addWidget(main_splitter) main_wrapper.setSpacing(0) self.setLayout(main_wrapper) def _setup_models(self): # left list self._methods_model = QStandardItemModel(0, 3) self._methods_model.setHeaderData(0, Qt.Horizontal, 'Name') self._methods_model.setHeaderData(1, Qt.Horizontal, 'Return') self._methods_model.setHeaderData(2, Qt.Horizontal, 'Arguments') self._methods_list.setModel(self._methods_model) self._methods_list.header().setSectionResizeMode( 0, QHeaderView.ResizeToContents) self._methods_list.header().setSectionResizeMode( 1, QHeaderView.ResizeToContents) # center list self._native_fields_model = QStandardItemModel(0, 2) self._native_fields_model.setHeaderData(0, Qt.Horizontal, 'Name') self._native_fields_model.setHeaderData(1, Qt.Horizontal, 'Value') self._native_fields_list.setModel(self._native_fields_model) self._native_fields_list.header().setSectionResizeMode( 0, QHeaderView.ResizeToContents) # right list self._fields_model = QStandardItemModel(0, 2) self._fields_model.setHeaderData(0, Qt.Horizontal, 'Name') self._fields_model.setHeaderData(1, Qt.Horizontal, 'Class') self._fields_list.setModel(self._fields_model) self._fields_list.header().setSectionResizeMode( 0, QHeaderView.ResizeToContents) # ************************************************************************ # **************************** Functions ********************************* # ************************************************************************ def _set_data(self, data): if 'class' not in data: return self._top_class_name.setText(data['class']) data = data['data'] self._methods_list.clear() self._native_fields_list.clear() self._fields_list.clear() for key in data: ref = data[key] if ref['type'] == 'function': if not key.startswith('$'): self._add_method(key, ref) elif ref['type'] == 'object': if ref['handle'] is not None: if not key.startswith('$'): self._add_field(key, ref['value'], ref['handle'], ref['handle_class']) else: if not key.startswith('$'): self._add_field(key, ref['value'], is_native=True) self._methods_list.sortByColumn(0, 0) self._native_fields_list.sortByColumn(0, 0) self._fields_list.sortByColumn(0, 0) def _add_method(self, name, ref): ref_overloads = ref['overloads'] for _, ref_overload in enumerate(ref_overloads): args = [] if 'args' in ref_overload: for arg in ref_overload['args']: if 'className' in arg: args.append(arg['className']) self._methods_model.appendRow([ QStandardItem(name), QStandardItem(ref_overload['return']['className']), QStandardItem('(%s)' % ', '.join(args)), ]) def _add_field(self, name, value, handle=None, handle_class=None, is_native=False): if handle: handle = {'handle': handle, 'handle_class': handle_class} handle_item = QStandardItem(name) handle_item.setData(handle, Qt.UserRole + 1) else: handle_item = QStandardItem(name) if not is_native: self._fields_model.appendRow( [handle_item, QStandardItem(str(value))]) else: self._native_fields_model.appendRow( [handle_item, QStandardItem(str(value))]) def _set_handle(self, handle): data = self._app_window.dwarf.dwarf_api('javaExplorer', handle) if not data: return self._handle_history.append({'handle': handle}) self._set_data(data) def _set_handle_arg(self, arg): data = self._app_window.dwarf.dwarf_api('javaExplorer', arg) if not data: return self._handle_history.append({'handle': arg}) self._set_data(data) def clear_panel(self): self._top_class_name.setText('') self._handle_history = [] self._methods_list.clear() self._native_fields_list.clear() self._fields_list.clear() def _back(self): if len(self._handle_history) < 2: return self._handle_history.pop() data = self._handle_history.pop(len(self._handle_history) - 1)['handle'] if isinstance(data, int): self._set_handle_arg(data) else: self._set_handle(data) # ************************************************************************ # **************************** Handlers ********************************** # ************************************************************************ def _on_field_dblclicked(self, _): field_row = self._fields_list.selectionModel().currentIndex().row() if field_row >= 0: field_handle = self._fields_model.item(field_row, 0).data(Qt.UserRole + 1) if field_handle: self._set_handle(field_handle) def _on_native_field_dblclicked(self, _): field_row = self._native_fields_list.selectionModel().currentIndex( ).row() if field_row: field_handle = self._native_fields_model.item( field_row, 0).data(Qt.UserRole + 1) if field_handle: self._set_handle(field_handle) def keyPressEvent(self, event): # pylint: disable=invalid-name if event.key() == Qt.Key_Backspace: self._back() return super().keyPressEvent(event)
class JavaInspector(QWidget): """ Java Class/Methods Lists """ def __init__(self, parent=None): super(JavaInspector, self).__init__(parent) self._app_window = parent self._app_window.dwarf.onEnumerateJavaMethodsComplete.connect( self._on_method_enumeration_complete) self._app_window.dwarf.onEnumerateJavaClassesStart.connect( self._on_class_enumeration_start) self._app_window.dwarf.onEnumerateJavaClassesMatch.connect( self._on_class_enumeration_match) self._app_window.dwarf.onEnumerateJavaClassesComplete.connect( self._on_class_enumeration_complete) self._java_classes = DwarfListView(self) self._javaclass_model = QStandardItemModel(0, 1) self._javaclass_model.setHeaderData(0, Qt.Horizontal, 'Class') self._java_classes.setModel(self._javaclass_model) self._java_classes.selectionModel().selectionChanged.connect( self._class_clicked) self._java_classes.header().setSectionResizeMode( 0, QHeaderView.ResizeToContents) self._java_classes.setContextMenuPolicy(Qt.CustomContextMenu) self._java_classes.customContextMenuRequested.connect( self._on_class_contextmenu) self._java_classes.doubleClicked.connect(self._class_dblclicked) self._java_methods = DwarfListView(self) self._javamethod_model = QStandardItemModel(0, 1) self._javamethod_model.setHeaderData(0, Qt.Horizontal, 'Method') self._java_methods.setModel(self._javamethod_model) self._java_methods.header().setSectionResizeMode( 0, QHeaderView.ResizeToContents) self._java_methods.setContextMenuPolicy(Qt.CustomContextMenu) self._java_methods.customContextMenuRequested.connect( self._on_method_contextmenu) self._java_methods.doubleClicked.connect(self._method_dblclicked) h_box = QHBoxLayout() h_box.setContentsMargins(0, 0, 0, 0) h_box.addWidget(self._java_classes) h_box.addWidget(self._java_methods) self.setLayout(h_box) # ************************************************************************ # **************************** Functions ********************************* # ************************************************************************ def update_classes(self): """ Refresh Classeslist """ self._app_window.dwarf.dwarf_api('enumerateJavaClasses') def update_methods(self, class_name): """ Refresh Methodslist """ if class_name: self._app_window.dwarf.dwarf_api('enumerateJavaMethods', class_name) # ************************************************************************ # **************************** Handlers ********************************** # ************************************************************************ def _class_clicked(self): index = self._java_classes.selectionModel().currentIndex().row() _class = self._javaclass_model.item(index, 0) if _class is None: return self._app_window.dwarf.dwarf_api('enumerateJavaMethods', _class.text()) def _on_class_enumeration_start(self): self._java_classes.clear() def _on_class_enumeration_match(self, java_class): _class_name = QStandardItem() _class_name.setText(java_class) self._javaclass_model.appendRow(_class_name) def _on_class_enumeration_complete(self): self._java_classes.sortByColumn(0, 0) def _on_method_enumeration_complete(self, data): self._java_methods.clear() _class, methods = data for method in methods: _method_name = QStandardItem() _method_name.setText(method) self._javamethod_model.appendRow(_method_name) def _class_dblclicked(self): """ Class DoubleClicked """ index = self._java_classes.selectionModel().currentIndex().row() if index: class_item = self._javaclass_model.item(index, 0) if class_item: class_name = class_item.text() if class_name: self._hook_class(class_name) def _method_dblclicked(self): """ Function DoubleClicked """ class_index = self._java_classes.selectionModel().currentIndex().row() method_index = self._java_methods.selectionModel().currentIndex().row() if class_index and method_index: class_item = self._javaclass_model.item(class_index, 0) method_item = self._javamethod_model.item(method_index, 0) if class_item and method_item: class_name = class_item.text() method_name = method_item.text() if class_name and method_name: self._app_window.dwarf.hook_java(class_name + '.' + method_name) def _hook_class(self, class_name): if class_name: self._app_window.dwarf.hook_java(class_name) def _hook_class_functions(self, class_name): if class_name: self._app_window.dwarf.dwarf_api('hookAllJavaMethods', class_name) def _on_class_contextmenu(self, pos): """ Modules ContextMenu """ index = self._java_classes.indexAt(pos).row() glbl_pt = self._java_classes.mapToGlobal(pos) context_menu = QMenu(self) if index != -1: context_menu.addAction( 'Hook constructor', lambda: self._hook_class( self._javaclass_model.item(index, 0).text())) context_menu.addAction( 'Hook all methods', lambda: self._hook_class_functions( self._javaclass_model.item(index, 0).text())) context_menu.addSeparator() context_menu.addAction('Refresh', self.update_classes) context_menu.exec_(glbl_pt) def _hook_method(self, method_name): class_index = self._java_classes.selectionModel().currentIndex().row() if class_index: class_item = self._javaclass_model.item(class_index, 0) if class_item: class_name = class_item.text() if class_name and method_name: self._app_window.dwarf.hook_java(class_name + '.' + method_name) def _cm_refresh_methods(self): index = self._java_classes.selectionModel().currentIndex().row() _class = self._javaclass_model.item(index, 0) if _class is None: return self.update_methods(_class.text()) def _on_method_contextmenu(self, pos): """ Modules ContextMenu """ index = self._java_methods.indexAt(pos).row() glbl_pt = self._java_methods.mapToGlobal(pos) context_menu = QMenu(self) if index != -1: context_menu.addAction( 'Hook method', lambda: self._hook_method( self._javamethod_model.item(index, 0).text())) context_menu.addSeparator() context_menu.addAction('Refresh', self._cm_refresh_methods) context_menu.exec_(glbl_pt)