From 5f8099dde03829e7ecb14a71a34fedc7dd014d59 Mon Sep 17 00:00:00 2001
From: Liam Dalgarno
Date: Wed, 25 Nov 2020 15:38:46 +0000
Subject: [PATCH] include ROPGadget
---
ROPgadget/.github/workflows/main.yml | 31 +
ROPgadget/.gitignore | 4 +
ROPgadget/AUTHORS | 42 ++
ROPgadget/LICENSE_BSD.txt | 27 +
ROPgadget/README.md | 114 +++
ROPgadget/ROPgadget.py | 12 +
ROPgadget/ropgadget/__init__.py | 29 +
ROPgadget/ropgadget/args.py | 138 ++++
ROPgadget/ropgadget/binary.py | 72 ++
ROPgadget/ropgadget/core.py | 652 ++++++++++++++++++
ROPgadget/ropgadget/gadgets.py | 358 ++++++++++
ROPgadget/ropgadget/loaders/__init__.py | 12 +
ROPgadget/ropgadget/loaders/elf.py | 350 ++++++++++
ROPgadget/ropgadget/loaders/macho.py | 211 ++++++
ROPgadget/ropgadget/loaders/pe.py | 233 +++++++
ROPgadget/ropgadget/loaders/raw.py | 72 ++
ROPgadget/ropgadget/loaders/universal.py | 107 +++
ROPgadget/ropgadget/options.py | 145 ++++
ROPgadget/ropgadget/rgutils.py | 22 +
ROPgadget/ropgadget/ropchain/__init__.py | 10 +
ROPgadget/ropgadget/ropchain/arch/__init__.py | 11 +
.../ropgadget/ropchain/arch/ropmakerx64.py | 225 ++++++
.../ropgadget/ropchain/arch/ropmakerx86.py | 223 ++++++
ROPgadget/ropgadget/ropchain/ropmaker.py | 35 +
ROPgadget/ropgadget/updateAlert.py | 38 +
ROPgadget/ropgadget/version.py | 11 +
ROPgadget/scripts/ROPgadget | 12 +
ROPgadget/setup.py | 67 ++
.../UNIVERSAL-x86-x64-libSystem.B.dylib | Bin 0 -> 59088 bytes
ROPgadget/test-suite-binaries/elf-ARM64-bash | Bin 0 -> 847400 bytes
ROPgadget/test-suite-binaries/elf-ARMv7-ls | Bin 0 -> 90808 bytes
ROPgadget/test-suite-binaries/elf-FreeBSD-x86 | Bin 0 -> 236630 bytes
ROPgadget/test-suite-binaries/elf-Linux-x64 | Bin 0 -> 863316 bytes
ROPgadget/test-suite-binaries/elf-Linux-x86 | Bin 0 -> 773246 bytes
.../elf-Linux-x86-NDH-chall | Bin 0 -> 608977 bytes
.../elf-Mips-Defcon-20-pwn100 | Bin 0 -> 6034000 bytes
.../test-suite-binaries/elf-PowerPC-bash | Bin 0 -> 954028 bytes
.../test-suite-binaries/elf-SparcV8-bash | Bin 0 -> 856496 bytes
.../test-suite-binaries/elf-x64-bash-v4.1.5.1 | Bin 0 -> 926536 bytes
.../test-suite-binaries/elf-x86-bash-v4.1.5.1 | Bin 0 -> 811156 bytes
ROPgadget/test-suite-binaries/macho-x64-ls | Bin 0 -> 39584 bytes
ROPgadget/test-suite-binaries/macho-x86-ls | Bin 0 -> 35696 bytes
.../pe-Windows-ARMv7-Thumb2LE-HelloWorld | Bin 0 -> 6656 bytes
.../test-suite-binaries/pe-x64-cmd-v6.1.7601 | Bin 0 -> 345088 bytes
.../test-suite-binaries/pe-x86-cmd-v6.1.7600 | Bin 0 -> 301568 bytes
ROPgadget/test-suite-binaries/raw-x86.raw | 1 +
ROPgadget/test-suite-binaries/ref_output.bz2 | Bin 0 -> 3388808 bytes
ROPgadget/test-suite-binaries/test.sh | 61 ++
48 files changed, 3325 insertions(+)
create mode 100644 ROPgadget/.github/workflows/main.yml
create mode 100644 ROPgadget/.gitignore
create mode 100644 ROPgadget/AUTHORS
create mode 100644 ROPgadget/LICENSE_BSD.txt
create mode 100644 ROPgadget/README.md
create mode 100755 ROPgadget/ROPgadget.py
create mode 100644 ROPgadget/ropgadget/__init__.py
create mode 100644 ROPgadget/ropgadget/args.py
create mode 100644 ROPgadget/ropgadget/binary.py
create mode 100644 ROPgadget/ropgadget/core.py
create mode 100644 ROPgadget/ropgadget/gadgets.py
create mode 100644 ROPgadget/ropgadget/loaders/__init__.py
create mode 100644 ROPgadget/ropgadget/loaders/elf.py
create mode 100644 ROPgadget/ropgadget/loaders/macho.py
create mode 100644 ROPgadget/ropgadget/loaders/pe.py
create mode 100644 ROPgadget/ropgadget/loaders/raw.py
create mode 100644 ROPgadget/ropgadget/loaders/universal.py
create mode 100644 ROPgadget/ropgadget/options.py
create mode 100644 ROPgadget/ropgadget/rgutils.py
create mode 100644 ROPgadget/ropgadget/ropchain/__init__.py
create mode 100644 ROPgadget/ropgadget/ropchain/arch/__init__.py
create mode 100644 ROPgadget/ropgadget/ropchain/arch/ropmakerx64.py
create mode 100644 ROPgadget/ropgadget/ropchain/arch/ropmakerx86.py
create mode 100644 ROPgadget/ropgadget/ropchain/ropmaker.py
create mode 100644 ROPgadget/ropgadget/updateAlert.py
create mode 100644 ROPgadget/ropgadget/version.py
create mode 100755 ROPgadget/scripts/ROPgadget
create mode 100644 ROPgadget/setup.py
create mode 100644 ROPgadget/test-suite-binaries/UNIVERSAL-x86-x64-libSystem.B.dylib
create mode 100644 ROPgadget/test-suite-binaries/elf-ARM64-bash
create mode 100644 ROPgadget/test-suite-binaries/elf-ARMv7-ls
create mode 100644 ROPgadget/test-suite-binaries/elf-FreeBSD-x86
create mode 100644 ROPgadget/test-suite-binaries/elf-Linux-x64
create mode 100644 ROPgadget/test-suite-binaries/elf-Linux-x86
create mode 100644 ROPgadget/test-suite-binaries/elf-Linux-x86-NDH-chall
create mode 100644 ROPgadget/test-suite-binaries/elf-Mips-Defcon-20-pwn100
create mode 100644 ROPgadget/test-suite-binaries/elf-PowerPC-bash
create mode 100644 ROPgadget/test-suite-binaries/elf-SparcV8-bash
create mode 100644 ROPgadget/test-suite-binaries/elf-x64-bash-v4.1.5.1
create mode 100644 ROPgadget/test-suite-binaries/elf-x86-bash-v4.1.5.1
create mode 100644 ROPgadget/test-suite-binaries/macho-x64-ls
create mode 100644 ROPgadget/test-suite-binaries/macho-x86-ls
create mode 100644 ROPgadget/test-suite-binaries/pe-Windows-ARMv7-Thumb2LE-HelloWorld
create mode 100644 ROPgadget/test-suite-binaries/pe-x64-cmd-v6.1.7601
create mode 100644 ROPgadget/test-suite-binaries/pe-x86-cmd-v6.1.7600
create mode 100644 ROPgadget/test-suite-binaries/raw-x86.raw
create mode 100644 ROPgadget/test-suite-binaries/ref_output.bz2
create mode 100755 ROPgadget/test-suite-binaries/test.sh
diff --git a/ROPgadget/.github/workflows/main.yml b/ROPgadget/.github/workflows/main.yml
new file mode 100644
index 0000000..d87c8fb
--- /dev/null
+++ b/ROPgadget/.github/workflows/main.yml
@@ -0,0 +1,31 @@
+name: CI
+
+on:
+ push:
+ branches:
+ - master
+ pull_request:
+
+jobs:
+ build:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v2
+ - name: Build
+ run: |
+ python2 -m pip install --upgrade setuptools wheel
+ python3 -m pip install --upgrade setuptools wheel
+ python2 setup.py sdist bdist_wheel
+ python3 setup.py sdist bdist_wheel
+ - name: Install
+ run: |
+ python2 -m pip install dist/ROPGadget*py2*.whl
+ python3 -m pip install dist/ROPGadget*py3*.whl
+ - name: Run tests
+ run: |
+ cd test-suite-binaries
+ ./test.sh
+ ./test.sh python2
+ cd ..
diff --git a/ROPgadget/.gitignore b/ROPgadget/.gitignore
new file mode 100644
index 0000000..c4db969
--- /dev/null
+++ b/ROPgadget/.gitignore
@@ -0,0 +1,4 @@
+*.pyc
+build
+dist
+ROPGadget.egg-info
diff --git a/ROPgadget/AUTHORS b/ROPgadget/AUTHORS
new file mode 100644
index 0000000..8e36c76
--- /dev/null
+++ b/ROPgadget/AUTHORS
@@ -0,0 +1,42 @@
+
+ROPgadget project (>= v5.0)
+===================================================
+
+Authors:
+
+* Jonathan Salwan
+
+
+Contributors:
+
+* hugsy - Has added the --offset option
+* Francisco Falcon - Has fixed the --badbytes option and added the count/loaddb/save2db console features
+* Kevin Hamacher - Added some functionality for using ROPgadget as library. Also added support for x86(_64) jmp {r,e}sp/call {r,e}sp instructions
+* Florian Meier - Added the 64b ROP chain generation
+* Álvaro Felipe Melchor (increase performance)
+* Sascha Schirra (features, python3, bugs fix)
+* Stephen Edwards (increase performance)
+* Mikhail Davidov (features)
+* penguin-wwy (features)
+
+
+
+ROPgadget initial project (<= v4.0.3)
+===================================================
+
+Authors:
+
+* Jonathan Salwan
+* Allan Wirth
+
+
+Contributors:
+
+* Hellman (Bug Fix)
+* Axel "0vercl0k" Souchet (Bug Fix)
+* k3rensk1 (Bug repport)
+* brianairb (Bug Fix)
+* cao (Bug Fix)
+* dark-rose (Made searching for gadgets faster)
+* Dennis Semakin (Bug Fix)
+
diff --git a/ROPgadget/LICENSE_BSD.txt b/ROPgadget/LICENSE_BSD.txt
new file mode 100644
index 0000000..e52c765
--- /dev/null
+++ b/ROPgadget/LICENSE_BSD.txt
@@ -0,0 +1,27 @@
+This is the software license for the ROPgadget project.
+
+Copyright (c) 2016. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+* Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+* Neither the name of the developers nor the names of its
+ contributors may be used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
diff --git a/ROPgadget/README.md b/ROPgadget/README.md
new file mode 100644
index 0000000..ec20899
--- /dev/null
+++ b/ROPgadget/README.md
@@ -0,0 +1,114 @@
+ROPgadget Tool
+================
+
+This tool lets you search your gadgets on your binaries to facilitate your ROP exploitation.
+ROPgadget supports ELF/PE/Mach-O format on x86, x64, ARM, ARM64, PowerPC, SPARC and MIPS
+architectures. Since the version 5, ROPgadget has a new core which is written in Python
+using Capstone disassembly framework for the gadgets search engine - The older version can
+be found in the Archives directory but it will not be maintained.
+
+Install
+-------
+
+If you want to use ROPgadget, you have to install [Capstone](http://www.capstone-engine.org/) first.
+
+For the Capstone's installation on nix machine:
+
+ $ sudo pip install capstone
+
+Capstone supports multi-platforms (windows, ios, android, cygwin...). For the cross-compilation,
+please refer to the https://github.com/aquynh/capstone/blob/master/COMPILE.TXT file.
+
+After Capstone is installed, ROPgadget can be used as a standalone tool:
+
+ $ ROPgadget.py
+
+Or installed into the Python site-packages library, and executed from $PATH.
+
+ $ python setup.py install
+ $ ROPgadget
+
+Or installed from PyPi
+
+ $ pip install ropgadget
+ $ ROPgadget
+
+Usage
+-----
+
+ usage: ROPgadget.py [-h] [-v] [-c] [--binary ] [--opcode ]
+ [--string ] [--memstr ] [--depth ]
+ [--only ] [--filter ] [--range ]
+ [--badbytes ] [--rawArch ] [--rawMode ]
+ [--rawEndian ] [--re ] [--offset ]
+ [--ropchain] [--thumb] [--console] [--norop] [--nojop]
+ [--callPreceded] [--nosys] [--multibr] [--all] [--noinstr]
+ [--dump]
+
+ optional arguments:
+ -h, --help show this help message and exit
+ -v, --version Display the ROPgadget's version
+ -c, --checkUpdate Checks if a new version is available
+ --binary Specify a binary filename to analyze
+ --opcode Search opcode in executable segment
+ --string Search string in readable segment
+ --memstr Search each byte in all readable segment
+ --depth Depth for search engine (default 10)
+ --only Only show specific instructions
+ --filter Suppress specific instructions
+ --range Search between two addresses (0x...-0x...)
+ --badbytes Rejects specific bytes in the gadget's address
+ --rawArch Specify an arch for a raw file
+ --rawMode Specify a mode for a raw file
+ --rawEndian Specify an endianness for a raw file
+ --re Regular expression
+ --offset Specify an offset for gadget addresses
+ --ropchain Enable the ROP chain generation
+ --thumb Use the thumb mode for the search engine (ARM only)
+ --console Use an interactive console for search engine
+ --norop Disable ROP search engine
+ --nojop Disable JOP search engine
+ --callPreceded Only show gadgets which are call-preceded
+ --nosys Disable SYS search engine
+ --multibr Enable multiple branch gadgets
+ --all Disables the removal of duplicate gadgets
+ --noinstr Disable the gadget instructions console printing
+ --dump Outputs the gadget bytes
+
+
+How can I contribute ?
+----------------------
+
+- Use Z3 to solve the ROP chain
+- Add system gadgets for PPC, Sparc, ARM64 (Gadgets.addSYSGadgets())
+- Manage big endian in Mach-O format like the ELF classe.
+- Everything you think is cool :)
+
+Bugs/Patches/Contact
+--------------------
+
+Please report bugs, submit pull requests, etc. on github at https://github.com/JonathanSalwan/ROPgadget
+The offical page is on shell-storm.org at http://shell-storm.org/project/ROPgadget/
+
+License
+-------
+
+See COPYING and the license header on all source files. For the files in the dependencies/ there are
+individual licenses in each folder.
+
+
+Screenshots
+-----------
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ROPgadget/ROPgadget.py b/ROPgadget/ROPgadget.py
new file mode 100755
index 0000000..777f2d5
--- /dev/null
+++ b/ROPgadget/ROPgadget.py
@@ -0,0 +1,12 @@
+#!/usr/bin/env python
+## -*- coding: utf-8 -*-
+##
+## Jonathan Salwan - 2014-05-12 - ROPgadget tool
+##
+## http://twitter.com/JonathanSalwan
+## http://shell-storm.org/project/ROPgadget/
+##
+
+import ropgadget
+
+ropgadget.main()
diff --git a/ROPgadget/ropgadget/__init__.py b/ROPgadget/ropgadget/__init__.py
new file mode 100644
index 0000000..2201b59
--- /dev/null
+++ b/ROPgadget/ropgadget/__init__.py
@@ -0,0 +1,29 @@
+## -*- coding: utf-8 -*-
+##
+## Jonathan Salwan - 2014-05-12 - ROPgadget tool
+##
+## http://twitter.com/JonathanSalwan
+## http://shell-storm.org/project/ROPgadget/
+##
+
+import ropgadget.args
+import ropgadget.binary
+import ropgadget.core
+import ropgadget.gadgets
+import ropgadget.options
+import ropgadget.rgutils
+import ropgadget.updateAlert
+import ropgadget.version
+import ropgadget.loaders
+import ropgadget.ropchain
+
+def main():
+ import sys
+ from ropgadget.args import Args
+ from ropgadget.core import Core
+ try:
+ args = Args()
+ except ValueError as e:
+ print(e)
+ sys.exit(-1)
+ sys.exit(0 if Core(args.getArgs()).analyze() else 1)
diff --git a/ROPgadget/ropgadget/args.py b/ROPgadget/ropgadget/args.py
new file mode 100644
index 0000000..1bb53b0
--- /dev/null
+++ b/ROPgadget/ropgadget/args.py
@@ -0,0 +1,138 @@
+## -*- coding: utf-8 -*-
+##
+## Jonathan Salwan - 2014-05-12 - ROPgadget tool
+##
+## http://twitter.com/JonathanSalwan
+## http://shell-storm.org/project/ROPgadget/
+##
+
+import argparse
+import sys
+
+from ropgadget.updateAlert import UpdateAlert
+from ropgadget.version import *
+
+class Args(object):
+ def __init__(self, arguments=None):
+ self.__args = None
+ custom_arguments_provided = True
+
+ # If no custom arguments are provided, use the program arguments
+ if not arguments:
+ arguments = sys.argv[1:]
+ custom_arguments_provided = False
+
+
+ self.__parse(arguments, custom_arguments_provided)
+
+ def __parse(self, arguments, custom_arguments_provided=False):
+ parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter,
+ description="""description:
+ ROPgadget lets you search your gadgets on a binary. It supports several
+ file formats and architectures and uses the Capstone disassembler for
+ the search engine.
+
+formats supported:
+ - ELF
+ - PE
+ - Mach-O
+ - Raw
+
+architectures supported:
+ - x86
+ - x86-64
+ - ARM
+ - ARM64
+ - MIPS
+ - PowerPC
+ - Sparc
+""",
+ epilog="""examples:
+ ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86
+ ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 --ropchain
+ ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 --depth 3
+ ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 --string "main"
+ ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 --string "m..n"
+ ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 --opcode c9c3
+ ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 --only "mov|ret"
+ ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 --only "mov|pop|xor|ret"
+ ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 --filter "xchg|add|sub|cmov.*"
+ ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 --norop --nosys
+ ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 --range 0x08041000-0x08042000
+ ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 --string main --range 0x080c9aaa-0x080c9aba
+ ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 --memstr "/bin/sh"
+ ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 --console
+ ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 --badbytes "00|01-1f|7f|42"
+ ROPgadget.py --binary ./test-suite-binaries/Linux_lib64.so --offset 0xdeadbeef00000000
+ ROPgadget.py --binary ./test-suite-binaries/elf-ARMv7-ls --depth 5
+ ROPgadget.py --binary ./test-suite-binaries/elf-ARM64-bash --depth 5
+ ROPgadget.py --binary ./test-suite-binaries/raw-x86.raw --rawArch=x86 --rawMode=32""")
+
+ parser.add_argument("-v", "--version", action="store_true", help="Display the ROPgadget's version")
+ parser.add_argument("-c", "--checkUpdate", action="store_true", help="Checks if a new version is available")
+ parser.add_argument("--binary", type=str, metavar="", help="Specify a binary filename to analyze")
+ parser.add_argument("--opcode", type=str, metavar="", help="Search opcode in executable segment")
+ parser.add_argument("--string", type=str, metavar="", help="Search string in readable segment")
+ parser.add_argument("--memstr", type=str, metavar="", help="Search each byte in all readable segment")
+ parser.add_argument("--depth", type=int, metavar="", default=10, help="Depth for search engine (default 10)")
+ parser.add_argument("--only", type=str, metavar="", help="Only show specific instructions")
+ parser.add_argument("--filter", type=str, metavar="", help="Suppress specific mnemonics")
+ parser.add_argument("--range", type=str, metavar="", default="0x0-0x0", help="Search between two addresses (0x...-0x...)")
+ parser.add_argument("--badbytes", type=str, metavar="", help="Rejects specific bytes in the gadget's address")
+ parser.add_argument("--rawArch", type=str, metavar="", help="Specify an arch for a raw file")
+ parser.add_argument("--rawMode", type=str, metavar="", help="Specify a mode for a raw file")
+ parser.add_argument("--rawEndian", type=str, metavar="", help="Specify an endianness for a raw file")
+ parser.add_argument("--re", type=str, metavar="", help="Regular expression")
+ parser.add_argument("--offset", type=str, metavar="", help="Specify an offset for gadget addresses")
+ parser.add_argument("--ropchain", action="store_true", help="Enable the ROP chain generation")
+ parser.add_argument("--thumb" , action="store_true", help="Use the thumb mode for the search engine (ARM only)")
+ parser.add_argument("--console", action="store_true", help="Use an interactive console for search engine")
+ parser.add_argument("--norop", action="store_true", help="Disable ROP search engine")
+ parser.add_argument("--nojop", action="store_true", help="Disable JOP search engine")
+ parser.add_argument("--callPreceded", action="store_true", help="Only show gadgets which are call-preceded")
+ parser.add_argument("--nosys", action="store_true", help="Disable SYS search engine")
+ parser.add_argument("--multibr", action="store_true", help="Enable multiple branch gadgets")
+ parser.add_argument("--all", action="store_true", help="Disables the removal of duplicate gadgets")
+ parser.add_argument("--noinstr", action="store_true", help="Disable the gadget instructions console printing")
+ parser.add_argument("--dump", action="store_true", help="Outputs the gadget bytes")
+ parser.add_argument("--silent", action="store_true", help="Disables printing of gadgets during analysis")
+
+ self.__args = parser.parse_args(arguments)
+
+ if self.__args.noinstr and self.__args.only:
+ raise ValueError("[Error] --noinstr and --only= can't be used together")
+
+ if self.__args.noinstr and self.__args.re:
+ raise ValueError("[Error] --noinstr and --re= can't be used together")
+
+ if self.__args.version:
+ self.__printVersion()
+ sys.exit(0)
+
+ elif self.__args.checkUpdate:
+ UpdateAlert().checkUpdate()
+ sys.exit(0)
+
+ elif self.__args.depth < 2:
+ raise ValueError("[Error] The depth must be >= 2")
+
+ elif not custom_arguments_provided and not self.__args.binary and not self.__args.console:
+ raise ValueError("[Error] Need a binary filename (--binary/--console or --help)")
+
+ elif self.__args.range:
+ try:
+ rangeS = int(self.__args.range.split('-')[0], 16)
+ rangeE = int(self.__args.range.split('-')[1], 16)
+ except:
+ raise ValueError("[Error] A range must be set in hexadecimal. Ex: 0x08041000-0x08042000")
+ if rangeS > rangeE:
+ raise ValueError("[Error] The start value must be greater than end value")
+
+ def __printVersion(self):
+ print("Version: %s" %(PYROPGADGET_VERSION))
+ print("Author: Jonathan Salwan" )
+ print("Author page: https://twitter.com/JonathanSalwan" )
+ print("Project page: http://shell-storm.org/project/ROPgadget/" )
+
+ def getArgs(self):
+ return self.__args
diff --git a/ROPgadget/ropgadget/binary.py b/ROPgadget/ropgadget/binary.py
new file mode 100644
index 0000000..b40e0fe
--- /dev/null
+++ b/ROPgadget/ropgadget/binary.py
@@ -0,0 +1,72 @@
+## -*- coding: utf-8 -*-
+##
+## Jonathan Salwan - 2014-05-12 - ROPgadget tool
+##
+## http://twitter.com/JonathanSalwan
+## http://shell-storm.org/project/ROPgadget/
+##
+
+from ropgadget.loaders.elf import *
+from ropgadget.loaders.pe import *
+from ropgadget.loaders.raw import *
+from ropgadget.loaders.macho import *
+from ropgadget.loaders.universal import *
+from binascii import unhexlify
+
+class Binary(object):
+ def __init__(self, options):
+ self.__fileName = options.binary
+ self.__rawBinary = None
+ self.__binary = None
+
+ try:
+ fd = open(self.__fileName, "rb")
+ self.__rawBinary = fd.read()
+ fd.close()
+ except:
+ print("[Error] Can't open the binary or binary not found")
+ return None
+
+ if options.rawArch and options.rawMode:
+ self.__binary = Raw(self.__rawBinary, options.rawArch, options.rawMode, options.rawEndian)
+ elif self.__rawBinary[:4] == unhexlify(b"7f454c46"):
+ self.__binary = ELF(self.__rawBinary)
+ elif self.__rawBinary[:2] == unhexlify(b"4d5a"):
+ self.__binary = PE(self.__rawBinary)
+ elif self.__rawBinary[:4] == unhexlify(b"cafebabe"):
+ self.__binary = UNIVERSAL(self.__rawBinary)
+ elif self.__rawBinary[:4] == unhexlify(b"cefaedfe") or self.__rawBinary[:4] == unhexlify(b"cffaedfe"):
+ self.__binary = MACHO(self.__rawBinary)
+ else:
+ print("[Error] Binary format not supported")
+ return None
+
+ def getFileName(self):
+ return self.__fileName
+
+ def getRawBinary(self):
+ return self.__rawBinary
+
+ def getBinary(self):
+ return self.__binary
+
+ def getEntryPoint(self):
+ return self.__binary.getEntryPoint()
+
+ def getDataSections(self):
+ return self.__binary.getDataSections()
+
+ def getExecSections(self):
+ return self.__binary.getExecSections()
+
+ def getArch(self):
+ return self.__binary.getArch()
+
+ def getArchMode(self):
+ return self.__binary.getArchMode()
+
+ def getEndian(self):
+ return self.__binary.getEndian()
+
+ def getFormat(self):
+ return self.__binary.getFormat()
diff --git a/ROPgadget/ropgadget/core.py b/ROPgadget/ropgadget/core.py
new file mode 100644
index 0000000..6cebb95
--- /dev/null
+++ b/ROPgadget/ropgadget/core.py
@@ -0,0 +1,652 @@
+## -*- coding: utf-8 -*-
+##
+## Jonathan Salwan - 2014-05-17 - ROPgadget tool
+##
+## http://twitter.com/JonathanSalwan
+## http://shell-storm.org/project/ROPgadget/
+##
+
+import cmd
+import os
+import re
+import codecs
+import ropgadget.rgutils as rgutils
+import binascii
+
+from ropgadget.binary import Binary
+from capstone import CS_MODE_32
+from ropgadget.gadgets import Gadgets
+from ropgadget.options import Options
+from ropgadget.ropchain.ropmaker import ROPMaker
+
+class Core(cmd.Cmd):
+ def __init__(self, options):
+ cmd.Cmd.__init__(self)
+ self.__options = options
+ self.__binary = None
+ self.__gadgets = []
+ self.__offset = 0
+ self.prompt = '(ROPgadget)> '
+
+
+ def __checksBeforeManipulations(self):
+ if self.__binary == None or self.__binary.getBinary() == None or self.__binary.getArch() == None or self.__binary.getArchMode() == None or self.__binary.getEndian() == None:
+ return False
+ return True
+
+ def _sectionInRange(self, section):
+ """
+ given a section and a range, edit the section so that all opcodes are within the range
+ """
+ if self.__options.range == "0x0-0x0":
+ return section
+
+ rangeStart, rangeEnd = map(lambda x:int(x, 16), self.__options.range.split('-'))
+
+ sectionStart = section['vaddr']
+ sectionEnd = sectionStart + section['size']
+
+ opcodes = section['opcodes']
+ if rangeEnd < sectionStart or rangeStart > sectionEnd:
+ return None
+ if rangeStart > sectionStart:
+ diff = rangeStart - sectionStart
+ opcodes = opcodes[diff:]
+ section['vaddr'] += diff
+ section['offset'] += diff
+ section['size'] -= diff
+ if rangeEnd < sectionEnd:
+ diff = sectionEnd - rangeEnd
+ opcodes = opcodes[:-diff]
+ section['size'] -= diff
+
+ if not section['size']:
+ return None
+ section['opcodes'] = opcodes
+ return section
+
+ def __getGadgets(self):
+ if self.__checksBeforeManipulations() == False:
+ return False
+
+ G = Gadgets(self.__binary, self.__options, self.__offset)
+ execSections = self.__binary.getExecSections()
+
+ # Find ROP/JOP/SYS gadgets
+ self.__gadgets = []
+ for section in execSections:
+ section = self._sectionInRange(section)
+ if not section: continue
+ if not self.__options.norop: self.__gadgets += G.addROPGadgets(section)
+ if not self.__options.nojop: self.__gadgets += G.addJOPGadgets(section)
+ if not self.__options.nosys: self.__gadgets += G.addSYSGadgets(section)
+
+ # Delete duplicate gadgets
+ if not self.__options.all and not self.__options.noinstr:
+ self.__gadgets = rgutils.deleteDuplicateGadgets(self.__gadgets)
+
+ # Applicate some Options
+ self.__gadgets = Options(self.__options, self.__binary, self.__gadgets).getGadgets()
+
+ # Sorted alphabetically
+ if not self.__options.noinstr:
+ self.__gadgets = rgutils.alphaSortgadgets(self.__gadgets)
+
+ return True
+
+
+ def __lookingForGadgets(self):
+
+ if self.__checksBeforeManipulations() == False:
+ return False
+
+ if self.__options.silent:
+ return True
+
+ arch = self.__binary.getArchMode()
+ print("Gadgets information\n============================================================")
+ for gadget in self.__gadgets:
+ vaddr = gadget["vaddr"]
+ insts = gadget.get("gadget", "")
+ bytesStr = " // " + binascii.hexlify(gadget["bytes"]).decode('utf8') if self.__options.dump else ""
+
+ print(("0x%08x" %(vaddr) if arch == CS_MODE_32 else "0x%016x" %(vaddr)) +
+ (" : %s" %(insts) if insts else "") + bytesStr)
+
+ print("\nUnique gadgets found: %d" %(len(self.__gadgets)))
+ return True
+
+
+ def __lookingForAString(self, string):
+
+ if self.__checksBeforeManipulations() == False:
+ return False
+
+ if self.__options.silent:
+ return True
+
+ dataSections = self.__binary.getDataSections()
+ arch = self.__binary.getArchMode()
+ print("Strings information\n============================================================")
+ for section in dataSections:
+ section = self._sectionInRange(section)
+ if not section: continue
+ allRef = [m.start() for m in re.finditer(string.encode(), section["opcodes"])]
+ for ref in allRef:
+ vaddr = self.__offset + section["vaddr"] + ref
+ match = section["opcodes"][ref:ref+len(string)]
+ print(("0x%08x" %(vaddr) if arch == CS_MODE_32 else "0x%016x" %(vaddr)) + " : %s" %(match.decode()))
+ return True
+
+
+ def __lookingForOpcodes(self, opcodes):
+ import binascii
+
+ if self.__checksBeforeManipulations() == False:
+ return False
+
+ if self.__options.silent:
+ return True
+
+ execSections = self.__binary.getExecSections()
+ arch = self.__binary.getArchMode()
+ print("Opcodes information\n============================================================")
+ for section in execSections:
+ section = self._sectionInRange(section)
+ if not section: continue
+ allRef = [m.start() for m in re.finditer(re.escape(binascii.unhexlify(opcodes)), section["opcodes"])]
+ for ref in allRef:
+ vaddr = self.__offset + section["vaddr"] + ref
+ print(("0x%08x" %(vaddr) if arch == CS_MODE_32 else "0x%016x" %(vaddr)) + " : %s" %(opcodes))
+ return True
+
+
+ def __lookingForMemStr(self, memstr):
+ if self.__checksBeforeManipulations() == False:
+ return False
+
+ if self.__options.silent:
+ return True
+
+ sections = self.__binary.getExecSections()
+ sections += self.__binary.getDataSections()
+ arch = self.__binary.getArchMode()
+ print("Memory bytes information\n=======================================================")
+ chars = list(memstr)
+ for char in chars:
+ try:
+ for section in sections:
+ section = self._sectionInRange(section)
+ if not section: continue
+ allRef = [m.start() for m in re.finditer(char.encode('utf-8'), section["opcodes"])]
+ for ref in allRef:
+ vaddr = self.__offset + section["vaddr"] + ref
+ print(("0x%08x" %(vaddr) if arch == CS_MODE_32 else "0x%016x" %(vaddr)) + " : '%c'" %(char))
+ raise
+ except:
+ pass
+ return True
+
+
+ def analyze(self):
+
+ try:
+ self.__offset = int(self.__options.offset, 16) if self.__options.offset else 0
+ except ValueError:
+ print("[Error] The offset must be in hexadecimal")
+ return False
+
+ if self.__options.console:
+ if self.__options.binary:
+ self.__binary = Binary(self.__options)
+ if self.__checksBeforeManipulations() == False:
+ return False
+ self.cmdloop()
+ return True
+
+ self.__binary = Binary(self.__options)
+ if self.__checksBeforeManipulations() == False:
+ return False
+
+ if self.__options.string: return self.__lookingForAString(self.__options.string)
+ elif self.__options.opcode: return self.__lookingForOpcodes(self.__options.opcode)
+ elif self.__options.memstr: return self.__lookingForMemStr(self.__options.memstr)
+ else:
+ self.__getGadgets()
+ self.__lookingForGadgets()
+ if self.__options.ropchain:
+ ROPMaker(self.__binary, self.__gadgets, self.__offset)
+ return True
+
+
+ def gadgets(self):
+ return self.__gadgets
+
+
+
+
+ # Console methods ============================================
+
+ def do_binary(self, s, silent=False):
+ # Do not split the filename with spaces since it might contain
+ # whitespaces
+ if len(s) == 0:
+ if not silent:
+ return self.help_binary()
+ return False
+
+ binary = s
+
+ self.__options.binary = binary
+ self.__binary = Binary(self.__options)
+ if self.__checksBeforeManipulations() == False:
+ return False
+
+ if not silent:
+ print("[+] Binary loaded")
+
+
+ def help_binary(self):
+ print("Syntax: binary -- Load a binary")
+ return False
+
+
+ def do_EOF(self, s, silent=False):
+ return self.do_quit(s, silent)
+
+ def do_quit(self, s, silent=False):
+ return True
+
+
+ def help_quit(self):
+ print("Syntax: quit -- Terminates the application")
+ return False
+
+
+ def do_load(self, s, silent=False):
+
+ if self.__binary == None:
+ if not silent:
+ print("[-] No binary loaded.")
+ return False
+
+ if not silent:
+ print("[+] Loading gadgets, please wait...")
+ self.__getGadgets()
+
+ if not silent:
+ print("[+] Gadgets loaded !")
+
+
+ def help_load(self):
+ print("Syntax: load -- Load all gadgets")
+ return False
+
+
+ def do_display(self, s, silent=False):
+ self.__lookingForGadgets()
+
+
+ def help_display(self):
+ print("Syntax: display -- Display all gadgets loaded")
+ return False
+
+
+ def do_depth(self, s, silent=False):
+ try:
+ depth = int(s.split()[0])
+ except:
+ if not silent:
+ return self.help_depth()
+ return False
+ if depth <= 0:
+ if not silent:
+ print("[-] The depth value must be > 0")
+ return False
+ self.__options.depth = int(depth)
+
+ if not silent:
+ print("[+] Depth updated. You have to reload gadgets")
+
+
+ def help_depth(self):
+ print("Syntax: depth -- Set the depth search engine")
+ return False
+
+
+ def do_badbytes(self, s, silent=False):
+ try:
+ bb = s.split()[0]
+ except:
+ if not silent:
+ return self.help_badbytes()
+ else:
+ return False
+ self.__options.badbytes = bb
+
+ if not silent:
+ print("[+] Bad bytes updated. You have to reload gadgets")
+
+
+ def help_badbytes(self):
+ print("Syntax: badbytes -- ")
+ return False
+
+
+ def __withK(self, listK, gadget):
+ if len(listK) == 0:
+ return True
+ for a in listK:
+ if a not in gadget:
+ return False
+ return True
+
+ def __withoutK(self, listK, gadget):
+ for a in listK:
+ if a in gadget:
+ return False
+ return True
+
+ def do_search(self, s, silent=False):
+ args = s.split()
+ if not len(args):
+ return self.help_search()
+ withK, withoutK = [], []
+ for a in args:
+ if a[0:1] == "!":
+ withoutK += [a[1:]]
+ else:
+ withK += [a]
+ if self.__checksBeforeManipulations() == False:
+ if not silent:
+ print("[-] You have to load a binary")
+ return False
+ arch = self.__binary.getArchMode()
+ for gadget in self.__gadgets:
+ vaddr = gadget["vaddr"]
+ insts = gadget["gadget"]
+ if self.__withK(withK, insts) and self.__withoutK(withoutK, insts):
+ # What to do if silent = True?
+ print(("0x%08x" %(vaddr) if arch == CS_MODE_32 else "0x%016x" %(vaddr)) + " : %s" %(insts))
+
+
+ def help_search(self):
+ print("Syntax: search -- Filter with or without keywords")
+ print("keyword = with")
+ print("!keyword = without")
+ return False
+
+
+ def count(self):
+ return len(self.__gadgets)
+
+ def do_count(self, s, silent=False):
+ if not silent:
+ print("[+] %d loaded gadgets." % self.count())
+
+
+ def help_count(self):
+ print("Shows the number of loaded gadgets.")
+ return False
+
+
+ def do_filter(self, s, silent=False):
+ try:
+ self.__options.filter = s.split()[0]
+ except:
+ if not silent:
+ return self.help_filter()
+ return False
+
+ if not silent:
+ print("[+] Filter setted. You have to reload gadgets")
+
+
+ def help_filter(self):
+ print("Syntax: filter - Suppress specific mnemonics")
+ return False
+
+
+ def do_only(self, s, silent=False):
+ try:
+ if s.lower() == "none":
+ self.__options.only = None
+ else:
+ self.__options.only = s.split()[0]
+ except:
+ if not silent:
+ return self.help_only()
+ return False
+
+ if not silent:
+ print("[+] Only setted. You have to reload gadgets")
+
+
+ def help_only(self):
+ print("Syntax: only - Only show specific instructions")
+ return False
+
+
+ def do_range(self, s, silent=False):
+ try:
+ rangeS = int(s.split('-')[0], 16)
+ rangeE = int(s.split('-')[1], 16)
+ self.__options.range = s.split()[0]
+ except:
+ if not silent:
+ return self.help_range()
+ return False
+
+ if rangeS > rangeE:
+ if not silent:
+ print("[-] The start value must be greater than the end value")
+ return False
+
+ if not silent:
+ print("[+] Range setted. You have to reload gadgets")
+
+
+ def help_range(self):
+ print("Syntax: range - Search between two addresses (0x...-0x...)")
+ return False
+
+
+ def do_settings(self, s, silent=False):
+ print("All: %s" %(self.__options.all))
+ print("Badbytes: %s" %(self.__options.badbytes))
+ print("Binary: %s" %(self.__options.binary))
+ print("Depth: %s" %(self.__options.depth))
+ print("Filter: %s" %(self.__options.filter))
+ print("Memstr: %s" %(self.__options.memstr))
+ print("MultiBr: %s" %(self.__options.multibr))
+ print("NoJOP: %s" %(self.__options.nojop))
+ print("NoROP: %s" %(self.__options.norop))
+ print("NoSYS: %s" %(self.__options.nosys))
+ print("Offset: %s" %(self.__options.offset))
+ print("Only: %s" %(self.__options.only))
+ print("Opcode: %s" %(self.__options.opcode))
+ print("ROPchain: %s" %(self.__options.ropchain))
+ print("Range: %s" %(self.__options.range))
+ print("RawArch: %s" %(self.__options.rawArch))
+ print("RawMode: %s" %(self.__options.rawMode))
+ print("RawEndian: %s" %(self.__options.rawEndian))
+ print("Re: %s" %(self.__options.re))
+ print("String: %s" %(self.__options.string))
+ print("Thumb: %s" %(self.__options.thumb))
+
+ def help_settings(self):
+ print("Display setting's environment")
+ return False
+
+
+ def do_nojop(self, s, silent=False):
+ try:
+ arg = s.split()[0]
+ except:
+ return self.help_nojop()
+
+ if arg == "enable":
+ self.__options.nojop = True
+ if not silent:
+ print("[+] NoJOP enable. You have to reload gadgets")
+
+ elif arg == "disable":
+ self.__options.nojop = False
+ if not silent:
+ print("[+] NoJOP disable. You have to reload gadgets")
+
+ else:
+ if not silent:
+ return self.help_nojop()
+ return False
+
+
+ def help_nojop(self):
+ print("Syntax: nojop - Disable JOP search engin")
+ return False
+
+
+ def do_norop(self, s, silent=False):
+ try:
+ arg = s.split()[0]
+ except:
+ return self.help_norop()
+
+ if arg == "enable":
+ self.__options.norop = True
+ if not silent:
+ print("[+] NoROP enable. You have to reload gadgets")
+
+ elif arg == "disable":
+ self.__options.norop = False
+ if not silent:
+ print("[+] NoROP disable. You have to reload gadgets")
+
+ else:
+ if not silent:
+ return self.help_norop()
+ return False
+
+
+ def help_norop(self):
+ print("Syntax: norop - Disable ROP search engin")
+ return False
+
+
+ def do_nosys(self, s, silent=False):
+ try:
+ arg = s.split()[0]
+ except:
+ return self.help_nosys()
+
+ if arg == "enable":
+ self.__options.nosys = True
+ if not silent:
+ print("[+] NoSYS enable. You have to reload gadgets")
+
+ elif arg == "disable":
+ self.__options.nosys = False
+ if not silent:
+ print("[+] NoSYS disable. You have to reload gadgets")
+
+ else:
+ if not silent:
+ return self.help_nosys()
+
+ return False
+
+
+ def help_nosys(self):
+ print("Syntax: nosys - Disable SYS search engin")
+ return False
+
+
+ def do_thumb(self, s, silent=False):
+ try:
+ arg = s.split()[0]
+ except:
+ return self.help_thumb()
+
+ if arg == "enable":
+ self.__options.thumb = True
+ if not silent:
+ print("[+] Thumb enable. You have to reload gadgets")
+
+ elif arg == "disable":
+ self.__options.thumb = False
+ if not silent:
+ print("[+] Thumb disable. You have to reload gadgets")
+
+ else:
+ if not silent:
+ return self.help_thumb()
+ return False
+
+
+ def help_thumb(self):
+ print("Syntax: thumb - Use the thumb mode for the search engine (ARM only)")
+ return False
+
+
+ def do_all(self, s, silent=False):
+ if s == "enable":
+ self.__options.all = True
+ if not silent:
+ print("[+] Showing all gadgets enabled. You have to reload gadgets")
+
+ elif s == "disable":
+ self.__options.all = False
+ if not silent:
+ print("[+] Showing all gadgets disabled. You have to reload gadgets")
+
+ else:
+ if not silent:
+ return self.help_all()
+
+ return False
+
+
+ def help_multibr(self):
+ print("Syntax: multibr - Enable/Disable multiple branch gadgets")
+ return False
+
+
+ def do_multibr(self, s, silent=False):
+ if s == "enable":
+ self.__options.multibr = True
+ if not silent:
+ print("[+] Multiple branch gadgets enabled. You have to reload gadgets")
+
+ elif s == "disable":
+ self.__options.multibr = False
+ if not silent:
+ print("[+] Multiple branch gadgets disabled. You have to reload gadgets")
+
+ else:
+ if not silent:
+ return self.help_all()
+
+ return False
+
+
+ def help_all(self):
+ print("Syntax: all - Regular expression")
+ return False
+
+
+ def do_re(self, s, silent=False):
+ if s.lower() == 'none':
+ self.__options.re = None
+ elif s == "":
+ self.help_re()
+ silent = True
+ else:
+ self.__options.re = s
+
+ if not silent:
+ print("[+] Re setted. You have to reload gadgets")
diff --git a/ROPgadget/ropgadget/gadgets.py b/ROPgadget/ropgadget/gadgets.py
new file mode 100644
index 0000000..07c4ec7
--- /dev/null
+++ b/ROPgadget/ropgadget/gadgets.py
@@ -0,0 +1,358 @@
+## -*- coding: utf-8 -*-
+##
+## Jonathan Salwan - 2014-05-12 - ROPgadget tool
+##
+## http://twitter.com/JonathanSalwan
+## http://shell-storm.org/project/ROPgadget/
+##
+
+import re
+from capstone import *
+
+
+class Gadgets(object):
+ def __init__(self, binary, options, offset):
+ self.__binary = binary
+ self.__options = options
+ self.__offset = offset
+ self.__arch = self.__binary.getArch()
+
+ re_str = ""
+ if self.__arch == CS_ARCH_X86:
+ re_str = "db|int3"
+ elif self.__arch == CS_ARCH_ARM64:
+ re_str = "brk|smc|hvc"
+ if self.__options.filter:
+ if re_str:
+ re_str += "|"
+ re_str += self.__options.filter
+
+ self.__filterRE = re.compile("({})$".format(re_str)) if re_str else None
+
+ def __passCleanX86(self, decodes):
+ br = ["ret", "retf", "int", "sysenter", "jmp", "call", "syscall"]
+
+ if decodes[-1][2] not in br:
+ return True
+ if not self.__options.multibr and any(mnemonic in br for _, _, mnemonic, _ in decodes[:-1]):
+ return True
+ if any("ret" in mnemonic for _, _, mnemonic, _ in decodes[:-1]):
+ return True
+
+ return False
+
+ def __gadgetsFinding(self, section, gadgets, arch, mode):
+
+ PREV_BYTES = 9 # Number of bytes prior to the gadget to store.
+
+ opcodes = section["opcodes"]
+ sec_vaddr = section["vaddr"]
+
+ ret = []
+ md = Cs(arch, mode)
+ for gad_op, gad_size, gad_align in gadgets:
+ allRefRet = [m.start() for m in re.finditer(gad_op, opcodes)]
+ for ref in allRefRet:
+ end = ref + gad_size
+ for i in range(self.__options.depth):
+ start = ref - (i * gad_align)
+ if (sec_vaddr+start) % gad_align == 0:
+ code = opcodes[start:end]
+ decodes = md.disasm_lite(code, sec_vaddr+ref)
+ decodes = list(decodes)
+ if sum(size for _, size, _, _ in decodes) != i*gad_align + gad_size:
+ # We've read less instructions than planned so something went wrong
+ continue
+ if self.passClean(decodes):
+ continue
+ off = self.__offset
+ vaddr = off+sec_vaddr+start
+ g = {"vaddr" : vaddr}
+ if not self.__options.noinstr:
+ g["gadget"] = " ; ".join("{}{}{}".format(mnemonic, " " if op_str else "", op_str)
+ for _, _, mnemonic, op_str in decodes).replace(" ", " ")
+ if self.__options.callPreceded:
+ prevBytesAddr = max(sec_vaddr, vaddr - PREV_BYTES)
+ g["prev"] = opcodes[prevBytesAddr-sec_vaddr:vaddr-sec_vaddr]
+ if self.__options.dump:
+ g["bytes"] = code
+ ret.append(g)
+ return ret
+
+ def addROPGadgets(self, section):
+
+ arch = self.__binary.getArch()
+ arch_mode = self.__binary.getArchMode()
+ arch_endian = self.__binary.getEndian()
+
+ if arch == CS_ARCH_X86:
+ gadgets = [
+ [b"\xc3", 1, 1], # ret
+ [b"\xc2[\x00-\xff]{2}", 3, 1], # ret
+ [b"\xcb", 1, 1], # retf
+ [b"\xca[\x00-\xff]{2}", 3, 1], # retf
+ # MPX
+ [b"\xf2\xc3", 2, 1], # ret
+ [b"\xf2\xc2[\x00-\xff]{2}", 4, 1], # ret
+ ]
+
+ elif arch == CS_ARCH_MIPS: gadgets = [] # MIPS doesn't contains RET instruction set. Only JOP gadgets
+ elif arch == CS_ARCH_PPC:
+ if arch_endian == CS_MODE_BIG_ENDIAN:
+ gadgets = [
+ [b"\x4e\x80\x00\x20", 4, 4] # blr
+ ]
+ else:
+ gadgets = [
+ [b"\x20\x00\x80\x4e", 4, 4] # blr
+ ]
+
+ elif arch == CS_ARCH_SPARC:
+ if arch_endian == CS_MODE_BIG_ENDIAN:
+ gadgets = [
+ [b"\x81\xc3\xe0\x08", 4, 4], # retl
+ [b"\x81\xc7\xe0\x08", 4, 4], # ret
+ [b"\x81\xe8\x00\x00", 4, 4] # restore
+ ]
+ else:
+ gadgets = [
+ [b"\x08\xe0\xc3\x81", 4, 4], # retl
+ [b"\x08\xe0\xc7\x81", 4, 4], # ret
+ [b"\x00\x00\xe8\x81", 4, 4] # restore
+ ]
+ arch_mode = 0
+
+ elif arch == CS_ARCH_ARM: gadgets = [] # ARM doesn't contains RET instruction set. Only JOP gadgets
+ elif arch == CS_ARCH_ARM64:
+ if arch_endian == CS_MODE_BIG_ENDIAN:
+ gadgets = [
+ [b"\xd6\x5f\x03\xc0", 4, 4] # ret
+ ]
+ else:
+ gadgets = [
+ [b"\xc0\x03\x5f\xd6", 4, 4] # ret
+ ]
+ arch_mode = CS_MODE_ARM
+
+ else:
+ print("Gadgets().addROPGadgets() - Architecture not supported")
+ return None
+
+ if len(gadgets) > 0 :
+ return self.__gadgetsFinding(section, gadgets, arch, arch_mode + arch_endian)
+ return gadgets
+
+
+ def addJOPGadgets(self, section):
+ arch = self.__binary.getArch()
+ arch_mode = self.__binary.getArchMode()
+ arch_endian = self.__binary.getEndian()
+
+
+
+ if arch == CS_ARCH_X86:
+ # we start with x86 and x64 common sequences operating on registers
+ gadgets = [
+ # call/jmp reg
+ # d0-d7=call,e0-e7=jmp
+ # x86: 0=eax,1=ecx,2=edx,3=ebx,4=esp,5=ebp,6=esi,7=edi
+ # x64: 0=rax,1=rcx,2=rdx,3=rbx,4=rsp,5=rbp,6=rsi,7=rdi
+ [b"\xff[\xd0-\xd7\xe0-\xe7]", 2, 1],
+
+ # call/jmp [reg]
+ # 10-17=call,20-27=jmp
+ # x86: 0=eax,1=ecx,2=edx,3=ebx, 6=esi,7=edi
+ # x64: 0=rax,1=rcx,2=rdx,3=rbx, 6=rsi,7=rdi
+ [b"\xff[\x10-\x13\x16-\x17\x20-\x23\x26-\x27]", 2, 1],
+ # call/jmp [reg]
+ # 14=call,24=jmp
+ # x86: esp
+ # x64: rsp
+ [b"\xff[\x14\x24]\x24", 3, 1],
+
+ # call/jmp [reg + offset], -0x80 <= offset <= 0x7f
+ # 50-57=call,60-67=jmp
+ # x86: 0=eax,1=ecx,2=edx,3=ebx, 5=ebp,6=esi,7=edi
+ # x64: 0=rax,1=rcx,2=rdx,3=rbx, 5=rbp,6=rsi,7=rdi
+ [b"\xff[\x50-\x53\x55-\x57\x60-\x63\x65-\x67][\x00-\xff]", 3, 1],
+ # call/jmp [reg + offset], -0x80 <= offset <= 0x7f
+ # 54=call,64=jmp
+ # x86: esp
+ # x64: rsp
+ [b"\xff[\x54\x64]\x24[\x00-\xff]", 4, 1],
+
+ # call/jmp [reg + offset], -0x80000000 <= offset <= 0x7fffffff
+ # 90-97=call,a0-a7=jmp
+ # x86: 0=eax,1=ecx,2=edx,3=ebx, 5=ebp,6=esi,7=edi
+ # x64: 0=rax,1=rcx,2=rdx,3=rbx, 5=rbp,6=rsi,7=rdi
+ [b"\xff[\x90-\x93\x95-\x97\xa0-\xa3\xa5-\xa7][\x00-\xff]{4}", 6, 1],
+ # call/jmp [reg + offset], -0x80000000 <= offset <= 0x7fffffff
+ # 94=call,a4=jmp
+ # x86: esp
+ # x64: rsp
+ [b"\xff[\x94\xa4]\x24[\x00-\xff]{4}", 7, 1]
+ ]
+ # in x64, by adding 41 before a sequence with
+ # 0=rax,1=rcx,2=rdx,3=rbx,4=rsp,5=rbp,6=rsi,7=rdi
+ # we convert it to the same sequence with
+ # 0= r8,1= r9,2=r10,3=r11,4=r12,5=r13,6=r14,7=r15
+ if arch_mode == CS_MODE_64:
+ gadgets += [(b"\x41" + op, size + 1, align) for (op, size, align) in gadgets]
+ # finally, add extra sequences common to x86 and x64
+ gadgets += [
+ [b"\xeb[\x00-\xff]", 2, 1], # jmp offset
+ [b"\xe9[\x00-\xff]{4}", 5, 1], # jmp offset
+ # MPX
+ [b"\xf2\xff[\x20\x21\x22\x23\x26\x27]{1}", 3, 1], # jmp [reg]
+ [b"\xf2\xff[\xe0\xe1\xe2\xe3\xe4\xe6\xe7]{1}", 3, 1], # jmp [reg]
+ [b"\xf2\xff[\x10\x11\x12\x13\x16\x17]{1}", 3, 1], # jmp [reg]
+ [b"\xf2\xff[\xd0\xd1\xd2\xd3\xd4\xd6\xd7]{1}", 3, 1] # call [reg]
+ ]
+
+
+ elif arch == CS_ARCH_MIPS:
+ if arch_endian == CS_MODE_BIG_ENDIAN:
+ gadgets = [
+ [b"\x00[\x40\x60\x80\xa0\xc0\xe0]\xf8\x09[\x00-\xff]{4}", 8, 4], # jalr $v[0-1]|$a[0-3]
+ [b"[\x01\x02][\x00\x20\x40\x60\x80\xa0\xc0\xe0]\xf8\x09[\x00-\xff]{4}", 8, 4], # jalr $t[0-7]|$s[0-7]
+ [b"\x03[\x00\x20\xc0\xe0]\xf8\x09[\x00-\xff]{4}", 8, 4], # jalr $t[8-9]|$s8|$ra
+ [b"\x00[\x40\x60\x80\xa0\xc0\xe0]\x00\x08[\x00-\xff]{4}", 8, 4], # jr $v[0-1]|$a[0-3]
+ [b"[\x01\x02][\x00\x20\x40\x60\x80\xa0\xc0\xe0]\x00\x08[\x00-\xff]{4}", 8, 4], # jr $t[0-7]|$s[0-7]
+ [b"\x03[\x00\x20\xc0\xe0]\x00\x08[\x00-\xff]{4}", 8, 4], # jr $t[8-9]|$s8|$ra
+ [b"[\x0c-\x0f][\x00-\xff]{7}", 8, 4], # jal addr
+ [b"[\x08-\x0b][\x00-\xff]{7}", 8, 4] # j addr
+ ]
+ else:
+ gadgets = [
+ [b"\x09\xf8[\x40\x60\x80\xa0\xc0\xe0]\x00[\x00-\xff]{4}", 8, 4], # jalr $v[0-1]|$a[0-3]
+ [b"\x09\xf8[\x00\x20\x40\x60\x80\xa0\xc0\xe0][\x01\x02][\x00-\xff]{4}", 8, 4], # jalr $t[0-7]|$s[0-7]
+ [b"\x09\xf8[\x00\x20\xc0\xe0]\x03[\x00-\xff]{4}", 8, 4], # jalr $t[8-9]|$s8|$ra
+ [b"\x08\x00[\x40\x60\x80\xa0\xc0\xe0]\x00[\x00-\xff]{4}", 8, 4], # jr $v[0-1]|$a[0-3]
+ [b"\x08\x00[\x00\x20\x40\x60\x80\xa0\xc0\xe0][\x01\x02][\x00-\xff]{4}", 8, 4], # jr $t[0-7]|$s[0-7]
+ [b"\x08\x00[\x00\x20\xc0\xe0]\x03[\x00-\xff]{4}", 8, 4], # jr $t[8-9]|$s8|$ra
+ [b"[\x00-\xff]{3}[\x0c-\x0f][\x00-\xff]{4}", 8, 4], # jal addr
+ [b"[\x00-\xff]{3}[\x08-\x0b][\x00-\xff]{4}", 8, 4] # j addr
+ ]
+ elif arch == CS_ARCH_PPC: gadgets = [] # PPC architecture doesn't contains reg branch instruction
+ elif arch == CS_ARCH_SPARC:
+ if arch_endian == CS_MODE_BIG_ENDIAN:
+ gadgets = [
+ [b"\x81\xc0[\x00\x40\x80\xc0]{1}\x00", 4, 4] # jmp %g[0-3]
+ ]
+ else:
+ gadgets = [
+ [b"\x00[\x00\x40\x80\xc0]{1}\xc0\x81", 4, 4] # jmp %g[0-3]
+ ]
+ arch_mode = 0
+ elif arch == CS_ARCH_ARM64:
+ if arch_endian == CS_MODE_BIG_ENDIAN:
+ gadgets = [
+ [b"\xd6[\x1f\x5f]{1}[\x00-\x03]{1}[\x00\x20\x40\x60\x80\xa0\xc0\xe0]{1}", 4, 4], # br reg
+ [b"\xd6\?[\x00-\x03]{1}[\x00\x20\x40\x60\x80\xa0\xc0\xe0]{1}", 4, 4] # blr reg
+ ]
+ else:
+ gadgets = [
+ [b"[\x00\x20\x40\x60\x80\xa0\xc0\xe0]{1}[\x00-\x03]{1}[\x1f\x5f]{1}\xd6", 4, 4], # br reg
+ [b"[\x00\x20\x40\x60\x80\xa0\xc0\xe0]{1}[\x00-\x03]{1}\?\xd6", 4, 4] # blr reg
+ ]
+ arch_mode = CS_MODE_ARM
+ elif arch == CS_ARCH_ARM:
+ if self.__options.thumb or self.__options.rawMode == "thumb":
+ if arch_endian == CS_MODE_BIG_ENDIAN:
+ gadgets = [
+ [b"\x47[\x00\x08\x10\x18\x20\x28\x30\x38\x40\x48\x70]{1}", 2, 2], # bx reg
+ [b"\x47[\x80\x88\x90\x98\xa0\xa8\xb0\xb8\xc0\xc8\xf0]{1}", 2, 2], # blx reg
+ [b"\xbd[\x00-\xff]{1}", 2, 2] # pop {,pc}
+ ]
+ else:
+ gadgets = [
+ [b"[\x00\x08\x10\x18\x20\x28\x30\x38\x40\x48\x70]{1}\x47", 2, 2], # bx reg
+ [b"[\x80\x88\x90\x98\xa0\xa8\xb0\xb8\xc0\xc8\xf0]{1}\x47", 2, 2], # blx reg
+ [b"[\x00-\xff]{1}\xbd", 2, 2] # pop {,pc}
+ ]
+ arch_mode = CS_MODE_THUMB
+ else:
+ if arch_endian == CS_MODE_BIG_ENDIAN:
+ gadgets = [
+ [b"\xe1\x2f\xff[\x10-\x19\x1e]{1}", 4, 4], # bx reg
+ [b"\xe1\x2f\xff[\x30-\x39\x3e]{1}", 4, 4], # blx reg
+ [b"[\xe8\xe9][\x10-\x1e\x30-\x3e\x50-\x5e\x70-\x7e\x90-\x9e\xb0-\xbe\xd0-\xde\xf0-\xfe][\x80-\xff][\x00-\xff]", 4, 4] # ldm {,pc}
+ ]
+ else:
+ gadgets = [
+ [b"[\x10-\x19\x1e]{1}\xff\x2f\xe1", 4, 4], # bx reg
+ [b"[\x30-\x39\x3e]{1}\xff\x2f\xe1", 4, 4], # blx reg
+ [b"[\x00-\xff][\x80-\xff][\x10-\x1e\x30-\x3e\x50-\x5e\x70-\x7e\x90-\x9e\xb0-\xbe\xd0-\xde\xf0-\xfe][\xe8\xe9]", 4, 4] # ldm {,pc}
+ ]
+ arch_mode = CS_MODE_ARM
+ else:
+ print("Gadgets().addJOPGadgets() - Architecture not supported")
+ return None
+
+ if len(gadgets) > 0 :
+ return self.__gadgetsFinding(section, gadgets, arch, arch_mode + arch_endian)
+ return gadgets
+
+ def addSYSGadgets(self, section):
+
+ arch = self.__binary.getArch()
+ arch_mode = self.__binary.getArchMode()
+ arch_endian = self.__binary.getEndian()
+
+ if arch == CS_ARCH_X86:
+ gadgets = [
+ [b"\xcd\x80", 2, 1], # int 0x80
+ [b"\x0f\x34", 2, 1], # sysenter
+ [b"\x0f\x05", 2, 1], # syscall
+ [b"\x65\xff\x15\x10\x00\x00\x00", 7, 1], # call DWORD PTR gs:0x10
+ [b"\xcd\x80\xc3", 3, 1], # int 0x80 ; ret
+ [b"\x0f\x34\xc3", 3, 1], # sysenter ; ret
+ [b"\x0f\x05\xc3", 3, 1], # syscall ; ret
+ [b"\x65\xff\x15\x10\x00\x00\x00\xc3", 8, 1], # call DWORD PTR gs:0x10 ; ret
+ ]
+
+ elif arch == CS_ARCH_MIPS:
+ if arch_endian == CS_MODE_BIG_ENDIAN:
+ gadgets = [
+ [b"\x00\x00\x00\x0c", 4, 4] # syscall
+ ]
+ else:
+ gadgets = [
+ [b"\x0c\x00\x00\x00", 4, 4] # syscall
+ ]
+ elif arch == CS_ARCH_PPC: gadgets = [] # TODO (sc inst)
+ elif arch == CS_ARCH_SPARC: gadgets = [] # TODO (ta inst)
+ elif arch == CS_ARCH_ARM64: gadgets = [] # TODO
+ elif arch == CS_ARCH_ARM:
+ if self.__options.thumb or self.__options.rawMode == "thumb":
+ gadgets = [
+ [b"\x00-\xff]{1}\xef", 2, 2] # FIXME: svc
+ ]
+ arch_mode = CS_MODE_THUMB
+ else:
+ gadgets = [
+ [b"\x00-\xff]{3}\xef", 4, 4] # FIXME: svc
+ ]
+ arch_mode = CS_MODE_ARM
+ else:
+ print("Gadgets().addSYSGadgets() - Architecture not supported")
+ return None
+
+ if len(gadgets) > 0 :
+ return self.__gadgetsFinding(section, gadgets, arch, arch_mode + arch_endian)
+ return []
+
+
+ def passClean(self, decodes):
+
+ if not decodes:
+ return True
+
+ if self.__arch == CS_ARCH_X86 and self.__passCleanX86(decodes):
+ return True
+
+ if self.__filterRE and any(self.__filterRE.match(mnemonic) for _, _, mnemonic, _ in decodes):
+ return True
+
+ return False
+
diff --git a/ROPgadget/ropgadget/loaders/__init__.py b/ROPgadget/ropgadget/loaders/__init__.py
new file mode 100644
index 0000000..98666c2
--- /dev/null
+++ b/ROPgadget/ropgadget/loaders/__init__.py
@@ -0,0 +1,12 @@
+## -*- coding: utf-8 -*-
+##
+## Jonathan Salwan - 2014-05-12 - ROPgadget tool
+##
+## http://twitter.com/JonathanSalwan
+## http://shell-storm.org/project/ROPgadget/
+##
+
+import ropgadget.loaders.elf
+import ropgadget.loaders.macho
+import ropgadget.loaders.pe
+import ropgadget.loaders.raw
diff --git a/ROPgadget/ropgadget/loaders/elf.py b/ROPgadget/ropgadget/loaders/elf.py
new file mode 100644
index 0000000..8f267fb
--- /dev/null
+++ b/ROPgadget/ropgadget/loaders/elf.py
@@ -0,0 +1,350 @@
+## -*- coding: utf-8 -*-
+##
+## Jonathan Salwan - 2014-05-12 - ROPgadget tool
+##
+## http://twitter.com/JonathanSalwan
+## http://shell-storm.org/project/ROPgadget/
+##
+
+from capstone import *
+from ctypes import *
+from struct import unpack
+
+class ELFFlags(object):
+ ELFCLASS32 = 0x01
+ ELFCLASS64 = 0x02
+ EI_CLASS = 0x04
+ EI_DATA = 0x05
+ ELFDATA2LSB = 0x01
+ ELFDATA2MSB = 0x02
+ EM_386 = 0x03
+ EM_X86_64 = 0x3e
+ EM_ARM = 0x28
+ EM_MIPS = 0x08
+ EM_SPARCv8p = 0x12
+ EM_PowerPC = 0x14
+ EM_ARM64 = 0xb7
+
+class Elf32_Ehdr_LSB(LittleEndianStructure):
+ _fields_ = [
+ ("e_ident", c_ubyte * 16),
+ ("e_type", c_ushort),
+ ("e_machine", c_ushort),
+ ("e_version", c_uint),
+ ("e_entry", c_uint),
+ ("e_phoff", c_uint),
+ ("e_shoff", c_uint),
+ ("e_flags", c_uint),
+ ("e_ehsize", c_ushort),
+ ("e_phentsize", c_ushort),
+ ("e_phnum", c_ushort),
+ ("e_shentsize", c_ushort),
+ ("e_shnum", c_ushort),
+ ("e_shstrndx", c_ushort)
+ ]
+
+class Elf64_Ehdr_LSB(LittleEndianStructure):
+ _fields_ = [
+ ("e_ident", c_ubyte * 16),
+ ("e_type", c_ushort),
+ ("e_machine", c_ushort),
+ ("e_version", c_uint),
+ ("e_entry", c_ulonglong),
+ ("e_phoff", c_ulonglong),
+ ("e_shoff", c_ulonglong),
+ ("e_flags", c_uint),
+ ("e_ehsize", c_ushort),
+ ("e_phentsize", c_ushort),
+ ("e_phnum", c_ushort),
+ ("e_shentsize", c_ushort),
+ ("e_shnum", c_ushort),
+ ("e_shstrndx", c_ushort)
+ ]
+
+class Elf32_Phdr_LSB(LittleEndianStructure):
+ _fields_ = [
+ ("p_type", c_uint),
+ ("p_offset", c_uint),
+ ("p_vaddr", c_uint),
+ ("p_paddr", c_uint),
+ ("p_filesz", c_uint),
+ ("p_memsz", c_uint),
+ ("p_flags", c_uint),
+ ("p_align", c_uint)
+ ]
+
+class Elf64_Phdr_LSB(LittleEndianStructure):
+ _fields_ = [
+ ("p_type", c_uint),
+ ("p_flags", c_uint),
+ ("p_offset", c_ulonglong),
+ ("p_vaddr", c_ulonglong),
+ ("p_paddr", c_ulonglong),
+ ("p_filesz", c_ulonglong),
+ ("p_memsz", c_ulonglong),
+ ("p_align", c_ulonglong)
+ ]
+
+class Elf32_Shdr_LSB(LittleEndianStructure):
+ _fields_ = [
+ ("sh_name", c_uint),
+ ("sh_type", c_uint),
+ ("sh_flags", c_uint),
+ ("sh_addr", c_uint),
+ ("sh_offset", c_uint),
+ ("sh_size", c_uint),
+ ("sh_link", c_uint),
+ ("sh_info", c_uint),
+ ("sh_addralign", c_uint),
+ ("sh_entsize", c_uint)
+ ]
+
+class Elf64_Shdr_LSB(LittleEndianStructure):
+ _fields_ = [
+ ("sh_name", c_uint),
+ ("sh_type", c_uint),
+ ("sh_flags", c_ulonglong),
+ ("sh_addr", c_ulonglong),
+ ("sh_offset", c_ulonglong),
+ ("sh_size", c_ulonglong),
+ ("sh_link", c_uint),
+ ("sh_info", c_uint),
+ ("sh_addralign", c_ulonglong),
+ ("sh_entsize", c_ulonglong)
+ ]
+
+class Elf32_Ehdr_MSB(BigEndianStructure):
+ _fields_ = [
+ ("e_ident", c_ubyte * 16),
+ ("e_type", c_ushort),
+ ("e_machine", c_ushort),
+ ("e_version", c_uint),
+ ("e_entry", c_uint),
+ ("e_phoff", c_uint),
+ ("e_shoff", c_uint),
+ ("e_flags", c_uint),
+ ("e_ehsize", c_ushort),
+ ("e_phentsize", c_ushort),
+ ("e_phnum", c_ushort),
+ ("e_shentsize", c_ushort),
+ ("e_shnum", c_ushort),
+ ("e_shstrndx", c_ushort)
+ ]
+
+class Elf64_Ehdr_MSB(BigEndianStructure):
+ _fields_ = [
+ ("e_ident", c_ubyte * 16),
+ ("e_type", c_ushort),
+ ("e_machine", c_ushort),
+ ("e_version", c_uint),
+ ("e_entry", c_ulonglong),
+ ("e_phoff", c_ulonglong),
+ ("e_shoff", c_ulonglong),
+ ("e_flags", c_uint),
+ ("e_ehsize", c_ushort),
+ ("e_phentsize", c_ushort),
+ ("e_phnum", c_ushort),
+ ("e_shentsize", c_ushort),
+ ("e_shnum", c_ushort),
+ ("e_shstrndx", c_ushort)
+ ]
+
+class Elf32_Phdr_MSB(BigEndianStructure):
+ _fields_ = [
+ ("p_type", c_uint),
+ ("p_offset", c_uint),
+ ("p_vaddr", c_uint),
+ ("p_paddr", c_uint),
+ ("p_filesz", c_uint),
+ ("p_memsz", c_uint),
+ ("p_flags", c_uint),
+ ("p_align", c_uint)
+ ]
+
+class Elf64_Phdr_MSB(BigEndianStructure):
+ _fields_ = [
+ ("p_type", c_uint),
+ ("p_flags", c_uint),
+ ("p_offset", c_ulonglong),
+ ("p_vaddr", c_ulonglong),
+ ("p_paddr", c_ulonglong),
+ ("p_filesz", c_ulonglong),
+ ("p_memsz", c_ulonglong),
+ ("p_align", c_ulonglong)
+ ]
+
+class Elf32_Shdr_MSB(BigEndianStructure):
+ _fields_ = [
+ ("sh_name", c_uint),
+ ("sh_type", c_uint),
+ ("sh_flags", c_uint),
+ ("sh_addr", c_uint),
+ ("sh_offset", c_uint),
+ ("sh_size", c_uint),
+ ("sh_link", c_uint),
+ ("sh_info", c_uint),
+ ("sh_addralign", c_uint),
+ ("sh_entsize", c_uint)
+ ]
+
+class Elf64_Shdr_MSB(BigEndianStructure):
+ _fields_ = [
+ ("sh_name", c_uint),
+ ("sh_type", c_uint),
+ ("sh_flags", c_ulonglong),
+ ("sh_addr", c_ulonglong),
+ ("sh_offset", c_ulonglong),
+ ("sh_size", c_ulonglong),
+ ("sh_link", c_uint),
+ ("sh_info", c_uint),
+ ("sh_addralign", c_ulonglong),
+ ("sh_entsize", c_ulonglong)
+ ]
+
+""" This class parses the ELF """
+class ELF(object):
+ def __init__(self, binary):
+ self.__binary = bytearray(binary)
+ self.__ElfHeader = None
+ self.__shdr_l = []
+ self.__phdr_l = []
+
+ self.__setHeaderElf()
+ self.__setShdr()
+ self.__setPhdr()
+
+ """ Parse ELF header """
+ def __setHeaderElf(self):
+ e_ident = self.__binary[:15]
+
+ ei_class = e_ident[ELFFlags.EI_CLASS]
+ ei_data = e_ident[ELFFlags.EI_DATA]
+
+ if ei_class != ELFFlags.ELFCLASS32 and ei_class != ELFFlags.ELFCLASS64:
+ print("[Error] ELF.__setHeaderElf() - Bad Arch size")
+ return None
+
+ if ei_data != ELFFlags.ELFDATA2LSB and ei_data != ELFFlags.ELFDATA2MSB:
+ print("[Error] ELF.__setHeaderElf() - Bad architecture endian")
+ return None
+
+ if ei_class == ELFFlags.ELFCLASS32:
+ if ei_data == ELFFlags.ELFDATA2LSB: self.__ElfHeader = Elf32_Ehdr_LSB.from_buffer_copy(self.__binary)
+ elif ei_data == ELFFlags.ELFDATA2MSB: self.__ElfHeader = Elf32_Ehdr_MSB.from_buffer_copy(self.__binary)
+ elif ei_class == ELFFlags.ELFCLASS64:
+ if ei_data == ELFFlags.ELFDATA2LSB: self.__ElfHeader = Elf64_Ehdr_LSB.from_buffer_copy(self.__binary)
+ elif ei_data == ELFFlags.ELFDATA2MSB: self.__ElfHeader = Elf64_Ehdr_MSB.from_buffer_copy(self.__binary)
+
+ self.getArch() # Check if architecture is supported
+
+ """ Parse Section header """
+ def __setShdr(self):
+ shdr_num = self.__ElfHeader.e_shnum
+ base = self.__binary[self.__ElfHeader.e_shoff:]
+ shdr_l = []
+
+ e_ident = self.__binary[:15]
+ ei_data = e_ident[ELFFlags.EI_DATA]
+
+ for i in range(shdr_num):
+
+ if self.getArchMode() == CS_MODE_32:
+ if ei_data == ELFFlags.ELFDATA2LSB: shdr = Elf32_Shdr_LSB.from_buffer_copy(base)
+ elif ei_data == ELFFlags.ELFDATA2MSB: shdr = Elf32_Shdr_MSB.from_buffer_copy(base)
+ elif self.getArchMode() == CS_MODE_64:
+ if ei_data == ELFFlags.ELFDATA2LSB: shdr = Elf64_Shdr_LSB.from_buffer_copy(base)
+ elif ei_data == ELFFlags.ELFDATA2MSB: shdr = Elf64_Shdr_MSB.from_buffer_copy(base)
+
+ self.__shdr_l.append(shdr)
+ base = base[self.__ElfHeader.e_shentsize:]
+
+ # setup name from the strings table
+ if self.__ElfHeader.e_shstrndx != 0:
+ string_table = bytes(self.__binary[(self.__shdr_l[self.__ElfHeader.e_shstrndx].sh_offset):])
+ for i in range(shdr_num):
+ self.__shdr_l[i].str_name = string_table[self.__shdr_l[i].sh_name:].split(b'\x00')[0].decode('utf8')
+
+ """ Parse Program header """
+ def __setPhdr(self):
+ pdhr_num = self.__ElfHeader.e_phnum
+ base = self.__binary[self.__ElfHeader.e_phoff:]
+ phdr_l = []
+
+ e_ident = self.__binary[:15]
+ ei_data = e_ident[ELFFlags.EI_DATA]
+
+ for i in range(pdhr_num):
+ if self.getArchMode() == CS_MODE_32:
+ if ei_data == ELFFlags.ELFDATA2LSB: phdr = Elf32_Phdr_LSB.from_buffer_copy(base)
+ elif ei_data == ELFFlags.ELFDATA2MSB: phdr = Elf32_Phdr_MSB.from_buffer_copy(base)
+ elif self.getArchMode() == CS_MODE_64:
+ if ei_data == ELFFlags.ELFDATA2LSB: phdr = Elf64_Phdr_LSB.from_buffer_copy(base)
+ elif ei_data == ELFFlags.ELFDATA2MSB: phdr = Elf64_Phdr_MSB.from_buffer_copy(base)
+
+ self.__phdr_l.append(phdr)
+ base = base[self.__ElfHeader.e_phentsize:]
+
+ def getEntryPoint(self):
+ return self.__e_entry
+
+ def getExecSections(self):
+ ret = []
+ for segment in self.__phdr_l:
+ if segment.p_flags & 0x1:
+ ret += [{
+ "offset" : segment.p_offset,
+ "size" : segment.p_memsz,
+ "vaddr" : segment.p_vaddr,
+ "opcodes" : bytes(self.__binary[segment.p_offset:segment.p_offset+segment.p_memsz])
+ }]
+ return ret
+
+ def getDataSections(self):
+ ret = []
+ for section in self.__shdr_l:
+ if not (section.sh_flags & 0x4) and (section.sh_flags & 0x2):
+ ret += [{
+ "name" : section.str_name,
+ "offset" : section.sh_offset,
+ "size" : section.sh_size,
+ "vaddr" : section.sh_addr,
+ "opcodes" : bytes(self.__binary[section.sh_offset:section.sh_offset+section.sh_size])
+ }]
+ return ret
+
+ def getArch(self):
+ if self.__ElfHeader.e_machine == ELFFlags.EM_386 or self.__ElfHeader.e_machine == ELFFlags.EM_X86_64:
+ return CS_ARCH_X86
+ elif self.__ElfHeader.e_machine == ELFFlags.EM_ARM:
+ return CS_ARCH_ARM
+ elif self.__ElfHeader.e_machine == ELFFlags.EM_ARM64:
+ return CS_ARCH_ARM64
+ elif self.__ElfHeader.e_machine == ELFFlags.EM_MIPS:
+ return CS_ARCH_MIPS
+ elif self.__ElfHeader.e_machine == ELFFlags.EM_PowerPC:
+ return CS_ARCH_PPC
+ elif self.__ElfHeader.e_machine == ELFFlags.EM_SPARCv8p:
+ return CS_ARCH_SPARC
+ else:
+ print("[Error] ELF.getArch() - Architecture not supported")
+ return None
+
+ def getArchMode(self):
+ if self.__ElfHeader.e_ident[ELFFlags.EI_CLASS] == ELFFlags.ELFCLASS32:
+ return CS_MODE_32
+ elif self.__ElfHeader.e_ident[ELFFlags.EI_CLASS] == ELFFlags.ELFCLASS64:
+ return CS_MODE_64
+ else:
+ print("[Error] ELF.getArchMode() - Bad Arch size")
+ return None
+
+ def getEndian(self):
+ if self.__ElfHeader.e_ident[ELFFlags.EI_DATA] == ELFFlags.ELFDATA2LSB:
+ return 0
+ if self.__ElfHeader.e_ident[ELFFlags.EI_DATA] == ELFFlags.ELFDATA2MSB:
+ return CS_MODE_BIG_ENDIAN
+ print("[Error] ELF.getEndian() - Bad Endianness")
+ return None
+
+ def getFormat(self):
+ return "ELF"
diff --git a/ROPgadget/ropgadget/loaders/macho.py b/ROPgadget/ropgadget/loaders/macho.py
new file mode 100644
index 0000000..27c52bd
--- /dev/null
+++ b/ROPgadget/ropgadget/loaders/macho.py
@@ -0,0 +1,211 @@
+## -*- coding: utf-8 -*-
+##
+## Jonathan Salwan - 2014-05-12 - ROPgadget tool
+##
+## http://twitter.com/JonathanSalwan
+## http://shell-storm.org/project/ROPgadget/
+##
+
+from capstone import *
+from ctypes import *
+
+class MACH_HEADER(Structure):
+ _fields_ = [
+ ("magic", c_uint),
+ ("cputype", c_uint),
+ ("cpusubtype", c_uint),
+ ("filetype", c_uint),
+ ("ncmds", c_uint),
+ ("sizeofcmds", c_uint),
+ ("flags", c_uint)
+ ]
+
+class LOAD_COMMAND(Structure):
+ _fields_ = [
+ ("cmd", c_uint),
+ ("cmdsize", c_uint)
+ ]
+
+class SEGMENT_COMMAND(Structure):
+ _fields_ = [
+ ("cmd", c_uint),
+ ("cmdsize", c_uint),
+ ("segname", c_ubyte * 16),
+ ("vmaddr", c_uint),
+ ("vmsize", c_uint),
+ ("fileoff", c_uint),
+ ("filesize", c_uint),
+ ("maxprot", c_uint),
+ ("initprot", c_uint),
+ ("nsects", c_uint),
+ ("flags", c_uint)
+ ]
+
+class SEGMENT_COMMAND64(Structure):
+ _fields_ = [
+ ("cmd", c_uint),
+ ("cmdsize", c_uint),
+ ("segname", c_ubyte * 16),
+ ("vmaddr", c_ulonglong),
+ ("vmsize", c_ulonglong),
+ ("fileoff", c_ulonglong),
+ ("filesize", c_ulonglong),
+ ("maxprot", c_uint),
+ ("initprot", c_uint),
+ ("nsects", c_uint),
+ ("flags", c_uint)
+ ]
+
+class SECTION(Structure):
+ _fields_ = [
+ ("sectname", c_ubyte * 16),
+ ("segname", c_ubyte * 16),
+ ("addr", c_uint),
+ ("size", c_uint),
+ ("offset", c_uint),
+ ("align", c_uint),
+ ("reloff", c_uint),
+ ("nreloc", c_uint),
+ ("flags", c_uint),
+ ("reserved1", c_uint),
+ ("reserved2", c_uint)
+ ]
+
+class SECTION64(Structure):
+ _fields_ = [
+ ("sectname", c_ubyte * 16),
+ ("segname", c_ubyte * 16),
+ ("addr", c_ulonglong),
+ ("size", c_ulonglong),
+ ("offset", c_uint),
+ ("align", c_uint),
+ ("reloff", c_uint),
+ ("nreloc", c_uint),
+ ("flags", c_uint),
+ ("reserved1", c_uint),
+ ("reserved2", c_uint)
+ ]
+
+class MACHOFlags(object):
+ CPU_TYPE_I386 = 0x7
+ CPU_TYPE_X86_64 = (CPU_TYPE_I386 | 0x1000000)
+ CPU_TYPE_MIPS = 0x8
+ CPU_TYPE_ARM = 12
+ CPU_TYPE_ARM64 = (CPU_TYPE_ARM | 0x1000000)
+ CPU_TYPE_SPARC = 14
+ CPU_TYPE_POWERPC = 18
+ CPU_TYPE_POWERPC64 = (CPU_TYPE_POWERPC | 0x1000000)
+ LC_SEGMENT = 0x1
+ LC_SEGMENT_64 = 0x19
+ S_ATTR_SOME_INSTRUCTIONS = 0x00000400
+ S_ATTR_PURE_INSTRUCTIONS = 0x80000000
+
+""" This class parses the Mach-O """
+class MACHO(object):
+ def __init__(self, binary):
+ self.__binary = bytearray(binary)
+
+ self.__machHeader = None
+ self.__rawLoadCmd = None
+ self.__sections_l = []
+
+ self.__setHeader()
+ self.__setLoadCmd()
+
+ def __setHeader(self):
+ self.__machHeader = MACH_HEADER.from_buffer_copy(self.__binary)
+
+ if self.getArchMode() == CS_MODE_32:
+ self.__rawLoadCmd = self.__binary[28:28+self.__machHeader.sizeofcmds]
+
+ elif self.getArchMode() == CS_MODE_64:
+ self.__rawLoadCmd = self.__binary[32:32+self.__machHeader.sizeofcmds]
+
+ def __setLoadCmd(self):
+ base = self.__rawLoadCmd
+ for i in range(self.__machHeader.ncmds):
+ command = LOAD_COMMAND.from_buffer_copy(base)
+
+ if command.cmd == MACHOFlags.LC_SEGMENT:
+ segment = SEGMENT_COMMAND.from_buffer_copy(base)
+ self.__setSections(segment, base[56:], 32)
+
+ elif command.cmd == MACHOFlags.LC_SEGMENT_64:
+ segment = SEGMENT_COMMAND64.from_buffer_copy(base)
+ self.__setSections(segment, base[72:], 64)
+
+ base = base[command.cmdsize:]
+
+ def __setSections(self, segment, base, sizeHeader):
+ for i in range(segment.nsects):
+ if sizeHeader == 32:
+ section = SECTION.from_buffer_copy(base)
+ section.offset = segment.fileoff + section.addr - segment.vmaddr
+ base = base[68:]
+ self.__sections_l += [section]
+ elif sizeHeader == 64:
+ section = SECTION64.from_buffer_copy(base)
+ section.offset = segment.fileoff + section.addr - segment.vmaddr
+ base = base[80:]
+ self.__sections_l += [section]
+
+ def getEntryPoint(self):
+ for section in self.__sections_l:
+ if section.sectname[0:6] == "__text":
+ return section.addr
+
+ def getExecSections(self):
+ ret = []
+ for section in self.__sections_l:
+ if section.flags & MACHOFlags.S_ATTR_SOME_INSTRUCTIONS or section.flags & MACHOFlags.S_ATTR_PURE_INSTRUCTIONS:
+ ret += [{
+ "name" : section.sectname,
+ "offset" : section.offset,
+ "size" : section.size,
+ "vaddr" : section.addr,
+ "opcodes" : bytes(self.__binary[section.offset:section.offset+section.size])
+ }]
+ return ret
+
+ def getDataSections(self):
+ ret = []
+ for section in self.__sections_l:
+ if not section.flags & MACHOFlags.S_ATTR_SOME_INSTRUCTIONS and not section.flags & MACHOFlags.S_ATTR_PURE_INSTRUCTIONS:
+ ret += [{
+ "name" : section.sectname,
+ "offset" : section.offset,
+ "size" : section.size,
+ "vaddr" : section.addr,
+ "opcodes" : bytes(self.__binary[section.offset:section.offset+section.size])
+ }]
+ return ret
+
+ def getArch(self):
+ if self.__machHeader.cputype == MACHOFlags.CPU_TYPE_I386 or self.__machHeader.cputype == MACHOFlags.CPU_TYPE_X86_64:
+ return CS_ARCH_X86
+ if self.__machHeader.cputype == MACHOFlags.CPU_TYPE_ARM:
+ return CS_ARCH_ARM
+ if self.__machHeader.cputype == MACHOFlags.CPU_TYPE_ARM64:
+ return CS_ARCH_ARM64
+ if self.__machHeader.cputype == MACHOFlags.CPU_TYPE_MIPS:
+ return CS_ARCH_MIPS
+ else:
+ print("[Error] MACHO.getArch() - Architecture not supported")
+ return None
+
+ def getArchMode(self):
+ if self.__machHeader.magic == 0xfeedface:
+ return CS_MODE_32
+ elif self.__machHeader.magic == 0xfeedfacf:
+ return CS_MODE_64
+ else:
+ print("[Error] MACHO.getArchMode() - Bad Arch size")
+ return None
+ pass
+
+ def getEndian(self):
+ # TODO: Support other endianness
+ return 0
+
+ def getFormat(self):
+ return "Mach-O"
diff --git a/ROPgadget/ropgadget/loaders/pe.py b/ROPgadget/ropgadget/loaders/pe.py
new file mode 100644
index 0000000..42c6fd8
--- /dev/null
+++ b/ROPgadget/ropgadget/loaders/pe.py
@@ -0,0 +1,233 @@
+## -*- coding: utf-8 -*-
+##
+## Jonathan Salwan - 2014-05-12 - ROPgadget tool
+##
+## http://twitter.com/JonathanSalwan
+## http://shell-storm.org/project/ROPgadget/
+##
+
+from capstone import *
+from ctypes import *
+from struct import unpack
+from binascii import unhexlify
+
+class PEFlags(object):
+ IMAGE_MACHINE_INTEL_386 = 0x014c
+ IMAGE_MACHINE_AMD_8664 = 0x8664
+ IMAGE_FILE_MACHINE_ARM = 0x1c0
+ IMAGE_FILE_MACHINE_ARMV7 = 0x1c4
+ IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x10b
+ IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x20b
+ IMAGE_SIZEOF_SHORT_NAME = 0x8
+
+class IMAGE_FILE_HEADER(Structure):
+ _fields_ = [
+ ("Magic", c_uint),
+ ("Machine", c_ushort),
+ ("NumberOfSections", c_ushort),
+ ("TimeDateStamp", c_uint),
+ ("PointerToSymbolTable", c_uint),
+ ("NumberOfSymbols", c_uint),
+ ("SizeOfOptionalHeader", c_ushort),
+ ("Characteristics", c_ushort)
+ ]
+
+class IMAGE_OPTIONAL_HEADER(Structure):
+ _fields_ = [
+ ("Magic", c_ushort),
+ ("MajorLinkerVersion", c_ubyte),
+ ("MinorLinkerVersion", c_ubyte),
+ ("SizeOfCode", c_uint),
+ ("SizeOfInitializedData", c_uint),
+ ("SizeOfUninitializedData", c_uint),
+ ("AddressOfEntryPoint", c_uint),
+ ("BaseOfCode", c_uint),
+ ("BaseOfData", c_uint),
+ ("ImageBase", c_uint),
+ ("SectionAlignment", c_uint),
+ ("FileAlignment", c_uint),
+ ("MajorOperatingSystemVersion", c_ushort),
+ ("MinorOperatingSystemVersion", c_ushort),
+ ("MajorImageVersion", c_ushort),
+ ("MinorImageVersion", c_ushort),
+ ("MajorSubsystemVersion", c_ushort),
+ ("MinorSubsystemVersion", c_ushort),
+ ("Win32VersionValue", c_uint),
+ ("SizeOfImage", c_uint),
+ ("SizeOfHeaders", c_uint),
+ ("CheckSum", c_uint),
+ ("Subsystem", c_ushort),
+ ("DllCharacteristics", c_ushort),
+ ("SizeOfStackReserve", c_uint),
+ ("SizeOfStackCommit", c_uint),
+ ("SizeOfHeapReserve", c_uint),
+ ("SizeOfHeapCommit", c_uint),
+ ("LoaderFlags", c_uint),
+ ("NumberOfRvaAndSizes", c_uint)
+ ]
+
+class IMAGE_OPTIONAL_HEADER64(Structure):
+ _fields_ = [
+ ("Magic", c_ushort),
+ ("MajorLinkerVersion", c_ubyte),
+ ("MinorLinkerVersion", c_ubyte),
+ ("SizeOfCode", c_uint),
+ ("SizeOfInitializedData", c_uint),
+ ("SizeOfUninitializedData", c_uint),
+ ("AddressOfEntryPoint", c_uint),
+ ("BaseOfCode", c_uint),
+ ("ImageBase", c_ulonglong),
+ ("SectionAlignment", c_uint),
+ ("FileAlignment", c_uint),
+ ("MajorOperatingSystemVersion", c_ushort),
+ ("MinorOperatingSystemVersion", c_ushort),
+ ("MajorImageVersion", c_ushort),
+ ("MinorImageVersion", c_ushort),
+ ("MajorSubsystemVersion", c_ushort),
+ ("MinorSubsystemVersion", c_ushort),
+ ("Win32VersionValue", c_uint),
+ ("SizeOfImage", c_uint),
+ ("SizeOfHeaders", c_uint),
+ ("CheckSum", c_uint),
+ ("Subsystem", c_ushort),
+ ("DllCharacteristics", c_ushort),
+ ("SizeOfStackReserve", c_ulonglong),
+ ("SizeOfStackCommit", c_ulonglong),
+ ("SizeOfHeapReserve", c_ulonglong),
+ ("SizeOfHeapCommit", c_ulonglong),
+ ("LoaderFlags", c_uint),
+ ("NumberOfRvaAndSizes", c_uint)
+ ]
+
+class IMAGE_NT_HEADERS(Structure):
+ _fields_ = [
+ ("Signature", c_uint),
+ ("FileHeader", IMAGE_FILE_HEADER),
+ ("OptionalHeader", IMAGE_OPTIONAL_HEADER)
+ ]
+
+class IMAGE_NT_HEADERS64(Structure):
+ _fields_ = [
+ ("Signature", c_uint),
+ ("FileHeader", IMAGE_FILE_HEADER),
+ ("OptionalHeader", IMAGE_OPTIONAL_HEADER64)
+ ]
+
+class IMAGE_SECTION_HEADER(Structure):
+ _fields_ = [
+ ("Name", c_ubyte * PEFlags.IMAGE_SIZEOF_SHORT_NAME),
+ ("PhysicalAddress", c_uint),
+ ("VirtualAddress", c_uint),
+ ("SizeOfRawData", c_uint),
+ ("PointerToRawData", c_uint),
+ ("PointerToRelocations", c_uint),
+ ("PointerToLinenumbers", c_uint),
+ ("NumberOfRelocations", c_ushort),
+ ("NumberOfLinenumbers", c_ushort),
+ ("Characteristics", c_uint)
+ ]
+
+""" This class parses the PE format """
+class PE(object):
+ def __init__(self, binary):
+ self.__binary = bytearray(binary)
+
+ self.__PEOffset = 0x00000000
+ self.__IMAGE_FILE_HEADER = None
+ self.__IMAGE_OPTIONAL_HEADER = None
+
+ self.__sections_l = []
+
+ self.__getPEOffset()
+ self.__parsePEHeader()
+ self.__parseOptHeader()
+ self.__parseSections()
+
+ def __getPEOffset(self):
+ self.__PEOffset = unpack("= rangeS and vaddr <= rangeE:
+ new += [gadget]
+ self.__gadgets = new
+
+ def __reOption(self):
+ new = []
+ re_strs = []
+
+ if not self.__options.re:
+ return
+
+ if '|' in self.__options.re:
+ re_strs = self.__options.re.split(' | ')
+ if 1 == len(re_strs):
+ re_strs = self.__options.re.split('|')
+ else:
+ re_strs.append(self.__options.re)
+
+ patterns = []
+ for __re_str in re_strs:
+ pattern = re.compile(__re_str)
+ patterns.append(pattern)
+
+ for gadget in self.__gadgets:
+ flag = 1
+ insts = gadget["gadget"].split(" ; ")
+ for pattern in patterns:
+ for ins in insts:
+ res = pattern.search(ins)
+ if res:
+ flag = 1
+ break
+ else:
+ flag = 0
+ if not flag:
+ break
+ if flag:
+ new += [gadget]
+ self.__gadgets = new
+
+ def __removeNonCallPreceded(self):
+ def __isGadgetCallPreceded(gadget):
+ # Given a gadget, determine if the bytes immediately preceding are a call instruction
+ prevBytes = gadget["prev"]
+ # TODO: Improve / Semantically document each of these cases.
+ callPrecededExpressions = [
+ "\xe8[\x00-\xff][\x00-\xff][\x00-\xff][\x00-\xff]$",
+ "\xe8[\x00-\xff][\x00-\xff][\x00-\xff][\x00-\xff][\x00-\xff][\x00-\xff][\x00-\xff][\x00-\xff]$",
+ "\xff[\x00-\xff]$",
+ "\xff[\x00-\xff][\x00-\xff]$",
+ "\xff[\x00-\xff][\x00-\xff][\x00-\xff][\x00-\xff]$"
+ "\xff[\x00-\xff][\x00-\xff][\x00-\xff][\x00-\xff][\x00-\xff][\x00-\xff][\x00-\xff][\x00-\xff]$"
+ ]
+ return bool(reduce(lambda x,y: x or y, map(lambda x: re.search(x, prevBytes), callPrecededExpressions)))
+ arch = self.__binary.getArch()
+ if arch == CS_ARCH_X86:
+ initial_length = len(self.__gadgets)
+ self.__gadgets = filter(__isGadgetCallPreceded, self.__gadgets)
+ print("Options().removeNonCallPreceded(): Filtered out {} gadgets.".format(initial_length - len(self.__gadgets)))
+ else:
+ print("Options().removeNonCallPreceded(): Unsupported architecture.")
+
+ def __deleteBadBytes(self):
+ archMode = self.__binary.getArchMode()
+ if not self.__options.badbytes:
+ return
+ new = []
+ #Filter out empty badbytes (i.e if badbytes was set to 00|ff| there's an empty badbyte after the last '|')
+ #and convert each one to the corresponding byte
+ bbytes = []
+ for bb in self.__options.badbytes.split("|"):
+ if '-' in bb:
+ rng = bb.split('-')
+ low = ord(codecs.decode(rng[0], "hex"))
+ high = ord(codecs.decode(rng[1], "hex"))
+ for i in range(low, high):
+ bbytes.append(chr(i))
+ else:
+ bbytes.append(codecs.decode(bb.encode("ascii"), "hex"))
+
+ archMode = self.__binary.getArchMode()
+ for gadget in self.__gadgets:
+ gadAddr = pack("([(rax)|(rbx)|(rcx)|(rdx)|(rsi)|(rdi)|(r9)|(r10)|(r11)|(r12)|(r13)|(r14)|(r15)]{3}))\], (?P([(rax)|(rbx)|(rcx)|(rdx)|(rsi)|(rdi)|(r9)|(r10)|(r11)|(r12)|(r13)|(r14)|(r15)]{3}))$", f)
+ if regex:
+ lg = gadget["gadget"].split(" ; ")[1:]
+ try:
+ for g in lg:
+ if g.split()[0] != "pop" and g.split()[0] != "ret":
+ raise
+ # we need this to filterout 'ret' instructions with an offset like 'ret 0x6', because they ruin the stack pointer
+ if g != "ret":
+ if g.split()[0] == "ret" and g.split()[1] != "":
+ raise
+ print("\t[+] Gadget found: 0x%x %s" %(gadget["vaddr"], gadget["gadget"]))
+ return [gadget, regex.group("dst"), regex.group("src")]
+ except:
+ continue
+ return None
+
+ def __lookingForSomeThing(self, something):
+ for gadget in self.__gadgets:
+ lg = gadget["gadget"].split(" ; ")
+ if lg[0] == something:
+ try:
+ for g in lg[1:]:
+ if g.split()[0] != "pop" and g.split()[0] != "ret":
+ raise
+ if g != "ret":
+ # we need this to filterout 'ret' instructions with an offset like 'ret 0x6', because they ruin the stack pointer
+ if g.split()[0] == "ret" and g.split()[1] != "":
+ raise
+ print("\t[+] Gadget found: 0x%x %s" %(gadget["vaddr"], gadget["gadget"]))
+ return gadget
+ except:
+ continue
+ return None
+
+ def __padding(self, gadget, regAlreadSetted):
+ lg = gadget["gadget"].split(" ; ")
+ for g in lg[1:]:
+ if g.split()[0] == "pop":
+ reg = g.split()[1]
+ try:
+ print("\tp += pack(' mov dword ptr [r32], r32
+ regex = re.search("mov dword ptr \[(?P([(eax)|(ebx)|(ecx)|(edx)|(esi)|(edi)]{3}))\], (?P([(eax)|(ebx)|(ecx)|(edx)|(esi)|(edi)]{3}))$", f)
+ if regex:
+ lg = gadget["gadget"].split(" ; ")[1:]
+ try:
+ for g in lg:
+ if g.split()[0] != "pop" and g.split()[0] != "ret":
+ raise
+ # we need this to filterout 'ret' instructions with an offset like 'ret 0x6', because they ruin the stack pointer
+ if g != "ret":
+ if g.split()[0] == "ret" and g.split()[1] != "":
+ raise
+ print("\t[+] Gadget found: 0x%x %s" %(gadget["vaddr"], gadget["gadget"]))
+ return [gadget, regex.group("dst"), regex.group("src")]
+ except:
+ continue
+ return None
+
+ def __lookingForSomeThing(self, something):
+ for gadget in self.__gadgets:
+ lg = gadget["gadget"].split(" ; ")
+ if lg[0] == something:
+ try:
+ for g in lg[1:]:
+ if g.split()[0] != "pop" and g.split()[0] != "ret":
+ raise
+ # we need this to filterout 'ret' instructions with an offset like 'ret 0x6', because they ruin the stack pointer
+ if g != "ret":
+ if g.split()[0] == "ret" and g.split()[1] != "":
+ raise
+ print("\t[+] Gadget found: 0x%x %s" %(gadget["vaddr"], gadget["gadget"]))
+ return gadget
+ except:
+ continue
+ return None
+
+ def __padding(self, gadget, regAlreadSetted):
+ lg = gadget["gadget"].split(" ; ")
+ for g in lg[1:]:
+ if g.split()[0] == "pop":
+ reg = g.split()[1]
+ try:
+ print("\tp += pack('[\d])", d).group("value")
+ minorVersion = re.search("MINOR_VERSION.+=.+(?P[\d])", d).group("value")
+ webVersion = int("%s%s" %(majorVersion, minorVersion))
+ curVersion = int("%s%s" %(MAJOR_VERSION, MINOR_VERSION))
+ if webVersion > curVersion:
+ print("The version %s.%s is available. Currently, you use the version %d.%d." %(majorVersion, minorVersion, MAJOR_VERSION, MINOR_VERSION))
+ else:
+ print("Your version is up-to-date.")
+
diff --git a/ROPgadget/ropgadget/version.py b/ROPgadget/ropgadget/version.py
new file mode 100644
index 0000000..16821ac
--- /dev/null
+++ b/ROPgadget/ropgadget/version.py
@@ -0,0 +1,11 @@
+## -*- coding: utf-8 -*-
+##
+## Jonathan Salwan - 2014-05-12 - ROPgadget tool
+##
+## http://twitter.com/JonathanSalwan
+## http://shell-storm.org/project/ROPgadget/
+##
+
+MAJOR_VERSION = 6
+MINOR_VERSION = 3
+PYROPGADGET_VERSION = "ROPgadget v%d.%d" %(MAJOR_VERSION, MINOR_VERSION)
diff --git a/ROPgadget/scripts/ROPgadget b/ROPgadget/scripts/ROPgadget
new file mode 100755
index 0000000..777f2d5
--- /dev/null
+++ b/ROPgadget/scripts/ROPgadget
@@ -0,0 +1,12 @@
+#!/usr/bin/env python
+## -*- coding: utf-8 -*-
+##
+## Jonathan Salwan - 2014-05-12 - ROPgadget tool
+##
+## http://twitter.com/JonathanSalwan
+## http://shell-storm.org/project/ROPgadget/
+##
+
+import ropgadget
+
+ropgadget.main()
diff --git a/ROPgadget/setup.py b/ROPgadget/setup.py
new file mode 100644
index 0000000..4a095f7
--- /dev/null
+++ b/ROPgadget/setup.py
@@ -0,0 +1,67 @@
+#!/usr/bin/env python
+
+from setuptools import setup
+import os
+
+package_name = "ROPGadget"
+package_dir = "ropgadget"
+package_description = """
+This tool lets you search your gadgets on your binaries to facilitate your ROP exploitation.
+ROPgadget supports ELF, PE and Mach-O format on x86, x64, ARM, ARM64, PowerPC, SPARC and MIPS architectures.
+http://www.shell-storm.org/project/ROPgadget/
+""".strip()
+
+
+def fullsplit(path, result=None):
+ """
+ Split a pathname into components (the opposite of os.path.join) in a
+ platform-neutral way.
+ """
+ if result is None:
+ result = []
+ head, tail = os.path.split(path)
+ if head == '':
+ return [tail] + result
+ if head == path:
+ return result
+ return fullsplit(head, [tail] + result)
+
+# Compile the list of packages available, because distutils doesn't have
+# an easy way to do this.
+packages, data_files = [], []
+root_dir = os.path.dirname(__file__)
+if root_dir != '':
+ os.chdir(root_dir)
+
+for dirpath, dirnames, filenames in os.walk(package_dir):
+ # Ignore dirnames that start with '.'
+ for i, dirname in enumerate(dirnames):
+ if dirname.startswith('.'): del dirnames[i]
+ if '__init__.py' in filenames:
+ packages.append('.'.join(fullsplit(dirpath)))
+ elif filenames:
+ data_files.append([dirpath, [os.path.join(dirpath, f) for f in filenames]])
+
+version = "6.3"
+
+setup(
+ name = package_name,
+ version = version,
+ description = package_description,
+ packages = packages,
+ license = "BSD",
+ author = "Jonathan Salwan",
+ author_email = "jonathan.salwan@gmail.com",
+ url = "https://github.com/JonathanSalwan/ROPgadget",
+ scripts = ['scripts/ROPgadget'],
+ install_requires = ['capstone'],
+ classifiers = [
+ 'Topic :: Security',
+ 'Environment :: Console',
+ 'Operating System :: OS Independent',
+ 'License :: OSI Approved :: BSD License',
+ 'Programming Language :: Python :: 2.7',
+ 'Programming Language :: Python :: 3',
+ 'Intended Audience :: Developers'
+ ]
+)
diff --git a/ROPgadget/test-suite-binaries/UNIVERSAL-x86-x64-libSystem.B.dylib b/ROPgadget/test-suite-binaries/UNIVERSAL-x86-x64-libSystem.B.dylib
new file mode 100644
index 0000000000000000000000000000000000000000..adbbf1f64bb43aac16074ad3133dabfa6bcc8e45
GIT binary patch
literal 59088
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
I|AGeo59pd-h5!Hn
literal 0
HcmV?d00001
diff --git a/ROPgadget/test-suite-binaries/elf-ARM64-bash b/ROPgadget/test-suite-binaries/elf-ARM64-bash
new file mode 100644
index 0000000000000000000000000000000000000000..5bffdf72ee3b203e371fa56203fc4ac2999aef55
GIT binary patch
literal 847400
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