def hook_on_load(self): input = InputDialog.input(hint='insert module name') if input[0]: module = input[1] if not module.endswith('.so'): module += '.so' self.insertRow(self.rowCount()) h = Hook() h.set_ptr(0) h.set_input(module) h.set_widget_row(self.rowCount() - 1) self.onloads[module] = h q = HookWidget(h.get_input()) q.set_hook_data(h) q.setForeground(Qt.darkGreen) self.setItem(self.rowCount() - 1, 0, q) q = NotEditableTableWidgetItem(hex(0)) q.setForeground(Qt.gray) self.setItem(self.rowCount() - 1, 1, q) q = NotEditableTableWidgetItem('-') q.setForeground(Qt.gray) self.setItem(self.rowCount() - 1, 2, q) self.app.get_script().exports.onload(module)
def add_hook(self): input = InputDialog.input(hint='insert pointer') if input[0]: ptr = int(self.app.get_script().exports.getpt(input[1]), 16) if ptr > 0: hook = self.app.get_script().exports.hook(ptr) if hook: self.insertRow(self.rowCount()) h = Hook() h.set_ptr(ptr) h.set_input(input[1]) h.set_widget_row(self.rowCount() - 1) self.hooks[ptr] = h q = HookWidget(h.get_input()) q.set_hook_data(h) q.setForeground(Qt.gray) self.setItem(self.rowCount() - 1, 0, q) q = NotEditableTableWidgetItem(hex(ptr)) q.setForeground(Qt.red) self.setItem(self.rowCount() - 1, 1, q) q = NotEditableTableWidgetItem('0') self.setItem(self.rowCount() - 1, 2, q) self.resizeColumnsToContents()
def hook_onload(self, input=None): if input is None or not isinstance(input, str): input = InputDialog.input(hint='insert module name') if not input[0]: return input = input[1] if not input.endswith('.so'): input += '.so' if input in self.onloads: return self.insertRow(self.rowCount()) h = Hook() h.set_ptr(0) h.set_input(input) self.onloads[input] = h q = HookWidget(h.get_input()) q.set_hook_data(h) q.setForeground(Qt.darkGreen) self.setItem(self.rowCount() - 1, 0, q) q = NotEditableTableWidgetItem(hex(0)) q.setForeground(Qt.gray) self.setItem(self.rowCount() - 1, 1, q) q = NotEditableTableWidgetItem('-') q.setForeground(Qt.gray) self.setItem(self.rowCount() - 1, 2, q) self.app.get_script().exports.onload(input) self.resizeRowToContents(0) self.resizeRowToContents(1)
def hook_native_callback(self, ptr): self.insertRow(self.rowCount()) h = Hook(Hook.HOOK_NATIVE) h.set_ptr(ptr) h.set_input(self.temporary_input) self.temporary_input = '' if self.native_pending_args: h.set_condition(self.native_pending_args['condition']) h.set_logic(self.native_pending_args['logic']) self.native_pending_args = {} self.hooks[ptr] = h q = HookWidget(h.get_input()) q.set_hook_data(h) q.setFlags(Qt.NoItemFlags) q.setForeground(Qt.gray) self.setItem(self.rowCount() - 1, 0, q) q = MemoryAddressWidget(hex(ptr)) self.setItem(self.rowCount() - 1, 1, q) self.resizeRowsToContents() self.horizontalHeader().setStretchLastSection(True)
def hook_native_callback(self, ptr): self.insertRow(self.rowCount()) h = Hook() h.set_ptr(ptr) h.set_input(self.temporary_input) if self.native_pending_args: h.set_condition(self.native_pending_args['condition']) h.set_logic(self.native_pending_args['logic']) self.hooks[ptr] = h q = HookWidget(h.get_input()) q.set_hook_data(h) q.setForeground(Qt.gray) self.setItem(self.rowCount() - 1, 0, q) q = NotEditableTableWidgetItem(hex(ptr)) q.setForeground(Qt.red) self.setItem(self.rowCount() - 1, 1, q) q = NotEditableTableWidgetItem('0') q.setForeground(Qt.gray) self.setItem(self.rowCount() - 1, 2, q) self.resizeRowToContents(0) self.resizeRowToContents(1)
def hook_onload(self, input=None): if input is None or not isinstance(input, str): input = InputDialog.input(hint='insert module name') if not input[0]: return input = input[1] if len(input) == 0: return if not input.endswith('.so'): input += '.so' if input in self.onloads: return self.insertRow(self.rowCount()) h = Hook(Hook.HOOK_ONLOAD) h.set_ptr(0) h.set_input(input) self.onloads[input] = h q = HookWidget(h.get_input()) q.set_hook_data(h) q.setFlags(Qt.NoItemFlags) q.setForeground(Qt.darkGreen) self.setItem(self.rowCount() - 1, 0, q) q = NotEditableTableWidgetItem(hex(0)) q.setFlags(Qt.NoItemFlags) q.setForeground(Qt.gray) self.setItem(self.rowCount() - 1, 1, q) self.app.dwarf_api('hookOnLoad', input) self.resizeRowsToContents() self.horizontalHeader().setStretchLastSection(True)
def _on_message(self, message, data): QApplication.processEvents() if 'payload' not in message: print('payload: ' + str(message)) return what = message['payload'] parts = what.split(':::') if len(parts) < 2: print(what) return cmd = parts[0] if cmd == 'api_ping_timeout': self._script.post({"type": str(parts[1])}) elif cmd == 'backtrace': self.onBackTrace.emit(json.loads(parts[1])) elif cmd == 'class_loader_loading_class': str_fmt = ('@thread {0} loading class := {1}'.format(parts[1], parts[2])) self.log_event(str_fmt) elif cmd == 'emulator': self.onEmulator.emit(parts[1:]) elif cmd == 'enumerate_java_classes_start': self.onEnumerateJavaClassesStart.emit() elif cmd == 'enumerate_java_classes_match': self.onEnumerateJavaClassesMatch.emit(parts[1]) elif cmd == 'enumerate_java_classes_complete': self.onEnumerateJavaClassesComplete.emit() elif cmd == 'enumerate_java_methods_complete': self.onEnumerateJavaMethodsComplete.emit([parts[1], json.loads(parts[2])]) elif cmd == 'ftrace': if self.app.get_ftrace_panel() is not None: self.app.get_ftrace_panel().append_data(parts[1]) elif cmd == 'enable_kernel': self._app_window.get_menu().enable_kernel_menu() elif cmd == 'hook_java_callback': h = Hook(HOOK_JAVA) h.set_ptr(1) h.set_input(parts[1]) if self.java_pending_args: h.set_condition(self.java_pending_args['condition']) h.set_logic(self.java_pending_args['logic']) self.java_pending_args = None self.java_hooks[h.get_input()] = h self.onAddJavaHook.emit(h) elif cmd == 'hook_java_on_load_callback': h = Hook(HOOK_JAVA) h.set_ptr(0) h.set_input(parts[1]) self.java_on_loads[parts[1]] = h self.onAddJavaOnLoadHook.emit(h) elif cmd == 'hook_native_callback': h = Hook(HOOK_NATIVE) h.set_ptr(int(parts[1], 16)) h.set_input(self.temporary_input) h.set_bytes(binascii.unhexlify(parts[2])) self.temporary_input = '' h.set_condition(parts[4]) h.set_logic(parts[3]) h.internalHook = parts[5] == 'true' h.set_debug_symbol(json.loads(parts[6])) self.native_pending_args = None if not h.internal_hook: self.hooks[h.get_ptr()] = h self.onAddNativeHook.emit(h) elif cmd == 'hook_native_on_load_callback': h = Hook(HOOK_ONLOAD) h.set_ptr(0) h.set_input(parts[1]) self.native_on_loads[parts[1]] = h self.onAddNativeOnLoadHook.emit(h) elif cmd == 'hook_deleted': if parts[1] == 'java': self.java_hooks.pop(parts[2]) elif parts[1] == 'native_on_load': self.native_on_loads.pop(parts[2]) elif parts[1] == 'java_on_load': self.java_on_loads.pop(parts[2]) else: self.hooks.pop(utils.parse_ptr(parts[2])) self.onDeleteHook.emit(parts) elif cmd == 'java_on_load_callback': str_fmt = ('Hook java onload {0} @thread := {1}'.format(parts[1], parts[2])) self.log_event(str_fmt) self.onHitJavaOnLoad.emit(parts[1]) elif cmd == 'java_trace': self.onJavaTraceEvent.emit(parts) elif cmd == 'log': self.log(parts[1]) elif cmd == 'native_on_load_callback': str_fmt = ('Hook native onload {0} @thread := {1}'.format(parts[1], parts[3])) self.log_event(str_fmt) self.onHitNativeOnLoad.emit([parts[1], parts[2]]) elif cmd == 'native_on_load_module_loading': str_fmt = ('@thread {0} loading module := {1}'.format(parts[1], parts[2])) self.log_event(str_fmt) elif cmd == 'new_thread': str_fmt = ('@thread {0} starting new thread with target fn := {1}'.format(parts[1], parts[2])) self.log_event(str_fmt) elif cmd == 'release': str_fmt = ('releasing := {0}'.format(parts[1])) self.log_event(str_fmt) if parts[1] in self.contexts: del self.contexts[parts[1]] self.onThreadResumed.emit(int(parts[1])) elif cmd == 'resume': if not self.resumed: self.resume_proc() elif cmd == 'release_js': # releasing the thread must be done by calling py funct dwarf_api('release') # there are cases in which we want to release the thread from a js api so we need to call this self.onRequestJsThreadResume.emit(int(parts[1])) elif cmd == 'set_context': data = json.loads(parts[1]) if 'modules' in data: self.onSetModules.emit(data['modules']) if 'ranges' in data: self.onSetRanges.emit(data['ranges']) if 'backtrace' in data: self.onBackTrace.emit(data['backtrace']) self.onApplyContext.emit(data) elif cmd == 'set_context_value': context_property = parts[1] value = parts[2] self.onContextChanged.emit(str(context_property), value) elif cmd == 'set_data': if data is not None: self.onSetData.emit(['raw', parts[1], data]) else: self.onSetData.emit(['plain', parts[1], str(parts[2])]) elif cmd == 'unhandled_exception': # todo pass elif cmd == 'update_modules': modules = json.loads(parts[2]) # todo update onloads bases self.onSetModules.emit(modules) elif cmd == 'update_ranges': self.onSetRanges.emit(json.loads(parts[2])) elif cmd == 'watcher': exception = json.loads(parts[1]) self.log_event('watcher hit op %s address %s @thread := %s' % (exception['memory']['operation'], exception['memory']['address'], parts[2])) elif cmd == 'watcher_added': ptr = utils.parse_ptr(parts[1]) hex_ptr = hex(ptr) flags = int(parts[2]) h = Hook(HOOK_WATCHER) h.set_ptr(ptr) h.set_logic(flags) h.set_debug_symbol(json.loads(parts[3])) self.watchers[hex_ptr] = h self.onWatcherAdded.emit(hex_ptr, flags) elif cmd == 'watcher_removed': hex_ptr = hex(utils.parse_ptr(parts[1])) self.watchers.pop(hex_ptr) self.onWatcherRemoved.emit(hex_ptr) elif cmd == 'memoryscan_result': if parts[1] == '': self.onMemoryScanResult.emit([]) else: self.onMemoryScanResult.emit(json.loads(parts[1])) else: print('unknown message: ' + what)
def on_message(self, message, data): if 'payload' not in message: print(message) return what = message['payload'] parts = what.split(':::') if len(parts) < 2: print(what) return cmd = parts[0] if cmd == 'enumerate_java_classes_start': if self.app.get_java_classes_panel() is not None: self.app.get_java_classes_panel().on_enumeration_start() elif cmd == 'enumerate_java_classes_match': if self.app.get_java_classes_panel() is not None: self.app.get_java_classes_panel().on_enumeration_match( parts[1]) elif cmd == 'enumerate_java_classes_complete': self.app_window.get_menu().on_java_classes_enumeration_complete() if self.app.get_java_classes_panel() is not None: self.app.get_java_classes_panel().on_enumeration_complete() elif cmd == 'enumerate_java_methods_complete': Dwarf.bus.emit(parts[1], json.loads(parts[2])) elif cmd == 'log': self.app.get_log_panel().log(parts[1]) elif cmd == 'hook_java_callback': h = Hook(Hook.HOOK_JAVA) h.set_ptr(1) h.set_input(parts[1]) if self.java_pending_args: h.set_condition(self.java_pending_args['condition']) h.set_logic(self.java_pending_args['logic']) self.java_pending_args = None self.java_hooks[h.get_input()] = h self.app.get_hooks_panel().hook_java_callback(h) elif cmd == 'hook_native_callback': h = Hook(Hook.HOOK_NATIVE) h.set_ptr(int(parts[1], 16)) h.set_input(self.temporary_input) self.temporary_input = '' if self.native_pending_args: h.set_condition(self.native_pending_args['condition']) h.set_logic(self.native_pending_args['logic']) self.native_pending_args = None self.hooks[h.get_ptr()] = h self.app.get_hooks_panel().hook_native_callback(h) elif cmd == 'memory_scan_match': Dwarf.bus.emit(parts[1], parts[2], json.loads(parts[3])) elif cmd == 'memory_scan_complete': self.app_window.get_menu().on_bytes_search_complete() Dwarf.bus.emit(parts[1] + ' complete', 0, 0) elif cmd == 'onload_callback': self.loading_library = parts[1] self.app.get_log_panel().log('hook onload %s @thread := %s' % (parts[1], parts[3])) self.app.get_hooks_panel().hit_onload(parts[1], parts[2]) elif cmd == 'set_context': data = json.loads(parts[1]) self.app.get_contexts().append(data) if 'context' in data: sym = '' if 'pc' in data['context']: name = data['ptr'] if 'moduleName' in data['symbol']: sym = '(%s - %s)' % (data['symbol']['moduleName'], data['symbol']['name']) else: name = data['ptr'] self.app.get_contexts_panel().add_context( data, library_onload=self.loading_library) if self.loading_library is None: self.app.get_log_panel().log('hook %s %s @thread := %d' % (name, sym, data['tid'])) if len(self.app.get_contexts() ) > 1 and self.app.get_registers_panel().have_context(): return self.app.get_session_ui().request_session_ui_focus() else: self.app.set_arch(data['arch']) if self.app.get_arch() == 'arm': self.app.pointer_size = 4 else: self.app.pointer_size = 8 self.pid = data['pid'] self.java_available = data['java'] self.app.get_log_panel().log('injected into := ' + str(self.pid)) self.app_window.on_context_info() self.app.apply_context(data) if self.loading_library is not None: self.loading_library = None elif cmd == 'set_data': key = parts[1] if data: self.app.get_data_panel().append_data( key, hexdump(data, result='return')) else: self.app.get_data_panel().append_data(key, str(parts[2])) elif cmd == 'update_modules': self.app.apply_context({ 'tid': parts[1], 'modules': json.loads(parts[2]) }) elif cmd == 'update_ranges': self.app.apply_context({ 'tid': parts[1], 'ranges': json.loads(parts[2]) }) else: print(what)
def on_message(self, message, data): if 'payload' not in message: print(message) return what = message['payload'] parts = what.split(':::') if len(parts) < 2: print(what) return cmd = parts[0] if cmd == 'backtrace': if self.app.get_session_ui() is not None and self.app.get_backtrace_panel() is not None: try: self.app.get_backtrace_panel().set_backtrace(json.loads(parts[1])) except: pass elif cmd == 'enumerate_java_classes_start': if self.app.get_java_classes_panel() is not None: self.app.get_java_classes_panel().on_enumeration_start() if self.app.get_java_trace_panel() is not None: self.app.get_java_trace_panel().on_enumeration_start() elif cmd == 'enumerate_java_classes_match': if self.app.get_java_classes_panel() is not None: self.app.get_java_classes_panel().on_enumeration_match(parts[1]) if self.app.get_java_trace_panel() is not None: self.app.get_java_trace_panel().on_enumeration_match(parts[1]) elif cmd == 'enumerate_java_classes_complete': self.app_window.get_menu().on_java_classes_enumeration_complete() if self.app.get_java_classes_panel() is not None: self.app.get_java_classes_panel().on_enumeration_complete() if self.app.get_java_trace_panel() is not None: self.app.get_java_trace_panel().on_enumeration_complete() elif cmd == 'enumerate_java_methods_complete': Dwarf.bus.emit(parts[1], json.loads(parts[2]), parts[1]) elif cmd == 'ftrace': if self.app.get_ftrace_panel() is not None: self.app.get_ftrace_panel().append_data(parts[1]) elif cmd == 'enable_kernel': self.app_window.get_menu().enable_kernel_menu() elif cmd == 'hook_java_callback': h = Hook(Hook.HOOK_JAVA) h.set_ptr(1) h.set_input(parts[1]) if self.java_pending_args: h.set_condition(self.java_pending_args['condition']) h.set_logic(self.java_pending_args['logic']) self.java_pending_args = None self.java_hooks[h.get_input()] = h self.app.get_hooks_panel().hook_java_callback(h) elif cmd == 'hook_native_callback': h = Hook(Hook.HOOK_NATIVE) h.set_ptr(int(parts[1], 16)) h.set_input(self.temporary_input) self.temporary_input = '' if self.native_pending_args: h.set_condition(self.native_pending_args['condition']) h.set_logic(self.native_pending_args['logic']) self.native_pending_args = None self.hooks[h.get_ptr()] = h self.app.get_hooks_panel().hook_native_callback(h) elif cmd == 'hook_onload_callback': h = Hook(Hook.HOOK_ONLOAD) h.set_ptr(0) h.set_input(parts[1]) self.on_loads[parts[1]] = h if self.app.session_ui is not None and self.app.get_hooks_panel() is not None: self.app.get_hooks_panel().hook_onload_callback(h) elif cmd == 'java_trace': panel = self.app.get_java_trace_panel() if panel is None: panel = self.app.get_session_ui().add_dwarf_tab(SessionUi.TAB_JAVA_TRACE) panel.on_event(parts[1], parts[2], parts[3]) elif cmd == 'log': self.app.get_log_panel().log(parts[1]) elif cmd == 'memory_scan_match': Dwarf.bus.emit(parts[1], parts[2], json.loads(parts[3])) elif cmd == 'memory_scan_complete': self.app_window.get_menu().on_bytes_search_complete() Dwarf.bus.emit(parts[1] + ' complete', 0, 0) elif cmd == 'onload_callback': self.loading_library = parts[1] self.app.get_log_panel().log('hook onload %s @thread := %s' % ( parts[1], parts[3])) self.app.get_hooks_panel().hit_onload(parts[1], parts[2]) elif cmd == 'set_context': data = json.loads(parts[1]) self.app.get_contexts().append(data) if 'context' in data: sym = '' if 'pc' in data['context']: name = data['ptr'] if 'moduleName' in data['symbol']: sym = '(%s - %s)' % (data['symbol']['moduleName'], data['symbol']['name']) else: name = data['ptr'] self.app.get_contexts_panel().add_context(data, library_onload=self.loading_library) # check if data['reason'] is 0 (REASON_HOOK) if self.loading_library is None and data['reason'] == 0: self.log('hook %s %s @thread := %d' % (name, sym, data['tid'])) if len(self.app.get_contexts()) > 1 and self.app.get_registers_panel().have_context(): return self.app.get_session_ui().request_session_ui_focus() else: self.app.set_arch(data['arch']) if self.app.get_arch() == 'arm': self.app.pointer_size = 4 else: self.app.pointer_size = 8 self.pid = data['pid'] self.java_available = data['java'] self.app.get_log_panel().log('injected into := ' + str(self.pid)) self.app_window.on_context_info() self.app.apply_context(data) if self.loading_library is not None: self.loading_library = None elif cmd == 'set_data': key = parts[1] if data: self.app.get_data_panel().append_data(key, hexdump(data, result='return')) else: self.app.get_data_panel().append_data(key, str(parts[2])) elif cmd == 'tracer': panel = self.app.get_trace_panel() if panel is None: panel = self.app.get_session_ui().add_dwarf_tab(SessionUi.TAB_TRACE) if panel is not None: # safely checked later panel.start() trace_events_parts = parts[1].split(',') while len(trace_events_parts) > 0: t = TraceEvent(trace_events_parts.pop(0), trace_events_parts.pop(0), trace_events_parts.pop(0), trace_events_parts.pop(0)) panel.event_queue.append(t) elif cmd == 'update_modules': self.app.apply_context({'tid': parts[1], 'modules': json.loads(parts[2])}) elif cmd == 'update_ranges': self.app.apply_context({'tid': parts[1], 'ranges': json.loads(parts[2])}) elif cmd == 'watcher': exception = json.loads(parts[1]) self.log('watcher hit op %s address %s @thread := %s' % (exception['memory']['operation'], exception['memory']['address'], parts[2])) elif cmd == 'watcher_added': if self.app.get_watchers_panel() is not None: self.app.get_watchers_panel().add_watcher_callback(parts[1]) elif cmd == 'watcher_removed': if self.app.get_watchers_panel() is not None: self.app.get_watchers_panel().remove_watcher_callback(parts[1]) else: print(what)
def _on_message(self, message, data): QApplication.processEvents() if 'payload' not in message: print('payload: ' + str(message)) return what = message['payload'] parts = what.split(':::') if len(parts) < 2: print(what) return cmd = parts[0] if cmd == 'backtrace': try: self._app_window.backtrace_panel.set_backtrace( json.loads(parts[1])) except: pass elif cmd == 'emulator': self.onEmulator.emit(parts[1:]) elif cmd == 'enumerate_java_classes_start': self.onEnumerateJavaClassesStart.emit() elif cmd == 'enumerate_java_classes_match': self.onEnumerateJavaClassesMatch.emit(parts[1]) elif cmd == 'enumerate_java_classes_complete': self.onEnumerateJavaClassesComplete.emit() elif cmd == 'enumerate_java_methods_complete': self.onEnumerateJavaMethodsComplete.emit( [parts[1], json.loads(parts[2])]) elif cmd == 'ftrace': if self.app.get_ftrace_panel() is not None: self.app.get_ftrace_panel().append_data(parts[1]) elif cmd == 'enable_kernel': self._app_window.get_menu().enable_kernel_menu() elif cmd == 'hook_java_callback': h = Hook(Hook.HOOK_JAVA) h.set_ptr(1) h.set_input(parts[1]) if self.java_pending_args: h.set_condition(self.java_pending_args['condition']) h.set_logic(self.java_pending_args['logic']) self.java_pending_args = None self.java_hooks[h.get_input()] = h self.onAddJavaHook.emit(h) elif cmd == 'hook_native_callback': h = Hook(Hook.HOOK_NATIVE) h.set_ptr(int(parts[1], 16)) h.set_input(self.temporary_input) h.set_bytes(binascii.unhexlify(parts[2])) self.temporary_input = '' h.set_condition(parts[4]) h.set_logic(parts[3]) self.native_pending_args = None self.hooks[h.get_ptr()] = h self.onAddNativeHook.emit(h) elif cmd == 'hook_onload_callback': h = Hook(Hook.HOOK_ONLOAD) h.set_ptr(0) h.set_input(parts[1]) self.on_loads[parts[1]] = h self.onAddOnLoadHook.emit(h) elif cmd == 'java_trace': self.onJavaTraceEvent.emit(parts) elif cmd == 'log': self.log(parts[1]) elif cmd == 'memory_scan_match': self.onMemoryScanMatch.emit( [parts[1], parts[2], json.loads(parts[3])]) elif cmd == 'memory_scan_complete': self._app_window.get_menu().on_bytes_search_complete() self.onMemoryScanComplete.emit([parts[1] + ' complete', 0, 0]) elif cmd == 'onload_callback': self.loading_library = parts[1] str_fmt = ('Hook onload {0} @thread := {1}'.format( parts[1], parts[3])) self.log(str_fmt) self.onHitOnLoad.emit([parts[1], parts[2]]) elif cmd == 'release': if parts[1] in self.contexts: del self.contexts[parts[1]] self.onThreadResumed.emit(int(parts[1])) elif cmd == 'set_context': data = json.loads(parts[1]) if 'modules' in data: self.onSetModules.emit(data['modules']) if 'ranges' in data: self.onSetRanges.emit(data['ranges']) self.onApplyContext.emit(data) elif cmd == 'set_data': if data: self.onSetData.emit(['raw', parts[1], data]) else: self.onSetData.emit(['plain', parts[1], str(parts[2])]) elif cmd == 'tracer': self.onTraceData.emit(parts[1]) elif cmd == 'unhandled_exception': # todo pass elif cmd == 'update_modules': modules = json.loads(parts[2]) # todo update onloads bases self.onSetModules.emit(modules) elif cmd == 'update_ranges': self.onSetRanges.emit(json.loads(parts[2])) elif cmd == 'watcher': exception = json.loads(parts[1]) self.log('watcher hit op %s address %s @thread := %s' % (exception['memory']['operation'], exception['memory']['address'], parts[2])) elif cmd == 'watcher_added': self._watchers.append(utils.parse_ptr(parts[1])) self.onWatcherAdded.emit(parts[1], int(parts[2])) elif cmd == 'watcher_removed': self._watchers.remove(utils.parse_ptr(parts[1])) self.onWatcherRemoved.emit(parts[1]) else: print('unknown message: ' + what)