diff --git a/ROPgadget/.github/workflows/main.yml b/ROPgadget/.github/workflows/main.yml new file mode 100644 index 0000000..d87c8fb --- /dev/null +++ b/ROPgadget/.github/workflows/main.yml @@ -0,0 +1,31 @@ +name: CI + +on: + push: + branches: + - master + pull_request: + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Build + run: | + python2 -m pip install --upgrade setuptools wheel + python3 -m pip install --upgrade setuptools wheel + python2 setup.py sdist bdist_wheel + python3 setup.py sdist bdist_wheel + - name: Install + run: | + python2 -m pip install dist/ROPGadget*py2*.whl + python3 -m pip install dist/ROPGadget*py3*.whl + - name: Run tests + run: | + cd test-suite-binaries + ./test.sh + ./test.sh python2 + cd .. diff --git a/ROPgadget/.gitignore b/ROPgadget/.gitignore new file mode 100644 index 0000000..c4db969 --- /dev/null +++ b/ROPgadget/.gitignore @@ -0,0 +1,4 @@ +*.pyc +build +dist +ROPGadget.egg-info diff --git a/ROPgadget/AUTHORS b/ROPgadget/AUTHORS new file mode 100644 index 0000000..8e36c76 --- /dev/null +++ b/ROPgadget/AUTHORS @@ -0,0 +1,42 @@ + +ROPgadget project (>= v5.0) +=================================================== + +Authors: + +* Jonathan Salwan + + +Contributors: + +* hugsy - Has added the --offset option +* Francisco Falcon - Has fixed the --badbytes option and added the count/loaddb/save2db console features +* Kevin Hamacher - Added some functionality for using ROPgadget as library. Also added support for x86(_64) jmp {r,e}sp/call {r,e}sp instructions +* Florian Meier - Added the 64b ROP chain generation +* Álvaro Felipe Melchor (increase performance) +* Sascha Schirra (features, python3, bugs fix) +* Stephen Edwards (increase performance) +* Mikhail Davidov (features) +* penguin-wwy (features) + + + +ROPgadget initial project (<= v4.0.3) +=================================================== + +Authors: + +* Jonathan Salwan +* Allan Wirth + + +Contributors: + +* Hellman (Bug Fix) +* Axel "0vercl0k" Souchet (Bug Fix) +* k3rensk1 (Bug repport) +* brianairb (Bug Fix) +* cao (Bug Fix) +* dark-rose (Made searching for gadgets faster) +* Dennis Semakin (Bug Fix) + diff --git a/ROPgadget/LICENSE_BSD.txt b/ROPgadget/LICENSE_BSD.txt new file mode 100644 index 0000000..e52c765 --- /dev/null +++ b/ROPgadget/LICENSE_BSD.txt @@ -0,0 +1,27 @@ +This is the software license for the ROPgadget project. + +Copyright (c) 2016. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +* 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. +* Neither the name of the developers 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" +AND 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 HOLDERS 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. diff --git a/ROPgadget/README.md b/ROPgadget/README.md new file mode 100644 index 0000000..ec20899 --- /dev/null +++ b/ROPgadget/README.md @@ -0,0 +1,114 @@ +ROPgadget Tool +================ + +This tool lets you search your gadgets on your binaries to facilitate your ROP exploitation. +ROPgadget supports ELF/PE/Mach-O format on x86, x64, ARM, ARM64, PowerPC, SPARC and MIPS +architectures. Since the version 5, ROPgadget has a new core which is written in Python +using Capstone disassembly framework for the gadgets search engine - The older version can +be found in the Archives directory but it will not be maintained. + +Install +------- + +If you want to use ROPgadget, you have to install [Capstone](http://www.capstone-engine.org/) first. + +For the Capstone's installation on nix machine: + + $ sudo pip install capstone + +Capstone supports multi-platforms (windows, ios, android, cygwin...). For the cross-compilation, +please refer to the https://github.com/aquynh/capstone/blob/master/COMPILE.TXT file. + +After Capstone is installed, ROPgadget can be used as a standalone tool: + + $ ROPgadget.py + +Or installed into the Python site-packages library, and executed from $PATH. + + $ python setup.py install + $ ROPgadget + +Or installed from PyPi + + $ pip install ropgadget + $ ROPgadget + +Usage +----- + + usage: ROPgadget.py [-h] [-v] [-c] [--binary ] [--opcode ] + [--string ] [--memstr ] [--depth ] + [--only ] [--filter ] [--range ] + [--badbytes ] [--rawArch ] [--rawMode ] + [--rawEndian ] [--re ] [--offset ] + [--ropchain] [--thumb] [--console] [--norop] [--nojop] + [--callPreceded] [--nosys] [--multibr] [--all] [--noinstr] + [--dump] + + optional arguments: + -h, --help show this help message and exit + -v, --version Display the ROPgadget's version + -c, --checkUpdate Checks if a new version is available + --binary Specify a binary filename to analyze + --opcode Search opcode in executable segment + --string Search string in readable segment + --memstr Search each byte in all readable segment + --depth Depth for search engine (default 10) + --only Only show specific instructions + --filter Suppress specific instructions + --range Search between two addresses (0x...-0x...) + --badbytes Rejects specific bytes in the gadget's address + --rawArch Specify an arch for a raw file + --rawMode Specify a mode for a raw file + --rawEndian Specify an endianness for a raw file + --re Regular expression + --offset Specify an offset for gadget addresses + --ropchain Enable the ROP chain generation + --thumb Use the thumb mode for the search engine (ARM only) + --console Use an interactive console for search engine + --norop Disable ROP search engine + --nojop Disable JOP search engine + --callPreceded Only show gadgets which are call-preceded + --nosys Disable SYS search engine + --multibr Enable multiple branch gadgets + --all Disables the removal of duplicate gadgets + --noinstr Disable the gadget instructions console printing + --dump Outputs the gadget bytes + + +How can I contribute ? +---------------------- + +- Use Z3 to solve the ROP chain +- Add system gadgets for PPC, Sparc, ARM64 (Gadgets.addSYSGadgets()) +- Manage big endian in Mach-O format like the ELF classe. +- Everything you think is cool :) + +Bugs/Patches/Contact +-------------------- + +Please report bugs, submit pull requests, etc. on github at https://github.com/JonathanSalwan/ROPgadget +The offical page is on shell-storm.org at http://shell-storm.org/project/ROPgadget/ + +License +------- + +See COPYING and the license header on all source files. For the files in the dependencies/ there are +individual licenses in each folder.

+ + +Screenshots +----------- + +x64 + +ARM + +Sparc + +MIPS + +PowerPC + +ROP chain + diff --git a/ROPgadget/ROPgadget.py b/ROPgadget/ROPgadget.py new file mode 100755 index 0000000..777f2d5 --- /dev/null +++ b/ROPgadget/ROPgadget.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python +## -*- coding: utf-8 -*- +## +## Jonathan Salwan - 2014-05-12 - ROPgadget tool +## +## http://twitter.com/JonathanSalwan +## http://shell-storm.org/project/ROPgadget/ +## + +import ropgadget + +ropgadget.main() diff --git a/ROPgadget/ropgadget/__init__.py b/ROPgadget/ropgadget/__init__.py new file mode 100644 index 0000000..2201b59 --- /dev/null +++ b/ROPgadget/ropgadget/__init__.py @@ -0,0 +1,29 @@ +## -*- coding: utf-8 -*- +## +## Jonathan Salwan - 2014-05-12 - ROPgadget tool +## +## http://twitter.com/JonathanSalwan +## http://shell-storm.org/project/ROPgadget/ +## + +import ropgadget.args +import ropgadget.binary +import ropgadget.core +import ropgadget.gadgets +import ropgadget.options +import ropgadget.rgutils +import ropgadget.updateAlert +import ropgadget.version +import ropgadget.loaders +import ropgadget.ropchain + +def main(): + import sys + from ropgadget.args import Args + from ropgadget.core import Core + try: + args = Args() + except ValueError as e: + print(e) + sys.exit(-1) + sys.exit(0 if Core(args.getArgs()).analyze() else 1) diff --git a/ROPgadget/ropgadget/args.py b/ROPgadget/ropgadget/args.py new file mode 100644 index 0000000..1bb53b0 --- /dev/null +++ b/ROPgadget/ropgadget/args.py @@ -0,0 +1,138 @@ +## -*- coding: utf-8 -*- +## +## Jonathan Salwan - 2014-05-12 - ROPgadget tool +## +## http://twitter.com/JonathanSalwan +## http://shell-storm.org/project/ROPgadget/ +## + +import argparse +import sys + +from ropgadget.updateAlert import UpdateAlert +from ropgadget.version import * + +class Args(object): + def __init__(self, arguments=None): + self.__args = None + custom_arguments_provided = True + + # If no custom arguments are provided, use the program arguments + if not arguments: + arguments = sys.argv[1:] + custom_arguments_provided = False + + + self.__parse(arguments, custom_arguments_provided) + + def __parse(self, arguments, custom_arguments_provided=False): + parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter, + description="""description: + ROPgadget lets you search your gadgets on a binary. It supports several + file formats and architectures and uses the Capstone disassembler for + the search engine. + +formats supported: + - ELF + - PE + - Mach-O + - Raw + +architectures supported: + - x86 + - x86-64 + - ARM + - ARM64 + - MIPS + - PowerPC + - Sparc +""", + epilog="""examples: + ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 + ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 --ropchain + ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 --depth 3 + ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 --string "main" + ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 --string "m..n" + ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 --opcode c9c3 + ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 --only "mov|ret" + ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 --only "mov|pop|xor|ret" + ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 --filter "xchg|add|sub|cmov.*" + ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 --norop --nosys + ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 --range 0x08041000-0x08042000 + ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 --string main --range 0x080c9aaa-0x080c9aba + ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 --memstr "/bin/sh" + ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 --console + ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 --badbytes "00|01-1f|7f|42" + ROPgadget.py --binary ./test-suite-binaries/Linux_lib64.so --offset 0xdeadbeef00000000 + ROPgadget.py --binary ./test-suite-binaries/elf-ARMv7-ls --depth 5 + ROPgadget.py --binary ./test-suite-binaries/elf-ARM64-bash --depth 5 + ROPgadget.py --binary ./test-suite-binaries/raw-x86.raw --rawArch=x86 --rawMode=32""") + + parser.add_argument("-v", "--version", action="store_true", help="Display the ROPgadget's version") + parser.add_argument("-c", "--checkUpdate", action="store_true", help="Checks if a new version is available") + parser.add_argument("--binary", type=str, metavar="", help="Specify a binary filename to analyze") + parser.add_argument("--opcode", type=str, metavar="", help="Search opcode in executable segment") + parser.add_argument("--string", type=str, metavar="", help="Search string in readable segment") + parser.add_argument("--memstr", type=str, metavar="", help="Search each byte in all readable segment") + parser.add_argument("--depth", type=int, metavar="", default=10, help="Depth for search engine (default 10)") + parser.add_argument("--only", type=str, metavar="", help="Only show specific instructions") + parser.add_argument("--filter", type=str, metavar="", help="Suppress specific mnemonics") + parser.add_argument("--range", type=str, metavar="", default="0x0-0x0", help="Search between two addresses (0x...-0x...)") + parser.add_argument("--badbytes", type=str, metavar="", help="Rejects specific bytes in the gadget's address") + parser.add_argument("--rawArch", type=str, metavar="", help="Specify an arch for a raw file") + parser.add_argument("--rawMode", type=str, metavar="", help="Specify a mode for a raw file") + parser.add_argument("--rawEndian", type=str, metavar="", help="Specify an endianness for a raw file") + parser.add_argument("--re", type=str, metavar="", help="Regular expression") + parser.add_argument("--offset", type=str, metavar="", help="Specify an offset for gadget addresses") + parser.add_argument("--ropchain", action="store_true", help="Enable the ROP chain generation") + parser.add_argument("--thumb" , action="store_true", help="Use the thumb mode for the search engine (ARM only)") + parser.add_argument("--console", action="store_true", help="Use an interactive console for search engine") + parser.add_argument("--norop", action="store_true", help="Disable ROP search engine") + parser.add_argument("--nojop", action="store_true", help="Disable JOP search engine") + parser.add_argument("--callPreceded", action="store_true", help="Only show gadgets which are call-preceded") + parser.add_argument("--nosys", action="store_true", help="Disable SYS search engine") + parser.add_argument("--multibr", action="store_true", help="Enable multiple branch gadgets") + parser.add_argument("--all", action="store_true", help="Disables the removal of duplicate gadgets") + parser.add_argument("--noinstr", action="store_true", help="Disable the gadget instructions console printing") + parser.add_argument("--dump", action="store_true", help="Outputs the gadget bytes") + parser.add_argument("--silent", action="store_true", help="Disables printing of gadgets during analysis") + + self.__args = parser.parse_args(arguments) + + if self.__args.noinstr and self.__args.only: + raise ValueError("[Error] --noinstr and --only= can't be used together") + + if self.__args.noinstr and self.__args.re: + raise ValueError("[Error] --noinstr and --re= can't be used together") + + if self.__args.version: + self.__printVersion() + sys.exit(0) + + elif self.__args.checkUpdate: + UpdateAlert().checkUpdate() + sys.exit(0) + + elif self.__args.depth < 2: + raise ValueError("[Error] The depth must be >= 2") + + elif not custom_arguments_provided and not self.__args.binary and not self.__args.console: + raise ValueError("[Error] Need a binary filename (--binary/--console or --help)") + + elif self.__args.range: + try: + rangeS = int(self.__args.range.split('-')[0], 16) + rangeE = int(self.__args.range.split('-')[1], 16) + except: + raise ValueError("[Error] A range must be set in hexadecimal. Ex: 0x08041000-0x08042000") + if rangeS > rangeE: + raise ValueError("[Error] The start value must be greater than end value") + + def __printVersion(self): + print("Version: %s" %(PYROPGADGET_VERSION)) + print("Author: Jonathan Salwan" ) + print("Author page: https://twitter.com/JonathanSalwan" ) + print("Project page: http://shell-storm.org/project/ROPgadget/" ) + + def getArgs(self): + return self.__args diff --git a/ROPgadget/ropgadget/binary.py b/ROPgadget/ropgadget/binary.py new file mode 100644 index 0000000..b40e0fe --- /dev/null +++ b/ROPgadget/ropgadget/binary.py @@ -0,0 +1,72 @@ +## -*- coding: utf-8 -*- +## +## Jonathan Salwan - 2014-05-12 - ROPgadget tool +## +## http://twitter.com/JonathanSalwan +## http://shell-storm.org/project/ROPgadget/ +## + +from ropgadget.loaders.elf import * +from ropgadget.loaders.pe import * +from ropgadget.loaders.raw import * +from ropgadget.loaders.macho import * +from ropgadget.loaders.universal import * +from binascii import unhexlify + +class Binary(object): + def __init__(self, options): + self.__fileName = options.binary + self.__rawBinary = None + self.__binary = None + + try: + fd = open(self.__fileName, "rb") + self.__rawBinary = fd.read() + fd.close() + except: + print("[Error] Can't open the binary or binary not found") + return None + + if options.rawArch and options.rawMode: + self.__binary = Raw(self.__rawBinary, options.rawArch, options.rawMode, options.rawEndian) + elif self.__rawBinary[:4] == unhexlify(b"7f454c46"): + self.__binary = ELF(self.__rawBinary) + elif self.__rawBinary[:2] == unhexlify(b"4d5a"): + self.__binary = PE(self.__rawBinary) + elif self.__rawBinary[:4] == unhexlify(b"cafebabe"): + self.__binary = UNIVERSAL(self.__rawBinary) + elif self.__rawBinary[:4] == unhexlify(b"cefaedfe") or self.__rawBinary[:4] == unhexlify(b"cffaedfe"): + self.__binary = MACHO(self.__rawBinary) + else: + print("[Error] Binary format not supported") + return None + + def getFileName(self): + return self.__fileName + + def getRawBinary(self): + return self.__rawBinary + + def getBinary(self): + return self.__binary + + def getEntryPoint(self): + return self.__binary.getEntryPoint() + + def getDataSections(self): + return self.__binary.getDataSections() + + def getExecSections(self): + return self.__binary.getExecSections() + + def getArch(self): + return self.__binary.getArch() + + def getArchMode(self): + return self.__binary.getArchMode() + + def getEndian(self): + return self.__binary.getEndian() + + def getFormat(self): + return self.__binary.getFormat() diff --git a/ROPgadget/ropgadget/core.py b/ROPgadget/ropgadget/core.py new file mode 100644 index 0000000..6cebb95 --- /dev/null +++ b/ROPgadget/ropgadget/core.py @@ -0,0 +1,652 @@ +## -*- 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.__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 -- 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 -- 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 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 -- 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)) + + 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") diff --git a/ROPgadget/ropgadget/gadgets.py b/ROPgadget/ropgadget/gadgets.py new file mode 100644 index 0000000..07c4ec7 --- /dev/null +++ b/ROPgadget/ropgadget/gadgets.py @@ -0,0 +1,358 @@ +## -*- coding: utf-8 -*- +## +## Jonathan Salwan - 2014-05-12 - ROPgadget tool +## +## http://twitter.com/JonathanSalwan +## http://shell-storm.org/project/ROPgadget/ +## + +import re +from capstone import * + + +class Gadgets(object): + def __init__(self, binary, options, offset): + self.__binary = binary + self.__options = options + self.__offset = offset + self.__arch = self.__binary.getArch() + + re_str = "" + if self.__arch == CS_ARCH_X86: + re_str = "db|int3" + elif self.__arch == CS_ARCH_ARM64: + re_str = "brk|smc|hvc" + if self.__options.filter: + if re_str: + re_str += "|" + re_str += self.__options.filter + + self.__filterRE = re.compile("({})$".format(re_str)) if re_str else None + + def __passCleanX86(self, decodes): + br = ["ret", "retf", "int", "sysenter", "jmp", "call", "syscall"] + + if decodes[-1][2] not in br: + return True + if not self.__options.multibr and any(mnemonic in br for _, _, mnemonic, _ in decodes[:-1]): + return True + if any("ret" in mnemonic for _, _, mnemonic, _ in decodes[:-1]): + return True + + return False + + def __gadgetsFinding(self, section, gadgets, arch, mode): + + PREV_BYTES = 9 # Number of bytes prior to the gadget to store. + + opcodes = section["opcodes"] + sec_vaddr = section["vaddr"] + + ret = [] + md = Cs(arch, mode) + for gad_op, gad_size, gad_align in gadgets: + allRefRet = [m.start() for m in re.finditer(gad_op, opcodes)] + for ref in allRefRet: + end = ref + gad_size + for i in range(self.__options.depth): + start = ref - (i * gad_align) + if (sec_vaddr+start) % gad_align == 0: + code = opcodes[start:end] + decodes = md.disasm_lite(code, sec_vaddr+ref) + decodes = list(decodes) + if sum(size for _, size, _, _ in decodes) != i*gad_align + gad_size: + # We've read less instructions than planned so something went wrong + continue + if self.passClean(decodes): + continue + off = self.__offset + vaddr = off+sec_vaddr+start + g = {"vaddr" : vaddr} + if not self.__options.noinstr: + g["gadget"] = " ; ".join("{}{}{}".format(mnemonic, " " if op_str else "", op_str) + for _, _, mnemonic, op_str in decodes).replace(" ", " ") + if self.__options.callPreceded: + prevBytesAddr = max(sec_vaddr, vaddr - PREV_BYTES) + g["prev"] = opcodes[prevBytesAddr-sec_vaddr:vaddr-sec_vaddr] + if self.__options.dump: + g["bytes"] = code + ret.append(g) + return ret + + def addROPGadgets(self, section): + + arch = self.__binary.getArch() + arch_mode = self.__binary.getArchMode() + arch_endian = self.__binary.getEndian() + + if arch == CS_ARCH_X86: + gadgets = [ + [b"\xc3", 1, 1], # ret + [b"\xc2[\x00-\xff]{2}", 3, 1], # ret + [b"\xcb", 1, 1], # retf + [b"\xca[\x00-\xff]{2}", 3, 1], # retf + # MPX + [b"\xf2\xc3", 2, 1], # ret + [b"\xf2\xc2[\x00-\xff]{2}", 4, 1], # ret + ] + + elif arch == CS_ARCH_MIPS: gadgets = [] # MIPS doesn't contains RET instruction set. Only JOP gadgets + elif arch == CS_ARCH_PPC: + if arch_endian == CS_MODE_BIG_ENDIAN: + gadgets = [ + [b"\x4e\x80\x00\x20", 4, 4] # blr + ] + else: + gadgets = [ + [b"\x20\x00\x80\x4e", 4, 4] # blr + ] + + elif arch == CS_ARCH_SPARC: + if arch_endian == CS_MODE_BIG_ENDIAN: + gadgets = [ + [b"\x81\xc3\xe0\x08", 4, 4], # retl + [b"\x81\xc7\xe0\x08", 4, 4], # ret + [b"\x81\xe8\x00\x00", 4, 4] # restore + ] + else: + gadgets = [ + [b"\x08\xe0\xc3\x81", 4, 4], # retl + [b"\x08\xe0\xc7\x81", 4, 4], # ret + [b"\x00\x00\xe8\x81", 4, 4] # restore + ] + arch_mode = 0 + + elif arch == CS_ARCH_ARM: gadgets = [] # ARM doesn't contains RET instruction set. Only JOP gadgets + elif arch == CS_ARCH_ARM64: + if arch_endian == CS_MODE_BIG_ENDIAN: + gadgets = [ + [b"\xd6\x5f\x03\xc0", 4, 4] # ret + ] + else: + gadgets = [ + [b"\xc0\x03\x5f\xd6", 4, 4] # ret + ] + arch_mode = CS_MODE_ARM + + else: + print("Gadgets().addROPGadgets() - Architecture not supported") + return None + + if len(gadgets) > 0 : + return self.__gadgetsFinding(section, gadgets, arch, arch_mode + arch_endian) + return gadgets + + + def addJOPGadgets(self, section): + arch = self.__binary.getArch() + arch_mode = self.__binary.getArchMode() + arch_endian = self.__binary.getEndian() + + + + if arch == CS_ARCH_X86: + # we start with x86 and x64 common sequences operating on registers + gadgets = [ + # call/jmp reg + # d0-d7=call,e0-e7=jmp + # x86: 0=eax,1=ecx,2=edx,3=ebx,4=esp,5=ebp,6=esi,7=edi + # x64: 0=rax,1=rcx,2=rdx,3=rbx,4=rsp,5=rbp,6=rsi,7=rdi + [b"\xff[\xd0-\xd7\xe0-\xe7]", 2, 1], + + # call/jmp [reg] + # 10-17=call,20-27=jmp + # x86: 0=eax,1=ecx,2=edx,3=ebx, 6=esi,7=edi + # x64: 0=rax,1=rcx,2=rdx,3=rbx, 6=rsi,7=rdi + [b"\xff[\x10-\x13\x16-\x17\x20-\x23\x26-\x27]", 2, 1], + # call/jmp [reg] + # 14=call,24=jmp + # x86: esp + # x64: rsp + [b"\xff[\x14\x24]\x24", 3, 1], + + # call/jmp [reg + offset], -0x80 <= offset <= 0x7f + # 50-57=call,60-67=jmp + # x86: 0=eax,1=ecx,2=edx,3=ebx, 5=ebp,6=esi,7=edi + # x64: 0=rax,1=rcx,2=rdx,3=rbx, 5=rbp,6=rsi,7=rdi + [b"\xff[\x50-\x53\x55-\x57\x60-\x63\x65-\x67][\x00-\xff]", 3, 1], + # call/jmp [reg + offset], -0x80 <= offset <= 0x7f + # 54=call,64=jmp + # x86: esp + # x64: rsp + [b"\xff[\x54\x64]\x24[\x00-\xff]", 4, 1], + + # call/jmp [reg + offset], -0x80000000 <= offset <= 0x7fffffff + # 90-97=call,a0-a7=jmp + # x86: 0=eax,1=ecx,2=edx,3=ebx, 5=ebp,6=esi,7=edi + # x64: 0=rax,1=rcx,2=rdx,3=rbx, 5=rbp,6=rsi,7=rdi + [b"\xff[\x90-\x93\x95-\x97\xa0-\xa3\xa5-\xa7][\x00-\xff]{4}", 6, 1], + # call/jmp [reg + offset], -0x80000000 <= offset <= 0x7fffffff + # 94=call,a4=jmp + # x86: esp + # x64: rsp + [b"\xff[\x94\xa4]\x24[\x00-\xff]{4}", 7, 1] + ] + # in x64, by adding 41 before a sequence with + # 0=rax,1=rcx,2=rdx,3=rbx,4=rsp,5=rbp,6=rsi,7=rdi + # we convert it to the same sequence with + # 0= r8,1= r9,2=r10,3=r11,4=r12,5=r13,6=r14,7=r15 + if arch_mode == CS_MODE_64: + gadgets += [(b"\x41" + op, size + 1, align) for (op, size, align) in gadgets] + # finally, add extra sequences common to x86 and x64 + gadgets += [ + [b"\xeb[\x00-\xff]", 2, 1], # jmp offset + [b"\xe9[\x00-\xff]{4}", 5, 1], # jmp offset + # MPX + [b"\xf2\xff[\x20\x21\x22\x23\x26\x27]{1}", 3, 1], # jmp [reg] + [b"\xf2\xff[\xe0\xe1\xe2\xe3\xe4\xe6\xe7]{1}", 3, 1], # jmp [reg] + [b"\xf2\xff[\x10\x11\x12\x13\x16\x17]{1}", 3, 1], # jmp [reg] + [b"\xf2\xff[\xd0\xd1\xd2\xd3\xd4\xd6\xd7]{1}", 3, 1] # call [reg] + ] + + + elif arch == CS_ARCH_MIPS: + if arch_endian == CS_MODE_BIG_ENDIAN: + gadgets = [ + [b"\x00[\x40\x60\x80\xa0\xc0\xe0]\xf8\x09[\x00-\xff]{4}", 8, 4], # jalr $v[0-1]|$a[0-3] + [b"[\x01\x02][\x00\x20\x40\x60\x80\xa0\xc0\xe0]\xf8\x09[\x00-\xff]{4}", 8, 4], # jalr $t[0-7]|$s[0-7] + [b"\x03[\x00\x20\xc0\xe0]\xf8\x09[\x00-\xff]{4}", 8, 4], # jalr $t[8-9]|$s8|$ra + [b"\x00[\x40\x60\x80\xa0\xc0\xe0]\x00\x08[\x00-\xff]{4}", 8, 4], # jr $v[0-1]|$a[0-3] + [b"[\x01\x02][\x00\x20\x40\x60\x80\xa0\xc0\xe0]\x00\x08[\x00-\xff]{4}", 8, 4], # jr $t[0-7]|$s[0-7] + [b"\x03[\x00\x20\xc0\xe0]\x00\x08[\x00-\xff]{4}", 8, 4], # jr $t[8-9]|$s8|$ra + [b"[\x0c-\x0f][\x00-\xff]{7}", 8, 4], # jal addr + [b"[\x08-\x0b][\x00-\xff]{7}", 8, 4] # j addr + ] + else: + gadgets = [ + [b"\x09\xf8[\x40\x60\x80\xa0\xc0\xe0]\x00[\x00-\xff]{4}", 8, 4], # jalr $v[0-1]|$a[0-3] + [b"\x09\xf8[\x00\x20\x40\x60\x80\xa0\xc0\xe0][\x01\x02][\x00-\xff]{4}", 8, 4], # jalr $t[0-7]|$s[0-7] + [b"\x09\xf8[\x00\x20\xc0\xe0]\x03[\x00-\xff]{4}", 8, 4], # jalr $t[8-9]|$s8|$ra + [b"\x08\x00[\x40\x60\x80\xa0\xc0\xe0]\x00[\x00-\xff]{4}", 8, 4], # jr $v[0-1]|$a[0-3] + [b"\x08\x00[\x00\x20\x40\x60\x80\xa0\xc0\xe0][\x01\x02][\x00-\xff]{4}", 8, 4], # jr $t[0-7]|$s[0-7] + [b"\x08\x00[\x00\x20\xc0\xe0]\x03[\x00-\xff]{4}", 8, 4], # jr $t[8-9]|$s8|$ra + [b"[\x00-\xff]{3}[\x0c-\x0f][\x00-\xff]{4}", 8, 4], # jal addr + [b"[\x00-\xff]{3}[\x08-\x0b][\x00-\xff]{4}", 8, 4] # j addr + ] + elif arch == CS_ARCH_PPC: gadgets = [] # PPC architecture doesn't contains reg branch instruction + elif arch == CS_ARCH_SPARC: + if arch_endian == CS_MODE_BIG_ENDIAN: + gadgets = [ + [b"\x81\xc0[\x00\x40\x80\xc0]{1}\x00", 4, 4] # jmp %g[0-3] + ] + else: + gadgets = [ + [b"\x00[\x00\x40\x80\xc0]{1}\xc0\x81", 4, 4] # jmp %g[0-3] + ] + arch_mode = 0 + elif arch == CS_ARCH_ARM64: + if arch_endian == CS_MODE_BIG_ENDIAN: + gadgets = [ + [b"\xd6[\x1f\x5f]{1}[\x00-\x03]{1}[\x00\x20\x40\x60\x80\xa0\xc0\xe0]{1}", 4, 4], # br reg + [b"\xd6\?[\x00-\x03]{1}[\x00\x20\x40\x60\x80\xa0\xc0\xe0]{1}", 4, 4] # blr reg + ] + else: + gadgets = [ + [b"[\x00\x20\x40\x60\x80\xa0\xc0\xe0]{1}[\x00-\x03]{1}[\x1f\x5f]{1}\xd6", 4, 4], # br reg + [b"[\x00\x20\x40\x60\x80\xa0\xc0\xe0]{1}[\x00-\x03]{1}\?\xd6", 4, 4] # blr reg + ] + arch_mode = CS_MODE_ARM + elif arch == CS_ARCH_ARM: + if self.__options.thumb or self.__options.rawMode == "thumb": + if arch_endian == CS_MODE_BIG_ENDIAN: + gadgets = [ + [b"\x47[\x00\x08\x10\x18\x20\x28\x30\x38\x40\x48\x70]{1}", 2, 2], # bx reg + [b"\x47[\x80\x88\x90\x98\xa0\xa8\xb0\xb8\xc0\xc8\xf0]{1}", 2, 2], # blx reg + [b"\xbd[\x00-\xff]{1}", 2, 2] # pop {,pc} + ] + else: + gadgets = [ + [b"[\x00\x08\x10\x18\x20\x28\x30\x38\x40\x48\x70]{1}\x47", 2, 2], # bx reg + [b"[\x80\x88\x90\x98\xa0\xa8\xb0\xb8\xc0\xc8\xf0]{1}\x47", 2, 2], # blx reg + [b"[\x00-\xff]{1}\xbd", 2, 2] # pop {,pc} + ] + arch_mode = CS_MODE_THUMB + else: + if arch_endian == CS_MODE_BIG_ENDIAN: + gadgets = [ + [b"\xe1\x2f\xff[\x10-\x19\x1e]{1}", 4, 4], # bx reg + [b"\xe1\x2f\xff[\x30-\x39\x3e]{1}", 4, 4], # blx reg + [b"[\xe8\xe9][\x10-\x1e\x30-\x3e\x50-\x5e\x70-\x7e\x90-\x9e\xb0-\xbe\xd0-\xde\xf0-\xfe][\x80-\xff][\x00-\xff]", 4, 4] # ldm {,pc} + ] + else: + gadgets = [ + [b"[\x10-\x19\x1e]{1}\xff\x2f\xe1", 4, 4], # bx reg + [b"[\x30-\x39\x3e]{1}\xff\x2f\xe1", 4, 4], # blx reg + [b"[\x00-\xff][\x80-\xff][\x10-\x1e\x30-\x3e\x50-\x5e\x70-\x7e\x90-\x9e\xb0-\xbe\xd0-\xde\xf0-\xfe][\xe8\xe9]", 4, 4] # ldm {,pc} + ] + arch_mode = CS_MODE_ARM + else: + print("Gadgets().addJOPGadgets() - Architecture not supported") + return None + + if len(gadgets) > 0 : + return self.__gadgetsFinding(section, gadgets, arch, arch_mode + arch_endian) + return gadgets + + def addSYSGadgets(self, section): + + arch = self.__binary.getArch() + arch_mode = self.__binary.getArchMode() + arch_endian = self.__binary.getEndian() + + if arch == CS_ARCH_X86: + gadgets = [ + [b"\xcd\x80", 2, 1], # int 0x80 + [b"\x0f\x34", 2, 1], # sysenter + [b"\x0f\x05", 2, 1], # syscall + [b"\x65\xff\x15\x10\x00\x00\x00", 7, 1], # call DWORD PTR gs:0x10 + [b"\xcd\x80\xc3", 3, 1], # int 0x80 ; ret + [b"\x0f\x34\xc3", 3, 1], # sysenter ; ret + [b"\x0f\x05\xc3", 3, 1], # syscall ; ret + [b"\x65\xff\x15\x10\x00\x00\x00\xc3", 8, 1], # call DWORD PTR gs:0x10 ; ret + ] + + elif arch == CS_ARCH_MIPS: + if arch_endian == CS_MODE_BIG_ENDIAN: + gadgets = [ + [b"\x00\x00\x00\x0c", 4, 4] # syscall + ] + else: + gadgets = [ + [b"\x0c\x00\x00\x00", 4, 4] # syscall + ] + elif arch == CS_ARCH_PPC: gadgets = [] # TODO (sc inst) + elif arch == CS_ARCH_SPARC: gadgets = [] # TODO (ta inst) + elif arch == CS_ARCH_ARM64: gadgets = [] # TODO + elif arch == CS_ARCH_ARM: + if self.__options.thumb or self.__options.rawMode == "thumb": + gadgets = [ + [b"\x00-\xff]{1}\xef", 2, 2] # FIXME: svc + ] + arch_mode = CS_MODE_THUMB + else: + gadgets = [ + [b"\x00-\xff]{3}\xef", 4, 4] # FIXME: svc + ] + arch_mode = CS_MODE_ARM + else: + print("Gadgets().addSYSGadgets() - Architecture not supported") + return None + + if len(gadgets) > 0 : + return self.__gadgetsFinding(section, gadgets, arch, arch_mode + arch_endian) + return [] + + + def passClean(self, decodes): + + if not decodes: + return True + + if self.__arch == CS_ARCH_X86 and self.__passCleanX86(decodes): + return True + + if self.__filterRE and any(self.__filterRE.match(mnemonic) for _, _, mnemonic, _ in decodes): + return True + + return False + diff --git a/ROPgadget/ropgadget/loaders/__init__.py b/ROPgadget/ropgadget/loaders/__init__.py new file mode 100644 index 0000000..98666c2 --- /dev/null +++ b/ROPgadget/ropgadget/loaders/__init__.py @@ -0,0 +1,12 @@ +## -*- coding: utf-8 -*- +## +## Jonathan Salwan - 2014-05-12 - ROPgadget tool +## +## http://twitter.com/JonathanSalwan +## http://shell-storm.org/project/ROPgadget/ +## + +import ropgadget.loaders.elf +import ropgadget.loaders.macho +import ropgadget.loaders.pe +import ropgadget.loaders.raw diff --git a/ROPgadget/ropgadget/loaders/elf.py b/ROPgadget/ropgadget/loaders/elf.py new file mode 100644 index 0000000..8f267fb --- /dev/null +++ b/ROPgadget/ropgadget/loaders/elf.py @@ -0,0 +1,350 @@ +## -*- coding: utf-8 -*- +## +## Jonathan Salwan - 2014-05-12 - ROPgadget tool +## +## http://twitter.com/JonathanSalwan +## http://shell-storm.org/project/ROPgadget/ +## + +from capstone import * +from ctypes import * +from struct import unpack + +class ELFFlags(object): + ELFCLASS32 = 0x01 + ELFCLASS64 = 0x02 + EI_CLASS = 0x04 + EI_DATA = 0x05 + ELFDATA2LSB = 0x01 + ELFDATA2MSB = 0x02 + EM_386 = 0x03 + EM_X86_64 = 0x3e + EM_ARM = 0x28 + EM_MIPS = 0x08 + EM_SPARCv8p = 0x12 + EM_PowerPC = 0x14 + EM_ARM64 = 0xb7 + +class Elf32_Ehdr_LSB(LittleEndianStructure): + _fields_ = [ + ("e_ident", c_ubyte * 16), + ("e_type", c_ushort), + ("e_machine", c_ushort), + ("e_version", c_uint), + ("e_entry", c_uint), + ("e_phoff", c_uint), + ("e_shoff", c_uint), + ("e_flags", c_uint), + ("e_ehsize", c_ushort), + ("e_phentsize", c_ushort), + ("e_phnum", c_ushort), + ("e_shentsize", c_ushort), + ("e_shnum", c_ushort), + ("e_shstrndx", c_ushort) + ] + +class Elf64_Ehdr_LSB(LittleEndianStructure): + _fields_ = [ + ("e_ident", c_ubyte * 16), + ("e_type", c_ushort), + ("e_machine", c_ushort), + ("e_version", c_uint), + ("e_entry", c_ulonglong), + ("e_phoff", c_ulonglong), + ("e_shoff", c_ulonglong), + ("e_flags", c_uint), + ("e_ehsize", c_ushort), + ("e_phentsize", c_ushort), + ("e_phnum", c_ushort), + ("e_shentsize", c_ushort), + ("e_shnum", c_ushort), + ("e_shstrndx", c_ushort) + ] + +class Elf32_Phdr_LSB(LittleEndianStructure): + _fields_ = [ + ("p_type", c_uint), + ("p_offset", c_uint), + ("p_vaddr", c_uint), + ("p_paddr", c_uint), + ("p_filesz", c_uint), + ("p_memsz", c_uint), + ("p_flags", c_uint), + ("p_align", c_uint) + ] + +class Elf64_Phdr_LSB(LittleEndianStructure): + _fields_ = [ + ("p_type", c_uint), + ("p_flags", c_uint), + ("p_offset", c_ulonglong), + ("p_vaddr", c_ulonglong), + ("p_paddr", c_ulonglong), + ("p_filesz", c_ulonglong), + ("p_memsz", c_ulonglong), + ("p_align", c_ulonglong) + ] + +class Elf32_Shdr_LSB(LittleEndianStructure): + _fields_ = [ + ("sh_name", c_uint), + ("sh_type", c_uint), + ("sh_flags", c_uint), + ("sh_addr", c_uint), + ("sh_offset", c_uint), + ("sh_size", c_uint), + ("sh_link", c_uint), + ("sh_info", c_uint), + ("sh_addralign", c_uint), + ("sh_entsize", c_uint) + ] + +class Elf64_Shdr_LSB(LittleEndianStructure): + _fields_ = [ + ("sh_name", c_uint), + ("sh_type", c_uint), + ("sh_flags", c_ulonglong), + ("sh_addr", c_ulonglong), + ("sh_offset", c_ulonglong), + ("sh_size", c_ulonglong), + ("sh_link", c_uint), + ("sh_info", c_uint), + ("sh_addralign", c_ulonglong), + ("sh_entsize", c_ulonglong) + ] + +class Elf32_Ehdr_MSB(BigEndianStructure): + _fields_ = [ + ("e_ident", c_ubyte * 16), + ("e_type", c_ushort), + ("e_machine", c_ushort), + ("e_version", c_uint), + ("e_entry", c_uint), + ("e_phoff", c_uint), + ("e_shoff", c_uint), + ("e_flags", c_uint), + ("e_ehsize", c_ushort), + ("e_phentsize", c_ushort), + ("e_phnum", c_ushort), + ("e_shentsize", c_ushort), + ("e_shnum", c_ushort), + ("e_shstrndx", c_ushort) + ] + +class Elf64_Ehdr_MSB(BigEndianStructure): + _fields_ = [ + ("e_ident", c_ubyte * 16), + ("e_type", c_ushort), + ("e_machine", c_ushort), + ("e_version", c_uint), + ("e_entry", c_ulonglong), + ("e_phoff", c_ulonglong), + ("e_shoff", c_ulonglong), + ("e_flags", c_uint), + ("e_ehsize", c_ushort), + ("e_phentsize", c_ushort), + ("e_phnum", c_ushort), + ("e_shentsize", c_ushort), + ("e_shnum", c_ushort), + ("e_shstrndx", c_ushort) + ] + +class Elf32_Phdr_MSB(BigEndianStructure): + _fields_ = [ + ("p_type", c_uint), + ("p_offset", c_uint), + ("p_vaddr", c_uint), + ("p_paddr", c_uint), + ("p_filesz", c_uint), + ("p_memsz", c_uint), + ("p_flags", c_uint), + ("p_align", c_uint) + ] + +class Elf64_Phdr_MSB(BigEndianStructure): + _fields_ = [ + ("p_type", c_uint), + ("p_flags", c_uint), + ("p_offset", c_ulonglong), + ("p_vaddr", c_ulonglong), + ("p_paddr", c_ulonglong), + ("p_filesz", c_ulonglong), + ("p_memsz", c_ulonglong), + ("p_align", c_ulonglong) + ] + +class Elf32_Shdr_MSB(BigEndianStructure): + _fields_ = [ + ("sh_name", c_uint), + ("sh_type", c_uint), + ("sh_flags", c_uint), + ("sh_addr", c_uint), + ("sh_offset", c_uint), + ("sh_size", c_uint), + ("sh_link", c_uint), + ("sh_info", c_uint), + ("sh_addralign", c_uint), + ("sh_entsize", c_uint) + ] + +class Elf64_Shdr_MSB(BigEndianStructure): + _fields_ = [ + ("sh_name", c_uint), + ("sh_type", c_uint), + ("sh_flags", c_ulonglong), + ("sh_addr", c_ulonglong), + ("sh_offset", c_ulonglong), + ("sh_size", c_ulonglong), + ("sh_link", c_uint), + ("sh_info", c_uint), + ("sh_addralign", c_ulonglong), + ("sh_entsize", c_ulonglong) + ] + +""" This class parses the ELF """ +class ELF(object): + def __init__(self, binary): + self.__binary = bytearray(binary) + self.__ElfHeader = None + self.__shdr_l = [] + self.__phdr_l = [] + + self.__setHeaderElf() + self.__setShdr() + self.__setPhdr() + + """ Parse ELF header """ + def __setHeaderElf(self): + e_ident = self.__binary[:15] + + ei_class = e_ident[ELFFlags.EI_CLASS] + ei_data = e_ident[ELFFlags.EI_DATA] + + if ei_class != ELFFlags.ELFCLASS32 and ei_class != ELFFlags.ELFCLASS64: + print("[Error] ELF.__setHeaderElf() - Bad Arch size") + return None + + if ei_data != ELFFlags.ELFDATA2LSB and ei_data != ELFFlags.ELFDATA2MSB: + print("[Error] ELF.__setHeaderElf() - Bad architecture endian") + return None + + if ei_class == ELFFlags.ELFCLASS32: + if ei_data == ELFFlags.ELFDATA2LSB: self.__ElfHeader = Elf32_Ehdr_LSB.from_buffer_copy(self.__binary) + elif ei_data == ELFFlags.ELFDATA2MSB: self.__ElfHeader = Elf32_Ehdr_MSB.from_buffer_copy(self.__binary) + elif ei_class == ELFFlags.ELFCLASS64: + if ei_data == ELFFlags.ELFDATA2LSB: self.__ElfHeader = Elf64_Ehdr_LSB.from_buffer_copy(self.__binary) + elif ei_data == ELFFlags.ELFDATA2MSB: self.__ElfHeader = Elf64_Ehdr_MSB.from_buffer_copy(self.__binary) + + self.getArch() # Check if architecture is supported + + """ Parse Section header """ + def __setShdr(self): + shdr_num = self.__ElfHeader.e_shnum + base = self.__binary[self.__ElfHeader.e_shoff:] + shdr_l = [] + + e_ident = self.__binary[:15] + ei_data = e_ident[ELFFlags.EI_DATA] + + for i in range(shdr_num): + + if self.getArchMode() == CS_MODE_32: + if ei_data == ELFFlags.ELFDATA2LSB: shdr = Elf32_Shdr_LSB.from_buffer_copy(base) + elif ei_data == ELFFlags.ELFDATA2MSB: shdr = Elf32_Shdr_MSB.from_buffer_copy(base) + elif self.getArchMode() == CS_MODE_64: + if ei_data == ELFFlags.ELFDATA2LSB: shdr = Elf64_Shdr_LSB.from_buffer_copy(base) + elif ei_data == ELFFlags.ELFDATA2MSB: shdr = Elf64_Shdr_MSB.from_buffer_copy(base) + + self.__shdr_l.append(shdr) + base = base[self.__ElfHeader.e_shentsize:] + + # setup name from the strings table + if self.__ElfHeader.e_shstrndx != 0: + string_table = bytes(self.__binary[(self.__shdr_l[self.__ElfHeader.e_shstrndx].sh_offset):]) + for i in range(shdr_num): + self.__shdr_l[i].str_name = string_table[self.__shdr_l[i].sh_name:].split(b'\x00')[0].decode('utf8') + + """ Parse Program header """ + def __setPhdr(self): + pdhr_num = self.__ElfHeader.e_phnum + base = self.__binary[self.__ElfHeader.e_phoff:] + phdr_l = [] + + e_ident = self.__binary[:15] + ei_data = e_ident[ELFFlags.EI_DATA] + + for i in range(pdhr_num): + if self.getArchMode() == CS_MODE_32: + if ei_data == ELFFlags.ELFDATA2LSB: phdr = Elf32_Phdr_LSB.from_buffer_copy(base) + elif ei_data == ELFFlags.ELFDATA2MSB: phdr = Elf32_Phdr_MSB.from_buffer_copy(base) + elif self.getArchMode() == CS_MODE_64: + if ei_data == ELFFlags.ELFDATA2LSB: phdr = Elf64_Phdr_LSB.from_buffer_copy(base) + elif ei_data == ELFFlags.ELFDATA2MSB: phdr = Elf64_Phdr_MSB.from_buffer_copy(base) + + self.__phdr_l.append(phdr) + base = base[self.__ElfHeader.e_phentsize:] + + def getEntryPoint(self): + return self.__e_entry + + def getExecSections(self): + ret = [] + for segment in self.__phdr_l: + if segment.p_flags & 0x1: + ret += [{ + "offset" : segment.p_offset, + "size" : segment.p_memsz, + "vaddr" : segment.p_vaddr, + "opcodes" : bytes(self.__binary[segment.p_offset:segment.p_offset+segment.p_memsz]) + }] + return ret + + def getDataSections(self): + ret = [] + for section in self.__shdr_l: + if not (section.sh_flags & 0x4) and (section.sh_flags & 0x2): + ret += [{ + "name" : section.str_name, + "offset" : section.sh_offset, + "size" : section.sh_size, + "vaddr" : section.sh_addr, + "opcodes" : bytes(self.__binary[section.sh_offset:section.sh_offset+section.sh_size]) + }] + return ret + + def getArch(self): + if self.__ElfHeader.e_machine == ELFFlags.EM_386 or self.__ElfHeader.e_machine == ELFFlags.EM_X86_64: + return CS_ARCH_X86 + elif self.__ElfHeader.e_machine == ELFFlags.EM_ARM: + return CS_ARCH_ARM + elif self.__ElfHeader.e_machine == ELFFlags.EM_ARM64: + return CS_ARCH_ARM64 + elif self.__ElfHeader.e_machine == ELFFlags.EM_MIPS: + return CS_ARCH_MIPS + elif self.__ElfHeader.e_machine == ELFFlags.EM_PowerPC: + return CS_ARCH_PPC + elif self.__ElfHeader.e_machine == ELFFlags.EM_SPARCv8p: + return CS_ARCH_SPARC + else: + print("[Error] ELF.getArch() - Architecture not supported") + return None + + def getArchMode(self): + if self.__ElfHeader.e_ident[ELFFlags.EI_CLASS] == ELFFlags.ELFCLASS32: + return CS_MODE_32 + elif self.__ElfHeader.e_ident[ELFFlags.EI_CLASS] == ELFFlags.ELFCLASS64: + return CS_MODE_64 + else: + print("[Error] ELF.getArchMode() - Bad Arch size") + return None + + def getEndian(self): + if self.__ElfHeader.e_ident[ELFFlags.EI_DATA] == ELFFlags.ELFDATA2LSB: + return 0 + if self.__ElfHeader.e_ident[ELFFlags.EI_DATA] == ELFFlags.ELFDATA2MSB: + return CS_MODE_BIG_ENDIAN + print("[Error] ELF.getEndian() - Bad Endianness") + return None + + def getFormat(self): + return "ELF" diff --git a/ROPgadget/ropgadget/loaders/macho.py b/ROPgadget/ropgadget/loaders/macho.py new file mode 100644 index 0000000..27c52bd --- /dev/null +++ b/ROPgadget/ropgadget/loaders/macho.py @@ -0,0 +1,211 @@ +## -*- coding: utf-8 -*- +## +## Jonathan Salwan - 2014-05-12 - ROPgadget tool +## +## http://twitter.com/JonathanSalwan +## http://shell-storm.org/project/ROPgadget/ +## + +from capstone import * +from ctypes import * + +class MACH_HEADER(Structure): + _fields_ = [ + ("magic", c_uint), + ("cputype", c_uint), + ("cpusubtype", c_uint), + ("filetype", c_uint), + ("ncmds", c_uint), + ("sizeofcmds", c_uint), + ("flags", c_uint) + ] + +class LOAD_COMMAND(Structure): + _fields_ = [ + ("cmd", c_uint), + ("cmdsize", c_uint) + ] + +class SEGMENT_COMMAND(Structure): + _fields_ = [ + ("cmd", c_uint), + ("cmdsize", c_uint), + ("segname", c_ubyte * 16), + ("vmaddr", c_uint), + ("vmsize", c_uint), + ("fileoff", c_uint), + ("filesize", c_uint), + ("maxprot", c_uint), + ("initprot", c_uint), + ("nsects", c_uint), + ("flags", c_uint) + ] + +class SEGMENT_COMMAND64(Structure): + _fields_ = [ + ("cmd", c_uint), + ("cmdsize", c_uint), + ("segname", c_ubyte * 16), + ("vmaddr", c_ulonglong), + ("vmsize", c_ulonglong), + ("fileoff", c_ulonglong), + ("filesize", c_ulonglong), + ("maxprot", c_uint), + ("initprot", c_uint), + ("nsects", c_uint), + ("flags", c_uint) + ] + +class SECTION(Structure): + _fields_ = [ + ("sectname", c_ubyte * 16), + ("segname", c_ubyte * 16), + ("addr", c_uint), + ("size", c_uint), + ("offset", c_uint), + ("align", c_uint), + ("reloff", c_uint), + ("nreloc", c_uint), + ("flags", c_uint), + ("reserved1", c_uint), + ("reserved2", c_uint) + ] + +class SECTION64(Structure): + _fields_ = [ + ("sectname", c_ubyte * 16), + ("segname", c_ubyte * 16), + ("addr", c_ulonglong), + ("size", c_ulonglong), + ("offset", c_uint), + ("align", c_uint), + ("reloff", c_uint), + ("nreloc", c_uint), + ("flags", c_uint), + ("reserved1", c_uint), + ("reserved2", c_uint) + ] + +class MACHOFlags(object): + CPU_TYPE_I386 = 0x7 + CPU_TYPE_X86_64 = (CPU_TYPE_I386 | 0x1000000) + CPU_TYPE_MIPS = 0x8 + CPU_TYPE_ARM = 12 + CPU_TYPE_ARM64 = (CPU_TYPE_ARM | 0x1000000) + CPU_TYPE_SPARC = 14 + CPU_TYPE_POWERPC = 18 + CPU_TYPE_POWERPC64 = (CPU_TYPE_POWERPC | 0x1000000) + LC_SEGMENT = 0x1 + LC_SEGMENT_64 = 0x19 + S_ATTR_SOME_INSTRUCTIONS = 0x00000400 + S_ATTR_PURE_INSTRUCTIONS = 0x80000000 + +""" This class parses the Mach-O """ +class MACHO(object): + def __init__(self, binary): + self.__binary = bytearray(binary) + + self.__machHeader = None + self.__rawLoadCmd = None + self.__sections_l = [] + + self.__setHeader() + self.__setLoadCmd() + + def __setHeader(self): + self.__machHeader = MACH_HEADER.from_buffer_copy(self.__binary) + + if self.getArchMode() == CS_MODE_32: + self.__rawLoadCmd = self.__binary[28:28+self.__machHeader.sizeofcmds] + + elif self.getArchMode() == CS_MODE_64: + self.__rawLoadCmd = self.__binary[32:32+self.__machHeader.sizeofcmds] + + def __setLoadCmd(self): + base = self.__rawLoadCmd + for i in range(self.__machHeader.ncmds): + command = LOAD_COMMAND.from_buffer_copy(base) + + if command.cmd == MACHOFlags.LC_SEGMENT: + segment = SEGMENT_COMMAND.from_buffer_copy(base) + self.__setSections(segment, base[56:], 32) + + elif command.cmd == MACHOFlags.LC_SEGMENT_64: + segment = SEGMENT_COMMAND64.from_buffer_copy(base) + self.__setSections(segment, base[72:], 64) + + base = base[command.cmdsize:] + + def __setSections(self, segment, base, sizeHeader): + for i in range(segment.nsects): + if sizeHeader == 32: + section = SECTION.from_buffer_copy(base) + section.offset = segment.fileoff + section.addr - segment.vmaddr + base = base[68:] + self.__sections_l += [section] + elif sizeHeader == 64: + section = SECTION64.from_buffer_copy(base) + section.offset = segment.fileoff + section.addr - segment.vmaddr + base = base[80:] + self.__sections_l += [section] + + def getEntryPoint(self): + for section in self.__sections_l: + if section.sectname[0:6] == "__text": + return section.addr + + def getExecSections(self): + ret = [] + for section in self.__sections_l: + if section.flags & MACHOFlags.S_ATTR_SOME_INSTRUCTIONS or section.flags & MACHOFlags.S_ATTR_PURE_INSTRUCTIONS: + ret += [{ + "name" : section.sectname, + "offset" : section.offset, + "size" : section.size, + "vaddr" : section.addr, + "opcodes" : bytes(self.__binary[section.offset:section.offset+section.size]) + }] + return ret + + def getDataSections(self): + ret = [] + for section in self.__sections_l: + if not section.flags & MACHOFlags.S_ATTR_SOME_INSTRUCTIONS and not section.flags & MACHOFlags.S_ATTR_PURE_INSTRUCTIONS: + ret += [{ + "name" : section.sectname, + "offset" : section.offset, + "size" : section.size, + "vaddr" : section.addr, + "opcodes" : bytes(self.__binary[section.offset:section.offset+section.size]) + }] + return ret + + def getArch(self): + if self.__machHeader.cputype == MACHOFlags.CPU_TYPE_I386 or self.__machHeader.cputype == MACHOFlags.CPU_TYPE_X86_64: + return CS_ARCH_X86 + if self.__machHeader.cputype == MACHOFlags.CPU_TYPE_ARM: + return CS_ARCH_ARM + if self.__machHeader.cputype == MACHOFlags.CPU_TYPE_ARM64: + return CS_ARCH_ARM64 + if self.__machHeader.cputype == MACHOFlags.CPU_TYPE_MIPS: + return CS_ARCH_MIPS + else: + print("[Error] MACHO.getArch() - Architecture not supported") + return None + + def getArchMode(self): + if self.__machHeader.magic == 0xfeedface: + return CS_MODE_32 + elif self.__machHeader.magic == 0xfeedfacf: + return CS_MODE_64 + else: + print("[Error] MACHO.getArchMode() - Bad Arch size") + return None + pass + + def getEndian(self): + # TODO: Support other endianness + return 0 + + def getFormat(self): + return "Mach-O" diff --git a/ROPgadget/ropgadget/loaders/pe.py b/ROPgadget/ropgadget/loaders/pe.py new file mode 100644 index 0000000..42c6fd8 --- /dev/null +++ b/ROPgadget/ropgadget/loaders/pe.py @@ -0,0 +1,233 @@ +## -*- coding: utf-8 -*- +## +## Jonathan Salwan - 2014-05-12 - ROPgadget tool +## +## http://twitter.com/JonathanSalwan +## http://shell-storm.org/project/ROPgadget/ +## + +from capstone import * +from ctypes import * +from struct import unpack +from binascii import unhexlify + +class PEFlags(object): + IMAGE_MACHINE_INTEL_386 = 0x014c + IMAGE_MACHINE_AMD_8664 = 0x8664 + IMAGE_FILE_MACHINE_ARM = 0x1c0 + IMAGE_FILE_MACHINE_ARMV7 = 0x1c4 + IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x10b + IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x20b + IMAGE_SIZEOF_SHORT_NAME = 0x8 + +class IMAGE_FILE_HEADER(Structure): + _fields_ = [ + ("Magic", c_uint), + ("Machine", c_ushort), + ("NumberOfSections", c_ushort), + ("TimeDateStamp", c_uint), + ("PointerToSymbolTable", c_uint), + ("NumberOfSymbols", c_uint), + ("SizeOfOptionalHeader", c_ushort), + ("Characteristics", c_ushort) + ] + +class IMAGE_OPTIONAL_HEADER(Structure): + _fields_ = [ + ("Magic", c_ushort), + ("MajorLinkerVersion", c_ubyte), + ("MinorLinkerVersion", c_ubyte), + ("SizeOfCode", c_uint), + ("SizeOfInitializedData", c_uint), + ("SizeOfUninitializedData", c_uint), + ("AddressOfEntryPoint", c_uint), + ("BaseOfCode", c_uint), + ("BaseOfData", c_uint), + ("ImageBase", c_uint), + ("SectionAlignment", c_uint), + ("FileAlignment", c_uint), + ("MajorOperatingSystemVersion", c_ushort), + ("MinorOperatingSystemVersion", c_ushort), + ("MajorImageVersion", c_ushort), + ("MinorImageVersion", c_ushort), + ("MajorSubsystemVersion", c_ushort), + ("MinorSubsystemVersion", c_ushort), + ("Win32VersionValue", c_uint), + ("SizeOfImage", c_uint), + ("SizeOfHeaders", c_uint), + ("CheckSum", c_uint), + ("Subsystem", c_ushort), + ("DllCharacteristics", c_ushort), + ("SizeOfStackReserve", c_uint), + ("SizeOfStackCommit", c_uint), + ("SizeOfHeapReserve", c_uint), + ("SizeOfHeapCommit", c_uint), + ("LoaderFlags", c_uint), + ("NumberOfRvaAndSizes", c_uint) + ] + +class IMAGE_OPTIONAL_HEADER64(Structure): + _fields_ = [ + ("Magic", c_ushort), + ("MajorLinkerVersion", c_ubyte), + ("MinorLinkerVersion", c_ubyte), + ("SizeOfCode", c_uint), + ("SizeOfInitializedData", c_uint), + ("SizeOfUninitializedData", c_uint), + ("AddressOfEntryPoint", c_uint), + ("BaseOfCode", c_uint), + ("ImageBase", c_ulonglong), + ("SectionAlignment", c_uint), + ("FileAlignment", c_uint), + ("MajorOperatingSystemVersion", c_ushort), + ("MinorOperatingSystemVersion", c_ushort), + ("MajorImageVersion", c_ushort), + ("MinorImageVersion", c_ushort), + ("MajorSubsystemVersion", c_ushort), + ("MinorSubsystemVersion", c_ushort), + ("Win32VersionValue", c_uint), + ("SizeOfImage", c_uint), + ("SizeOfHeaders", c_uint), + ("CheckSum", c_uint), + ("Subsystem", c_ushort), + ("DllCharacteristics", c_ushort), + ("SizeOfStackReserve", c_ulonglong), + ("SizeOfStackCommit", c_ulonglong), + ("SizeOfHeapReserve", c_ulonglong), + ("SizeOfHeapCommit", c_ulonglong), + ("LoaderFlags", c_uint), + ("NumberOfRvaAndSizes", c_uint) + ] + +class IMAGE_NT_HEADERS(Structure): + _fields_ = [ + ("Signature", c_uint), + ("FileHeader", IMAGE_FILE_HEADER), + ("OptionalHeader", IMAGE_OPTIONAL_HEADER) + ] + +class IMAGE_NT_HEADERS64(Structure): + _fields_ = [ + ("Signature", c_uint), + ("FileHeader", IMAGE_FILE_HEADER), + ("OptionalHeader", IMAGE_OPTIONAL_HEADER64) + ] + +class IMAGE_SECTION_HEADER(Structure): + _fields_ = [ + ("Name", c_ubyte * PEFlags.IMAGE_SIZEOF_SHORT_NAME), + ("PhysicalAddress", c_uint), + ("VirtualAddress", c_uint), + ("SizeOfRawData", c_uint), + ("PointerToRawData", c_uint), + ("PointerToRelocations", c_uint), + ("PointerToLinenumbers", c_uint), + ("NumberOfRelocations", c_ushort), + ("NumberOfLinenumbers", c_ushort), + ("Characteristics", c_uint) + ] + +""" This class parses the PE format """ +class PE(object): + def __init__(self, binary): + self.__binary = bytearray(binary) + + self.__PEOffset = 0x00000000 + self.__IMAGE_FILE_HEADER = None + self.__IMAGE_OPTIONAL_HEADER = None + + self.__sections_l = [] + + self.__getPEOffset() + self.__parsePEHeader() + self.__parseOptHeader() + self.__parseSections() + + def __getPEOffset(self): + self.__PEOffset = unpack("= 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("([(rax)|(rbx)|(rcx)|(rdx)|(rsi)|(rdi)|(r9)|(r10)|(r11)|(r12)|(r13)|(r14)|(r15)]{3}))\], (?P([(rax)|(rbx)|(rcx)|(rdx)|(rsi)|(rdi)|(r9)|(r10)|(r11)|(r12)|(r13)|(r14)|(r15)]{3}))$", f) + if regex: + lg = gadget["gadget"].split(" ; ")[1:] + try: + for g in lg: + if g.split()[0] != "pop" and g.split()[0] != "ret": + raise + # we need this to filterout 'ret' instructions with an offset like 'ret 0x6', because they ruin the stack pointer + if g != "ret": + if g.split()[0] == "ret" and g.split()[1] != "": + raise + print("\t[+] Gadget found: 0x%x %s" %(gadget["vaddr"], gadget["gadget"])) + return [gadget, regex.group("dst"), regex.group("src")] + except: + continue + return None + + def __lookingForSomeThing(self, something): + for gadget in self.__gadgets: + lg = gadget["gadget"].split(" ; ") + if lg[0] == something: + try: + for g in lg[1:]: + if g.split()[0] != "pop" and g.split()[0] != "ret": + raise + if g != "ret": + # we need this to filterout 'ret' instructions with an offset like 'ret 0x6', because they ruin the stack pointer + if g.split()[0] == "ret" and g.split()[1] != "": + raise + print("\t[+] Gadget found: 0x%x %s" %(gadget["vaddr"], gadget["gadget"])) + return gadget + except: + continue + return None + + def __padding(self, gadget, regAlreadSetted): + lg = gadget["gadget"].split(" ; ") + for g in lg[1:]: + if g.split()[0] == "pop": + reg = g.split()[1] + try: + print("\tp += pack(' mov dword ptr [r32], r32 + regex = re.search("mov dword ptr \[(?P([(eax)|(ebx)|(ecx)|(edx)|(esi)|(edi)]{3}))\], (?P([(eax)|(ebx)|(ecx)|(edx)|(esi)|(edi)]{3}))$", f) + if regex: + lg = gadget["gadget"].split(" ; ")[1:] + try: + for g in lg: + if g.split()[0] != "pop" and g.split()[0] != "ret": + raise + # we need this to filterout 'ret' instructions with an offset like 'ret 0x6', because they ruin the stack pointer + if g != "ret": + if g.split()[0] == "ret" and g.split()[1] != "": + raise + print("\t[+] Gadget found: 0x%x %s" %(gadget["vaddr"], gadget["gadget"])) + return [gadget, regex.group("dst"), regex.group("src")] + except: + continue + return None + + def __lookingForSomeThing(self, something): + for gadget in self.__gadgets: + lg = gadget["gadget"].split(" ; ") + if lg[0] == something: + try: + for g in lg[1:]: + if g.split()[0] != "pop" and g.split()[0] != "ret": + raise + # we need this to filterout 'ret' instructions with an offset like 'ret 0x6', because they ruin the stack pointer + if g != "ret": + if g.split()[0] == "ret" and g.split()[1] != "": + raise + print("\t[+] Gadget found: 0x%x %s" %(gadget["vaddr"], gadget["gadget"])) + return gadget + except: + continue + return None + + def __padding(self, gadget, regAlreadSetted): + lg = gadget["gadget"].split(" ; ") + for g in lg[1:]: + if g.split()[0] == "pop": + reg = g.split()[1] + try: + print("\tp += pack('[\d])", d).group("value") + minorVersion = re.search("MINOR_VERSION.+=.+(?P[\d])", d).group("value") + webVersion = int("%s%s" %(majorVersion, minorVersion)) + curVersion = int("%s%s" %(MAJOR_VERSION, MINOR_VERSION)) + if webVersion > curVersion: + print("The version %s.%s is available. Currently, you use the version %d.%d." %(majorVersion, minorVersion, MAJOR_VERSION, MINOR_VERSION)) + else: + print("Your version is up-to-date.") + diff --git a/ROPgadget/ropgadget/version.py b/ROPgadget/ropgadget/version.py new file mode 100644 index 0000000..16821ac --- /dev/null +++ b/ROPgadget/ropgadget/version.py @@ -0,0 +1,11 @@ +## -*- coding: utf-8 -*- +## +## Jonathan Salwan - 2014-05-12 - ROPgadget tool +## +## http://twitter.com/JonathanSalwan +## http://shell-storm.org/project/ROPgadget/ +## + +MAJOR_VERSION = 6 +MINOR_VERSION = 3 +PYROPGADGET_VERSION = "ROPgadget v%d.%d" %(MAJOR_VERSION, MINOR_VERSION) diff --git a/ROPgadget/scripts/ROPgadget b/ROPgadget/scripts/ROPgadget new file mode 100755 index 0000000..777f2d5 --- /dev/null +++ b/ROPgadget/scripts/ROPgadget @@ -0,0 +1,12 @@ +#!/usr/bin/env python +## -*- coding: utf-8 -*- +## +## Jonathan Salwan - 2014-05-12 - ROPgadget tool +## +## http://twitter.com/JonathanSalwan +## http://shell-storm.org/project/ROPgadget/ +## + +import ropgadget + +ropgadget.main() diff --git a/ROPgadget/setup.py b/ROPgadget/setup.py new file mode 100644 index 0000000..4a095f7 --- /dev/null +++ b/ROPgadget/setup.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python + +from setuptools import setup +import os + +package_name = "ROPGadget" +package_dir = "ropgadget" +package_description = """ +This tool lets you search your gadgets on your binaries to facilitate your ROP exploitation. +ROPgadget supports ELF, PE and Mach-O format on x86, x64, ARM, ARM64, PowerPC, SPARC and MIPS architectures. +http://www.shell-storm.org/project/ROPgadget/ +""".strip() + + +def fullsplit(path, result=None): + """ + Split a pathname into components (the opposite of os.path.join) in a + platform-neutral way. + """ + if result is None: + result = [] + head, tail = os.path.split(path) + if head == '': + return [tail] + result + if head == path: + return result + return fullsplit(head, [tail] + result) + +# Compile the list of packages available, because distutils doesn't have +# an easy way to do this. +packages, data_files = [], [] +root_dir = os.path.dirname(__file__) +if root_dir != '': + os.chdir(root_dir) + +for dirpath, dirnames, filenames in os.walk(package_dir): + # Ignore dirnames that start with '.' + for i, dirname in enumerate(dirnames): + if dirname.startswith('.'): del dirnames[i] + if '__init__.py' in filenames: + packages.append('.'.join(fullsplit(dirpath))) + elif filenames: + data_files.append([dirpath, [os.path.join(dirpath, f) for f in filenames]]) + +version = "6.3" + +setup( + name = package_name, + version = version, + description = package_description, + packages = packages, + license = "BSD", + author = "Jonathan Salwan", + author_email = "jonathan.salwan@gmail.com", + url = "https://github.com/JonathanSalwan/ROPgadget", + scripts = ['scripts/ROPgadget'], + install_requires = ['capstone'], + classifiers = [ + 'Topic :: Security', + 'Environment :: Console', + 'Operating System :: OS Independent', + 'License :: OSI Approved :: BSD License', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Intended Audience :: Developers' + ] +) diff --git a/ROPgadget/test-suite-binaries/UNIVERSAL-x86-x64-libSystem.B.dylib b/ROPgadget/test-suite-binaries/UNIVERSAL-x86-x64-libSystem.B.dylib new file mode 100644 index 0000000..adbbf1f Binary files /dev/null and b/ROPgadget/test-suite-binaries/UNIVERSAL-x86-x64-libSystem.B.dylib differ diff --git a/ROPgadget/test-suite-binaries/elf-ARM64-bash b/ROPgadget/test-suite-binaries/elf-ARM64-bash new file mode 100644 index 0000000..5bffdf7 Binary files /dev/null and b/ROPgadget/test-suite-binaries/elf-ARM64-bash differ diff --git a/ROPgadget/test-suite-binaries/elf-ARMv7-ls b/ROPgadget/test-suite-binaries/elf-ARMv7-ls new file mode 100644 index 0000000..3a2573f Binary files /dev/null and b/ROPgadget/test-suite-binaries/elf-ARMv7-ls differ diff --git a/ROPgadget/test-suite-binaries/elf-FreeBSD-x86 b/ROPgadget/test-suite-binaries/elf-FreeBSD-x86 new file mode 100644 index 0000000..4d13e98 Binary files /dev/null and b/ROPgadget/test-suite-binaries/elf-FreeBSD-x86 differ diff --git a/ROPgadget/test-suite-binaries/elf-Linux-x64 b/ROPgadget/test-suite-binaries/elf-Linux-x64 new file mode 100644 index 0000000..7db51f5 Binary files /dev/null and b/ROPgadget/test-suite-binaries/elf-Linux-x64 differ diff --git a/ROPgadget/test-suite-binaries/elf-Linux-x86 b/ROPgadget/test-suite-binaries/elf-Linux-x86 new file mode 100644 index 0000000..78b6959 Binary files /dev/null and b/ROPgadget/test-suite-binaries/elf-Linux-x86 differ diff --git a/ROPgadget/test-suite-binaries/elf-Linux-x86-NDH-chall b/ROPgadget/test-suite-binaries/elf-Linux-x86-NDH-chall new file mode 100644 index 0000000..6b3d280 Binary files /dev/null and b/ROPgadget/test-suite-binaries/elf-Linux-x86-NDH-chall differ diff --git a/ROPgadget/test-suite-binaries/elf-Mips-Defcon-20-pwn100 b/ROPgadget/test-suite-binaries/elf-Mips-Defcon-20-pwn100 new file mode 100644 index 0000000..3fc5532 Binary files /dev/null and b/ROPgadget/test-suite-binaries/elf-Mips-Defcon-20-pwn100 differ diff --git a/ROPgadget/test-suite-binaries/elf-PowerPC-bash b/ROPgadget/test-suite-binaries/elf-PowerPC-bash new file mode 100644 index 0000000..d443085 Binary files /dev/null and b/ROPgadget/test-suite-binaries/elf-PowerPC-bash differ diff --git a/ROPgadget/test-suite-binaries/elf-SparcV8-bash b/ROPgadget/test-suite-binaries/elf-SparcV8-bash new file mode 100644 index 0000000..501038f Binary files /dev/null and b/ROPgadget/test-suite-binaries/elf-SparcV8-bash differ diff --git a/ROPgadget/test-suite-binaries/elf-x64-bash-v4.1.5.1 b/ROPgadget/test-suite-binaries/elf-x64-bash-v4.1.5.1 new file mode 100644 index 0000000..0d861f2 Binary files /dev/null and b/ROPgadget/test-suite-binaries/elf-x64-bash-v4.1.5.1 differ diff --git a/ROPgadget/test-suite-binaries/elf-x86-bash-v4.1.5.1 b/ROPgadget/test-suite-binaries/elf-x86-bash-v4.1.5.1 new file mode 100644 index 0000000..a3a4bac Binary files /dev/null and b/ROPgadget/test-suite-binaries/elf-x86-bash-v4.1.5.1 differ diff --git a/ROPgadget/test-suite-binaries/macho-x64-ls b/ROPgadget/test-suite-binaries/macho-x64-ls new file mode 100644 index 0000000..d80548d Binary files /dev/null and b/ROPgadget/test-suite-binaries/macho-x64-ls differ diff --git a/ROPgadget/test-suite-binaries/macho-x86-ls b/ROPgadget/test-suite-binaries/macho-x86-ls new file mode 100644 index 0000000..e33a51e Binary files /dev/null and b/ROPgadget/test-suite-binaries/macho-x86-ls differ diff --git a/ROPgadget/test-suite-binaries/pe-Windows-ARMv7-Thumb2LE-HelloWorld b/ROPgadget/test-suite-binaries/pe-Windows-ARMv7-Thumb2LE-HelloWorld new file mode 100644 index 0000000..dd9a51b Binary files /dev/null and b/ROPgadget/test-suite-binaries/pe-Windows-ARMv7-Thumb2LE-HelloWorld differ diff --git a/ROPgadget/test-suite-binaries/pe-x64-cmd-v6.1.7601 b/ROPgadget/test-suite-binaries/pe-x64-cmd-v6.1.7601 new file mode 100644 index 0000000..dca94f4 Binary files /dev/null and b/ROPgadget/test-suite-binaries/pe-x64-cmd-v6.1.7601 differ diff --git a/ROPgadget/test-suite-binaries/pe-x86-cmd-v6.1.7600 b/ROPgadget/test-suite-binaries/pe-x86-cmd-v6.1.7600 new file mode 100644 index 0000000..9dc724d Binary files /dev/null and b/ROPgadget/test-suite-binaries/pe-x86-cmd-v6.1.7600 differ diff --git a/ROPgadget/test-suite-binaries/raw-x86.raw b/ROPgadget/test-suite-binaries/raw-x86.raw new file mode 100644 index 0000000..84baa4c --- /dev/null +++ b/ROPgadget/test-suite-binaries/raw-x86.raw @@ -0,0 +1 @@ +1ɉHH L$1 \ No newline at end of file diff --git a/ROPgadget/test-suite-binaries/ref_output.bz2 b/ROPgadget/test-suite-binaries/ref_output.bz2 new file mode 100644 index 0000000..b366286 Binary files /dev/null and b/ROPgadget/test-suite-binaries/ref_output.bz2 differ diff --git a/ROPgadget/test-suite-binaries/test.sh b/ROPgadget/test-suite-binaries/test.sh new file mode 100755 index 0000000..8fb5b11 --- /dev/null +++ b/ROPgadget/test-suite-binaries/test.sh @@ -0,0 +1,61 @@ +#!/bin/bash + +export PYTHONPATH=../ +if [ "$#" == "1" ] +then + RUN="python2 ../ROPgadget.py" +else + RUN="python3 ../ROPgadget.py" +fi + +rm -rf test_output + +FILES=`ls | sort -f` +for f in $FILES +do + if [ "$f" != "test.sh" ] && [ "$f" != "ref_output.bz2" ] && [ "$f" != "test_output" ] + then + echo "RUN $f" | tee -a ./test_output + if [ "$f" == "raw-x86.raw" ] + then + $RUN --rawArch=x86 --rawMode=32 --depth 5 --binary $f 1>> ./test_output + else + $RUN --depth 5 --binary $f 1>> ./test_output + fi + fi +done + +echo "RUN elf-Linux-x86 --ropchain" | tee -a ./test_output +$RUN --binary ./elf-Linux-x86 --ropchain 1>> ./test_output +echo "RUN elf-Linux-x86 --depth 3" | tee -a ./test_output +$RUN --binary ./elf-Linux-x86 --depth 3 1>> ./test_output +echo "RUN elf-Linux-x86 --string \"main\"" | tee -a ./test_output +$RUN --binary ./elf-Linux-x86 --string "main" 1>> ./test_output +echo "RUN elf-Linux-x86 --string \"m..n\"" | tee -a ./test_output +$RUN --binary ./elf-Linux-x86 --string "m..n" 1>> ./test_output +echo "RUN elf-Linux-x86 --opcode c9c3" | tee -a ./test_output +$RUN --binary ./elf-Linux-x86 --opcode c9c3 1>> ./test_output +echo "RUN elf-Linux-x86 --only \"mov|ret\"" | tee -a ./test_output +$RUN --binary ./elf-Linux-x86 --only "mov|ret" 1>> ./test_output +echo "RUN elf-Linux-x86 --only \"mov|pop|xor|ret\"" | tee -a ./test_output +$RUN --binary ./elf-Linux-x86 --only "mov|pop|xor|ret" 1>> ./test_output +echo "RUN elf-Linux-x86 --filter \"xchg|add|sub|cmov.*\"" | tee -a ./test_output +$RUN --binary ./elf-Linux-x86 --filter "xchg|add|sub|cmov.*" 1>> ./test_output +echo "RUN elf-Linux-x86 --norop --nosys" | tee -a ./test_output +$RUN --binary ./elf-Linux-x86 --norop --nosys 1>> ./test_output +echo "RUN elf-Linux-x86 --range 0x08041000-0x08042000" | tee -a ./test_output +$RUN --binary ./elf-Linux-x86 --range 0x08041000-0x08042000 1>> ./test_output +echo "RUN elf-Linux-x86 --string main --range 0x080c9aaa-0x080c9aba" | tee -a ./test_output +$RUN --binary ./elf-Linux-x86 --string main --range 0x080c9aaa-0x080c9aba 1>> ./test_output +echo "RUN elf-Linux-x86 --memstr \"/bin/sh\"" | tee -a ./test_output +$RUN --binary ./elf-Linux-x86 --memstr "/bin/sh" 1>> ./test_output +echo "RUN elf-Linux-x86 --badbytes \"00|01-1f|7f|42\"" | tee -a ./test_output +$RUN --binary ./elf-Linux-x86 --badbytes "00|01-1f|7f|42" 1>> ./test_output +echo "RUN Linux_lib64.so --offset 0xdeadbeef00000000" | tee -a ./test_output +$RUN --binary ./Linux_lib64.so --offset 0xdeadbeef00000000 1>> ./test_output +echo "RUN elf-ARMv7-ls --depth 5" | tee -a ./test_output +$RUN --binary ./elf-ARMv7-ls --depth 5 1>> ./test_output +echo "RUN elf-ARM64-bash --depth 5" | tee -a ./test_output +$RUN --binary ./elf-ARM64-bash --depth 5 1>> ./test_output + +diff test_output <(bunzip2 --stdout ref_output.bz2) 1>&2