예제 #1
0
파일: main.py 프로젝트: C4rt/sonare
class SonareWindow(QMainWindow):
    # TODO: HTML should use settings specified here
    FONT_NAME = 'Monospace'
    FONT_SIZE = 8

    WINDOW_COLOR       = QColor(0x3c, 0x3c, 0x3c)
    BG_COLOR           = QColor(0x50, 0x50, 0x64)
    DEFAULT_TEXT_COLOR = QColor(0xD8, 0xD8, 0xD8)

    # TODO: HTML should use settings specified here
    ADDR_COLOR = QColor(0x7F, 0xEC, 0x91)
    SYMBOL_COLOR = QColor(0xD8, 0xD8, 0xD8)


    def __init__(self, path):
        QMainWindow.__init__(self)
        self.setMinimumSize(QSize(600, 400))

        self.font = QFont(self.FONT_NAME, self.FONT_SIZE)
        self.fontMetrics = QFontMetricsF(self.font)

        palette = self.palette()
        # palette.setColor(QPalette.Window, self.WINDOW_COLOR)
        # palette.setColor(QPalette.WindowText, self.DEFAULT_TEXT_COLOR)
        palette.setColor(QPalette.Base, self.BG_COLOR)
        palette.setColor(QPalette.Text, self.DEFAULT_TEXT_COLOR)
        self.setPalette(palette)

        self._makeMenus()

        self.open(path)

        self._makeScene()
        self._makeFlagList()

        self.funcName = None

        self._updateWindowTitle()

    def viewGoto(self):
        addr = self.inputAddr('Sonare - Goto', 'Enter an address:')
        if addr is None:
            return

        self.gotoAddr(addr)

    def viewGraph(self):
        self.curView.setParent(None)

        if self.curView is self.textView:
            self.curView = self.graphView
        else:
            self.curView = self.textView

        self.setCentralWidget(self.curView)

    def _makeMenus(self):
        fileMenu = self.menuBar().addMenu(u"&File")

        quitAct = QAction(u"&Quit", self)
        quitAct.setShortcuts(QKeySequence.Quit)
        quitAct.triggered.connect(self.close)
        fileMenu.addAction(quitAct)


        viewMenu = self.menuBar().addMenu("&View")

        gotoAct = QAction(u"&Goto", self)
        gotoAct.setShortcuts(QKeySequence(u"Ctrl+G"))
        gotoAct.triggered.connect(self.viewGoto)
        viewMenu.addAction(gotoAct)

        # TODO: should probably be with a check mark
        textGraphAct = QAction(u"Switch text/g&raph view", self)
        textGraphAct.setShortcuts(QKeySequence(u"Ctrl+R"))
        textGraphAct.triggered.connect(self.viewGraph)
        viewMenu.addAction(textGraphAct)

    def _makeScene(self):
        self.textView = textview.SonareTextView(self)

        self.graphScene = graph.SonareGraphScene(self)
        self.graphView = QGraphicsView(self.graphScene)
        self.graphView.setRenderHints(
            QPainter.Antialiasing
            | QPainter.TextAntialiasing
            | QPainter.SmoothPixmapTransform
            | QPainter.HighQualityAntialiasing)

        self.curView = self.textView
        self.setCentralWidget(self.curView)

    def _makeFlagList(self):
        model = FlagListModel(self)

        ftdock = FilteredTreeDock(self, "Flags", model, self)
        ftdock.setFilterKeyColumn(1)                  # filter by name
        ftdock.sortByColumn(0, Qt.AscendingOrder)     # sort by address

        self.addDockWidget(Qt.LeftDockWidgetArea, ftdock)

        def onDblClick(modelIdx):
            addrItemIdx = modelIdx.sibling(modelIdx.row(), 0)

            # this is actually the proxy model
            model = ftdock.treeView.model()
            addr = model.data(addrItemIdx, Qt.UserRole + 1)
            self.gotoAddr(addr)

        ftdock.treeView.doubleClicked.connect(onDblClick)

    def _updateWindowTitle(self):
        self.setWindowTitle('Sonare - {} ({})'
            .format(self.funcName or '?', os.path.basename(self.filePath)))

    def open(self, path):
        self.r2core = RCore()
        self.r2core.flags.space_set(b'symbols')

        self.r2core.file_open(path.encode('ascii'), False, 0)
        self.r2core.bin_load("", 0)

        self.r2core.anal_all()
        print() # anal_all is noisy

        # clean up function overlaps
        self.r2core.cmd_str('aff')

        self.core = Core(self.r2core)

        self.filePath = path

        arch = self.r2core.config.get('asm.arch')
        if arch == 'x86':
            self.asmFormatter = X86AsmFormatter(self)
        elif arch == 'mips':
            self.asmFormatter = MipsAsmFormatter(self)
        else:
            raise NotImplementedError("asm formatting for {}".format(arch))

    def inputAddr(self, title, prompt):
        s, ok = QInputDialog.getText(self, title, prompt)
        if not ok:
            return None

        try:
            return self.getAddr(s)
        except ValueError:
            # TODO: message box
            return None

    def getAddr(self, addrName):
        try:
            return int(addrName, 16)
        except ValueError:
            pass

        if isinstance(addrName, unicode):
            addrName = addrName.encode('ascii')

        return self.r2core.num.get(addrName)

    def gotoFunc(self, funcName):
        funcAddr = self.getAddr(funcName)
        if funcAddr is None:
            raise ValueError("Unknown func '{}'".format(funcName))

        self.gotoAddr(funcAddr)

    def gotoAddr(self, funcAddr):
        func = self.r2core.anal.get_fcn_at(funcAddr, 1) # R_ANAL_FCN_TYPE_FCN
        if func is None:
            self.funcName = '?'
        else:
            self.funcName = func.name
            funcAddr = func.addr

        self.textView.gotoAddr(funcAddr)

        self.graphScene.loadFunc(funcAddr)

        if self.graphScene.myBlocks:
            firstBlock = self.graphScene.myBlocks[0]
            r = self.graphScene.getBlockRect(firstBlock.addr)
            self.graphView.centerOn(r.center().x(), r.top())

        self._updateWindowTitle()

    def getAddrName(self, addr):
        flag = self.r2core.flags.get_i(int(addr) & 0xffffffffffffffff)
        if flag is None:
            return None
        else:
            return flag.name

    @property
    def isBigEndian(self):
        return self.r2core.config.get('cfg.bigendian') == 'true'

    def getBytes(self, addr, size):
        hexStr = self.r2core.cmd_str('p8 {}@{:#x}'.format(size, addr)).strip()
        return unhexlify(hexStr)

    def getWord(self, addr):
        # TODO: use r2 api
        buf = self.getBytes(addr, 4)
        fmt = '>L' if self.isBigEndian else '<L'
        return unpack(fmt, buf)[0]

    def fmtNum(self, val):
        if abs(val) < 10:
            return str(val)


        if abs(val) <= 0xffff:
            return format(val, '#x')
        else:
            hexStr = format(val, '08x')
            # split off last 2 bytes
            return hexStr[:-4] + ':' + hexStr[-4:]
예제 #2
0
파일: main.py 프로젝트: blaquee/sonare
class SonareWindow(QMainWindow):
    # TODO: HTML should use settings specified here
    FONT_NAME = 'Monospace'
    FONT_SIZE = 8

    WINDOW_COLOR       = QColor(0x3c, 0x3c, 0x3c)
    BG_COLOR           = QColor(0x50, 0x50, 0x64)
    DEFAULT_TEXT_COLOR = QColor(0xD8, 0xD8, 0xD8)

    # TODO: HTML should use settings specified here
    ADDR_COLOR = QColor(0x7F, 0xEC, 0x91)
    SYMBOL_COLOR = QColor(0xD8, 0xD8, 0xD8)


    def __init__(self, path):
        QMainWindow.__init__(self)
        self.setMinimumSize(QSize(600, 400))

        self.font = QFont(self.FONT_NAME, self.FONT_SIZE)
        self.fontMetrics = QFontMetricsF(self.font)

        palette = self.palette()
        # palette.setColor(QPalette.Window, self.WINDOW_COLOR)
        # palette.setColor(QPalette.WindowText, self.DEFAULT_TEXT_COLOR)
        palette.setColor(QPalette.Base, self.BG_COLOR)
        palette.setColor(QPalette.Text, self.DEFAULT_TEXT_COLOR)
        self.setPalette(palette)

        self.open(path)

        self._makeScene()
        self._makeFlagList()

        self.funcName = None

        self._updateWindowTitle()

    def _makeScene(self):
        self.scene = graph.SonareGraphScene(self)
        self.view = QGraphicsView(self.scene)
        self.view.setRenderHints(
            QPainter.Antialiasing
            | QPainter.TextAntialiasing
            | QPainter.SmoothPixmapTransform
            | QPainter.HighQualityAntialiasing)
        self.setCentralWidget(self.view)

    def _makeFlagList(self):
        model = FlagListModel(self)

        ftdock = FilteredTreeDock(self, "Flags", model, self)
        ftdock.setFilterKeyColumn(1)                  # filter by name
        ftdock.sortByColumn(0, Qt.AscendingOrder)     # sort by address

        self.addDockWidget(Qt.LeftDockWidgetArea, ftdock)

        def onDblClick(modelIdx):
            addrItemIdx = modelIdx.sibling(modelIdx.row(), 0)

            # this is actually the proxy model
            model = ftdock.treeView.model()
            addr = model.data(addrItemIdx, Qt.UserRole + 1)
            self.gotoAddr(addr)

        ftdock.treeView.doubleClicked.connect(onDblClick)

    def _updateWindowTitle(self):
        self.setWindowTitle('Sonare - {} ({})'
            .format(self.funcName or '?', os.path.basename(self.filePath)))

    def open(self, path):
        self.r2core = RCore()
        self.r2core.flags.space_set(b'symbols')

        self.r2core.file_open(path.encode('ascii'), False, BASE_ADDR)
        self.r2core.bin_load("", 0)

        self.r2core.anal_all()
        print() # anal_all is noisy

        # clean up function overlaps
        self.r2core.cmd_str('aff')

        self.filePath = path

        arch = self.r2core.config.get('asm.arch')
        if arch == 'x86':
            self.asmFormatter = X86AsmFormatter(self)
        elif arch == 'mips':
            self.asmFormatter = MipsAsmFormatter(self)
        else:
            raise NotImplementedError("asm formatting for {}".format(arch))

    def getAddr(self, addrName):
        if isinstance(addrName, unicode):
            addrName = addrName.encode('ascii')

        return self.r2core.num.get(addrName)

    def gotoFunc(self, funcName):
        funcAddr = self.getAddr(funcName)
        if funcAddr is None:
            raise ValueError("Unknown func '{}'".format(funcName))

        self.gotoAddr(funcAddr)

    def gotoAddr(self, funcAddr):
        func = self.r2core.anal.get_fcn_at(funcAddr, 1) # R_ANAL_FCN_TYPE_FCN
        if func is None:
            self.funcName = '?'
        else:
            self.funcName = func.name
            funcAddr = func.addr

        self.scene.loadFunc(funcAddr)

        firstBlock = self.scene.myBlocks[0]
        r = self.scene.getBlockRect(firstBlock.addr)
        self.view.centerOn(r.center().x(), r.top())

        self._updateWindowTitle()

    def getAddrName(self, addr):
        flag = self.r2core.flags.get_i(int(addr) & 0xffffffffffffffff)
        if flag is None:
            return None
        else:
            return flag.name

    @property
    def isBigEndian(self):
        return self.r2core.config.get('cfg.bigendian') == 'true'

    def getWord(self, addr):
        # TODO: use r2 api
        hexStr = self.r2core.cmd_str('p8 4@{:#x}'.format(addr)).strip()
        buf = unhexlify(hexStr)
        fmt = '>L' if self.isBigEndian else '<L'
        return unpack(fmt, buf)[0]

    def fmtNum(self, val):
        if abs(val) < 10:
            return str(val)


        if abs(val) <= 0xffff:
            return format(val, '#x')
        else:
            hexStr = format(val, '08x')
            # split off last 2 bytes
            return hexStr[:-4] + ':' + hexStr[-4:]