146 lines
5.0 KiB
Python
146 lines
5.0 KiB
Python
|
## -*- coding: utf-8 -*-
|
||
|
##
|
||
|
## Jonathan Salwan - 2014-05-17 - ROPgadget tool
|
||
|
##
|
||
|
## http://twitter.com/JonathanSalwan
|
||
|
## http://shell-storm.org/project/ROPgadget/
|
||
|
##
|
||
|
|
||
|
import re
|
||
|
import codecs
|
||
|
from capstone import *
|
||
|
from struct import pack
|
||
|
|
||
|
class Options(object):
|
||
|
def __init__(self, options, binary, gadgets):
|
||
|
self.__options = options
|
||
|
self.__gadgets = gadgets
|
||
|
self.__binary = binary
|
||
|
|
||
|
if options.only: self.__onlyOption()
|
||
|
if options.range: self.__rangeOption()
|
||
|
if options.re: self.__reOption()
|
||
|
if options.badbytes: self.__deleteBadBytes()
|
||
|
if options.callPreceded: self.__removeNonCallPreceded()
|
||
|
|
||
|
def __onlyOption(self):
|
||
|
new = []
|
||
|
if not self.__options.only:
|
||
|
return
|
||
|
only = self.__options.only.split("|")
|
||
|
if not len(only):
|
||
|
return
|
||
|
for gadget in self.__gadgets:
|
||
|
flag = 0
|
||
|
insts = gadget["gadget"].split(" ; ")
|
||
|
for ins in insts:
|
||
|
if ins.split(" ")[0] not in only:
|
||
|
flag = 1
|
||
|
break
|
||
|
if not flag:
|
||
|
new += [gadget]
|
||
|
self.__gadgets = new
|
||
|
|
||
|
def __rangeOption(self):
|
||
|
new = []
|
||
|
rangeS = int(self.__options.range.split('-')[0], 16)
|
||
|
rangeE = int(self.__options.range.split('-')[1], 16)
|
||
|
if rangeS == 0 and rangeE == 0:
|
||
|
return
|
||
|
for gadget in self.__gadgets:
|
||
|
vaddr = gadget["vaddr"]
|
||
|
if vaddr >= rangeS and vaddr <= rangeE:
|
||
|
new += [gadget]
|
||
|
self.__gadgets = new
|
||
|
|
||
|
def __reOption(self):
|
||
|
new = []
|
||
|
re_strs = []
|
||
|
|
||
|
if not self.__options.re:
|
||
|
return
|
||
|
|
||
|
if '|' in self.__options.re:
|
||
|
re_strs = self.__options.re.split(' | ')
|
||
|
if 1 == len(re_strs):
|
||
|
re_strs = self.__options.re.split('|')
|
||
|
else:
|
||
|
re_strs.append(self.__options.re)
|
||
|
|
||
|
patterns = []
|
||
|
for __re_str in re_strs:
|
||
|
pattern = re.compile(__re_str)
|
||
|
patterns.append(pattern)
|
||
|
|
||
|
for gadget in self.__gadgets:
|
||
|
flag = 1
|
||
|
insts = gadget["gadget"].split(" ; ")
|
||
|
for pattern in patterns:
|
||
|
for ins in insts:
|
||
|
res = pattern.search(ins)
|
||
|
if res:
|
||
|
flag = 1
|
||
|
break
|
||
|
else:
|
||
|
flag = 0
|
||
|
if not flag:
|
||
|
break
|
||
|
if flag:
|
||
|
new += [gadget]
|
||
|
self.__gadgets = new
|
||
|
|
||
|
def __removeNonCallPreceded(self):
|
||
|
def __isGadgetCallPreceded(gadget):
|
||
|
# Given a gadget, determine if the bytes immediately preceding are a call instruction
|
||
|
prevBytes = gadget["prev"]
|
||
|
# TODO: Improve / Semantically document each of these cases.
|
||
|
callPrecededExpressions = [
|
||
|
"\xe8[\x00-\xff][\x00-\xff][\x00-\xff][\x00-\xff]$",
|
||
|
"\xe8[\x00-\xff][\x00-\xff][\x00-\xff][\x00-\xff][\x00-\xff][\x00-\xff][\x00-\xff][\x00-\xff]$",
|
||
|
"\xff[\x00-\xff]$",
|
||
|
"\xff[\x00-\xff][\x00-\xff]$",
|
||
|
"\xff[\x00-\xff][\x00-\xff][\x00-\xff][\x00-\xff]$"
|
||
|
"\xff[\x00-\xff][\x00-\xff][\x00-\xff][\x00-\xff][\x00-\xff][\x00-\xff][\x00-\xff][\x00-\xff]$"
|
||
|
]
|
||
|
return bool(reduce(lambda x,y: x or y, map(lambda x: re.search(x, prevBytes), callPrecededExpressions)))
|
||
|
arch = self.__binary.getArch()
|
||
|
if arch == CS_ARCH_X86:
|
||
|
initial_length = len(self.__gadgets)
|
||
|
self.__gadgets = filter(__isGadgetCallPreceded, self.__gadgets)
|
||
|
print("Options().removeNonCallPreceded(): Filtered out {} gadgets.".format(initial_length - len(self.__gadgets)))
|
||
|
else:
|
||
|
print("Options().removeNonCallPreceded(): Unsupported architecture.")
|
||
|
|
||
|
def __deleteBadBytes(self):
|
||
|
archMode = self.__binary.getArchMode()
|
||
|
if not self.__options.badbytes:
|
||
|
return
|
||
|
new = []
|
||
|
#Filter out empty badbytes (i.e if badbytes was set to 00|ff| there's an empty badbyte after the last '|')
|
||
|
#and convert each one to the corresponding byte
|
||
|
bbytes = []
|
||
|
for bb in self.__options.badbytes.split("|"):
|
||
|
if '-' in bb:
|
||
|
rng = bb.split('-')
|
||
|
low = ord(codecs.decode(rng[0], "hex"))
|
||
|
high = ord(codecs.decode(rng[1], "hex"))
|
||
|
for i in range(low, high):
|
||
|
bbytes.append(chr(i))
|
||
|
else:
|
||
|
bbytes.append(codecs.decode(bb.encode("ascii"), "hex"))
|
||
|
|
||
|
archMode = self.__binary.getArchMode()
|
||
|
for gadget in self.__gadgets:
|
||
|
gadAddr = pack("<L", gadget["vaddr"]) if archMode == CS_MODE_32 else pack("<Q", gadget["vaddr"])
|
||
|
try:
|
||
|
for x in bbytes:
|
||
|
if x in gadAddr: raise
|
||
|
new += [gadget]
|
||
|
except:
|
||
|
pass
|
||
|
self.__gadgets = new
|
||
|
|
||
|
def getGadgets(self):
|
||
|
return self.__gadgets
|
||
|
|