## -*- coding: utf-8 -*- ## ## Jonathan Salwan - 2014-05-17 - ROPgadget tool ## ## http://twitter.com/JonathanSalwan ## http://shell-storm.org/project/ROPgadget/ ## import binascii import cmd import re import string from capstone import CS_MODE_32 import ropgadget.rgutils as rgutils from ropgadget.binary import Binary from ropgadget.gadgets import Gadgets from ropgadget.options import Options from ropgadget.ropchain.ropmaker import ROPMaker class Core(cmd.Cmd): def __init__(self, options): cmd.Cmd.__init__(self) self.__options = options self.__binary = None self.__gadgets = [] self.__offset = 0 self.prompt = '(ROPgadget)> ' def __checksBeforeManipulations(self): if self.__binary is None or self.__binary.getBinary() is None or self.__binary.getArch() is None or self.__binary.getArchMode() is None or self.__binary.getEndian() is None: return False return True def _sectionInRange(self, section): """Given a section and a range, edit the section so that all opcodes are within the range""" if self.__options.range == "0x0-0x0": return section rangeStart, rangeEnd = map(lambda x: int(x, 16), self.__options.range.split('-')) sectionStart = section['vaddr'] sectionEnd = sectionStart + section['size'] opcodes = section['opcodes'] if rangeEnd < sectionStart or rangeStart > sectionEnd: return None if rangeStart > sectionStart: diff = rangeStart - sectionStart opcodes = opcodes[diff:] section['vaddr'] += diff section['offset'] += diff section['size'] -= diff if rangeEnd < sectionEnd: diff = sectionEnd - rangeEnd opcodes = opcodes[:-diff] section['size'] -= diff if not section['size']: return None section['opcodes'] = opcodes return section def __getGadgets(self): if not self.__checksBeforeManipulations(): return False G = Gadgets(self.__binary, self.__options, self.__offset) execSections = self.__binary.getExecSections() # Find ROP/JOP/SYS gadgets self.__gadgets = [] for section in execSections: section = self._sectionInRange(section) if not section: continue if not self.__options.norop: self.__gadgets += G.addROPGadgets(section) if not self.__options.nojop: self.__gadgets += G.addJOPGadgets(section) if not self.__options.nosys: self.__gadgets += G.addSYSGadgets(section) # Delete duplicate gadgets if not self.__options.all and not self.__options.noinstr: self.__gadgets = rgutils.deleteDuplicateGadgets(self.__gadgets) # Applicate some Options self.__gadgets = Options(self.__options, self.__binary, self.__gadgets).getGadgets() # Sorted alphabetically if not self.__options.noinstr: self.__gadgets = rgutils.alphaSortgadgets(self.__gadgets) return True def __lookingForGadgets(self): if not self.__checksBeforeManipulations(): return False if self.__options.silent: return True arch = self.__binary.getArchMode() print("Gadgets information\n============================================================") for gadget in self.__gadgets: vaddr = gadget["vaddr"] insts = gadget.get("gadget", "") insts = " : {}".format(insts) if insts else "" bytesStr = " // " + binascii.hexlify(gadget["bytes"]).decode('utf8') if self.__options.dump else "" print("0x{{0:0{}x}}{{1}}{{2}}".format(8 if arch == CS_MODE_32 else 16).format(vaddr, insts, bytesStr)) print("\nUnique gadgets found: %d" % (len(self.__gadgets))) return True def __lookingForMIPSgadgets(self, mips_option): if not self.__checksBeforeManipulations(): return False if self.__options.silent: return True arch = self.__binary.getArchMode() if mips_option == 'stackfinder': mipsFindRegex = [r'addiu .*, \$sp'] elif mips_option == 'system': mipsFindRegex = [r'addiu \$a0, \$sp'] elif mips_option == 'tails': mipsFindRegex = [r'lw \$t[0-9], 0x[0-9a-z]{0,4}\(\$s[0-9]', r'move \$t9, \$(s|a|v)'] elif mips_option == 'lia0': mipsFindRegex = [r'li \$a0'] elif mips_option == 'registers': mipsFindRegex = [r'lw \$ra, 0x[0-9a-z]{0,4}\(\$sp'] else: print("Unrecognized option " + mips_option) print("Accepted options stackfinder|system|tails|lia0|registers") return False print("MIPS ROP (" + mips_option + ")\n============================================================") self.__getGadgets() gadget_counter = 0 for gadget in self.__gadgets: vaddr = gadget["vaddr"] insts = gadget.get("gadget", "") insts = " : {}".format(insts) if insts else "" bytesStr = " // " + binascii.hexlify(gadget["bytes"]).decode('utf8') if self.__options.dump else "" for thisRegex in mipsFindRegex: toFind = re.findall(thisRegex, insts) if toFind: print("0x{{0:0{}x}}{{1}}{{2}}".format(8 if arch == CS_MODE_32 else 16).format(vaddr, insts, bytesStr)) gadget_counter += 1 print("\nUnique gadgets found: %d" % gadget_counter) return True def __lookingForAString(self, s): if not self.__checksBeforeManipulations(): return False if self.__options.silent: return True dataSections = self.__binary.getDataSections() arch = self.__binary.getArchMode() print("Strings information\n============================================================") for section in dataSections: section = self._sectionInRange(section) if not section: continue allRef = [m.start() for m in re.finditer(s.encode(), section["opcodes"])] for ref in allRef: vaddr = self.__offset + section["vaddr"] + ref match = section["opcodes"][ref:ref + len(s)] printable = string.printable.encode() match = "".join((chr(m) if isinstance(m, int) else m) if m in printable else "." for m in match) print("0x{{0:0{}x}} : {{1}}".format(8 if arch == CS_MODE_32 else 16).format(vaddr, match)) return True def __lookingForOpcodes(self, opcodes): if not self.__checksBeforeManipulations(): return False if self.__options.silent: return True execSections = self.__binary.getExecSections() arch = self.__binary.getArchMode() print("Opcodes information\n============================================================") for section in execSections: section = self._sectionInRange(section) if not section: continue allRef = [m.start() for m in re.finditer(re.escape(binascii.unhexlify(opcodes)), section["opcodes"])] for ref in allRef: vaddr = self.__offset + section["vaddr"] + ref print("0x{{0:0{}x}} : {{1}}".format(8 if arch == CS_MODE_32 else 16).format(vaddr, opcodes)) return True def __lookingForMemStr(self, memstr): if not self.__checksBeforeManipulations(): return False if self.__options.silent: return True sections = self.__binary.getExecSections() sections += self.__binary.getDataSections() arch = self.__binary.getArchMode() print("Memory bytes information\n=======================================================") chars = list(memstr) for char in chars: try: for section in sections: section = self._sectionInRange(section) if not section: continue allRef = [m.start() for m in re.finditer(char.encode('utf-8'), section["opcodes"])] for ref in allRef: vaddr = self.__offset + section["vaddr"] + ref print("0x{{0:0{}x}} : '{{1}}'".format(8 if arch == CS_MODE_32 else 16).format(vaddr, char)) raise except: pass return True def analyze(self): try: self.__offset = int(self.__options.offset, 16) if self.__options.offset else 0 except ValueError: print("[Error] The offset must be in hexadecimal") return False if self.__options.console: if self.__options.binary: self.__binary = Binary(self.__options) if not self.__checksBeforeManipulations(): return False self.cmdloop() return True self.__binary = Binary(self.__options) if not self.__checksBeforeManipulations(): return False if self.__options.string: return self.__lookingForAString(self.__options.string) elif self.__options.opcode: return self.__lookingForOpcodes(self.__options.opcode) elif self.__options.memstr: return self.__lookingForMemStr(self.__options.memstr) elif self.__options.mipsrop: return self.__lookingForMIPSgadgets(self.__options.mipsrop) else: self.__getGadgets() self.__lookingForGadgets() if self.__options.ropchain: ROPMaker(self.__binary, self.__gadgets, self.__offset) return True def gadgets(self): return self.__gadgets # Console methods ============================================ def do_binary(self, s, silent=False): # Do not split the filename with spaces since it might contain # whitespaces if not s: if not silent: return self.help_binary() return False binary = s self.__options.binary = binary self.__binary = Binary(self.__options) if not self.__checksBeforeManipulations(): return False if not silent: print("[+] Binary loaded") def help_binary(self): print("Syntax: binary -- Load a binary") return False def do_EOF(self, s, silent=False): return self.do_quit(s, silent) def do_quit(self, s, silent=False): return True def help_quit(self): print("Syntax: quit -- Terminates the application") return False def do_load(self, s, silent=False): if self.__binary is None: if not silent: print("[-] No binary loaded.") return False if not silent: print("[+] Loading gadgets, please wait...") self.__getGadgets() if not silent: print("[+] Gadgets loaded !") def help_load(self): print("Syntax: load -- Load all gadgets") return False def do_display(self, s, silent=False): self.__lookingForGadgets() def help_display(self): print("Syntax: display -- Display all gadgets loaded") return False def do_depth(self, s, silent=False): try: depth = int(s.split()[0]) except: if not silent: return self.help_depth() return False if depth <= 0: if not silent: print("[-] The depth value must be > 0") return False self.__options.depth = int(depth) if not silent: print("[+] Depth updated. You have to reload gadgets") def help_depth(self): print("Syntax: depth -- Set the depth search engine") return False def do_badbytes(self, s, silent=False): try: bb = s.split()[0] except: if not silent: return self.help_badbytes() else: return False self.__options.badbytes = bb if not silent: print("[+] Bad bytes updated. You have to reload gadgets") def help_badbytes(self): print("Syntax: badbytes -- ") return False def __withK(self, listK, gadget): if not listK: return True for a in listK: if a not in gadget: return False return True def __withoutK(self, listK, gadget): for a in listK: if a in gadget: return False return True def do_search(self, s, silent=False): args = s.split() if not len(args): return self.help_search() withK, withoutK = [], [] for a in args: if a[0] == "!": withoutK += [a[1:]] else: withK += [a] if not self.__checksBeforeManipulations(): if not silent: print("[-] You have to load a binary") return False arch = self.__binary.getArchMode() for gadget in self.__gadgets: vaddr = gadget["vaddr"] insts = gadget["gadget"] if self.__withK(withK, insts) and self.__withoutK(withoutK, insts): # What to do if silent = True? print("0x{{0:0{}x}} : {{1}}".format(8 if arch == CS_MODE_32 else 16).format(vaddr, insts)) def help_search(self): print("Syntax: search -- Filter with or without keywords") print("keyword = with") print("!keyword = without") return False def count(self): return len(self.__gadgets) def do_count(self, s, silent=False): if not silent: print("[+] %d loaded gadgets." % self.count()) def help_count(self): print("Shows the number of loaded gadgets.") return False def do_filter(self, s, silent=False): try: self.__options.filter = s.split()[0] except: if not silent: return self.help_filter() return False if not silent: print("[+] Filter setted. You have to reload gadgets") def help_filter(self): print("Syntax: filter - Suppress specific mnemonics") return False def do_only(self, s, silent=False): try: if s.lower() == "none": self.__options.only = None else: self.__options.only = s.split()[0] except: if not silent: return self.help_only() return False if not silent: print("[+] Only setted. You have to reload gadgets") def help_only(self): print("Syntax: only - Only show specific instructions") return False def do_range(self, s, silent=False): try: rangeS = int(s.split('-')[0], 16) rangeE = int(s.split('-')[1], 16) self.__options.range = s.split()[0] except: if not silent: return self.help_range() return False if rangeS > rangeE: if not silent: print("[-] The start value must be greater than the end value") return False if not silent: print("[+] Range setted. You have to reload gadgets") def help_range(self): print("Syntax: range - Search between two addresses (0x...-0x...)") return False def do_settings(self, s, silent=False): print("All: %s" % self.__options.all) print("Badbytes: %s" % self.__options.badbytes) print("Binary: %s" % self.__options.binary) print("Depth: %s" % self.__options.depth) print("Filter: %s" % self.__options.filter) print("Memstr: %s" % self.__options.memstr) print("MultiBr: %s" % self.__options.multibr) print("NoJOP: %s" % self.__options.nojop) print("NoROP: %s" % self.__options.norop) print("NoSYS: %s" % self.__options.nosys) print("Offset: %s" % self.__options.offset) print("Only: %s" % self.__options.only) print("Opcode: %s" % self.__options.opcode) print("ROPchain: %s" % self.__options.ropchain) print("Range: %s" % self.__options.range) print("RawArch: %s" % self.__options.rawArch) print("RawMode: %s" % self.__options.rawMode) print("RawEndian: %s" % self.__options.rawEndian) print("Re: %s" % self.__options.re) print("String: %s" % self.__options.string) print("Thumb: %s" % self.__options.thumb) print("Mipsrop: %s" % self.__options.mipsrop) def help_settings(self): print("Display setting's environment") return False def do_nojop(self, s, silent=False): try: arg = s.split()[0] except: return self.help_nojop() if arg == "enable": self.__options.nojop = True if not silent: print("[+] NoJOP enable. You have to reload gadgets") elif arg == "disable": self.__options.nojop = False if not silent: print("[+] NoJOP disable. You have to reload gadgets") else: if not silent: return self.help_nojop() return False def help_nojop(self): print("Syntax: nojop - Disable JOP search engin") return False def do_norop(self, s, silent=False): try: arg = s.split()[0] except: return self.help_norop() if arg == "enable": self.__options.norop = True if not silent: print("[+] NoROP enable. You have to reload gadgets") elif arg == "disable": self.__options.norop = False if not silent: print("[+] NoROP disable. You have to reload gadgets") else: if not silent: return self.help_norop() return False def help_norop(self): print("Syntax: norop - Disable ROP search engin") return False def do_nosys(self, s, silent=False): try: arg = s.split()[0] except: return self.help_nosys() if arg == "enable": self.__options.nosys = True if not silent: print("[+] NoSYS enable. You have to reload gadgets") elif arg == "disable": self.__options.nosys = False if not silent: print("[+] NoSYS disable. You have to reload gadgets") else: if not silent: return self.help_nosys() return False def help_nosys(self): print("Syntax: nosys - Disable SYS search engin") return False def do_thumb(self, s, silent=False): try: arg = s.split()[0] except: return self.help_thumb() if arg == "enable": self.__options.thumb = True if not silent: print("[+] Thumb enable. You have to reload gadgets") elif arg == "disable": self.__options.thumb = False if not silent: print("[+] Thumb disable. You have to reload gadgets") else: if not silent: return self.help_thumb() return False def help_thumb(self): print("Syntax: thumb - Use the thumb mode for the search engine (ARM only)") return False def do_all(self, s, silent=False): if s == "enable": self.__options.all = True if not silent: print("[+] Showing all gadgets enabled. You have to reload gadgets") elif s == "disable": self.__options.all = False if not silent: print("[+] Showing all gadgets disabled. You have to reload gadgets") else: if not silent: return self.help_all() return False def help_multibr(self): print("Syntax: multibr - Enable/Disable multiple branch gadgets") return False def do_multibr(self, s, silent=False): if s == "enable": self.__options.multibr = True if not silent: print("[+] Multiple branch gadgets enabled. You have to reload gadgets") elif s == "disable": self.__options.multibr = False if not silent: print("[+] Multiple branch gadgets disabled. You have to reload gadgets") else: if not silent: return self.help_all() return False def help_all(self): print("Syntax: all - Regular expression") return False def do_re(self, s, silent=False): if s.lower() == 'none': self.__options.re = None elif s == "": self.help_re() silent = True else: self.__options.re = s if not silent: print("[+] Re setted. You have to reload gadgets")