security-cw/ROPgadget/ropgadget/core.py

656 lines
20 KiB
Python

## -*- coding: utf-8 -*-
##
## Jonathan Salwan - 2014-05-17 - ROPgadget tool
##
## http://twitter.com/JonathanSalwan
## http://shell-storm.org/project/ROPgadget/
##
import cmd
import os
import re
import codecs
import ropgadget.rgutils as rgutils
import binascii
from ropgadget.binary import Binary
from capstone import CS_MODE_32
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 == None or self.__binary.getBinary() == None or self.__binary.getArch() == None or self.__binary.getArchMode() == None or self.__binary.getEndian() == 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 self.__checksBeforeManipulations() == False:
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 self.__checksBeforeManipulations() == False:
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", "")
bytesStr = " // " + binascii.hexlify(gadget["bytes"]).decode('utf8') if self.__options.dump else ""
print(("0x%08x" %(vaddr) if arch == CS_MODE_32 else "0x%016x" %(vaddr)) +
(" : %s" %(insts) if insts else "") + bytesStr)
print("\nUnique gadgets found: %d" %(len(self.__gadgets)))
return True
def __lookingForAString(self, string):
if self.__checksBeforeManipulations() == False:
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(string.encode(), section["opcodes"])]
for ref in allRef:
vaddr = self.__offset + section["vaddr"] + ref
match = section["opcodes"][ref:ref+len(string)]
print(("0x%08x" %(vaddr) if arch == CS_MODE_32 else "0x%016x" %(vaddr)) + " : %s" %(match.decode()))
return True
def __lookingForOpcodes(self, opcodes):
import binascii
if self.__checksBeforeManipulations() == False:
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%08x" %(vaddr) if arch == CS_MODE_32 else "0x%016x" %(vaddr)) + " : %s" %(opcodes))
return True
def __lookingForMemStr(self, memstr):
if self.__checksBeforeManipulations() == False:
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%08x" %(vaddr) if arch == CS_MODE_32 else "0x%016x" %(vaddr)) + " : '%c'" %(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 self.__checksBeforeManipulations() == False:
return False
self.cmdloop()
return True
self.__binary = Binary(self.__options)
if self.__checksBeforeManipulations() == False:
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)
else:
self.__getGadgets()
self.__lookingForGadgets()
if self.__options.ropchain:
ROPMaker(
self.__binary, self.__gadgets, self.__options.paddingLen,
self.__options.ropFile, self.__options.execPath, 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 len(s) == 0:
if not silent:
return self.help_binary()
return False
binary = s
self.__options.binary = binary
self.__binary = Binary(self.__options)
if self.__checksBeforeManipulations() == False:
return False
if not silent:
print("[+] Binary loaded")
def help_binary(self):
print("Syntax: binary <file> -- 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 == 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 <value> -- 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 <badbyte1|badbyte2...> -- ")
return False
def __withK(self, listK, gadget):
if len(listK) == 0:
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:1] == "!":
withoutK += [a[1:]]
else:
withK += [a]
if self.__checksBeforeManipulations() == False:
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%08x" %(vaddr) if arch == CS_MODE_32 else "0x%016x" %(vaddr)) + " : %s" %(insts))
def help_search(self):
print("Syntax: search <keyword1 keyword2 keyword3...> -- 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 <filter1|filter2|...> - 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 <only1|only2|...> - 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 <start-and> - 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))
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 <enable|disable> - 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 <enable|disable> - 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 <enable|disable> - 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 <enable|disable> - 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> - 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 <enable|disable - Show all gadgets (disable removing duplicate gadgets)")
return False
def help_re(self):
print("Syntax: re <pattern1 | pattern2 |...> - 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")