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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
VZZc@L9W;zFibq3WGz11Z1OVuoUxokx
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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