Example #1
0
class MainWindowController(NSWindowController):
    processCombo = objc.IBOutlet()
    triggerField = objc.IBOutlet()
    attachProgress = objc.IBOutlet()
    attachButton = objc.IBOutlet()
    detachButton = objc.IBOutlet()
    recvTotalLabel = objc.IBOutlet()
    callTableView = objc.IBOutlet()

    def __new__(cls):
        return cls.alloc().initWithTitle_("CpuShark")

    def initWithTitle_(self, title):
        self = self.initWithWindowNibName_("MainWindow")
        self.window().setTitle_(title)

        self.retain()

        return self

    def windowDidLoad(self):
        NSWindowController.windowDidLoad(self)

        device = [
            device
            for device in frida.get_device_manager().enumerate_devices()
            if device.type == 'local'
        ][0]
        self.processList = ProcessList(device)
        self.capture = Capture(device)
        self.processCombo.setUsesDataSource_(True)
        self.processCombo.setDataSource_(self.processList)
        self.capture.setDelegate_(self)

        self.callTableView.setDataSource_(self.capture.calls)
        self.capture.calls.setDelegate_(self)

        self.loadDefaults()

        self.updateAttachForm_(self)

    def windowWillClose_(self, notification):
        self.saveDefaults()

        self.autorelease()

    def loadDefaults(self):
        defaults = NSUserDefaults.standardUserDefaults()
        targetProcess = defaults.stringForKey_("targetProcess")
        if targetProcess is not None:
            for i, process in enumerate(self.processList.processes):
                if process.name == targetProcess:
                    self.processCombo.selectItemAtIndex_(i)
                    break
        triggerPort = defaults.integerForKey_("triggerPort") or 80
        self.triggerField.setStringValue_(str(triggerPort))

    def saveDefaults(self):
        defaults = NSUserDefaults.standardUserDefaults()
        process = self.selectedProcess()
        if process is not None:
            defaults.setObject_forKey_(process.name, "targetProcess")
        defaults.setInteger_forKey_(self.triggerField.integerValue(),
                                    "triggerPort")

    def selectedProcess(self):
        index = self.processCombo.indexOfSelectedItem()
        if index != -1:
            return self.processList.processes[index]
        return None

    def triggerPort(self):
        return self.triggerField.integerValue()

    @objc.IBAction
    def attach_(self, sender):
        self.capture.attachToProcess_triggerPort_(self.selectedProcess(),
                                                  self.triggerPort())

    @objc.IBAction
    def detach_(self, sender):
        self.capture.detach()

    @objc.IBAction
    def toggleTracing_(self, sender):
        item = sender.itemAtRow_(sender.selectedRow())
        if isinstance(item, TargetFunction):
            func = item
            if func.hasProbe:
                self.capture.calls.removeProbe_(func)
            else:
                self.capture.calls.addProbe_(func)
            func.hasProbe = not func.hasProbe
            self.callTableView.reloadItem_(func)

    def updateAttachForm_(self, sender):
        isDetached = self.capture.state == CaptureState.DETACHED
        hasProcess = self.selectedProcess() is not None
        hasTrigger = len(self.triggerField.stringValue()) > 0
        self.processCombo.setEnabled_(isDetached)
        self.triggerField.setEnabled_(isDetached)
        self.attachProgress.setHidden_(
            self.capture.state != CaptureState.ATTACHING)
        self.attachButton.setHidden_(
            self.capture.state == CaptureState.ATTACHED)
        self.attachButton.setEnabled_(isDetached and hasProcess and hasTrigger)
        self.detachButton.setHidden_(
            self.capture.state != CaptureState.ATTACHED)
        if self.capture.state == CaptureState.ATTACHING:
            self.attachProgress.startAnimation_(self)
        else:
            self.attachProgress.stopAnimation_(self)

    def controlTextDidChange_(self, notification):
        self.updateAttachForm_(self)

    def comboBoxSelectionDidChange_(self, notification):
        self.updateAttachForm_(self)

    def captureStateDidChange(self):
        self.updateAttachForm_(self)

    def captureFailedToAttachWithError_(self, error):
        NSRunCriticalAlertPanel("Error", "Failed to attach: %s" % error, None,
                                None, None)

    def captureRecvTotalDidChange(self):
        self.recvTotalLabel.setStringValue_(self.capture.recvTotal)

    def callsDidChange(self):
        self.callTableView.reloadData()

    def callItemDidChange_(self, item):
        self.callTableView.reloadItem_reloadChildren_(item, True)
class MainWindowController(NSWindowController):
    processCombo = objc.IBOutlet()
    triggerField = objc.IBOutlet()
    attachProgress = objc.IBOutlet()
    attachButton = objc.IBOutlet()
    detachButton = objc.IBOutlet()
    recvTotalLabel = objc.IBOutlet()
    callTableView = objc.IBOutlet()

    def __new__(cls):
        return cls.alloc().initWithTitle_("CpuShark")

    def initWithTitle_(self, title):
        self = self.initWithWindowNibName_("MainWindow")
        self.window().setTitle_(title)

        self.retain()

        return self

    def windowDidLoad(self):
        NSWindowController.windowDidLoad(self)

        device = [device for device in frida.get_device_manager().enumerate_devices() if device.type == 'local'][0]
        self.processList = ProcessList(device)
        self.capture = Capture(device)
        self.processCombo.setUsesDataSource_(True)
        self.processCombo.setDataSource_(self.processList)
        self.capture.setDelegate_(self)

        self.callTableView.setDataSource_(self.capture.calls)
        self.capture.calls.setDelegate_(self)

        self.loadDefaults()

        self.updateAttachForm_(self)

    def windowWillClose_(self, notification):
        self.saveDefaults()

        self.autorelease()

    def loadDefaults(self):
        defaults = NSUserDefaults.standardUserDefaults()
        targetProcess = defaults.stringForKey_("targetProcess")
        if targetProcess is not None:
            for i, process in enumerate(self.processList.processes):
                if process.name == targetProcess:
                    self.processCombo.selectItemAtIndex_(i)
                    break
        triggerPort = defaults.integerForKey_("triggerPort") or 80
        self.triggerField.setStringValue_(str(triggerPort))

    def saveDefaults(self):
        defaults = NSUserDefaults.standardUserDefaults()
        process = self.selectedProcess()
        if process is not None:
            defaults.setObject_forKey_(process.name, "targetProcess")
        defaults.setInteger_forKey_(self.triggerField.integerValue(), "triggerPort")

    def selectedProcess(self):
        index = self.processCombo.indexOfSelectedItem()
        if index != -1:
            return self.processList.processes[index]
        return None

    def triggerPort(self):
        return self.triggerField.integerValue()

    @objc.IBAction
    def attach_(self, sender):
        self.capture.attachToProcess_triggerPort_(self.selectedProcess(), self.triggerPort())

    @objc.IBAction
    def detach_(self, sender):
        self.capture.detach()

    @objc.IBAction
    def toggleTracing_(self, sender):
        item = sender.itemAtRow_(sender.selectedRow())
        if isinstance(item, TargetFunction):
            func = item
            if func.hasProbe:
                self.capture.calls.removeProbe_(func)
            else:
                self.capture.calls.addProbe_(func)
            func.hasProbe = not func.hasProbe
            self.callTableView.reloadItem_(func)

    def updateAttachForm_(self, sender):
        isDetached = self.capture.state == CaptureState.DETACHED
        hasProcess = self.selectedProcess() is not None
        hasTrigger = len(self.triggerField.stringValue()) > 0
        self.processCombo.setEnabled_(isDetached)
        self.triggerField.setEnabled_(isDetached)
        self.attachProgress.setHidden_(self.capture.state != CaptureState.ATTACHING)
        self.attachButton.setHidden_(self.capture.state == CaptureState.ATTACHED)
        self.attachButton.setEnabled_(isDetached and hasProcess and hasTrigger)
        self.detachButton.setHidden_(self.capture.state != CaptureState.ATTACHED)
        if self.capture.state == CaptureState.ATTACHING:
            self.attachProgress.startAnimation_(self)
        else:
            self.attachProgress.stopAnimation_(self)

    def controlTextDidChange_(self, notification):
        self.updateAttachForm_(self)

    def comboBoxSelectionDidChange_(self, notification):
        self.updateAttachForm_(self)

    def captureStateDidChange(self):
        self.updateAttachForm_(self)

    def captureFailedToAttachWithError_(self, error):
        NSRunCriticalAlertPanel("Error", "Failed to attach: %s" % error, None, None, None)

    def captureRecvTotalDidChange(self):
        self.recvTotalLabel.setStringValue_(self.capture.recvTotal)

    def callsDidChange(self):
        self.callTableView.reloadData()

    def callItemDidChange_(self, item):
        self.callTableView.reloadItem_reloadChildren_(item, True)