Files
2018-12-20 16:09:19 +01:00

1266 lines
44 KiB
Python

# coding=utf-8
# Copyright 2018 Sascha Schirra
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its contributors
# may be used to endorse or promote products derived from this software without
# specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" A ND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
from __future__ import print_function
from ropper.loaders.loader import Loader, Type
from ropper.printer.printer import FileDataPrinter
from ropper.rop import Ropper
from ropper.common.error import *
from ropper.gadget import GadgetType
from ropper.gadget import Category
from ropper.semantic import Analyser
from ropper.common.utils import isHex, getFileNameFromPath
from ropper.common.coloredstring import *
from ropper.common.utils import *
from ropper.ropchain.ropchain import *
from ropper.arch import getArchitecture
from ropper.service import RopperService
from binascii import unhexlify
from sys import stdout, stdin, stderr
import ropper
import cmd
import re
import os
import traceback
import time
# Python2 compatibility
try:
input = raw_input
except:
pass
def safe_cmd(func):
def cmd(self, text):
cp = ConsolePrinter()
try:
func(self, text)
except RopperError as e:
cp.printError(e)
except KeyboardInterrupt:
cp.println()
except:
cp.printError('Please report this error on https://github.com/sashs/ropper')
cp.printError( traceback.format_exc())
return cmd
class CallbackClass(object):
def __init__(self, console):
self.__console = console
def __gadgetSearchProgress__(self, section, gadgets, progress):
if gadgets is not None:
self.__console.cprinter.printProgress('loading...', progress)
if progress == 1.0:
self.__console.cprinter.finishProgress()
else:
self.__console.cprinter.printInfo(
'Load gadgets for section: ' + section.name)
def __deleteDoubleGadgetsProgress__(self, gadget, added, progress):
self.__console.cprinter.printProgress('removing double gadgets...', progress)
if progress == 1.0:
self.__console.cprinter.finishProgress()
def __filterCfgGadgetsProgress__(self, gadget, added, progress):
self.__console.cprinter.printProgress('filtering cfg gadgets...', progress)
if progress == 1.0:
self.__console.cprinter.finishProgress()
def __filterBadBytesGadgetsProgress__(self, gadget, added, progress):
self.__console.cprinter.printProgress('filtering badbytes...', progress)
if progress == 1.0:
self.__console.cprinter.finishProgress()
def __analyseGadgetsProgress__(self, gadget, progress):
self.__console.cprinter.printProgress('analyse gadgets...', progress)
if progress == 1.0:
self.__console.cprinter.finishProgress()
def __ropchainMessages__(self, message):
if message.startswith('[*]'):
self.__console.cprinter.puts('\r' + message)
else:
self.__console.cprinter.println()
self.__console.cprinter.printInfo(message)
def __message__(self, message):
self.__console.cprinter.printInfo(message)
class Console(cmd.Cmd):
def __init__(self, options):
cmd.Cmd.__init__(self)
self.__options = options
if not options.isWindows():
import readline
old_delims = readline.get_completer_delims()
old_delims = old_delims.replace('-', '')
old_delims = old_delims.replace('/', '')
readline.set_completer_delims(old_delims)
#ää Fix completion on mac os
import rlcompleter
if 'libedit' in readline.__doc__:
readline.parse_and_bind("bind ^I rl_complete")
else:
readline.parse_and_bind("tab: complete")
self.__rs = RopperService(self.__options.ropper_options, callbacks=CallbackClass(self))
self.__currentFileName = ''
self.__cprinter = ConsolePrinter()
self.__dataPrinter = {}
self.__updatePrompt()
@property
def cprinter(self):
return self.__cprinter
@property
def currentFileName(self):
if not self.__currentFileName:
raise RopperError('No binary loaded')
return self.__currentFileName
@property
def currentFile(self):
return self.__rs.getFileFor(self.currentFileName)
def cmdloop(self):
try:
cmd.Cmd.cmdloop(self)
except KeyboardInterrupt:
print()
self.cmdloop()
def emptyline(self):
pass
def __getDataPrinter(self, type):
p = self.__dataPrinter.get(type)
if not p:
p = FileDataPrinter.create(type)
self.__dataPrinter[type] = p
return p
def start(self):
if self.__options.version:
self.__printVersion()
return
if self.__options.clear_cache:
self.__rs.clearCache()
if self.__options.file and self.__options.asm is None:
for file in self.__options.file:
self.__loadFile(file)
if len(self.__options.file) > 1:
self.do_file('1')
if self.__options.console:
self.cmdloop()
self.__handleOptions(self.__options)
def __updatePrompt(self):
if self.__currentFileName:
name = getFileNameFromPath(self.__currentFileName)
self.prompt = cstr('(%s/%s/%s)> ' % (name, str(self.currentFile.type),self.currentFile.arch), Color.RED)
else:
self.prompt = cstr('(ropper)> ', Color.RED)
def __loadFile(self, file):
self.__rs.addFile(file, raw=self.__options.raw,
arch=self.__options.arch)
self.__options.arch = None
self.__currentFileName = file
self.__updatePrompt()
if self.__options.I is not None:
self.__rs.setImageBaseFor(file, self.__options.I)
if not self.__options.no_load and self.__options.console:
self.__loadGadgets()
#self.__binary.printer = FileDataPrinter.create(self.__binary.type)
def __printGadget(self, gadget, detailed=False):
if detailed:
self.__cprinter.println(gadget)
else:
self.__cprinter.println(gadget.simpleString())
def __printData(self, data):
cf = self.currentFile
self.__getDataPrinter(cf.type).printData(cf.loader, data)
def __printVersion(self):
self.__cprinter.println("Version: Ropper %s" %
'.'.join([str(x) for x in ropper.VERSION]))
self.__cprinter.println("Author: Sascha Schirra")
self.__cprinter.println("Website: http://scoding.de/ropper\n")
def __printHelpText(self, cmd, desc):
self.__cprinter.println('{} - {}\n'.format(cmd, desc))
def __printError(self, error):
self.__cprinter.printError(error)
def __printInfo(self, info):
self.__cprinter.printInfo(cstr(info))
def __printSeparator(self, before='', behind=''):
self.__cprinter.println(before + '-' * 40 + behind)
def __setASLR(self, enable):
self.currentFile.loader.setASLR(enable)
def __setNX(self, enable):
self.currentFile.loader.setNX(enable)
def __set(self, option, enable):
if option == 'aslr':
self.__setASLR(enable)
elif option == 'nx':
self.__setNX(enable)
else:
raise ArgumentError('Invalid option: {}'.format(option))
def __asm(self, code, arch, format):
r = Ropper()
if format == 'R':
f = 'raw'
elif format == 'H':
f = 'hex'
elif format == 'S':
f = 'string'
else:
raise RopperError('wrong format: %s' % f)
self.__cprinter.println(self.__rs.asm(code, arch, f))
def __disasm(self, code, arch):
r = Ropper()
self.__cprinter.println(self.__rs.disasm(code, arch))
def __searchJmpReg(self, regs):
regs = regs.split(',')
gadgets = self.__rs.searchJmpReg(name=self.currentFileName, regs=regs)
self.__printGadgets([g for g in gadgets.values()][0], header='JMP Instructions')
def __searchOpcode(self, opcode):
gadgets = self.__rs.searchOpcode(
name=self.currentFileName, opcode=opcode)
self.__printGadgets([g for g in gadgets.values()][0], header='Opcode')
def __searchInstructions(self, code):
gadgets = self.__rs.searchInstructions(
name=self.currentFileName, code=code)
self.__printGadgets([g for g in gadgets.values()]
[0], header='Instructions')
def __searchPopPopRet(self):
pprs = self.__rs.searchPopPopRet(self.currentFileName)
self.__printGadgets([g for g in pprs.values()][0],
header='POP;POP;RET Instructions')
def __loadGadgetsForAllFiles(self):
self.__rs.loadGadgetsFor()
def __loadGadgets(self):
self.__searchGadgetsFor(self.currentFileName)
def __searchGadgetsFor(self, binary):
self.__rs.loadGadgetsFor(binary)
return self.__rs.getFileFor(binary).gadgets
def __printGadgets(self, gadgets, category=None, header='Gadgets', detailed=False):
self.__getDataPrinter(self.currentFile.type).printTableHeader(header)
counter = 0
for g in gadgets:
if not category or category == g.category[0]:
self.__printGadget(g, detailed=detailed)
counter += 1
self.__cprinter.println('\n%d gadgets found' % counter)
def __printProgress(self, gadget, gnr, count):
if gnr >= 0:
self.__cprinter.printProgress('clearing up...', float(gnr) / count)
else:
self.__cprinter.printProgress('clearing up...', 1)
self.__cprinter.finishProgress()
def __loadAllGadgets(self):
self.__rs.loadGadgetsFor()
def __printGadgetsFromCurrentFile(self):
gadgets = self.currentFile.gadgets
self.__printGadgets(gadgets, detailed=self.__options.detailed)
def __search(self, filter, quality=None):
self.__printInfo('Searching for gadgets: ' + filter)
old = None
for fc, gadget in self.__rs.search(filter, quality):
if fc != old:
old = fc
self.__cprinter.println()
self.__printInfo('File: %s' % fc)
self.__printGadget(gadget, self.__options.detailed)
self.__cprinter.println()
def __generateChain(self, command):
split = command.split(' ')
try:
old = self.__rs.options.color
generator = split[0]
options = {}
if len(split) > 1:
for option in split[1:]:
if option.count('=') == 0 or option.count('=') > 1:
raise RopperError('Wrong option format. An option has to be set in the following format: option=value')
key, value = option.split('=')
options[key] = value
try:
self.__rs.options.color = False
chain = self.__rs.createRopChain(generator, str(self.currentFile.arch) ,options)
#generator = RopChain.get(self.__binaries, self.__gadgets, split[0], self.__ropchainInfoCallback, unhexlify(self.__options.badbytes))
self.__printInfo('generating rop chain')
# self.__printSeparator(behind='\n\n')
self.__cprinter.println(chain)
# self.__printSeparator(before='\n\n')
self.__printInfo('rop chain generated!')
except RopperError as e:
self.__rs.options.color = old
self.__printError(e)
except BaseException as e:
self.__rs.options.color = old
print( traceback.format_exc())
raise e
self.__rs.options.color = old
def __ropchainInfoCallback(self, message):
if message.startswith('[*]'):
self.__cprinter.puts('\r' + message)
self.__cprinter.printInfo(message)
def __setarch(self, arch):
if self.currentFile:
self.__rs.setArchitectureFor(self.currentFileName, arch)
self.__updatePrompt()
else:
self.__printError('No file loaded')
def __printStrings(self, string, sec=None):
strings = self.__rs.searchString(
string=string, name=self.currentFileName)
strings = [s for s in strings.values()][0]
strings = [(cstr(toHex(addr), Color.RED), cstr(s))
for addr, s in strings]
printTable('Strings', (cstr('Address'), cstr('Value')), strings)
def __disassembleAddress(self, addr, length):
ds = self.__rs.disassAddress(self.currentFileName, addr, length)
if len(ds.split('\n')) < length:
self.__cprinter.printInfo(
'Cannot disassemble specified count of instructions')
self.__getDataPrinter(
self.currentFile.type).printTableHeader('Instructions')
self.__cprinter.println(ds)
def __printSectionInHex(self, section):
section = self.currentFile.loader.getSection(section)
if section.bytes:
printHexFormat(section.bytes, section.virtualAddress,
not self.__rs.options.color)
else:
self.__printInfo('No bytes to print')
@safe_cmd
def __handleOptions(self, options):
if options.sections:
self.__printData('sections')
elif options.analyse:
self.__loadGadgets()
#self.do_analyse(options.analyse)
elif options.semantic:
self.__loadGadgets()
self.do_semantic(options.semantic)
elif options.symbols:
self.__printData('symbols')
elif options.segments:
self.__printData('segments')
elif options.dllcharacteristics:
self.__printData('dll_characteristics')
elif options.imagebase:
self.__printData('image_base')
elif options.e:
self.__printData('entry_point')
elif options.imports:
self.__printData('imports')
elif options.asm is not None:
format = 'H'
if options.file is not None:
with open(options.file[0]) as f:
code = f.read()
if len(options.asm) > 0:
format = options.asm[0]
else:
code = options.asm[0]
if len(options.asm) == 2:
code = options.asm[0]
format = options.asm[1]
arch = 'x86'
if options.arch:
arch = options.arch
self.__asm(code, arch, format)
elif options.disasm:
code = options.disasm
arch = 'x86'
if options.arch:
arch = options.arch
self.__disasm(code, arch)
elif options.set:
self.__set(options.set, True)
elif options.unset:
self.__set(options.unset, False)
elif options.info:
self.__printData('information')
elif options.ppr:
self.__searchPopPopRet()
elif options.jmp:
self.__searchJmpReg(options.jmp)
elif options.stack_pivot:
self.__loadGadgets()
self.__printGadgets(self.currentFile.gadgets, Category.STACK_PIVOT)
elif options.opcode:
self.__searchOpcode(self.__options.opcode)
elif options.instructions:
self.__searchInstructions(self.__options.instructions)
elif options.string:
self.__printStrings(options.string, options.section)
elif options.hex and options.section:
self.__printSectionInHex(options.section)
elif options.disassemble_address:
split = options.disassemble_address.split(':')
length = 1
if not isHex(split[0]):
raise RopperError('Number have to be in hex format 0x....')
if len(split) > 1:
if split[1][1:].isdigit() or (len(split[1]) >= 3 and split[1][1] == '-' and split[1][2:].isdigit()): # is L\d or L-\d
length = int(split[1][1:])
else:
raise RopperError(
'Length have to be in the following format L + Number e.g. L3')
self.__disassembleAddress(int(split[0], 16), length)
# elif options.checksec:
# self.__checksec()
elif options.chain:
self.__loadGadgetsForAllFiles()
self.__generateChain(options.chain)
elif self.__options.file:
self.__loadGadgets()
if options.search:
self.__search(options.search, options.quality)
else:
self.__printGadgetsFromCurrentFile()
####### cmd commands ######
@safe_cmd
def do_show(self, text):
if len(text) == 0:
self.help_show()
return
self.__printData(text)
def help_show(self):
desc = 'shows informations about the loaded file'
if self.__getDataPrinter(self.currentFile.type):
desc += ('Available informations:\n' +
('\n'.join(self.__getDataPrinter(self.currentFile.type).availableInformations)))
self.__printHelpText(
'show <info>', desc)
def complete_show(self, text, line, begidx, endidx):
if self.__getDataPrinter(self.currentFile.type):
return [i for i in self.__getDataPrinter(self.currentFile.type).availableInformations if i.startswith(
text)]
@safe_cmd
def do_close(self, text):
if text.isdigit():
idx = int(text)
if len(self.__rs.files) > idx - 1:
self.__rs.removeFile(self.__rs.files[idx - 1].loader.fileName)
if len(self.__rs.files) != 0:
self.__currentFileName = self.__rs.files[0].loader.fileName
else:
self.__currentFileName = None
self.__updatePrompt()
else:
self.__cprinter.printError('Index is too small or to large')
elif text == 'all':
for file in self.__rs.files:
self.__rs.removeFile(file.loader.fileName)
self.__currentFileName = None
self.__updatePrompt()
else:
self.help_close()
def help_close(self):
self.__printHelpText(
'close idx/all', 'closes opened files\nidx - index of file which should be closed\nall - closes all files')
@safe_cmd
def do_file(self, text):
if len(text) == 0:
data = []
for index, binary in enumerate(self.__rs.files):
if self.currentFileName == binary.loader.fileName:
data.append(
(cstr(index + 1), cstr(binary.loader.fileName + '*'), cstr(binary.arch),cstr(binary.loaded)))
else:
data.append(
(cstr(index + 1), cstr(binary.loader.fileName), cstr(binary.arch),cstr(binary.loaded)))
printTable('Opened Files', (cstr('No.'),
cstr('Name'), cstr('Architecture'),cstr('Loaded')), data)
elif text.isdigit():
idx = int(text) - 1
if idx >= len(self.__rs.files):
raise RopperError('Index is too small or to large')
self.__currentFileName = self.__rs.files[idx].loader.fileName
self.__updatePrompt()
self.__printInfo('File \'%s\' selected.' % self.currentFileName)
else:
self.__loadFile(text)
self.__printInfo('File loaded.')
def complete_file(self, text, line, begidx, endidx):
file = text
cwd = '.'
path = ''
if '/' in file:
cwd = file[:file.rindex('/') + 1]
file = file[file.rindex('/') + 1:]
path = cwd
return [path + i for i in os.listdir(cwd) if i.startswith(file)]
def help_file(self):
self.__printHelpText(
'file [<file>|<idx>]', '\nno argument shows all opened files\n<file> - loads the file <file>\n<idx> - select the file with number <idx>')
@safe_cmd
def do_set(self, text):
if not text:
self.help_set()
return
self.__set(text, True)
def help_set(self):
desc = """Sets options.
Options:
aslr\t- Sets the ASLR-Flag (PE)
nx\t- Sets the NX-Flag (ELF|PE)"""
self.__printHelpText('set <option>', desc)
def complete_set(self, text, line, begidx, endidx):
return [i for i in ['aslr', 'nx'] if i.startswith(text)]
@safe_cmd
def do_unset(self, text):
if not text:
self.help_unset()
return
self.__set(text, False)
def help_unset(self):
desc = """Clears options.
Options:
aslr\t- Clears the ASLR-Flag (PE)
nx\t- Clears the NX-Flag (ELF|PE)"""
self.__printHelpText('unset <option>', desc)
def complete_unset(self, text, line, begidx, endidx):
return self.complete_set(text, line, begidx, endidx)
@safe_cmd
def do_gadgets(self, text):
if not self.currentFile.loaded:
self.__printInfo('Gadgets have to be loaded with load')
return
self.__printGadgets(self.currentFile.gadgets, detailed=self.__rs.options.detailed)
def help_gadgets(self):
self.__printHelpText('gadgets', 'shows all loaded gadgets')
@safe_cmd
def do_load(self, text):
if text == 'all':
self.__loadAllGadgets()
else:
self.__loadGadgets()
self.__printInfo('gadgets loaded.')
def help_load(self):
self.__printHelpText(
'load [all]', '\nall - loads gadgets of all opened files\nwithout argument loads gadgets of current file')
@safe_cmd
def do_ppr(self, text):
self.__searchPopPopRet()
def help_ppr(self):
self.__printHelpText('ppr', 'shows all pop,pop,ret instructions')
@safe_cmd
def do_search(self, text):
if len(text) == 0:
self.help_search()
return
match = re.match('/\d+/', text)
qual = None
if match:
qual = int(match.group(0)[1:-1])
text = text[len(match.group(0)):].strip()
self.__search(text, qual)
def help_search(self):
desc = 'search gadgets.\n\n'
desc += '/quality/\tThe quality of the gadget (1 = best).'
desc += 'The better the quality the less instructions are between the found intruction and ret\n'
desc += '?\t\tany character\n%\t\tany string\n\n'
desc += 'Example:\n'
desc += 'search mov e?x\n\n'
desc += '0x000067f1: mov edx, dword ptr [ebp + 0x14]; mov dword ptr [esp], edx; call eax;\n'
desc += '0x00006d03: mov eax, esi; pop ebx; pop esi; pop edi; pop ebp; ret ;\n'
desc += '0x00006d6f: mov ebx, esi; mov esi, dword ptr [esp + 0x18]; add esp, 0x1c; ret ;\n'
desc += '0x000076f8: mov eax, dword ptr [eax]; mov byte ptr [eax + edx], 0; add esp, 0x18; pop ebx; ret ;\n\n\n'
desc += 'search mov [%], edx\n\n'
desc += '0x000067ed: mov dword ptr [esp + 4], edx; mov edx, dword ptr [ebp + 0x14]; mov dword ptr [esp], edx; call eax;\n'
desc += '0x00006f4e: mov dword ptr [ecx + 0x14], edx; add esp, 0x2c; pop ebx; pop esi; pop edi; pop ebp; ret ;\n'
desc += '0x000084b8: mov dword ptr [eax], edx; ret ;\n'
desc += '0x00008d9b: mov dword ptr [eax], edx; add esp, 0x18; pop ebx; ret ;\n\n\n'
desc += 'search /1/ mov [%], edx\n\n'
desc += '0x000084b8: mov dword ptr [eax], edx; ret ;\n'
self.__printHelpText('search [/<quality>/] <string>', desc)
@safe_cmd
def do_inst(self, text):
if len(text) == 0:
self.help_inst()
return
self.__searchInstructions(text)
def help_inst(self):
self.__printHelpText(
'inst <instructions>', 'searchs instructions in executable sections')
@safe_cmd
def do_opcode(self, text):
if len(text) == 0:
self.help_opcode()
return
self.__searchOpcode(text)
def help_opcode(self):
self.__printHelpText(
'opcode <opcode>', 'searchs opcode in executable sections\nExample:\nopcode ffe4\nopcode ff4?\nopcode ff??\n\nNot allowed:\nopcode ff?4')
@safe_cmd
def do_imagebase(self, text):
if len(text) == 0:
self.__rs.setImageBaseFor(self.currentFileName, None)
self.__printInfo('Imagebase reseted')
elif isHex(text):
self.__rs.setImageBaseFor(self.currentFileName, int(text, 16))
self.__printInfo('Imagebase set to %s' % text)
else:
self.help_imagebase()
def help_imagebase(self):
self.__printHelpText(
'imagebase [<base>]', 'sets a new imagebase. An empty imagebase sets the imagebase to the original value.')
@safe_cmd
def do_type(self, text):
if len(text) == 0:
self.help_type()
return
self.do_settings('type %s' % text)
def help_type(self):
self.__printHelpText(
'type <type>', 'sets the gadget type (rop, jop, sys, all, default:all)')
@safe_cmd
def do_jmp(self, text):
if len(text) == 0:
self.help_jmp()
return
self.__searchJmpReg(text)
def help_jmp(self):
self.__printHelpText(
'jmp <reg[,reg...]>', 'searchs jmp reg instructions')
@safe_cmd
def do_detailed(self, text):
self.do_settings('detailed %s' % text)
def help_detailed(self):
self.__printHelpText(
'detailed [on|off]', 'sets detailed gadget output')
def complete_detailed(self, text, line, begidx, endidx):
return [i for i in ['on', 'off'] if i.startswith(text)]
@safe_cmd
def do_settings(self, text):
if len(text):
try:
splits = text.strip().split(' ')
if len(splits) == 1:
if splits[0] == 'color':
self.__rs.options[splits[0]] = True
else:
self.__rs.options[splits[0]] = None
elif len(splits) == 2:
if splits[1] in ['on', 'off']:
self.__rs.options[splits[0]] = True if splits[1] == 'on' else False
elif splits[0] in ('inst_count', 'count_of_findings'):
self.__rs.options[splits[0]] = int(splits[1])
else:
self.__rs.options[splits[0]] = splits[1]
else:
raise RopperError('Invalid setting')
except TypeError as e:
raise RopperError(e)
except AttributeError as e:
raise RopperError(e)
else:
data = []
desc = {'cfg_only':'if on gadgets are filtered for use in CFG exploits (only PE)',
'all':'If on shows all found gadgets including double gadgets',
'color':'If on output is colored',
'badbytes':'Gadget addresses are not allowed to contain this bytes',
'type':'The file is scanned for this type of gadgets. (rop, jop, sys, all)',
'detailed':'If on the gadgets will be printed with more detailed information',
'inst_count':'The max count of instructions in a gadgets',
'count_of_findings':'The max count of findings which will be printed with semantic search (0 = undefined, default: 5'}
for key, value in self.__rs.options.items():
if isinstance(value, bool):
data.append((cstr(key), cstr('on' if value else 'off'), cstr(desc.get(key,''))))
else:
data.append((cstr(key), cstr(value), cstr(desc[key])))
printTable('Settings', (cstr('Name'), cstr('Value'), cstr('Description')), data)
def help_settings(self):
self.__printHelpText('settings', 'shows the current settings or set the settings\nHow to set:\nsettings badbytes 00 - sets badbytes to 00\nsettings badbytes - sets badbytes to default (empty)')
@safe_cmd
def do_badbytes(self, text):
if len(text) == 0:
self.__printInfo('badbytes cleared')
self.do_settings('badbytes %s' % text)
# for binary in self.__binaries:
# if binary.loaded:
# self.__gadgets[binary] = ropper.filterBadBytes(binary.gadgets, self.__options.badbytes)
# if not self.__options.all:
# self.__gadgets[binary] = ropper.deleteDuplicates(self.__gadgets[binary])
self.__cprinter.printInfo('Filter gadgets')
def help_badbytes(self):
self.__printHelpText(
'badbytes [bytes]', 'sets/clears bad bytes\n\n Example:\nbadbytes 000a0d -- sets 0x00, 0x0a and 0x0d as badbytes')
@safe_cmd
def do_color(self, text):
if self.__options.isWindows():
self.__printInfo('No color support for windows')
return
self.do_settings('color %s' % text)
def help_color(self):
self.__printHelpText('color [on|off]', 'sets colorized output')
def complete_color(self, text, line, begidx, endidx):
return [i for i in ['on', 'off'] if i.startswith(text)]
@safe_cmd
def do_ropchain(self, text):
if len(text) == 0:
self.help_ropchain()
return
if not self.currentFile.loaded:
self.do_load(text)
gadgets = []
for binary in self.__rs.files:
gadgets.append(binary.gadgets)
self.__generateChain(text)
def help_ropchain(self):
self.__printHelpText('ropchain <generator>[ argname=arg[ argname=arg...]]',
'uses the given generator and create a ropchain with args\n\nAvailable generators:\nexecve\nargs: cmd (optional)\navailable: x86, x86_64\nOS: linux\n\nmprotect\nargs: address, size\navailable: x86, x86_64\nOS: linux\n\nvirtualprotect\nargs: address (IAT)(optional)\navailable: x86\nOS: Windows\n\nExamples:\nropchain execve\nropchain mprotect address=0xbfff0000 size=0x21000')
def do_quit(self, text):
exit(0)
def help_quit(self):
self.__printHelpText('quit', 'quits the application')
@safe_cmd
def do_arch(self, text):
if not text:
self.help_arch()
return
self.__setarch(text)
def help_arch(self):
self.__printHelpText(
'arch <arch>', 'sets the architecture <arch> for the loaded file')
@safe_cmd
def do_string(self, text):
self.__printStrings(text)
def help_string(self):
self.__printHelpText(
'string [<string>]', 'Looks for string <string> in section <section>. If no string is given all strings are printed.')
@safe_cmd
def do_hex(self, text):
if not text:
self.help_hex()
return
self.__printSectionInHex(text)
def help_hex(self):
self.__printHelpText(
'hex <section>', 'Prints the section <section> in hex format')
@safe_cmd
def do_asm(self, text):
if not text:
self.help_asm()
return
text = text.strip()
format = 'H'
if text[-2:] in (' H', ' R', ' S'):
format = text[-1:]
text = text[:-2]
arch = None
if text.startswith('-a'):
text = text[3:]
index = text.index(' ')
arch = text[:index]
text = text[index:]
arch = arch
if not arch:
if self.__currentFileName:
arch = str(self.currentFile.arch)
else:
arch = 'x86'
self.__asm(text, arch, format)
def help_asm(self):
self.__printHelpText('asm [-a <arch>] <code> [<format>]',
'assembles the given code. \n Format:\nR - Raw\nS - String\nH - Hex\nDefault: H')
@safe_cmd
def do_disasm(self, text):
if not text:
self.help_disasm()
return
arch = None
if text.startswith('-a'):
text = text[3:]
index = text.index(' ')
arch = text[:index]
text = text[index:].strip()
arch = getArchitecture(arch)
if not arch:
if self.__currentFileName:
arch = str(self.currentFile.arch)
else:
arch = 'x86'
self.__disasm(text, arch)
def help_disasm(self):
self.__printHelpText('disasm <bytes>', 'disassembles the given bytes.\nExample:\ndisasm ffe4')
@safe_cmd
def do_disasm_address(self, text):
split = text.split(' ')
length = 1
if not isHex(split[0]):
self.__cprinter.printError(
'Number have to be in hex format 0x....')
return
if len(split) > 1:
if split[1][1:].isdigit() or (len(split[1]) >= 3 and split[1][1] == '-' and split[1][2:].isdigit()): # is L\d or L-\d
length = int(split[1][1:])
else:
self.__cprinter.printError(
'Length have to be in the following format L + Number e.g. L3')
return
addr = int(split[0], 16)
self.__disassembleAddress(addr, length)
def help_disasm_address(self):
self.__printHelpText(
'disassembleAddress <address> [<length>]', 'Disassembles instruction at address <address>. The count of instructions to disassemble can be specified (0x....:L...)\nExample:\ndisasm_address 0x8048cd8\ndisasm_address 0x8048cd8 L2\ndisasm_address 0x8048cd8 L-2')
@safe_cmd
def do_stack_pivot(self, text):
if self.currentFile.loaded:
self.__printGadgets(self.currentFile.gadgets, Category.STACK_PIVOT)
else:
self.__printInfo('No gadgets loaded. Please load gadgets with \'load\'')
def help_stack_pivot(self):
self.__printHelpText('stack_pivot','Prints all stack pivot gadgets')
def do_EOF(self, text):
self.__cprinter.println('')
self.do_quit(text)
@safe_cmd
def do_clearcache(self, text):
self.__rs.clearCache()
def help_clearcache(self):
self.__printHelpText('clearcache','Clears the cache')
@safe_cmd
def do_semantic(self, text):
if not text:
self.help_semantic()
return
if not self.currentFile.analysed:
self.__rs.analyseGadgets(self.currentFile)
constraint = None
constraints = text.split(';')
split = constraints[-1].split(' ')
stableRegs = []
for s in split:
if s.startswith('!'):
stableRegs.append(s[1:])
else:
constraint = s.strip()
constraints[-1] = constraint
for c in range(len(constraints)):
constraints[c] = constraints[c].strip()
self.__printInfo('Searching for gadgets: ' + text)
old = None
found = False
analysedCount = None
count = 0
for fc, gadget in self.__rs.semanticSearch(constraints, stableRegs=stableRegs):
if fc != old:
old = fc
self.__cprinter.println()
self.__printInfo('File: %s' % fc)
found = True
self.__printGadget(gadget, self.__options.detailed)
count += 1
self.__cprinter.printInfo('%d gadgets found' % count)
self.__cprinter.println()
def help_semantic(self):
self.__printHelpText('semantic', 'Searchs gadgets\nsemantic <constraint>[; <constraint>][ !<stable reg>*]\n\nExample:\nsemantc eax==ebx; ecx==1 !edx !esi\n\nValid constraints:\nreg==reg\nreg==number\nreg==[reg]\nreg<+|-|*|/>=<reg|number|[reg]>')
# @safe_cmd
# def do_analyse(self, text):
# import z3
# from ropper.slicing import Slicer
# slicer = Slicer()
# if text and isHex(text):
# addr = int(text, 16)
# for g in self.currentFile.gadgets:
# if g.address == addr:
# print(bytes(g.bytes).encode('hex'))
# print(g.info.regs)
# g.info.irsb.pp()
# print(g.info.expressions)
# set_reg = self.currentFile.arch.searcher.extractValues(["rsp=rbx"], g.info)[0][0]
# # print(self.currentFile.arch.searcher._createConstraint("eax=1",g.info))
# slice = slicer.slicing(g.info.irsb, set_reg)
# print(slice.instructions)
# solver = z3.Solver()
# expr_len = len(g.info.expressions)
# for inst in slice.instructions[::-1]:
# expr = g.info.expressions[expr_len-inst]
# if expr == False:
# continue
# solver.add(expr)
# c = None
# c2 = None
# constraint = self.currentFile.arch.searcher._createConstraint(["rsp=rbx"], g.info)
# print(constraint)
# if constraint is not None:
# solver.add(constraint)
# print(solver.assertions())
# print(solver.check())
# print(g.info.clobberedRegs)
# print(g.simpleString())
# else:
# self.__printInfo('No such gadget')
# @safe_cmd
# def do_edit(self, text):
# cmd = None
# if self.binary.type == Type.ELF:
# cmd = ELFConsole(self.binary, self.__cprinter)
# elif self.binary.type == Type.PE:
# cmd = PEConsole(self.binary, self.__cprinter)
# elif self.binary.type == Type.MACH_O:
# cmd = MachOConsole(self.binary, self.__cprinter)
# else:
# self.printError(
# 'This type is currently not supported: %s' % self.binary.type)
# return
# if cmd:
# cmd.cmdloop()
# def help_edit(self):
# self.__printHelpText('edit', 'edits a file ***experimental***')
class ConsolePrinter(object):
def __init__(self, out=stdout, err=stderr):
super(ConsolePrinter, self).__init__()
self._out = out
self._err = err
def putsErr(self, *args):
for i, arg in enumerate(args):
self._err.write(str(arg))
if i != len(args) - 1:
self._err.write(' ')
self._err.flush()
def puts(self, *args):
for i, arg in enumerate(args):
self._out.write(str(arg))
if i != len(args) - 1:
self._out.write(' ')
self._out.flush()
def println(self, *args):
self.puts(*args)
self._out.write('\n')
def printlnErr(self, *args):
self.putsErr(*args)
self._err.write('\n')
def printHelpText(self, cmd, desc):
self.println('{} - {}\n'.format(cmd, desc))
def printMessage(self, mtype, message):
self.printlnErr(mtype, message)
def printError(self, message):
self.printMessage(cstr('[ERROR]', Color.RED), message)
def printInfo(self, message):
self.printMessage(cstr('[INFO]', Color.GREEN), message)
def startProgress(self, message=None):
if message:
self.printInfo(message)
def printProgress(self, message, progress):
self.putsErr(cstr('\r') + cstr('[LOAD]', Color.GREEN),
message, cstr(int(progress * 100)) + cstr('%'))
def finishProgress(self, message=None):
self.printlnErr('')
if message:
self.printInfo(message)
class EditConsoleMixin(object):
def __init__(self, binary, printer):
cmd.Cmd.__init__(self)
self._binary = binary
self._printer = printer
self.prompt = cstr('(edit:%s) ' % str(binary.type).lower(), Color.RED)
self.intro = cstr('***EXPERIMENTAL***')
@property
def binary(self):
return self._binary
@property
def printer(self):
return self._printer
@safe_cmd
def do_save(self, text):
self.binary.save()
def help_save(self):
self.printer.printHelpText(
'save', 'Saves the changes in the opened file.')
def do_exit(self, text):
self.printer.println()
return True
do_EOF = do_exit
class ELFConsole(EditConsoleMixin, cmd.Cmd):
def __init__(self, binary, printer):
EditConsoleMixin.__init__(self, binary, printer)
cmd.Cmd.__init__(self)
@safe_cmd
def do_ehdr(self, text):
if not text:
ehdr = self._binary.ehdr
self._printer.println('ehdr')
for field in ehdr._fields_:
self._printer.println(
' %s' % field[0], '=', (bytes(ehdr.__getattribute__(field[0]))))
@safe_cmd
def do_phdr(self, text):
if not text:
phdrs = self._binary.phdrs
for i in range(len(phdrs)):
self._printer.println('phdr [%d]' % i)
phdr = phdrs[i].struct
for field in phdr._fields_:
self._printer.println(' %s' % field[0], '=', hex(
phdr.__getattribute__(field[0]))[:-1])
@safe_cmd
def do_shdr(self, text):
shdrs = self._binary.shdrs
if not text:
for i in range(len(shdrs)):
self._printer.println('shdr [%d]' % i)
shdr = shdrs[i].struct
for field in shdr._fields_:
self._printer.println(' %s' % field[0], '=', hex(
shdr.__getattribute__(field[0]))[:-1])
else:
if ' ' in text:
number, field = text.split()
number = int(number)
if '=' in field:
shdr = shdrs[number].struct
field, value = field.split('=')
shdr.__setattr__(field, int(value))
elif text.isdigit():
index = int(text)
self.printer.println('shdr [%d]' % index)
shdr = shdrs[index].struct
for field in shdr._fields_:
self._printer.println(' %s' % field[0], '=', hex(
shdr.__getattribute__(field[0]))[:-1])
class PEConsole(EditConsoleMixin, cmd.Cmd):
def __init__(self, binary, printer):
EditConsoleMixin.__init__(self, binary, printer)
cmd.Cmd.__init__(self)
class MachOConsole(EditConsoleMixin, cmd.Cmd):
def __init__(self, binary, printer):
EditConsoleMixin.__init__(self, binary, printer)
cmd.Cmd.__init__(self)