include ROPGadget

This commit is contained in:
Liam Dalgarno 2020-11-25 15:38:46 +00:00
parent 688bb5a1be
commit 5f8099dde0
48 changed files with 3325 additions and 0 deletions

31
ROPgadget/.github/workflows/main.yml vendored Normal file
View File

@ -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 ..

4
ROPgadget/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
*.pyc
build
dist
ROPGadget.egg-info

42
ROPgadget/AUTHORS Normal file
View File

@ -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)

27
ROPgadget/LICENSE_BSD.txt Normal file
View File

@ -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.

114
ROPgadget/README.md Normal file
View File

@ -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 <binary>] [--opcode <opcodes>]
[--string <string>] [--memstr <string>] [--depth <nbyte>]
[--only <key>] [--filter <key>] [--range <start-end>]
[--badbytes <byte>] [--rawArch <arch>] [--rawMode <mode>]
[--rawEndian <endian>] [--re <re>] [--offset <hexaddr>]
[--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 <binary> Specify a binary filename to analyze
--opcode <opcodes> Search opcode in executable segment
--string <string> Search string in readable segment
--memstr <string> Search each byte in all readable segment
--depth <nbyte> Depth for search engine (default 10)
--only <key> Only show specific instructions
--filter <key> Suppress specific instructions
--range <start-end> Search between two addresses (0x...-0x...)
--badbytes <byte> Rejects specific bytes in the gadget's address
--rawArch <arch> Specify an arch for a raw file
--rawMode <mode> Specify a mode for a raw file
--rawEndian <endian> Specify an endianness for a raw file
--re <re> Regular expression
--offset <hexaddr> 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.</p>
Screenshots
-----------
<img src="http://shell-storm.org/project/ROPgadget/x64.png" alt="x64"></img>
<img src="http://shell-storm.org/project/ROPgadget/arm.png" alt="ARM"></img>
<img src="http://shell-storm.org/project/ROPgadget/sparc.png" alt="Sparc"></img>
<img src="http://shell-storm.org/project/ROPgadget/mips.png" alt="MIPS"></img>
<img src="http://shell-storm.org/project/ROPgadget/ppc.png" alt="PowerPC"></img>
<img src="http://shell-storm.org/project/ROPgadget/ropchain.png" alt="ROP chain"></img>

12
ROPgadget/ROPgadget.py Executable file
View File

@ -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()

View File

@ -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)

138
ROPgadget/ropgadget/args.py Normal file
View File

@ -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="<binary>", help="Specify a binary filename to analyze")
parser.add_argument("--opcode", type=str, metavar="<opcodes>", help="Search opcode in executable segment")
parser.add_argument("--string", type=str, metavar="<string>", help="Search string in readable segment")
parser.add_argument("--memstr", type=str, metavar="<string>", help="Search each byte in all readable segment")
parser.add_argument("--depth", type=int, metavar="<nbyte>", default=10, help="Depth for search engine (default 10)")
parser.add_argument("--only", type=str, metavar="<key>", help="Only show specific instructions")
parser.add_argument("--filter", type=str, metavar="<key>", help="Suppress specific mnemonics")
parser.add_argument("--range", type=str, metavar="<start-end>", default="0x0-0x0", help="Search between two addresses (0x...-0x...)")
parser.add_argument("--badbytes", type=str, metavar="<byte>", help="Rejects specific bytes in the gadget's address")
parser.add_argument("--rawArch", type=str, metavar="<arch>", help="Specify an arch for a raw file")
parser.add_argument("--rawMode", type=str, metavar="<mode>", help="Specify a mode for a raw file")
parser.add_argument("--rawEndian", type=str, metavar="<endian>", help="Specify an endianness for a raw file")
parser.add_argument("--re", type=str, metavar="<re>", help="Regular expression")
parser.add_argument("--offset", type=str, metavar="<hexaddr>", 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=<key> can't be used together")
if self.__args.noinstr and self.__args.re:
raise ValueError("[Error] --noinstr and --re=<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

View File

@ -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()

652
ROPgadget/ropgadget/core.py Normal file
View File

@ -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 <file> -- Load a binary")
return False
def do_EOF(self, s, silent=False):
return self.do_quit(s, silent)
def do_quit(self, s, silent=False):
return True
def help_quit(self):
print("Syntax: quit -- Terminates the application")
return False
def do_load(self, s, silent=False):
if self.__binary == None:
if not silent:
print("[-] No binary loaded.")
return False
if not silent:
print("[+] Loading gadgets, please wait...")
self.__getGadgets()
if not silent:
print("[+] Gadgets loaded !")
def help_load(self):
print("Syntax: load -- Load all gadgets")
return False
def do_display(self, s, silent=False):
self.__lookingForGadgets()
def help_display(self):
print("Syntax: display -- Display all gadgets loaded")
return False
def do_depth(self, s, silent=False):
try:
depth = int(s.split()[0])
except:
if not silent:
return self.help_depth()
return False
if depth <= 0:
if not silent:
print("[-] The depth value must be > 0")
return False
self.__options.depth = int(depth)
if not silent:
print("[+] Depth updated. You have to reload gadgets")
def help_depth(self):
print("Syntax: depth <value> -- Set the depth search engine")
return False
def do_badbytes(self, s, silent=False):
try:
bb = s.split()[0]
except:
if not silent:
return self.help_badbytes()
else:
return False
self.__options.badbytes = bb
if not silent:
print("[+] Bad bytes updated. You have to reload gadgets")
def help_badbytes(self):
print("Syntax: badbytes <badbyte1|badbyte2...> -- ")
return False
def __withK(self, listK, gadget):
if len(listK) == 0:
return True
for a in listK:
if a not in gadget:
return False
return True
def __withoutK(self, listK, gadget):
for a in listK:
if a in gadget:
return False
return True
def do_search(self, s, silent=False):
args = s.split()
if not len(args):
return self.help_search()
withK, withoutK = [], []
for a in args:
if a[0:1] == "!":
withoutK += [a[1:]]
else:
withK += [a]
if self.__checksBeforeManipulations() == False:
if not silent:
print("[-] You have to load a binary")
return False
arch = self.__binary.getArchMode()
for gadget in self.__gadgets:
vaddr = gadget["vaddr"]
insts = gadget["gadget"]
if self.__withK(withK, insts) and self.__withoutK(withoutK, insts):
# What to do if silent = True?
print(("0x%08x" %(vaddr) if arch == CS_MODE_32 else "0x%016x" %(vaddr)) + " : %s" %(insts))
def help_search(self):
print("Syntax: search <keyword1 keyword2 keyword3...> -- Filter with or without keywords")
print("keyword = with")
print("!keyword = without")
return False
def count(self):
return len(self.__gadgets)
def do_count(self, s, silent=False):
if not silent:
print("[+] %d loaded gadgets." % self.count())
def help_count(self):
print("Shows the number of loaded gadgets.")
return False
def do_filter(self, s, silent=False):
try:
self.__options.filter = s.split()[0]
except:
if not silent:
return self.help_filter()
return False
if not silent:
print("[+] Filter setted. You have to reload gadgets")
def help_filter(self):
print("Syntax: filter <filter1|filter2|...> - Suppress specific mnemonics")
return False
def do_only(self, s, silent=False):
try:
if s.lower() == "none":
self.__options.only = None
else:
self.__options.only = s.split()[0]
except:
if not silent:
return self.help_only()
return False
if not silent:
print("[+] Only setted. You have to reload gadgets")
def help_only(self):
print("Syntax: only <only1|only2|...> - Only show specific instructions")
return False
def do_range(self, s, silent=False):
try:
rangeS = int(s.split('-')[0], 16)
rangeE = int(s.split('-')[1], 16)
self.__options.range = s.split()[0]
except:
if not silent:
return self.help_range()
return False
if rangeS > rangeE:
if not silent:
print("[-] The start value must be greater than the end value")
return False
if not silent:
print("[+] Range setted. You have to reload gadgets")
def help_range(self):
print("Syntax: range <start-and> - Search between two addresses (0x...-0x...)")
return False
def do_settings(self, s, silent=False):
print("All: %s" %(self.__options.all))
print("Badbytes: %s" %(self.__options.badbytes))
print("Binary: %s" %(self.__options.binary))
print("Depth: %s" %(self.__options.depth))
print("Filter: %s" %(self.__options.filter))
print("Memstr: %s" %(self.__options.memstr))
print("MultiBr: %s" %(self.__options.multibr))
print("NoJOP: %s" %(self.__options.nojop))
print("NoROP: %s" %(self.__options.norop))
print("NoSYS: %s" %(self.__options.nosys))
print("Offset: %s" %(self.__options.offset))
print("Only: %s" %(self.__options.only))
print("Opcode: %s" %(self.__options.opcode))
print("ROPchain: %s" %(self.__options.ropchain))
print("Range: %s" %(self.__options.range))
print("RawArch: %s" %(self.__options.rawArch))
print("RawMode: %s" %(self.__options.rawMode))
print("RawEndian: %s" %(self.__options.rawEndian))
print("Re: %s" %(self.__options.re))
print("String: %s" %(self.__options.string))
print("Thumb: %s" %(self.__options.thumb))
def help_settings(self):
print("Display setting's environment")
return False
def do_nojop(self, s, silent=False):
try:
arg = s.split()[0]
except:
return self.help_nojop()
if arg == "enable":
self.__options.nojop = True
if not silent:
print("[+] NoJOP enable. You have to reload gadgets")
elif arg == "disable":
self.__options.nojop = False
if not silent:
print("[+] NoJOP disable. You have to reload gadgets")
else:
if not silent:
return self.help_nojop()
return False
def help_nojop(self):
print("Syntax: nojop <enable|disable> - Disable JOP search engin")
return False
def do_norop(self, s, silent=False):
try:
arg = s.split()[0]
except:
return self.help_norop()
if arg == "enable":
self.__options.norop = True
if not silent:
print("[+] NoROP enable. You have to reload gadgets")
elif arg == "disable":
self.__options.norop = False
if not silent:
print("[+] NoROP disable. You have to reload gadgets")
else:
if not silent:
return self.help_norop()
return False
def help_norop(self):
print("Syntax: norop <enable|disable> - Disable ROP search engin")
return False
def do_nosys(self, s, silent=False):
try:
arg = s.split()[0]
except:
return self.help_nosys()
if arg == "enable":
self.__options.nosys = True
if not silent:
print("[+] NoSYS enable. You have to reload gadgets")
elif arg == "disable":
self.__options.nosys = False
if not silent:
print("[+] NoSYS disable. You have to reload gadgets")
else:
if not silent:
return self.help_nosys()
return False
def help_nosys(self):
print("Syntax: nosys <enable|disable> - Disable SYS search engin")
return False
def do_thumb(self, s, silent=False):
try:
arg = s.split()[0]
except:
return self.help_thumb()
if arg == "enable":
self.__options.thumb = True
if not silent:
print("[+] Thumb enable. You have to reload gadgets")
elif arg == "disable":
self.__options.thumb = False
if not silent:
print("[+] Thumb disable. You have to reload gadgets")
else:
if not silent:
return self.help_thumb()
return False
def help_thumb(self):
print("Syntax: thumb <enable|disable> - Use the thumb mode for the search engine (ARM only)")
return False
def do_all(self, s, silent=False):
if s == "enable":
self.__options.all = True
if not silent:
print("[+] Showing all gadgets enabled. You have to reload gadgets")
elif s == "disable":
self.__options.all = False
if not silent:
print("[+] Showing all gadgets disabled. You have to reload gadgets")
else:
if not silent:
return self.help_all()
return False
def help_multibr(self):
print("Syntax: multibr <enable|disable> - Enable/Disable multiple branch gadgets")
return False
def do_multibr(self, s, silent=False):
if s == "enable":
self.__options.multibr = True
if not silent:
print("[+] Multiple branch gadgets enabled. You have to reload gadgets")
elif s == "disable":
self.__options.multibr = False
if not silent:
print("[+] Multiple branch gadgets disabled. You have to reload gadgets")
else:
if not silent:
return self.help_all()
return False
def help_all(self):
print("Syntax: all <enable|disable - Show all gadgets (disable removing duplicate gadgets)")
return False
def help_re(self):
print("Syntax: re <pattern1 | pattern2 |...> - Regular expression")
return False
def do_re(self, s, silent=False):
if s.lower() == 'none':
self.__options.re = None
elif s == "":
self.help_re()
silent = True
else:
self.__options.re = s
if not silent:
print("[+] Re setted. You have to reload gadgets")

View File

@ -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 <imm>
[b"\xcb", 1, 1], # retf
[b"\xca[\x00-\xff]{2}", 3, 1], # retf <imm>
# MPX
[b"\xf2\xc3", 2, 1], # ret
[b"\xf2\xc2[\x00-\xff]{2}", 4, 1], # ret <imm>
]
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

View File

@ -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

View File

@ -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"

View File

@ -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"

View File

@ -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("<I", bytes(self.__binary[60:64]))[0]
if self.__binary[self.__PEOffset:self.__PEOffset+4] != unhexlify(b"50450000"):
print("[Error] PE.__getPEOffset() - Bad PE signature")
return None
def __parsePEHeader(self):
PEheader = self.__binary[self.__PEOffset:]
self.__IMAGE_FILE_HEADER = IMAGE_FILE_HEADER.from_buffer_copy(PEheader)
def __parseOptHeader(self):
PEoptHeader = self.__binary[self.__PEOffset+24:self.__PEOffset+24+self.__IMAGE_FILE_HEADER.SizeOfOptionalHeader]
if unpack("<H", bytes(PEoptHeader[0:2]))[0] == PEFlags.IMAGE_NT_OPTIONAL_HDR32_MAGIC:
self.__IMAGE_OPTIONAL_HEADER = IMAGE_OPTIONAL_HEADER.from_buffer_copy(PEoptHeader)
elif unpack("<H", bytes(PEoptHeader[0:2]))[0] == PEFlags.IMAGE_NT_OPTIONAL_HDR64_MAGIC:
self.__IMAGE_OPTIONAL_HEADER = IMAGE_OPTIONAL_HEADER64.from_buffer_copy(PEoptHeader)
else:
print("[Error] PE.__parseOptHeader - Bad size header")
return None
def __parseSections(self):
baseSections = self.__PEOffset+24+self.__IMAGE_FILE_HEADER.SizeOfOptionalHeader
sizeSections = self.__IMAGE_FILE_HEADER.NumberOfSections * sizeof(IMAGE_SECTION_HEADER)
base = self.__binary[baseSections:baseSections+sizeSections]
for i in range(self.__IMAGE_FILE_HEADER.NumberOfSections):
sec = IMAGE_SECTION_HEADER.from_buffer_copy(base)
base = base[sizeof(IMAGE_SECTION_HEADER):]
self.__sections_l += [sec]
return 0
def getEntryPoint(self):
return self.__IMAGE_OPTIONAL_HEADER.ImageBase + self.__IMAGE_OPTIONAL_HEADER.AddressOfEntryPoint
def getDataSections(self):
ret = []
for section in self.__sections_l:
if section.Characteristics & 0x80000000:
ret += [{
"name" : section.Name,
"offset" : section.PointerToRawData,
"size" : section.SizeOfRawData,
"vaddr" : section.VirtualAddress + self.__IMAGE_OPTIONAL_HEADER.ImageBase,
"opcodes" : bytes(self.__binary[section.PointerToRawData:section.PointerToRawData+section.SizeOfRawData])
}]
return ret
def getExecSections(self):
ret = []
for section in self.__sections_l:
if section.Characteristics & 0x20000000:
ret += [{
"name" : section.Name,
"offset" : section.PointerToRawData,
"size" : section.SizeOfRawData,
"vaddr" : section.VirtualAddress + self.__IMAGE_OPTIONAL_HEADER.ImageBase,
"opcodes" : bytes(self.__binary[section.PointerToRawData:section.PointerToRawData+section.SizeOfRawData])
}]
return ret
def getArch(self):
if self.__IMAGE_FILE_HEADER.Machine == PEFlags.IMAGE_MACHINE_INTEL_386 or self.__IMAGE_FILE_HEADER.Machine == PEFlags.IMAGE_MACHINE_AMD_8664:
return CS_ARCH_X86
if self.__IMAGE_FILE_HEADER.Machine == PEFlags.IMAGE_FILE_MACHINE_ARM or self.__IMAGE_FILE_HEADER.Machine == PEFlags.IMAGE_FILE_MACHINE_ARMV7:
return CS_ARCH_ARM
else:
print("[Error] PE.getArch() - Bad Arch")
return None
def getArchMode(self):
if self.__IMAGE_OPTIONAL_HEADER.Magic == PEFlags.IMAGE_NT_OPTIONAL_HDR32_MAGIC:
return CS_MODE_32
elif self.__IMAGE_OPTIONAL_HEADER.Magic == PEFlags.IMAGE_NT_OPTIONAL_HDR64_MAGIC:
return CS_MODE_64
else:
print("[Error] PE.getArch() - Bad arch size")
return None
def getEndian(self):
# PE is little-endian only
return 0
def getFormat(self):
return "PE"

View File

@ -0,0 +1,72 @@
## -*- coding: utf-8 -*-
##
## Jonathan Salwan - 2014-05-12
##
## http://shell-storm.org
## http://twitter.com/JonathanSalwan
##
from capstone import *
class Raw(object):
def __init__(self, binary, arch, mode, endian):
self.__binary = bytearray(binary)
self.__arch = arch
self.__mode = mode
self.__endian = endian
def getEntryPoint(self):
return 0x0
def getExecSections(self):
return [{"name": "raw", "offset": 0x0, "size": len(self.__binary), "vaddr": 0x0, "opcodes": bytes(self.__binary)}]
def getDataSections(self):
return []
def getArch(self):
arch = {
"x86": CS_ARCH_X86,
"arm": CS_ARCH_ARM,
"arm64": CS_ARCH_ARM64,
"sparc": CS_ARCH_SPARC,
"mips": CS_ARCH_MIPS,
"ppc": CS_ARCH_PPC
}
try:
ret = arch[self.__arch]
except:
print("[Error] Raw.getArch() - Architecture not supported. Only supported: x86 arm arm64 sparc mips ppc")
return None
return ret
def getArchMode(self):
mode = {
"32": CS_MODE_32,
"64": CS_MODE_64,
"arm": CS_MODE_ARM,
"thumb": CS_MODE_THUMB
}
try:
ret = mode[self.__mode]
except:
print("[Error] Raw.getArchMode() - Mode not supported. Only supported: 32 64 arm thumb")
return None
return ret
def getEndian(self):
if self.getArch() == CS_ARCH_X86:
return 0
endian ={
"little": 0,
"big": CS_MODE_BIG_ENDIAN
}
try:
ret = endian[self.__endian]
except:
print("[Error] Raw.getArchEndian() - Endianness not supported. Only supported: little big")
return None
return ret
def getFormat(self):
return "Raw"

View File

@ -0,0 +1,107 @@
## -*- coding: utf-8 -*-
##
## Christoffer Brodd-Reijer - 2014-07-20 - ROPgadget tool
##
## http://twitter.com/ephracis
## http://shell-storm.org/project/ROPgadget/
##
import sys
from capstone import *
from ctypes import *
from binascii import *
from ropgadget.loaders.macho import *
class FAT_HEADER(BigEndianStructure):
_fields_ = [
("magic", c_uint),
("nfat_arch", c_uint)
]
class FAT_ARC(BigEndianStructure):
_fields_ = [
("cputype", c_uint),
("cpusubtype", c_uint),
("offset", c_uint),
("size", c_uint),
("align", 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_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 Universal binary """
class UNIVERSAL(object):
def __init__(self, binary):
self.__binary = bytearray(binary)
self.__machoBinaries = []
self.__fatHeader = None
self.__rawLoadCmd = None
self.__sections_l = []
self.__setHeader()
self.__setBinaries()
def __setHeader(self):
self.__fatHeader = FAT_HEADER.from_buffer_copy(self.__binary)
def __setBinaries(self):
offset = 8
for i in xrange(self.__fatHeader.nfat_arch):
header = FAT_ARC.from_buffer_copy(self.__binary[offset:])
rawBinary = self.__binary[header.offset:header.offset+header.size]
if rawBinary[:4] == unhexlify(b"cefaedfe") or rawBinary[:4] == unhexlify(b"cffaedfe"):
self.__machoBinaries.append(MACHO(rawBinary))
else:
print("[Error] Binary #"+str(i+1)+" in Universal binary has an unsupported format")
offset += sizeof(header)
def getExecSections(self):
ret = []
for binary in self.__machoBinaries:
ret += binary.getExecSections()
return ret
def getDataSections(self):
ret = []
for binary in self.__machoBinaries:
ret += binary.getDataSections()
return ret
def getFormat(self):
return "Universal"
# TODO: These three will just return whatever is in the first binary.
# Perhaps the rest of ROPgadget should support loading multiple binaries?
def getEntryPoint(self):
for binary in self.__machoBinaries:
return binary.getEntryPoint()
def getArch(self):
for binary in self.__machoBinaries:
return binary.getArch()
def getArchMode(self):
for binary in self.__machoBinaries:
return binary.getArchMode()
def getEndian(self):
for binary in self.__machoBinaries:
return binary.getEndian()
if sys.version_info.major == 3:
xrange = range

View File

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

View File

@ -0,0 +1,22 @@
## -*- coding: utf-8 -*-
##
## Jonathan Salwan - 2014-05-17 - ROPgadget tool
##
## http://twitter.com/JonathanSalwan
## http://shell-storm.org/project/ROPgadget/
##
def deleteDuplicateGadgets(currentGadgets):
gadgets_content_set = set()
unique_gadgets = []
for gadget in currentGadgets:
gad = gadget["gadget"]
if gad in gadgets_content_set:
continue
gadgets_content_set.add(gad)
unique_gadgets += [gadget]
return unique_gadgets
def alphaSortgadgets(currentGadgets):
return sorted(currentGadgets, key=lambda key : key["gadget"])

View File

@ -0,0 +1,10 @@
## -*- coding: utf-8 -*-
##
## Jonathan Salwan - 2014-05-13
##
## http://shell-storm.org
## http://twitter.com/JonathanSalwan
##
import ropgadget.ropchain.ropmaker
import ropgadget.ropchain.arch

View File

@ -0,0 +1,11 @@
#!/usr/bin/env python2
## -*- coding: utf-8 -*-
##
## Jonathan Salwan - 2014-05-13
##
## http://shell-storm.org
## http://twitter.com/JonathanSalwan
##
import ropgadget.ropchain.arch.ropmakerx86
import ropgadget.ropchain.arch.ropmakerx64

View File

@ -0,0 +1,225 @@
#!/usr/bin/env python2
## -*- coding: utf-8 -*-
##
## Jonathan Salwan - 2014-05-13
## Florian Meier - 2014-08-31 - The 64b ROP chain generation
##
## http://shell-storm.org
## http://twitter.com/JonathanSalwan
##
import re
from capstone import *
class ROPMakerX64(object):
def __init__(self, binary, gadgets, liboffset=0x0):
self.__binary = binary
self.__gadgets = gadgets
# If it's a library, we have the option to add an offset to the addresses
self.__liboffset = liboffset
self.__generate()
def __lookingForWrite4Where(self, gadgetsAlreadyTested):
for gadget in self.__gadgets:
if gadget in gadgetsAlreadyTested:
continue
f = gadget["gadget"].split(" ; ")[0]
regex = re.search("mov .* ptr \[(?P<dst>([(rax)|(rbx)|(rcx)|(rdx)|(rsi)|(rdi)|(r9)|(r10)|(r11)|(r12)|(r13)|(r14)|(r15)]{3}))\], (?P<src>([(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('<Q', 0x%016x) # padding without overwrite %s" %(regAlreadSetted[reg], reg))
except KeyError:
print("\tp += pack('<Q', 0x4141414141414141) # padding")
def __buildRopChain(self, write4where, popDst, popSrc, xorSrc, xorRax, incRax, popRdi, popRsi, popRdx, syscall):
sects = self.__binary.getDataSections()
dataAddr = None
for s in sects:
if s["name"] == ".data":
dataAddr = s["vaddr"] + self.__liboffset
if dataAddr is None:
print("\n[-] Error - Can't find a writable section")
return
print("\t#!/usr/bin/env python2")
print("\t# execve generated by ROPgadget\n")
print("\tfrom struct import pack\n")
print("\t# Padding goes here")
print("\tp = ''\n")
print("\tp += pack('<Q', 0x%016x) # %s" %(popDst["vaddr"], popDst["gadget"]))
print("\tp += pack('<Q', 0x%016x) # @ .data" %(dataAddr))
self.__padding(popDst, {})
print("\tp += pack('<Q', 0x%016x) # %s" %(popSrc["vaddr"], popSrc["gadget"]))
print("\tp += '/bin//sh'")
self.__padding(popSrc, {popDst["gadget"].split()[1]: dataAddr}) # Don't overwrite reg dst
print("\tp += pack('<Q', 0x%016x) # %s" %(write4where["vaddr"], write4where["gadget"]))
self.__padding(write4where, {})
print("\tp += pack('<Q', 0x%016x) # %s" %(popDst["vaddr"], popDst["gadget"]))
print("\tp += pack('<Q', 0x%016x) # @ .data + 8" %(dataAddr + 8))
self.__padding(popDst, {})
print("\tp += pack('<Q', 0x%016x) # %s" %(xorSrc["vaddr"], xorSrc["gadget"]))
self.__padding(xorSrc, {})
print("\tp += pack('<Q', 0x%016x) # %s" %(write4where["vaddr"], write4where["gadget"]))
self.__padding(write4where, {})
print("\tp += pack('<Q', 0x%016x) # %s" %(popRdi["vaddr"], popRdi["gadget"]))
print("\tp += pack('<Q', 0x%016x) # @ .data" %(dataAddr))
self.__padding(popRdi, {})
print("\tp += pack('<Q', 0x%016x) # %s" %(popRsi["vaddr"], popRsi["gadget"]))
print("\tp += pack('<Q', 0x%016x) # @ .data + 8" %(dataAddr + 8))
self.__padding(popRsi, {"rdi": dataAddr}) # Don't overwrite rdi
print("\tp += pack('<Q', 0x%016x) # %s" %(popRdx["vaddr"], popRdx["gadget"]))
print("\tp += pack('<Q', 0x%016x) # @ .data + 8" %(dataAddr + 8))
self.__padding(popRdx, {"rdi": dataAddr, "rsi": dataAddr + 8}) # Don't overwrite rdi and rsi
print("\tp += pack('<Q', 0x%016x) # %s" %(xorRax["vaddr"], xorRax["gadget"]))
self.__padding(xorRax, {"rdi": dataAddr, "rsi": dataAddr + 8}) # Don't overwrite rdi and rsi
for i in range(59):
print("\tp += pack('<Q', 0x%016x) # %s" %(incRax["vaddr"], incRax["gadget"]))
self.__padding(incRax, {"rdi": dataAddr, "rsi": dataAddr + 8}) # Don't overwrite rdi and rsi
print("\tp += pack('<Q', 0x%016x) # %s" %(syscall["vaddr"], syscall["gadget"]))
def __generate(self):
# To find the smaller gadget
self.__gadgets.reverse()
print("\nROP chain generation\n===========================================================")
print("\n- Step 1 -- Write-what-where gadgets\n")
gadgetsAlreadyTested = []
while True:
write4where = self.__lookingForWrite4Where(gadgetsAlreadyTested)
if not write4where:
print("\t[-] Can't find the 'mov qword ptr [r64], r64' gadget")
return
popDst = self.__lookingForSomeThing("pop %s" %(write4where[1]))
if not popDst:
print("\t[-] Can't find the 'pop %s' gadget. Try with another 'mov [reg], reg'\n" %(write4where[1]))
gadgetsAlreadyTested += [write4where[0]]
continue
popSrc = self.__lookingForSomeThing("pop %s" %(write4where[2]))
if not popSrc:
print("\t[-] Can't find the 'pop %s' gadget. Try with another 'mov [reg], reg'\n" %(write4where[2]))
gadgetsAlreadyTested += [write4where[0]]
continue
xorSrc = self.__lookingForSomeThing("xor %s, %s" %(write4where[2], write4where[2]))
if not xorSrc:
print("\t[-] Can't find the 'xor %s, %s' gadget. Try with another 'mov [reg], reg'\n" %(write4where[2], write4where[2]))
gadgetsAlreadyTested += [write4where[0]]
continue
else:
break
print("\n- Step 2 -- Init syscall number gadgets\n")
xorRax = self.__lookingForSomeThing("xor rax, rax")
if not xorRax:
print("\t[-] Can't find the 'xor rax, rax' instruction")
return
incRax = self.__lookingForSomeThing("inc rax")
incEax = self.__lookingForSomeThing("inc eax")
incAx = self.__lookingForSomeThing("inc al")
addRax = self.__lookingForSomeThing("add rax, 1")
addEax = self.__lookingForSomeThing("add eax, 1")
addAx = self.__lookingForSomeThing("add al, 1")
instr = [incRax, incEax, incAx, addRax, addEax, addAx]
if all(v is None for v in instr):
print("\t[-] Can't find the 'inc rax' or 'add rax, 1' instruction")
return
for i in instr:
if i is not None:
incRax = i
break
print("\n- Step 3 -- Init syscall arguments gadgets\n")
popRdi = self.__lookingForSomeThing("pop rdi")
if not popRdi:
print("\t[-] Can't find the 'pop rdi' instruction")
return
popRsi = self.__lookingForSomeThing("pop rsi")
if not popRsi:
print("\t[-] Can't find the 'pop rsi' instruction")
return
popRdx = self.__lookingForSomeThing("pop rdx")
if not popRdx:
print("\t[-] Can't find the 'pop rdx' instruction")
return
print("\n- Step 4 -- Syscall gadget\n")
syscall = self.__lookingForSomeThing("syscall")
if not syscall:
print("\t[-] Can't find the 'syscall' instruction")
return
print("\n- Step 5 -- Build the ROP chain\n")
self.__buildRopChain(write4where[0], popDst, popSrc, xorSrc, xorRax, incRax, popRdi, popRsi, popRdx, syscall)

View File

@ -0,0 +1,223 @@
#!/usr/bin/env python2
## -*- coding: utf-8 -*-
##
## Jonathan Salwan - 2014-05-13
##
## http://shell-storm.org
## http://twitter.com/JonathanSalwan
##
import re
from capstone import *
class ROPMakerX86(object):
def __init__(self, binary, gadgets, liboffset=0x0):
self.__binary = binary
self.__gadgets = gadgets
# If it's a library, we have the option to add an offset to the addresses
self.__liboffset = liboffset
self.__generate()
def __lookingForWrite4Where(self, gadgetsAlreadyTested):
for gadget in self.__gadgets:
if gadget in gadgetsAlreadyTested:
continue
f = gadget["gadget"].split(" ; ")[0]
# regex -> mov dword ptr [r32], r32
regex = re.search("mov dword ptr \[(?P<dst>([(eax)|(ebx)|(ecx)|(edx)|(esi)|(edi)]{3}))\], (?P<src>([(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('<I', 0x%08x) # padding without overwrite %s" %(regAlreadSetted[reg], reg))
except KeyError:
print("\tp += pack('<I', 0x41414141) # padding")
def __buildRopChain(self, write4where, popDst, popSrc, xorSrc, xorEax, incEax, popEbx, popEcx, popEdx, syscall):
sects = self.__binary.getDataSections()
dataAddr = None
for s in sects:
if s["name"] == ".data":
dataAddr = s["vaddr"] + self.__liboffset
if dataAddr == None:
print("\n[-] Error - Can't find a writable section")
return
print("\t#!/usr/bin/env python2")
print("\t# execve generated by ROPgadget\n" )
print("\tfrom struct import pack\n")
print("\t# Padding goes here")
print("\tp = ''\n")
print("\tp += pack('<I', 0x%08x) # %s" %(popDst["vaddr"], popDst["gadget"]))
print("\tp += pack('<I', 0x%08x) # @ .data" %(dataAddr))
self.__padding(popDst, {})
print("\tp += pack('<I', 0x%08x) # %s" %(popSrc["vaddr"], popSrc["gadget"]))
print("\tp += '/bin'")
self.__padding(popSrc, {popDst["gadget"].split()[1]: dataAddr}) # Don't overwrite reg dst
print("\tp += pack('<I', 0x%08x) # %s" %(write4where["vaddr"], write4where["gadget"]))
self.__padding(write4where, {})
print("\tp += pack('<I', 0x%08x) # %s" %(popDst["vaddr"], popDst["gadget"]))
print("\tp += pack('<I', 0x%08x) # @ .data + 4" %(dataAddr + 4))
self.__padding(popDst, {})
print("\tp += pack('<I', 0x%08x) # %s" %(popSrc["vaddr"], popSrc["gadget"]))
print("\tp += '//sh'")
self.__padding(popSrc, {popDst["gadget"].split()[1]: dataAddr + 4}) # Don't overwrite reg dst
print("\tp += pack('<I', 0x%08x) # %s" %(write4where["vaddr"], write4where["gadget"]))
self.__padding(write4where, {})
print("\tp += pack('<I', 0x%08x) # %s" %(popDst["vaddr"], popDst["gadget"]))
print("\tp += pack('<I', 0x%08x) # @ .data + 8" %(dataAddr + 8))
self.__padding(popDst, {})
print("\tp += pack('<I', 0x%08x) # %s" %(xorSrc["vaddr"], xorSrc["gadget"]))
self.__padding(xorSrc, {})
print("\tp += pack('<I', 0x%08x) # %s" %(write4where["vaddr"], write4where["gadget"]))
self.__padding(write4where, {})
print("\tp += pack('<I', 0x%08x) # %s" %(popEbx["vaddr"], popEbx["gadget"]))
print("\tp += pack('<I', 0x%08x) # @ .data" %(dataAddr))
self.__padding(popEbx, {})
print("\tp += pack('<I', 0x%08x) # %s" %(popEcx["vaddr"], popEcx["gadget"]))
print("\tp += pack('<I', 0x%08x) # @ .data + 8" %(dataAddr + 8))
self.__padding(popEcx, {"ebx": dataAddr}) # Don't overwrite ebx
print("\tp += pack('<I', 0x%08x) # %s" %(popEdx["vaddr"], popEdx["gadget"]))
print("\tp += pack('<I', 0x%08x) # @ .data + 8" %(dataAddr + 8))
self.__padding(popEdx, {"ebx": dataAddr, "ecx": dataAddr + 8}) # Don't overwrite ebx and ecx
print("\tp += pack('<I', 0x%08x) # %s" %(xorEax["vaddr"], xorEax["gadget"]))
self.__padding(xorEax, {"ebx": dataAddr, "ecx": dataAddr + 8}) # Don't overwrite ebx and ecx
for i in range(11):
print("\tp += pack('<I', 0x%08x) # %s" %(incEax["vaddr"], incEax["gadget"]))
self.__padding(incEax, {"ebx": dataAddr, "ecx": dataAddr + 8}) # Don't overwrite ebx and ecx
print("\tp += pack('<I', 0x%08x) # %s" %(syscall["vaddr"], syscall["gadget"]))
def __generate(self):
# To find the smaller gadget
self.__gadgets.reverse()
print("\nROP chain generation\n===========================================================")
print("\n- Step 1 -- Write-what-where gadgets\n")
gadgetsAlreadyTested = []
while True:
write4where = self.__lookingForWrite4Where(gadgetsAlreadyTested)
if not write4where:
print("\t[-] Can't find the 'mov dword ptr [r32], r32' gadget")
return
popDst = self.__lookingForSomeThing("pop %s" %(write4where[1]))
if not popDst:
print("\t[-] Can't find the 'pop %s' gadget. Try with another 'mov [reg], reg'\n" %(write4where[1]))
gadgetsAlreadyTested += [write4where[0]]
continue
popSrc = self.__lookingForSomeThing("pop %s" %(write4where[2]))
if not popSrc:
print("\t[-] Can't find the 'pop %s' gadget. Try with another 'mov [reg], reg'\n" %(write4where[2]))
gadgetsAlreadyTested += [write4where[0]]
continue
xorSrc = self.__lookingForSomeThing("xor %s, %s" %(write4where[2], write4where[2]))
if not xorSrc:
print("\t[-] Can't find the 'xor %s, %s' gadget. Try with another 'mov [r], r'\n" %(write4where[2], write4where[2]))
gadgetsAlreadyTested += [write4where[0]]
continue
else:
break
print("\n- Step 2 -- Init syscall number gadgets\n")
xorEax = self.__lookingForSomeThing("xor eax, eax")
if not xorEax:
print("\t[-] Can't find the 'xor eax, eax' instruction")
return
incEax = self.__lookingForSomeThing("inc eax")
if not incEax:
print("\t[-] Can't find the 'inc eax' instruction")
return
print("\n- Step 3 -- Init syscall arguments gadgets\n")
popEbx = self.__lookingForSomeThing("pop ebx")
if not popEbx:
print("\t[-] Can't find the 'pop ebx' instruction")
return
popEcx = self.__lookingForSomeThing("pop ecx")
if not popEcx:
print("\t[-] Can't find the 'pop ecx' instruction")
return
popEdx = self.__lookingForSomeThing("pop edx")
if not popEdx:
print("\t[-] Can't find the 'pop edx' instruction")
return
print("\n- Step 4 -- Syscall gadget\n")
syscall = self.__lookingForSomeThing("int 0x80")
if not syscall:
print("\t[-] Can't find the 'syscall' instruction")
return
print("\n- Step 5 -- Build the ROP chain\n")
self.__buildRopChain(write4where[0], popDst, popSrc, xorSrc, xorEax, incEax, popEbx, popEcx, popEdx, syscall)

View File

@ -0,0 +1,35 @@
## -*- coding: utf-8 -*-
##
## Jonathan Salwan - 2014-05-13
##
## http://shell-storm.org
## http://twitter.com/JonathanSalwan
##
from capstone import *
from ropgadget.ropchain.arch.ropmakerx86 import *
from ropgadget.ropchain.arch.ropmakerx64 import *
class ROPMaker(object):
def __init__(self, binary, gadgets, offset):
self.__binary = binary
self.__gadgets = gadgets
self.__offset = offset
self.__handlerArch()
def __handlerArch(self):
if self.__binary.getArch() == CS_ARCH_X86 \
and self.__binary.getArchMode() == CS_MODE_32 \
and self.__binary.getFormat() == "ELF":
ROPMakerX86(self.__binary, self.__gadgets, self.__offset)
elif self.__binary.getArch() == CS_ARCH_X86 \
and self.__binary.getArchMode() == CS_MODE_64 \
and self.__binary.getFormat() == "ELF":
ROPMakerX64(self.__binary, self.__gadgets, self.__offset)
else:
print("\n[Error] ROPMaker.__handlerArch - Arch not supported yet for the rop chain generation")

View File

@ -0,0 +1,38 @@
## -*- coding: utf-8 -*-
##
## Jonathan Salwan - 2014-05-12 - ROPgadget tool
##
## http://twitter.com/JonathanSalwan
## http://shell-storm.org/project/ROPgadget/
##
import re
try:
import httplib
except ImportError:
import http.client as httplib
from ropgadget.version import *
class UpdateAlert(object):
@staticmethod
def checkUpdate():
try:
conn = httplib.HTTPSConnection("raw.githubusercontent.com", 443)
conn.request("GET", "/JonathanSalwan/ROPgadget/master/ropgadget/version.py")
except:
print("Can't connect to raw.githubusercontent.com")
return
d = conn.getresponse().read()
majorVersion = re.search("MAJOR_VERSION.+=.+(?P<value>[\d])", d).group("value")
minorVersion = re.search("MINOR_VERSION.+=.+(?P<value>[\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.")

View File

@ -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)

12
ROPgadget/scripts/ROPgadget Executable file
View File

@ -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()

67
ROPgadget/setup.py Normal file
View File

@ -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'
]
)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1 @@
1и┴H┴H L$1юц

Binary file not shown.

View File

@ -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