def test_badbytes(self): r = Ropper() badbytes = "adfd" gadgets = r.searchGadgets(self.file) gadgets = ropper.filterBadBytes(gadgets, badbytes) gadget = gadgets[0] self.assertNotEqual(gadget.lines[0][0], 0x1ADFD) badbytes = "52f8" gadgets = r.searchPopPopRet(self.file) gadgets = ropper.filterBadBytes(gadgets, badbytes) self.assertNotEqual(gadgets[0].lines[0][0], 0x52F8) badbytes = "b1c7" gadgets = r.searchJmpReg(self.file, ["rsp"]) gadgets = ropper.filterBadBytes(gadgets, badbytes) gadget = gadgets[0] self.assertNotEqual(gadget.lines[0][0], 0xB1C7) with self.assertRaises(RopperError): badbytes = "b1c" gadgets = ropper.filterBadBytes(gadgets, badbytes) with self.assertRaises(RopperError): badbytes = "qwer" gadgets = ropper.filterBadBytes(gadgets, badbytes)
def test_badbytes(self): r = Ropper() badbytes = 'adfd' gadgets = r.searchGadgets(self.file) gadgets = ropper.filterBadBytes(gadgets, badbytes) gadget = gadgets[0] self.assertNotEqual(gadget.lines[0][0], 0x1adfd) badbytes = '52f8' gadgets = r.searchPopPopRet(self.file) gadgets = ropper.filterBadBytes(gadgets, badbytes) self.assertNotEqual(gadgets[0].lines[0][0], 0x52f8) badbytes = 'b1c7' gadgets = r.searchJmpReg(self.file, ['rsp']) gadgets = ropper.filterBadBytes(gadgets, badbytes) gadget = gadgets[0] self.assertNotEqual(gadget.lines[0][0], 0xb1c7) with self.assertRaises(RopperError): badbytes = 'b1c' gadgets = ropper.filterBadBytes(gadgets, badbytes) with self.assertRaises(RopperError): badbytes = 'qwer' gadgets = ropper.filterBadBytes(gadgets, badbytes)
def __searchGadgets(self, binary): r = Ropper(self.__cprinter) gadgets = r.searchGadgets(binary, depth=self.__options.depth, gtype=GadgetType[self.__options.type.upper()]) binary.loaded = True binary.gadgets = gadgets self.__gadgets[binary] = ropper.filterBadBytes(gadgets, self.__options.badbytes) if not self.__options.all: self.__gadgets[binary] = ropper.deleteDuplicates(self.__gadgets[binary]) return self.__gadgets[binary]
def __searchGadgets(self, binary): r = Ropper(self.__searchGadgetCallback) gadgets = r.searchGadgets(binary, instructionCount=self.__options.inst_count, gtype=GadgetType[self.__options.type.upper()]) binary.loaded = True binary.gadgets = gadgets self.__gadgets[binary] = ropper.filterBadBytes(gadgets, self.__options.badbytes) if not self.__options.all: self.__cprinter.printInfo('deleting double gadgets...') self.__gadgets[binary] = ropper.deleteDuplicates(self.__gadgets[binary], self.__printProgress) return self.__gadgets[binary]
def __searchGadgets(self, binary): r = Ropper(self.__cprinter) gadgets = r.searchGadgets(binary, instructionCount=self.__options.inst_count, gtype=GadgetType[self.__options.type.upper()]) binary.loaded = True binary.gadgets = gadgets self.__gadgets[binary] = ropper.filterBadBytes(gadgets, self.__options.badbytes) if not self.__options.all: self.__cprinter.printInfo('deleting double gadgets...') self.__gadgets[binary] = ropper.deleteDuplicates(self.__gadgets[binary], self.__printProgress) return self.__gadgets[binary]
def test_gadgets_pe(self): ropper = Ropper() gadgets = ropper.searchGadgets(self.file) gadget = gadgets[0] self.assertGreater(len(gadgets), 4800) self.assertEqual(gadget.lines[0][0] + self.file.imageBase, gadget.address) self.assertEqual(gadget.imageBase, 0x4ad00000) self.file.imageBase = 0x0 self.assertEqual(gadget.imageBase, 0x0) self.file.imageBase = None self.assertEqual(gadget.imageBase, 0x4ad00000)
def test_search(self): r = Ropper() gadgets = r.searchGadgets(self.file) found_gadgets = self.file.arch.searcher.search(gadgets, "mov [rax]") self.assertEqual(len(found_gadgets), 1) found_gadgets = self.file.arch.searcher.search(gadgets, "mov [r?x]") self.assertEqual(len(found_gadgets), 5) found_gadgets = self.file.arch.searcher.search(gadgets, "mov [r?x%]") self.assertEqual(len(found_gadgets), 7)
def test_search(self): r = Ropper() gadgets = r.searchGadgets(self.file) found_gadgets = self.file.arch.searcher.search(gadgets, 'mov [rax]') self.assertGreater(len(found_gadgets), 5) found_gadgets = self.file.arch.searcher.search(gadgets, 'mov [r?x]') self.assertGreater(len(found_gadgets), 25) found_gadgets = self.file.arch.searcher.search(gadgets, 'mov [r?x%]') self.assertGreater(len(found_gadgets), 70)
def test_search(self): r = Ropper() gadgets = r.searchGadgets(self.file) found_gadgets = self.file.arch.searcher.search(gadgets, 'mov [rax]') self.assertEqual(len(found_gadgets), 8) found_gadgets = self.file.arch.searcher.search(gadgets, 'mov [r?x]') self.assertEqual(len(found_gadgets), 31) found_gadgets = self.file.arch.searcher.search(gadgets, 'mov [r?x%]') self.assertEqual(len(found_gadgets), 83)
def test_gadgets_pe(self): ropper = Ropper() gadgets = ropper.searchGadgets(self.file) gadget = gadgets[0] self.assertGreater(len(gadgets), 1300) self.assertEqual(gadget.lines[0][0] + self.file.imageBase, gadget.address) self.assertEqual(gadget.imageBase, 0x00008000) self.file.imageBase = 0x0 Gadget.IMAGE_BASES['test-binaries/ls-arm'] = self.file.imageBase self.assertEqual(gadget.imageBase, 0x0) self.file.imageBase = None Gadget.IMAGE_BASES[self.file.fileName] = self.file.imageBase self.assertEqual(gadget.imageBase, 0x00008000)
def test_gadgets_pe(self): ropper = Ropper() gadgets = ropper.searchGadgets(self.file) gadget = gadgets[0] self.assertGreater(len(gadgets), 1900) self.assertEqual(gadget.lines[0][0] + self.file.imageBase, gadget.address) self.assertEqual(gadget.imageBase, 0x00400000) self.file.imageBase = 0x0 Gadget.IMAGE_BASES[self.file.checksum] = self.file.imageBase self.assertEqual(gadget.imageBase, 0x0) self.file.imageBase = None Gadget.IMAGE_BASES[self.file.checksum] = self.file.imageBase self.assertEqual(gadget.imageBase, 0x00400000)
def test_gadgets_pe(self): ropper = Ropper() gadgets = ropper.searchGadgets(self.file) gadget = gadgets[0] self.assertGreater(len(gadgets), 1300) self.assertEqual(gadget.lines[0][0] + self.file.imageBase, gadget.address) self.assertEqual(gadget.imageBase, 0x00008000) self.file.imageBase = 0x0 Gadget.IMAGE_BASES[self.file.checksum] = self.file.imageBase self.assertEqual(gadget.imageBase, 0x0) self.file.imageBase = None Gadget.IMAGE_BASES[self.file.checksum] = self.file.imageBase self.assertEqual(gadget.imageBase, 0x00008000)
def __searchGadgets(self, binary): r = Ropper(self.__cprinter) gadgets = r.searchGadgets( binary, depth=self.__options.depth, gtype=GadgetType[self.__options.type.upper()]) binary.loaded = True binary.gadgets = gadgets self.__gadgets[binary] = ropper.filterBadBytes(gadgets, self.__options.badbytes) if not self.__options.all: self.__gadgets[binary] = ropper.deleteDuplicates( self.__gadgets[binary]) return self.__gadgets[binary]
def test_gadgets(self): self.assertEqual(self.file.arch, PPC) self.assertEqual(self.file.type, Type.ELF) ropper = Ropper() gadgets = ropper.searchGadgets(self.file) gadget = gadgets[0] self.assertGreater(len(gadgets), 1400) self.assertEqual(gadget.lines[0][0] + self.file.imageBase, gadget.address) self.assertEqual(gadget.imageBase, 0x10000000) self.file.imageBase = 0x0 Gadget.IMAGE_BASES[self.file.checksum] = self.file.imageBase self.assertEqual(gadget.imageBase, 0x0) self.file.imageBase = None Gadget.IMAGE_BASES[self.file.checksum] = self.file.imageBase self.assertEqual(gadget.imageBase, 0x10000000)
def test_gadgets(self): self.assertEqual(self.file.arch, PPC) self.assertEqual(self.file.type, Type.ELF) ropper = Ropper() gadgets = ropper.searchGadgets(self.file) gadget = gadgets[0] self.assertGreater(len(gadgets), 1400) self.assertEqual(gadget.lines[0][0] + self.file.imageBase, gadget.address) self.assertEqual(gadget.imageBase, 0x10000000) self.file.imageBase = 0x0 Gadget.IMAGE_BASES[self.file.fileName] = self.file.imageBase self.assertEqual(gadget.imageBase, 0x0) self.file.imageBase = None Gadget.IMAGE_BASES[self.file.fileName] = self.file.imageBase self.assertEqual(gadget.imageBase, 0x10000000)
def test_database(self): r = Ropper() db = './testdb.db' if os.path.exists(db): os.remove(db) dao = GadgetDAO(db) gadgets = r.searchGadgets(self.file) dao.save(gadgets) self.assertTrue(os.path.exists(db)) loaded_gadgets = dao.load(self.file) self.assertEqual(len(gadgets), len(loaded_gadgets)) self.assertEqual(gadgets[0].lines[0][0], loaded_gadgets[0].lines[0][0]) os.remove(db)
def test_database(self): r = Ropper() db = "./testdb.db" if os.path.exists(db): os.remove(db) dao = GadgetDAO(db) gadgets = r.searchGadgets(self.file) dao.save(gadgets) self.assertTrue(os.path.exists(db)) loaded_gadgets = dao.load(self.file) self.assertEqual(len(gadgets), len(loaded_gadgets)) self.assertEqual(gadgets[0].lines[0][0], loaded_gadgets[0].lines[0][0]) os.remove(db)
def __searchGadgets(self, binary): r = Ropper(self.__searchGadgetCallback) gadgets = r.searchGadgets(binary, instructionCount=self.__options.inst_count, gtype=GadgetType[self.__options.type.upper()]) binary.loaded = True if self.__options.cfg_only: if isinstance(binary, PE): optHeader = binary._binary.imageNtHeaders.header.OptionalHeader characteristics = optHeader.DllCharacteristics cfgFlag = ImageDllCharacteristics.CONTROL_FLOW_GUARD if characteristics & cfgFlag == cfgFlag: # do some filtering here self.__cprinter.printInfo('deleting CFG invalid gadgets...') gadgets = ropper.cfgFilterGadgets(gadgets, callback=self.__printCfgFilterProgress) binary.gadgets = gadgets self.__gadgets[binary] = ropper.filterBadBytes(gadgets, self.__options.badbytes) if not self.__options.all: self.__cprinter.printInfo('deleting double gadgets...') self.__gadgets[binary] = ropper.deleteDuplicates(self.__gadgets[binary], self.__printProgress) return self.__gadgets[binary]
class RopperService(object): ROPPER_FOLDER = os.path.expanduser('~') + os.path.sep + ".ropper/" CACHE_FOLDER = os.path.expanduser('~') + os.path.sep + ".ropper/cache/" CACHE_FILE_COUNT = 16 def __init__(self, options={}, callbacks=None): super(RopperService, self).__init__() self.__options = Options(options, self.__optionChanged) if callbacks and hasattr(callbacks, '__gadgetSearchProgress__'): self.__ropper = Ropper(callback=callbacks.__gadgetSearchProgress__) else: self.__ropper = Ropper() self.__files = [] self.__callbacks = callbacks if self.__options.color: cstr.COLOR = self.__options.color Gadget.DETAILED = self.__options.detailed @property def ropper(self): return self.__ropper @property def options(self): return self.__options @property def files(self): return list(self.__files) def __optionChanged(self, option, oldvalue, newvalue): if hasattr(self, '_%s_changed' % option): func = getattr(self, '_%s_changed' % option) func(newvalue) def __prepareGadgets(self, file, gadgets, type=None): gadgets = self.__filterBadBytes(gadgets) gadgets = self.__filterCfg(file, gadgets, type) if not self.__options.all: callback = None if self.__callbacks and hasattr(self.__callbacks, '__deleteDoubleGadgetsProgress__'): callback = self.__callbacks.__deleteDoubleGadgetsProgress__ gadgets = deleteDuplicates(gadgets, callback) return gadgets def __filterBadBytes(self, gadgets): if self.__options.badbytes: callback = None if self.__callbacks and hasattr(self.__callbacks, '__filterBadBytesGadgetsProgress__'): callback = self.__callbacks.__filterBadBytesGadgetsProgress__ gadgets = filterBadBytes(gadgets, self.options.badbytes, callback) return gadgets def __filterCfg(self, file, gadgets, type): if self.__options.cfg_only and type==Type.PE: callback = None if self.__callbacks and hasattr(self.__callbacks, '__filterCfgGadgetsProgress__'): callback = self.__callbacks.__filterCfgGadgetsProgress__ gadgets = cfgFilterGadgets(file.loader, gadgets, callback) return gadgets def __getCacheFileName(self, file): return "%s_%s_%d_%s_%d" % (file.loader.checksum, str(file.arch), self.options.inst_count,str(self.options.type), sys.version_info.major) def __saveCache(self, file): cache_file = None try: temp = RopperService.CACHE_FOLDER if not os.path.exists(temp): os.makedirs(temp) cache_file = temp + os.path.sep + self.__getCacheFileName(file) count = RopperService.CACHE_FILE_COUNT if not isWindows() and len(file.allGadgets) > 1000: if os.path.exists(cache_file): os.remove(cache_file) length = len(file.allGadgets) step = int(length / count) for i in range(count-1): gadgets = file.allGadgets[i*step: (i+1)*step] with open(cache_file+'_%d' % (i+1),'wb') as f: f.write(encode(repr(gadgets).encode('ascii'),'zip')) gadgets = file.allGadgets[(count-1)*step:] with open(cache_file+'_%d' % (count),'wb') as f: f.write(encode(repr(gadgets).encode('ascii'),'zip')) return with open(cache_file,'wb') as f: f.write(encode(repr(file.allGadgets).encode('ascii'),'zip')) except BaseException as e: print(e) if cache_file: for i in range(1, RopperService.CACHE_FILE_COUNT+1): if os.path.exists(cache_file+'_%d' % i): os.remove(cache_file+'_%d' % i) def __loadCachePerProcess(self, fqueue, gqueue): nan=0 while True: cacheFileName = fqueue.get() if cacheFileName is None: fqueue.task_done() break if os.path.exists(cacheFileName): with open(cacheFileName,'rb') as f: data = f.read() gqueue.put(eval(decode(data,'zip'))) else: gqueue.put([]) fqueue.task_done() def __loadCache(self, file): mp = False nan= 0 processes = [] single = False cache_file = None try: temp = RopperService.CACHE_FOLDER cache_file = temp + os.path.sep + self.__getCacheFileName(file) if not os.path.exists(cache_file): if not os.path.exists(cache_file+'_%d' % 1): return else: if isWindows(): raise RopperError('Cache has to be cleared.') mp = True and multiprocessing.cpu_count()>1 else: single = True if self.__callbacks and hasattr(self.__callbacks, '__message__'): self.__callbacks.__message__('Load gadgets from cache') if self.__callbacks and hasattr(self.__callbacks, '__gadgetSearchProgress__'): self.__callbacks.__gadgetSearchProgress__(None, [], 0) if not mp: all_gadgets = [] if single: with open(cache_file,'rb') as f: data = f.read() all_gadgets.extend(eval(decode(data,'zip'))) if self.__callbacks and hasattr(self.__callbacks, '__gadgetSearchProgress__'): self.__callbacks.__gadgetSearchProgress__(None, all_gadgets, 1.0) else: for i in range(1,RopperService.CACHE_FILE_COUNT+1): if os.path.exists(cache_file+'_%d' % i): with open(cache_file+'_%d' % i,'rb') as f: data = f.read() all_gadgets.extend(eval(decode(data,'zip'))) if self.__callbacks and hasattr(self.__callbacks, '__gadgetSearchProgress__'): self.__callbacks.__gadgetSearchProgress__(None, all_gadgets, float(i)/RopperService.CACHE_FILE_COUNT) return all_gadgets else: count = min(multiprocessing.cpu_count(),RopperService.CACHE_FILE_COUNT) gqueue = multiprocessing.Queue() fqueue = multiprocessing.JoinableQueue() for i in range(1,RopperService.CACHE_FILE_COUNT+1): fqueue.put(cache_file+'_%d' % i) all_gadgets = [] for i in range(count): p=multiprocessing.Process(target=self.__loadCachePerProcess, args=(fqueue, gqueue)) p.start() processes.append(p) for i in range(count): fqueue.put(None) for i in range(RopperService.CACHE_FILE_COUNT): gadgets = gqueue.get() all_gadgets.extend(gadgets) if self.__callbacks and hasattr(self.__callbacks, '__gadgetSearchProgress__'): self.__callbacks.__gadgetSearchProgress__(None, all_gadgets, float(i+1)/RopperService.CACHE_FILE_COUNT) return sorted(all_gadgets, key=Gadget.simpleInstructionString) except KeyboardInterrupt: if mp: for p in processes: if p and p.is_alive(): p.terminate() except BaseException as e: if mp: for p in processes: if p and p.is_alive(): p.terminate() if cache_file: for i in range(1,RopperService.CACHE_FILE_COUNT+1): if os.path.exists(cache_file+'_%d' % i): os.remove(cache_file+'_%d' % i) def _badbytes_changed(self, value): for f in self.__files: if f.loaded: f.gadgets = self.__prepareGadgets(f, f.allGadgets, f.type) def _all_changed(self, value): for f in self.__files: if f.loaded: f.gadgets = self.__prepareGadgets(f, f.allGadgets, f.type) def _color_changed(self, value): cstr.COLOR = value def _detailed_changed(self, value): Gadget.DETAILED = value def _cfg_only_changed(self, value): for f in self.__files: if f.loaded and f.type == Type.PE: f.gadgets = self.__prepareGadgets(f, f.allGadgets, f.type) def _type_changed(self, value): for f in self.__files: if f.loaded: self.loadGadgetsFor(f.name) def _inst_count_changed(self, value): for f in self.__files: if f.loaded: self.loadGadgetsFor(f.name) def _getFileFor(self, name): for file in self.__files: if file.loader.fileName == name: return file return None def clearCache(self): temp = RopperService.CACHE_FOLDER if os.path.exists(temp): import shutil shutil.rmtree(temp) def getFileFor(self, name): return self._getFileFor(name) def addFile(self, name, bytes=None, arch=None, raw=False): if self._getFileFor(name): raise RopperError('file is already added: %s' % name) if arch: arch=getArchitecture(arch) loader = Loader.open(name, bytes=bytes, raw=raw, arch=arch) file = FileContainer(loader) self.__files.append(file) def removeFile(self, name): for idx, fc in enumerate(self.__files): if fc.loader.fileName == name: del self.__files[idx] def asm(self, code, arch='x86', format='hex'): if format not in ('hex', 'string', 'raw'): raise RopperError('Invalid format: %s\n Valid formats are: hex, string, raw' % format) format = Format.HEX if format=='hex' else Format.STRING if format=='string' else Format.RAW return self.ropper.assemble(code, arch=getArchitecture(arch), format=format) def disasm(self, opcode, arch='x86'): return self.ropper.disassemble(opcode, arch=getArchitecture(arch)) def searchPopPopRet(self, name=None): to_return = {} if not name: for file in self.__files: to_return[file.loader.fileName] = self.__ropper.searchPopPopRet(file.loader) else: fc = self._getFileFor(name) if not fc: raise RopperError('No such file opened: %s' % name) to_return[name] = self.__ropper.searchPopPopRet(fc.loader) return self.__filterBadBytes(to_return) def searchJmpReg(self, regs=['esp'],name=None): to_return = {} if not name: for file in self.__files: to_return[file.loader.fileName] = self.__ropper.searchJmpReg(file.loader, regs) else: fc = self._getFileFor(name) if not fc: raise RopperError('No such file opened: %s' % name) to_return[name] = self.__ropper.searchJmpReg(fc.loader, regs) return self.__filterBadBytes(to_return) def searchOpcode(self, opcode, name=None): to_return = {} if not name: for file in self.__files: to_return[file.loader.fileName] = self.__ropper.searchOpcode(file.loader, opcode) else: fc = self.getFileFor(name) if not fc: raise RopperError('No such file opened: %s' % name) to_return[name] = self.__ropper.searchOpcode(fc.loader, opcode) return self.__filterBadBytes(to_return) def searchInstructions(self, code, name=None): to_return = {} if not name: for file in self.__files: to_return[file.loader.fileName] = self.__ropper.searchInstructions(file.loader, code) else: fc = self.getFileFor(name) if not fc: raise RopperError('No such file opened: %s' % name) to_return[name] = self.__ropper.searchInstructions(fc.loader, code) return self.__filterBadBytes(to_return) def analyseGadgets(self, fileObject): gadgets = fileObject.gadgets analyser = Analyser() cb = None lg = len(gadgets) if self.__callbacks and hasattr(self.__callbacks, '__analyseGadgetsProgress__'): cb = self.__callbacks.__analyseGadgetsProgress__ for i,g in enumerate(gadgets): g.info = analyser.analyse(g) if cb: cb(g, float(i)/lg) if cb: cb(None, 1.0) self.__saveCache(fileObject) fileObject.analysed = True def loadGadgetsFor(self, name=None): def load_gadgets(f): gtype = None cache = False Gadget.IMAGE_BASES[f.loader.checksum] = f.loader.imageBase if self.options.type == 'rop': gtype = GadgetType.ROP elif self.options.type == 'jop': gtype = GadgetType.JOP elif self.options.type == 'sys': gtype = GadgetType.SYS elif self.options.type == 'all': gtype = GadgetType.ALL f.allGadgets = self.__loadCache(f) if f.allGadgets == None: cache = True f.allGadgets = self.__ropper.searchGadgets(f.loader, instructionCount=self.options.inst_count, gtype=gtype) if cache: self.__saveCache(f) f.gadgets = self.__prepareGadgets(f, f.allGadgets, f.type) f.analysed = f.gadgets[0].info is not None if len(f.gadgets) > 0 else False #self._analyseGadgets(f.gadgets) if name is None: for fc in self.__files: load_gadgets(fc) else: for fc in self.__files: if fc.loader.fileName == name: load_gadgets(fc) def printGadgetsFor(self, name=None): def print_gadgets(f): print(f.loader.fileName) for g in f.gadgets: if self.options.detailed: print(g) else: print(g.simpleString()) if name is None: for f in self.__files: print_gadgets(f) else: for f in self.__files: if f.loader.fileName == name: print_gadgets(f) def searchString(self, string='', name=None): def search(f, string): data = [] if not string or string == '[ -~]{2}[ -~]*': string = '[ -~]{2}[ -~]*' else: string = f.arch.searcher.prepareFilter(string) sections = list(f.dataSections) string = string.encode('ascii') # python 3 compatibility for section in sections: b = bytes(bytearray(section.bytes)) for match in re.finditer(string, b): vaddr = f.imageBase + section.offset if f.imageBase != None else section.virtualAddress data.append( (match.start() + vaddr , match.group())) return data to_return = {} if not name: for file in self.__files: to_return[file.loader.fileName] = search(file.loader, string) else: fc = self._getFileFor(name) if not fc: raise RopperError('No such file opened: %s' % name) to_return[name] = search(fc.loader, string) return to_return def search(self, search, quality=None, name=None): if name: fc = self._getFileFor(name) if not fc: raise RopperError('No such file opened: %s' % name) s = fc.loader.arch.searcher for gadget in s.search(fc.gadgets, search, quality): yield(fc.name, gadget) else: for fc in self.__files: s = fc.loader.arch.searcher for gadget in s.search(fc.gadgets, search, quality): yield(fc.name, gadget) def semanticSearch(self, search, stableRegs=[], name=None): count = 0 if name: fc = self._getFileFor(name) if not fc: raise RopperError('No such file opened: %s' % name) s = fc.loader.arch.searcher for gadget in s.semanticSearch(fc.gadgets, search, self.options.inst_count, stableRegs): if self.options.count_of_findings == 0 or self.options.count_of_findings > count: yield(fc.name, gadget) else: break count += 1 self.__saveCache(fc) else: for fc in self.__files: s = fc.loader.arch.searcher for gadget in s.semanticSearch(fc.gadgets, search, self.options.inst_count, stableRegs): if self.options.count_of_findings == 0 or self.options.count_of_findings > count: yield(fc.name, gadget) else: break count += 1 self.__saveCache(fc) def searchdict(self, search, quality=None, name=None): to_return = {} for file, gadget in self.search(search, quality, name): l = to_return.get(file) if not l: l = [] to_return[file] = l l.append(gadget) return to_return def disassAddress(self, name, address, length): fc = self.getFileFor(name) if not fc: raise RopperError('No such file opened: %s' % name) eSections = fc.loader.executableSections for section in eSections: if section.virtualAddress <= address and section.virtualAddress + section.size > address: ropper = Ropper() g = ropper.disassembleAddress(section, fc.loader, address, address - (fc.loader.imageBase+section.offset), length) if not g: raise RopperError('Cannot disassemble address: %s' % toHex(address)) if length < 0: length = length * -1 return g.disassemblyString() return '' def createRopChain(self, chain, arch, options={}): callback = None if self.__callbacks and hasattr(self.__callbacks, '__ropchainMessages__'): callback = self.__callbacks.__ropchainMessages__ b = [] gadgets = {} for binary in self.__files: if str(binary.arch) == arch: gadgets[binary.loader] = binary.gadgets b.append(binary.loader) generator = RopChain.get(b, gadgets, chain, callback, unhexlify(self.options.badbytes)) if not generator: raise RopperError('%s does not have support for %s chain generation at the moment. Its a future feature.' % (self.files[0].loader.arch.__class__.__name__, chain)) return generator.create(options) def setImageBaseFor(self, name, imagebase): file = self._getFileFor(name) if not file: raise RopperError('No such file opened: %s' % name) file.loader.imageBase = imagebase Gadget.IMAGE_BASES[file.loader.checksum] = file.loader.imageBase if file.loaded and (self.options.badbytes or self.options.cfg_only and file.type == Type.PE): file.gadgets = self.__prepareGadgets(file, file.allGadgets, file.type) def setArchitectureFor(self, name, arch): file = self.getFileFor(name) if not file: raise RopperError('No such file opened: %s' % name) file.loader.arch = getArchitecture(arch) if file.loaded: self.loadGadgetsFor(name) def _setGadgets(self, name, gadgets): fc = self.getFileFor(name) if not fc: raise RopperError('No such file opened: %s' % name) fc.allGadgets = gadgets fc.gadgets = self.__prepareGadgets(fc, fc.allGadgets, fc.type)
class RopperService(object): ROPPER_FOLDER = os.path.expanduser('~') + os.path.sep + ".ropper/" CACHE_FOLDER = os.path.expanduser('~') + os.path.sep + ".ropper/cache/" CACHE_FILE_COUNT = 16 def __init__(self, options={}, callbacks=None): super(RopperService, self).__init__() self.__options = Options(options, self.__optionChanged) if callbacks and hasattr(callbacks, '__gadgetSearchProgress__'): self.__ropper = Ropper(callback=callbacks.__gadgetSearchProgress__) else: self.__ropper = Ropper() self.__files = [] self.__callbacks = callbacks if self.__options.color: cstr.COLOR = self.__options.color Gadget.DETAILED = self.__options.detailed @property def ropper(self): return self.__ropper @property def options(self): return self.__options @property def files(self): return list(self.__files) def __optionChanged(self, option, oldvalue, newvalue): if hasattr(self, '_%s_changed' % option): func = getattr(self, '_%s_changed' % option) func(newvalue) def __prepareGadgets(self, file, gadgets, type=None): gadgets = self.__filterBadBytes(gadgets) gadgets = self.__filterCfg(file, gadgets, type) if not self.__options.all: callback = None if self.__callbacks and hasattr(self.__callbacks, '__deleteDoubleGadgetsProgress__'): callback = self.__callbacks.__deleteDoubleGadgetsProgress__ gadgets = deleteDuplicates(gadgets, callback) return gadgets def __filterBadBytes(self, gadgets): if self.__options.badbytes: callback = None if self.__callbacks and hasattr(self.__callbacks, '__filterBadBytesGadgetsProgress__'): callback = self.__callbacks.__filterBadBytesGadgetsProgress__ gadgets = filterBadBytes(gadgets, self.options.badbytes, callback) return gadgets def __filterCfg(self, file, gadgets, type): if self.__options.cfg_only and type==Type.PE: callback = None if self.__callbacks and hasattr(self.__callbacks, '__filterCfgGadgetsProgress__'): callback = self.__callbacks.__filterCfgGadgetsProgress__ gadgets = cfgFilterGadgets(file.loader, gadgets, callback) return gadgets def __getCacheFileName(self, file): return "%s_%s_%d_%s_%d" % (file.loader.checksum, str(file.arch), self.options.inst_count,str(self.options.type), sys.version_info.major) def __saveCache(self, file): cache_file = None try: temp = RopperService.CACHE_FOLDER if not os.path.exists(temp): os.makedirs(temp) cache_file = temp + os.path.sep + self.__getCacheFileName(file) count = RopperService.CACHE_FILE_COUNT if len(file.allGadgets) > 1000: if os.path.exists(cache_file): os.remove(cache_file) length = len(file.allGadgets) step = int(length / count) for i in range(count-1): gadgets = file.allGadgets[i*step: (i+1)*step] with open(cache_file+'_%d' % (i+1),'wb') as f: f.write(encode(repr(gadgets).encode('ascii'),'zip')) gadgets = file.allGadgets[(count-1)*step:] with open(cache_file+'_%d' % (count),'wb') as f: f.write(encode(repr(gadgets).encode('ascii'),'zip')) return with open(cache_file,'wb') as f: f.write(encode(repr(file.allGadgets).encode('ascii'),'zip')) except BaseException as e: print(e) if cache_file: for i in range(1, RopperService.CACHE_FILE_COUNT+1): if os.path.exists(cache_file+'_%d' % i): os.remove(cache_file+'_%d' % i) def __loadCachePerProcess(self, fqueue, gqueue): nan=0 while True: cacheFileName = fqueue.get() if cacheFileName is None: fqueue.task_done() break if os.path.exists(cacheFileName): with open(cacheFileName,'rb') as f: data = f.read() gqueue.put(eval(decode(data,'zip'))) else: gqueue.put([]) fqueue.task_done() def __loadCache(self, file): mp = False nan= 0 processes = [] single = False cache_file = None try: temp = RopperService.CACHE_FOLDER cache_file = temp + os.path.sep + self.__getCacheFileName(file) if not os.path.exists(cache_file): if not os.path.exists(cache_file+'_%d' % 1): return else: mp = True and multiprocessing.cpu_count()>1 else: single = True if self.__callbacks and hasattr(self.__callbacks, '__message__'): self.__callbacks.__message__('Load gadgets from cache') if self.__callbacks and hasattr(self.__callbacks, '__gadgetSearchProgress__'): self.__callbacks.__gadgetSearchProgress__(None, [], 0) if not mp: all_gadgets = [] if single: with open(cache_file,'rb') as f: data = f.read() all_gadgets.extend(eval(decode(data,'zip'))) if self.__callbacks and hasattr(self.__callbacks, '__gadgetSearchProgress__'): self.__callbacks.__gadgetSearchProgress__(None, all_gadgets, 1.0) else: for i in range(1,RopperService.CACHE_FILE_COUNT+1): if os.path.exists(cache_file+'_%d' % i): with open(cache_file+'_%d' % i,'rb') as f: data = f.read() all_gadgets.extend(eval(decode(data,'zip'))) if self.__callbacks and hasattr(self.__callbacks, '__gadgetSearchProgress__'): self.__callbacks.__gadgetSearchProgress__(None, all_gadgets, float(i)/RopperService.CACHE_FILE_COUNT) return all_gadgets else: count = min(multiprocessing.cpu_count(),RopperService.CACHE_FILE_COUNT) gqueue = multiprocessing.Queue() fqueue = multiprocessing.JoinableQueue() for i in range(1,RopperService.CACHE_FILE_COUNT+1): fqueue.put(cache_file+'_%d' % i) all_gadgets = [] for i in range(count): p=multiprocessing.Process(target=self.__loadCachePerProcess, args=(fqueue, gqueue)) p.start() processes.append(p) for i in range(count): fqueue.put(None) for i in range(RopperService.CACHE_FILE_COUNT): gadgets = gqueue.get() all_gadgets.extend(gadgets) if self.__callbacks and hasattr(self.__callbacks, '__gadgetSearchProgress__'): self.__callbacks.__gadgetSearchProgress__(None, all_gadgets, float(i+1)/RopperService.CACHE_FILE_COUNT) return sorted(all_gadgets, key=Gadget.simpleInstructionString) except KeyboardInterrupt: if mp: for p in processes: if p and p.is_alive(): p.terminate() except BaseException as e: if mp: for p in processes: if p and p.is_alive(): p.terminate() if cache_file: for i in range(1,RopperService.CACHE_FILE_COUNT+1): if os.path.exists(cache_file+'_%d' % i): os.remove(cache_file+'_%d' % i) def _badbytes_changed(self, value): for f in self.__files: if f.loaded: f.gadgets = self.__prepareGadgets(f, f.allGadgets, f.type) def _all_changed(self, value): for f in self.__files: if f.loaded: f.gadgets = self.__prepareGadgets(f, f.allGadgets, f.type) def _color_changed(self, value): cstr.COLOR = value def _detailed_changed(self, value): Gadget.DETAILED = value def _cfg_only_changed(self, value): for f in self.__files: if f.loaded and f.type == Type.PE: f.gadgets = self.__prepareGadgets(f, f.allGadgets, f.type) def _type_changed(self, value): for f in self.__files: if f.loaded: self.loadGadgetsFor(f.name) def _inst_count_changed(self, value): for f in self.__files: if f.loaded: self.loadGadgetsFor(f.name) def _getFileFor(self, name): for file in self.__files: if file.loader.fileName == name: return file return None def clearCache(self): temp = RopperService.CACHE_FOLDER if os.path.exists(temp): import shutil shutil.rmtree(temp) def getFileFor(self, name): return self._getFileFor(name) def addFile(self, name, bytes=None, arch=None, raw=False): if self._getFileFor(name): raise RopperError('file is already added: %s' % name) if arch: arch=getArchitecture(arch) loader = Loader.open(name, bytes=bytes, raw=raw, arch=arch) file = FileContainer(loader) self.__files.append(file) def removeFile(self, name): for idx, fc in enumerate(self.__files): if fc.loader.fileName == name: del self.__files[idx] def asm(self, code, arch='x86', format='hex'): if format not in ('hex', 'string', 'raw'): raise RopperError('Invalid format: %s\n Valid formats are: hex, string, raw' % format) format = Format.HEX if format=='hex' else Format.STRING if format=='string' else Format.RAW return self.ropper.assemble(code, arch=getArchitecture(arch), format=format) def disasm(self, opcode, arch='x86'): return self.ropper.disassemble(opcode, arch=getArchitecture(arch)) def searchPopPopRet(self, name=None): to_return = {} if not name: for file in self.__files: to_return[file.loader.fileName] = self.__ropper.searchPopPopRet(file.loader) else: fc = self._getFileFor(name) if not fc: raise RopperError('No such file opened: %s' % name) to_return[name] = self.__ropper.searchPopPopRet(fc.loader) return self.__filterBadBytes(to_return) def searchJmpReg(self, regs=['esp'],name=None): to_return = {} if not name: for file in self.__files: to_return[file.loader.fileName] = self.__ropper.searchJmpReg(file.loader, regs) else: fc = self._getFileFor(name) if not fc: raise RopperError('No such file opened: %s' % name) to_return[name] = self.__ropper.searchJmpReg(fc.loader, regs) return self.__filterBadBytes(to_return) def searchOpcode(self, opcode, name=None): to_return = {} if not name: for file in self.__files: to_return[file.loader.fileName] = self.__ropper.searchOpcode(file.loader, opcode) else: fc = self.getFileFor(name) if not fc: raise RopperError('No such file opened: %s' % name) to_return[name] = self.__ropper.searchOpcode(fc.loader, opcode) return self.__filterBadBytes(to_return) def searchInstructions(self, code, name=None): to_return = {} if not name: for file in self.__files: to_return[file.loader.fileName] = self.__ropper.searchInstructions(file.loader, code) else: fc = self.getFileFor(name) if not fc: raise RopperError('No such file opened: %s' % name) to_return[name] = self.__ropper.searchInstructions(fc.loader, code) return self.__filterBadBytes(to_return) def analyseGadgets(self, fileObject): gadgets = fileObject.gadgets analyser = Analyser() cb = None lg = len(gadgets) if self.__callbacks and hasattr(self.__callbacks, '__analyseGadgetsProgress__'): cb = self.__callbacks.__analyseGadgetsProgress__ for i,g in enumerate(gadgets): g.info = analyser.analyse(g) if cb: cb(g, float(i)/lg) if cb: cb(None, 1.0) self.__saveCache(fileObject) fileObject.analysed = True def loadGadgetsFor(self, name=None): def load_gadgets(f): gtype = None cache = False Gadget.IMAGE_BASES[f.loader.checksum] = f.loader.imageBase if self.options.type == 'rop': gtype = GadgetType.ROP elif self.options.type == 'jop': gtype = GadgetType.JOP elif self.options.type == 'sys': gtype = GadgetType.SYS elif self.options.type == 'all': gtype = GadgetType.ALL f.allGadgets = self.__loadCache(f) if f.allGadgets == None: cache = True f.allGadgets = self.__ropper.searchGadgets(f.loader, instructionCount=self.options.inst_count, gtype=gtype) if cache: self.__saveCache(f) f.gadgets = self.__prepareGadgets(f, f.allGadgets, f.type) f.analysed = f.gadgets[0].info is not None if len(f.gadgets) > 0 else False #self._analyseGadgets(f.gadgets) if name is None: for fc in self.__files: load_gadgets(fc) else: for fc in self.__files: if fc.loader.fileName == name: load_gadgets(fc) def printGadgetsFor(self, name=None): def print_gadgets(f): print(f.loader.fileName) for g in f.gadgets: if self.options.detailed: print(g) else: print(g.simpleString()) if name is None: for f in self.__files: print_gadgets(f) else: for f in self.__files: if f.loader.fileName == name: print_gadgets(f) def searchString(self, string='', name=None): def search(f, string): data = [] if not string or string == '[ -~]{2}[ -~]*': string = '[ -~]{2}[ -~]*' else: string = f.arch.searcher.prepareFilter(string) sections = list(f.dataSections) string = string.encode('ascii') # python 3 compatibility for section in sections: b = bytes(bytearray(section.bytes)) for match in re.finditer(string, b): vaddr = f.imageBase + section.offset if f.imageBase != None else section.virtualAddress data.append( (match.start() + vaddr , match.group())) return data to_return = {} if not name: for file in self.__files: to_return[file.loader.fileName] = search(file.loader, string) else: fc = self._getFileFor(name) if not fc: raise RopperError('No such file opened: %s' % name) to_return[name] = search(fc.loader, string) return to_return def search(self, search, quality=None, name=None): if name: fc = self._getFileFor(name) if not fc: raise RopperError('No such file opened: %s' % name) s = fc.loader.arch.searcher for gadget in s.search(fc.gadgets, search, quality): yield(fc.name, gadget) else: for fc in self.__files: s = fc.loader.arch.searcher for gadget in s.search(fc.gadgets, search, quality): yield(fc.name, gadget) def semanticSearch(self, search, stableRegs=[], name=None): count = 0 if name: fc = self._getFileFor(name) if not fc: raise RopperError('No such file opened: %s' % name) s = fc.loader.arch.searcher for gadget in s.semanticSearch(fc.gadgets, search, self.options.inst_count, stableRegs): if self.options.count_of_findings == 0 or self.options.count_of_findings > count: yield(fc.name, gadget) else: break count += 1 self.__saveCache(fc) else: for fc in self.__files: s = fc.loader.arch.searcher for gadget in s.semanticSearch(fc.gadgets, search, self.options.inst_count, stableRegs): if self.options.count_of_findings == 0 or self.options.count_of_findings > count: yield(fc.name, gadget) else: break count += 1 self.__saveCache(fc) def searchdict(self, search, quality=None, name=None): to_return = {} for file, gadget in self.search(search, quality, name): l = to_return.get(file) if not l: l = [] to_return[file] = l l.append(gadget) return to_return def disassAddress(self, name, address, length): fc = self.getFileFor(name) if not fc: raise RopperError('No such file opened: %s' % name) eSections = fc.loader.executableSections for section in eSections: if section.virtualAddress <= address and section.virtualAddress + section.size > address: ropper = Ropper() g = ropper.disassembleAddress(section, fc.loader, address, address - (fc.loader.imageBase+section.offset), length) if not g: raise RopperError('Cannot disassemble address: %s' % toHex(address)) if length < 0: length = length * -1 return g.disassemblyString() return '' def createRopChain(self, chain, arch, options={}): callback = None if self.__callbacks and hasattr(self.__callbacks, '__ropchainMessages__'): callback = self.__callbacks.__ropchainMessages__ b = [] gadgets = {} for binary in self.__files: if str(binary.arch) == arch: gadgets[binary.loader] = binary.gadgets b.append(binary.loader) generator = RopChain.get(b, gadgets, chain, callback, unhexlify(self.options.badbytes)) if not generator: raise RopperError('%s does not have support for %s chain generation at the moment. Its a future feature.' % (self.files[0].loader.arch.__class__.__name__, chain)) return generator.create(options) def setImageBaseFor(self, name, imagebase): file = self._getFileFor(name) if not file: raise RopperError('No such file opened: %s' % name) file.loader.imageBase = imagebase Gadget.IMAGE_BASES[file.loader.checksum] = file.loader.imageBase if file.loaded and (self.options.badbytes or self.options.cfg_only and file.type == Type.PE): file.gadgets = self.__prepareGadgets(file, file.allGadgets, file.type) def setArchitectureFor(self, name, arch): file = self.getFileFor(name) if not file: raise RopperError('No such file opened: %s' % name) file.loader.arch = getArchitecture(arch) if file.loaded: self.loadGadgetsFor(name) def _setGadgets(self, name, gadgets): fc = self.getFileFor(name) if not fc: raise RopperError('No such file opened: %s' % name) fc.allGadgets = gadgets fc.gadgets = self.__prepareGadgets(fc, fc.allGadgets, fc.type)