def __init__(self, so_file_name, apk): # use radare2's RCore for lib analysis core = RCore() # load the file with RBin from r2 self.jni_on_load_vaddr = self.load_file(core, so_file_name, apk) if self.jni_on_load_vaddr == "": print "ERROR: library file has no JNI_OnLoad method" sys.exit(1) self.so_file_name = so_file_name self.sig = self.get_sig(core) print("Sig: %s " % (self.sig))
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))
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:]
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:]
def init_radare(path): import collections tags = collections.defaultdict(dict) core = RCore() desc = core.io.open(path, 0, 0) if desc == None: print "*** RBIN LOAD FAILED" return False core.bin.load(path, 0, 0, 0, desc.fd, False) print "*** radare bin loaded @",ghex(core.bin.get_baddr()) """ for e in core.bin.get_entries(): print e """ # why do i need to do this? info = core.bin.get_info() core.config.set("asm.arch", info.arch); core.config.set("asm.bits", str(info.bits)); #core.file_open(path, 0, 0) """ # find functions core.search_preludes() """ # you have to file_open to make analysis work core.file_open(path, False, 0) core.bin_load("", 0) core.anal_all() import collections tags = collections.defaultdict(dict) for s in core.bin.get_symbols(): print ghex(s.vaddr), s.name tags[s.vaddr]['name'] = s.name for f in core.anal.get_fcns(): print f.name, ghex(f.addr), f.size tags[f.addr]['funclength'] = f.size sa = f.addr starts = [] # find bblock starts, haxx while sa < (f.addr + f.size): op = core.op_anal(sa) t = op.type & 0xFFFF if t == 1 or t == 2: starts.append(op.jump) if op.size <= 0: break else: sa += op.size sa = f.addr will_pass = True while sa < (f.addr + f.size): #print core.op_str(sa) instr = core.op_str(sa) op = core.op_anal(sa) t = op.type t2 = t & 0xFFFF t3 = t & 0xFFFF0000 tags[sa]['len'] = op.size tags[sa]['semantics'] = [] tags[sa]['flow'] = [] tags[sa]['scope'] = ghex(f.addr) tags[sa]['flags'] = 0x10000 if will_pass else 0 will_pass = True if t2 == 1 or t2 == 2 or t2 == 5 or (sa+op.size) in starts: tags[sa]['semantics'].append("endbb") if t == 1 or t == 2: # jmp tags[sa]['flow'].append(ghex(op.jump)) will_pass = False elif t3 == 0x80000000 and (t2 == 1 or t2 == 2): # cond jmp tags[sa]['flow'].append(ghex(op.jump)) tags[sa]['instruction'] = instr print " ", ghex(sa), op.type & 0xFFFF, instr if op.size <= 0: break else: sa += op.size #bbs = f.get_bbs() """ for b in f.get_bbs(): print " ", ghex(b.addr), b.size """ # fix ctl-c import signal signal.signal(signal.SIGINT, signal.SIG_DFL) return tags
def init_radare(path): import collections tags = collections.defaultdict(dict) core = RCore() desc = core.io.open(path, 0, 0) if desc == None: print "*** RBIN LOAD FAILED" return False core.bin.load(path, 0, 0, 0, desc.fd, False) print "*** radare bin loaded @", ghex(core.bin.get_baddr()) """ for e in core.bin.get_entries(): print e """ # why do i need to do this? info = core.bin.get_info() core.config.set("asm.arch", info.arch) core.config.set("asm.bits", str(info.bits)) #core.file_open(path, 0, 0) """ # find functions core.search_preludes() """ # you have to file_open to make analysis work core.file_open(path, False, 0) core.bin_load("", 0) core.anal_all() import collections tags = collections.defaultdict(dict) for s in core.bin.get_symbols(): print ghex(s.vaddr), s.name tags[s.vaddr]['name'] = s.name for f in core.anal.get_fcns(): print f.name, ghex(f.addr), f.size tags[f.addr]['funclength'] = f.size sa = f.addr starts = [] # find bblock starts, haxx while sa < (f.addr + f.size): op = core.op_anal(sa) t = op.type & 0xFFFF if t == 1 or t == 2: starts.append(op.jump) if op.size <= 0: break else: sa += op.size sa = f.addr will_pass = True while sa < (f.addr + f.size): #print core.op_str(sa) instr = core.op_str(sa) op = core.op_anal(sa) t = op.type t2 = t & 0xFFFF t3 = t & 0xFFFF0000 tags[sa]['len'] = op.size tags[sa]['semantics'] = [] tags[sa]['flow'] = [] tags[sa]['scope'] = ghex(f.addr) tags[sa]['flags'] = 0x10000 if will_pass else 0 will_pass = True if t2 == 1 or t2 == 2 or t2 == 5 or (sa + op.size) in starts: tags[sa]['semantics'].append("endbb") if t == 1 or t == 2: # jmp tags[sa]['flow'].append(ghex(op.jump)) will_pass = False elif t3 == 0x80000000 and (t2 == 1 or t2 == 2): # cond jmp tags[sa]['flow'].append(ghex(op.jump)) tags[sa]['instruction'] = instr print " ", ghex(sa), op.type & 0xFFFF, instr if op.size <= 0: break else: sa += op.size #bbs = f.get_bbs() """ for b in f.get_bbs(): print " ", ghex(b.addr), b.size """ # fix ctl-c import signal signal.signal(signal.SIGINT, signal.SIG_DFL) return tags