#!/usr/bin/env python2

# Copyright 2015 ant4g0nist

#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at

#       http://www.apache.org/licenses/LICENSE-2.0

#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.

from __future__ import print_function

import os
import re
import cmd
import sys
import glob
import time
import shlex
import random
import string
import struct
import thread
import fnmatch
import datetime
import optparse
import argparse
import platform
import requests
import subprocess
from struct import *
from struct import pack
from binascii import *
from ctypes import *
from capstone import *

try:
    xrange          # Python 2
except NameError:
    xrange = range  # Python 3

import lldb

###################
# global vars #
lisaversion             =   'v-san'
PAGE_SIZE               =   4096
MAX_DISTANCE            =   PAGE_SIZE*10
###################

####################################
#             Misc Utils           #
####################################
g_ignore_frame_pointer  =   False

def banner(debugger,command,result,dict):
    
    lisa2="""
        
        lllllll   iiii
        l:::::l  i::::i
        l:::::l   iiii
        l:::::l
        l::::l iiiiiii     ssssssssss     aaaaaaaaaaaaa
        l::::l i:::::i   ss::::::::::s    a::::::::::::a
        l::::l  i::::i ss:::::::::::::s   aaaaaaaaa:::::a
        l::::l  i::::i s::::::ssss:::::s           a::::a
        l::::l  i::::i  s:::::s  ssssss     aaaaaaa:::::a
        l::::l  i::::i    s::::::s        aa::::::::::::a
        l::::l  i::::i       s::::::s    a::::aaaa::::::a
        l::::l  i::::i ssssss   s:::::s a::::a    a:::::a
        l::::::li::::::is:::::ssss::::::sa::::a    a:::::a
        l::::::li::::::is::::::::::::::s a:::::aaaa::::::a
        l::::::li::::::i s:::::::::::ss   a::::::::::aa:::a
        lllllllliiiiiiii  sssssssssss      aaaaaaaaaa  aaaa
        """
    print(tty_colors.green()+random.choice([lisa2])+tty_colors.default())
    print(tty_colors.red()+"\t-An Exploit Dev Swiss Army Knife. Version: "+lisaversion+tty_colors.default())

# convert to hex
def to_hex(var):
    """
        converts given value to hex
    """
    return hex(var)

# hextoascii
def hex2ascii(debugger,hex,result,dict):
    """
        converts Hex to ascii
        ex: h2a 0x41414141 prints AAAA
    """
    print(hex.replace('0x','').decode('hex'))

# generate random hex of length between n - m
def urandom(debugger,n,result,dict):
    """
        Generates random hex of given length
    """
    if not n:
        print('rand command an argument: example: rand 23')
        return
    print(open('/dev/urandom','r').read(random.randint(int(n)/2,int(n)/2)).encode('hex'))

# run os commands
def shell(debugger,command,result,dict):
    """
        runs shell command and prints output
    """
    try:
        if command:
            os.system(command)
        else:
            print('Please enter a proper shell command.Eg: shell ls')
            return
    except:
        print('Please enter a proper shell command.Eg: shell ls')
    return


# term colors
class TerminalColors:
    '''Simple terminal colors class'''
    def __init__(self, enabled=True):
        # TODO: discover terminal type from "file" and disable if
        # it can't handle the color codes
        self.enabled = enabled
    
    def reset(self):
        '''Reset all terminal colors and formatting.'''
        if self.enabled:
            return "\x1b[0m"
        return ''
    
    def bold(self, on=True):
        '''Enable or disable bold depending on the "on" parameter.'''
        if self.enabled:
            return "\x1b[1m" if on else "\x1b[22m"
        return ''
    
    def italics(self, on=True):
        '''Enable or disable italics depending on the "on" parameter.'''
        if self.enabled:
            return "\x1b[3m" if on else "\x1b[23m"
        return ''
    
    def underline(self, on=True):
        '''Enable or disable underline depending on the "on" parameter.'''
        if self.enabled:
            return "\x1b[4m" if on else "\x1b[24m"
        return ''
    
    def inverse(self, on=True):
        '''Enable or disable inverse depending on the "on" parameter.'''
        if self.enabled:
            return "\x1b[7m" if on else "\x1b[27m"
        return ''
    
    def strike(self, on=True):
        '''Enable or disable strike through depending on the "on" parameter.'''
        if self.enabled:
            return "\x1b[9m" if on else "\x1b[29m"
        return ''

    def black(self, fg=True):
        '''Set the foreground or background color to black.
            The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.'''
        if self.enabled:
            return "\x1b[30m" if fg else "\x1b[40m"
        return ''

    def red(self, fg=True):
        '''Set the foreground or background color to red.
            The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.'''
        if self.enabled:
            return "\x1b[31m" if fg else "\x1b[41m"
        return ''
    
    def green(self, fg=True):
        '''Set the foreground or background color to green.
            The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.'''
        if self.enabled:
            return "\x1b[32m" if fg else "\x1b[42m"
        return ''
    
    def yellow(self, fg=True):
        '''Set the foreground or background color to yellow.
            The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.'''
        if self.enabled:
            return "\x1b[43m" if fg else "\x1b[33m"
        return ''
    
    def blue(self, fg=True):
        '''Set the foreground or background color to blue.
            The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.'''
        if self.enabled:
            return "\x1b[34m" if fg else "\x1b[44m"
        return ''
    
    def magenta(self, fg=True):
        '''Set the foreground or background color to magenta.
            The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.'''
        if self.enabled:
            return "\x1b[35m" if fg else "\x1b[45m"
        return ''
    
    def cyan(self, fg=True):
        '''Set the foreground or background color to cyan.
            The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.'''
        if self.enabled:
            return "\x1b[36m" if fg else "\x1b[46m";
        return ''
    
    def white(self, fg=True):
        '''Set the foreground or background color to white.
            The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.'''
        if self.enabled:
            return "\x1b[37m" if fg else "\x1b[47m"
        return ''
    
    def default(self, fg=True):
        '''Set the foreground or background color to the default.
            The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.'''
        if self.enabled:
            return "\x1b[39m" if fg else "\x1b[49m"
        return ''

####################################
#       LLDB                       #
####################################

# set malloc debugging features
def setMallocDebug(debugger,c,result,dict):
    """sets DYLD_INSERT_LIBRARIES to /usr/lib/libgmalloc.dylib"""
    execute(debugger,'settings set target.env-vars DYLD_INSERT_LIBRARIES=/usr/lib/libgmalloc.dylib',result,dict)
    return True

# execute given LLDB command
def execute(debugger, lldb_command, result, dict):
    """
        Execute given command and print the outout to stdout
    """
    debugger.HandleCommand(lldb_command)

# execute command and return output
def executeReturnOutput(debugger,lldb_command, result, dict):
    """Execute given command and returns the outout"""
    ci = debugger.GetCommandInterpreter()
    res = lldb.SBCommandReturnObject()
    ci.HandleCommand(lldb_command,res)
    output = res.GetOutput()
    error = res.GetError()
    return (output,error)

def s(debugger,command,result,dict):
    """step command"""
    executeReturnOutput(debugger,"thread step-in",result,dict)

def si(debugger,command,result,dict):
    """step into command"""
    executeReturnOutput(debugger,"thread step-inst",result,dict)

def so(debugger,command,result,dict):
    """step over"""
    executeReturnOutput(debugger,"thread step-over",result,dict)

def stepnInstructions(debugger,count,result,dict):
    """step-in n time"""
    c=0

    while c<int(count):
        command=""
        executeReturnOutput(debugger,"thread step-in",result,dict)
        c+=1

def getArch():
    return lldb.debugger.GetSelectedTarget().triple.split('-')[0]

def testjump(debugger,command,result,dict):
        """
        Test if jump instruction is taken or not
        Returns:
            True if jump is taken or False if not 
        """
        inst=None
        flags = getEflags(debugger,command,result,dict)
        if not flags:
            return None

        if not inst:
            pc = getregvalue(debugger,"pc",result,dict)

            inst, error = executeReturnOutput(debugger,"x/1i $pc",result,dict)
            if not inst:
                return None

        opcode = inst.split('  ')[2]    

        if opcode == "jmp":
            return (True,inst.split('  ')[4])
        if opcode == "je" and flags["ZF"]:
            return (True,inst.split('  ')[4])
        if opcode == "jne" and not flags["ZF"]:
            return (True,inst.split('  ')[4])
        if opcode == "jg" and not flags["ZF"] and (flags["SF"] == flags["OF"]):
            return (True,inst.split('  ')[4])
        if opcode == "jge" and (flags["SF"] == flags["OF"]):
            return (True,inst.split('  ')[4])
        if opcode == "ja" and not flags["CF"] and not flags["ZF"]:
            return (True,inst.split('  ')[4])
        if opcode == "jae" and not flags["CF"]:
            return (True,inst.split('  ')[4])
        if opcode == "jl" and (flags["SF"] != flags["OF"]):
            return (True,inst.split('  ')[4])
        if opcode == "jle" and (flags["ZF"] or (flags["SF"] != flags["OF"])):
            return (True,inst.split('  ')[4])
        if opcode == "jb" and flags["CF"]:
            return (True,inst.split('  ')[4])
        if opcode == "jbe" and (flags["CF"] or flags["ZF"]):
            return (True,inst.split('  ')[4])
        if opcode == "jo" and flags["OF"]:
            return (True,inst.split('  ')[4])
        if opcode == "jno" and not flags["OF"]:
            return (True,inst.split('  ')[4])
        if opcode == "jz" and flags["ZF"]:
            return (True,inst.split('  ')[4])
        if opcode == "jnz" and flags["OF"]:
            return (True,inst.split('  ')[4])

        return (False,None)

def getProcess():
    return lldb.debugger.GetSelectedTarget().process

def getFrame():
    #return lldb.debugger.GetSelectedTarget().process.selected_thread.GetSelectedFrame(); 
    #return lldb.debugger.GetSelectedTarget().GetProcess().GetSelectedThread().GetSelectedFrame()
    #return lldb.debugger.GetTargetAtIndex(0).process.selected_thread.GetFrameAtIndex(0);
        
    #return frame for stopped thread... there should be one at least...
    ret = None;
    for t in getProcess():
        if t.GetStopReason() != lldb.eStopReasonNone and t.GetStopReason() != lldb.eStopReasonInvalid:
            ret = t.GetFrameAtIndex(0);

    return ret;

def ls(debugger, args, result, dict):
    for res in Symbol.list(target, args.glob, all=args.all, ignorecase=args.ignorecase):
        print (res)
    return

def searchMem(debugger, args, result, dict):

    args=shlex.split(args)
    parser = argparse.ArgumentParser(prog="searchmem");
    parser.add_argument("-s", "--string",  help="string to look for");
    parser.add_argument("-c", "--count", type=int, default=10,  help="number of results to print");

    args = parser.parse_args(args)

    to_search = args.string

    if not to_search:
        parser.print_help()
        return

    count     = args.count 

    process = getProcess()
    pid     = process.GetProcessID()

    vmmemory  = subprocess.check_output(["vmmap", "%d" % pid], shell=False)

    memory_address_ranges = []
    address_regex = re.compile("([0-9a-fA-F]{16})-([0-9a-fA-F]{16})")

    for line in vmmemory.split("\n"):
        mem = address_regex.search(line)

        if not mem:
            continue;

        mem_start       = long(mem.group(1), 16)
        mem_end         = long(mem.group(2), 16)

        mem_size        = mem_end-mem_start
        memory_buff     = readMem(mem_start, mem_size);

        offset = 0
        if memory_buff:
            while True:
                mem_location = memory_buff.find(to_search)
                if not mem_location==-1:
                    offset+=mem_location
                    value = memory_buff[mem_location:mem_location+len(to_search)+16]
                    print(tty_colors.green()+"%.016lX"%(mem_start+offset)+tty_colors.default()+"\t%s"%(value))
                    memory_buff = memory_buff[mem_location+len(to_search):]
                    offset += len(to_search)
                else:
                    break

                if count==1:
                    return
                count-=1


def readMem(addrStart, addrSize):
    error = lldb.SBError()
    process = getProcess() 
    memory_buff = process.ReadMemory(addrStart, addrSize, error)
    if not error.Success():
        return None

    return memory_buff

def vtable(debugger, command, result, dict):
    op, err = executeReturnOutput(debugger,'im loo -r -v -s "vtable for"', result, dict)
    if not err:
        print(op)
        return

def context(debugger,command,result,dict):
    """prints context of current execution"""
    debugger.SetAsync(True)
    frame = getFrame()

    if not frame:
        return
    
    thread = frame.GetThread()

    pc = getregvalue(debugger, "pc", result, dict)

    res = lldb.SBCommandReturnObject()
    #disassembly
    lldb.debugger.GetCommandInterpreter().HandleCommand("disassemble --start-address=" + pc + " --count=4", res)
    disas = res.GetOutput().split("\n")
    
    pc_text = int(str(pc).strip().split()[0], 16)
    pc_text = hex(pc_text)

    print(tty_colors.blue()+"[disassembly]"+tty_colors.default())

    for item,value in enumerate(disas):
        if pc_text in value:
            print(tty_colors.red()+value+tty_colors.default())
        else:
            print(value)

    print(tty_colors.blue()+"[/disassembly]"+tty_colors.default())
    
    #jump
    dis, error=executeReturnOutput(debugger,'disassemble -c 1 -s $pc',result,dict)
    if dis:
        dis = dis.split(': ')[1].split()[0]

        if 'j' in dis:
            print
            print(tty_colors.blue()+"[jump]"+tty_colors.default())
            jumpto, destination = testjump(debugger,command,result,dict)
            if jumpto==True:                
                print(tty_colors.red()+"Jumping to "+destination+tty_colors.default())
                print(tty_colors.blue()+"disassembly at "+destination+tty_colors.default())

                lldb.debugger.GetCommandInterpreter().HandleCommand("disassemble --start-address " + destination + " --count=2", res)
                disas = res.GetOutput().split("\n")

                for item,value in enumerate(disas):
                    if pc_text in value:
                        print(tty_colors.red()+value+tty_colors.default())
                    else:
                        print(value)                
            else:
                print(tty_colors.red()+"\tJump not taken."+tty_colors.default())

            print(tty_colors.blue()+"[/jump]"+tty_colors.default())

    #registers
    print
    lldb.debugger.GetCommandInterpreter().HandleCommand("disassemble --start-address=" + pc + " --count=1", res)
    data = res.GetOutput().split("\n")[1]

    
    op, error=executeReturnOutput(debugger,"register read",result,dict)
    print(tty_colors.blue()+"[registers]"+tty_colors.default())
    for reg in op.split("\n\n")[0].split('General Purpose Registers:\n')[1].split('eflags')[0].split("\n"):
        reg         = reg.split(" = ")
        reg_value   = " = ".join(reg[1:])
        reg         = reg[0].strip(" ")
        
        if reg in data:
            print("\t",tty_colors.red()+reg+tty_colors.default()+" = "+tty_colors.red()+reg_value+tty_colors.default())
        else:
            print("\t",tty_colors.green()+reg+tty_colors.default()+" = "+tty_colors.green()+reg_value+tty_colors.default())

    print(tty_colors.blue()+"[/registers]"+tty_colors.default())

    return

def getEflags(debugger,command,result,dict):
    """
    Get flags value from EFLAGS register

    Returns:
    - dictionary of named flags
    """

    # Eflags bit masks, source vdb
    eflags = (getregvalue(debugger,"eflags",result,dict) or
              getregvalue(debugger,"rflags",result,dict))
    eflags = int(eflags, 16)
    masks = {"CF":0, "PF":2, "AF":4, "ZF":6, "SF":7, "TF":8, "IF":9, "DF":10, "OF":11}
    return {key: bool(eflags & (1 << value)) for key, value in masks.items()}


####################################
#         Exploitation             #
####################################


#pattern create and pattern offset
def patternCreate(debugger,size, result, dict):
    """creates a cyclic pattern of given length"""

    try:
        length = int(size)
    except:
        print("[+] Usage: patternCreate <length>")
        return

    seta = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    setb = "abcdefghijklmnopqrstuvwxyz"
    setc = "0123456789"

    string = "" ; a = b = c = 0

    while len(string) < length:
        string += seta[a] + setb[b] + setc[c]
        c+=1
        if c == len(setc):c=0;b+=1
        if b == len(setb):b=0;a+=1
        if a == len(seta):a=0
    
    print(tty_colors.red() + string[:length] + tty_colors.default())

    return string[:length]

#check if given pattern is in cyclic pattern
def checkIfCyclic(debugger,pat,result,dict):
    """check if given pattern is in cyclic pattern"""

    #seta="ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    #setb="abcdefghijklmnopqrstuvwxyz"
    #setc="0123456789"
    
    if not pat:
        print('[+] Usage: checkIfCyclic <some string>')
        return

    string=pat ; a=0 ; b=0 ; c=0
    length=len(string)
    i=0
    while i<(length-2):
        if string[i].isalpha():
            if string[i].islower():
                if string[i+1].isupper():
                    if string[i+2].isdigit():
                        pass
                    else:
                        print(tty_colors.red()+"Not a cyclic pattern"+tty_colors.default())
                        return False
                else:
                    print(tty_colors.red()+"Not a cyclic pattern"+tty_colors.default())
                    return False
        
            elif string[i].isupper():
                if string[i+1].islower():
                    if string[i+2].isdigit():
                        pass
                    else:
                            print(tty_colors.red()+"Not a cyclic pattern"+tty_colors.default())
                            return False
                else:
                    print(tty_colors.red()+"Not a cyclic pattern"+tty_colors.default())
                    return False

        elif string[i].isdigit():
            if string[i+1].isalpha():
                if string[i+1].isupper():
                    if string[i+2].islower():
                        pass
                    else:
                        print(tty_colors.red()+"Not a cyclic pattern"+tty_colors.default())
                        return False
                else:
                    print(tty_colors.red()+"Not a cyclic pattern"+tty_colors.default())
                    return False


        i += 3
    print("seems to be a valid pattern")
    return True

#pattern search
def patternOffset(debugger,sizepat,result,dict):
    """search offset of pattern."""
    
    if len(sizepat.split(' '))==2:
        try:
            size=int(sizepat.split(' ')[0])
            pat=sizepat.split(' ')[1]
            pattern=patternCreate(debugger,size,result,dict)
            if "0x" in pat:
                pat=pat.replace("0x","")
                pat=pat.decode("hex")[::-1]
            try:
                p=int(pat)
                pat=pat.decode('hex')
            except:
                pass
            found=[m.start() for m in re.finditer(pat, pattern)]
            if found!=-1:
                print('offsets:',found)
        except:
            print("please check the syntax")
            print("patternOffset 250 Aa2A")
                
    elif len(sizepat.split(' '))==1:
        try:
            size=10000
            pat=sizepat.split(' ')[1]
            pattern=patternCreate(debugger,size,result,dict,True)
            if "0x" in pat:
                pat=pat.replace("0x","")
                pat=pat.decode("hex")[::-1]
            try:
                p=int(pat)
                pat=pat.decode('hex')
            except:
                pass
            found=[m.start() for m in re.finditer(pat, pattern)]
            #                found=pattern.find(pat)
            if found!=-1:
                print(found)
        except:
            print("please check the syntax")
            print("patternOffset 250 Aa2A")

#return address in register
def getregvalue(debugger,reg,result,dict):
    output,error=executeReturnOutput(debugger,'register read '+reg,result,dict)
    return output.split("= ")[-1].split(" ")[0]

lldb_stop_reasons =  { 'eStateCrashed' : 8, 'eStateDetached' : 9, 'eStateExited' : 10, 'eStateInvalid' : 0,
                      'eStateLaunching' : 4, 'eStateRunning' : 6, 'eStateStepping' : 7,'eStateStopped' : 5,
                      'eStateSuspended' : 11, 'eStopReasonBreakpoint' : 3, 'eStopReasonException' : 6,
                      'eStopReasonExec' : 7, 'eStopReasonInvalid' : 0, 'eStopReasonNone' : 1, 'eStopReasonSignal' : 5,
                      'eStopReasonThreadExiting' : 9, 'eStopReasonTrace' : 2, 'eStopReasonWatchpoint' : 4
                      }

def getexception(exception_description):
    type1  = exception_description
    try:
        exception = re.search("EXC_(.+?) ",type1).group().strip(' ')
    except:
        exception = None
    try:
        code =  re.search("\(code(.+?),",type1).group().split('=')[1].strip(',')
    except:
        try:
            code =  re.search("\(code(.+?)\)",type1).group().split('=')[1].strip(')')
        except:
            code = None
    try:
        address  = re.search(", address(.+?)\)",type1).group().split('=')[1].strip(')')
    except:
        address = None

    return exception,code,address

def getsignal(signal_description):
    return signal_description.split(' ')[1]

def type_for_two_memory(access_address, disassembly):
    first_reg_val = value_for_first_register(disassembly)
    return "read" if first_reg_val != access_address else "write"

def stack_access_crash(access_address, sp_val):
    return (int(sp_val, 0) - int(access_address, 0)) <= PAGE_SIZE

def getexceptiontype(access_address, disassembly, registers):
    if disassembly!=None:
        last_comma = disassembly.find(',')
        right_paren = disassembly.find(']')

        sp = registers['sp']

        if disassembly[right_paren+1:].find(']')!=-1:
            type_=type_for_two_memory(access_address, disassembly)
            return type_

        elif disassembly.find('call')!=-1:
            if not right_paren or last_comma:
                type_ = "recursion"
            elif stack_access_crash(access_address,sp):
                type_ = "recursion"
            else:
                type_ = "exec"
            return type_

        elif disassembly.find("cmp")!=-1 or disassembly.find("test")!=-1 or disassembly.find("fld")!=-1:
            type_ = "read"
            return type_

        elif disassembly.find("fst")!=-1:
            type_ = "write"
            return type_

        elif disassembly.find("mov")!=-1:
            if last_comma>right_paren:
                type_ = "read"
            else:
                type_ = "write"
            return type_

        elif disassembly.find('jmp')!=-1:
            type_ = "exec"
            return type_

        elif disassembly.find('push')!=-1:
            if right_paren:
                type_ = "read"
            else:
                type_ = "recursion"
            return type_

        elif disassembly.find('inc')!=-1 or disassembly.find('dec')!=-1:
            type_ = "write"
            return type_

        elif disassembly.find("stos")!=-1:
            type_ = "write"
            return type_

        elif disassembly.find("lods")!=-1:
            type_ = "read"
            return type_

        else:
            type_ = "unknown"
            return type_

        if disassembly.find("st") == 2:
            type_ = "write"
            return type_

        elif disassembly.find("ld") == 2:
            type_ = "read"
            return type_

        elif disassembly.find("push") == 2:
            type_ = "recursion"
            return type_

        else:
            type_ = "unknown"
            return type_

    else:
        type_ = "unknown"
        return type_


#return whether or not the base pointer is far away from the stack pointer.
def bp_inconsistent_with_sp(bp_val,sp_val):
    #define MAX_DISTANCE (PAGE_SIZE * 10)
    #    No check if bp_val > sp_val since bp_val - sp_val may have underflowed.

    return int(bp_val, 0) - int(sp_val, 0) > MAX_DISTANCE

class Lisa:
    def __init__(self, debugger,result,dict):
        
        self.debugger = debugger
        self.target = self.debugger.GetSelectedTarget()
        self.process = self.target.process
        self.thread = self.process.selected_thread
        self.frame = self.thread.GetFrameAtIndex(0)
        self.pc = hex(self.frame.pc)
        self.sp = hex(self.frame.sp)
        self.bp = hex(self.frame.fp)

        disas,disas_error = executeReturnOutput(debugger,"disassemble -c 1 -s $pc",result,dict)
        self.pc_disas = None if disas_error else re.search("->(.+?)\n",disas).group().split(':')[1]
        self.backtrace, self.backtrace_error = executeReturnOutput(debugger,"bt",result,dict)
        
        self.crash_reason = self.thread.GetStopReason()
        if self.crash_reason == lldb_stop_reasons['eStopReasonException']:
            self.exception = self.thread.GetStopDescription(80)
            self.exception,self.exc_code,self.exc_address = getexception(self.exception)
            self.signal = None

            print(tty_colors.red()+"Exception : "+self.exception+tty_colors.default())

        elif self.crash_reason == lldb_stop_reasons['eStopReasonSignal']:
            self.signal = self.thread.GetStopDescription(80)
            self.signal = getsignal(self.signal)
            self.exc_address = None
            self.exception = None

            print(tty_colors.red()+"Signal : "+self.signal+tty_colors.default())
            self.is_stack_suspicious()        

        else:
            return

        self.gen_registers =  list(self.frame.registers)[0]
        self.registers = {i.name: i.value for i in self.gen_registers.__iter__()}

        max_offset = 1024
        if self.exception:
            if self.exception=="EXC_BAD_ACCESS":
                # check pc == access_address
                
                if self.exc_address and int(self.exc_address,0)==int(self.pc,0):
                    # IP over write
                    self.is_exploitable = True
                    print(tty_colors.red()+"Exploitable = %s"%self.is_exploitable+tty_colors.default())
                    self.reportexploitable="Trying to execute a bad address, this is a potentially exploitable issue"
                    print(tty_colors.red() + self.reportexploitable + tty_colors.default())
                else:
                    self.access_type = getexceptiontype(self.exc_address, self.pc_disas, self.registers)
                    
                    if self.exc_address and int(self.exc_address,16)<int(PAGE_SIZE):
                        self.is_exploitable=False
                        print(tty_colors.red()+"Exploitable = %s"%self.is_exploitable+tty_colors.default())
                        self.reportexploitable="Null Dereference. Probably not exploitable"
                        print(tty_colors.blue() + self.reportexploitable + tty_colors.default())
                        return

                    elif self.access_type == "recursion":
                        self.is_exploitable=False

                        stack=self.backtrace
                        MINIMUM_RECURSION_LENGTH = 300
                        stack_length= len(stack.split("\n"))

                        if stack_length>MINIMUM_RECURSION_LENGTH:
                            print(tty_colors.red()+"Exploitable = %s"%self.is_exploitable+tty_colors.default())
                            self.reportexploitable="The crash is suspected to be not exploitable due to unbounded recursion since there were %d stack frames."%stack_length
                            print(tty_colors.blue() + self.reportexploitable + tty_colors.default())
                            return
                    else:
                        self.is_exploitable=True

                    if self.access_type == "exec":
                        self.is_exploitable = True

                    addr = self.exc_address
                    max_offset = 1024

                    if (addr > 0x55555555 - max_offset and addr < 0x55555555 + max_offset):
                        print(tty_colors.red()+"Exploitable = %s"%self.is_exploitable+tty_colors.default())
                        self.reportexploitable="The access address indicates the use of freed memory if MallocScribble was used, or uninitialized memory if libgmalloc and MALLOC_FILL_SPACE was used."
                        print(tty_colors.red() + self.reportexploitable + tty_colors.default())
                    
                    elif (addr > 0xaaaaaaaa - max_offset and addr < 0xaaaaaaaa + max_offset):
                        print(tty_colors.red()+"Exploitable = %s"%self.is_exploitable+tty_colors.default())                
                        self.reportexploitable="The access address indicates that uninitialized memory was being used if MallocScribble was used."
                        print(tty_colors.red() + self.reportexploitable + tty_colors.default())

                    elif "EXC_I386_GPFLT" == self.exc_code:
                        print(tty_colors.red()+"Exploitable = %s"%self.is_exploitable+tty_colors.default())
                        self.reportexploitable="The exception code indicates that the access address was invalid in the 64-bit ABI (it was > 0x0000800000000000)."
                        print(tty_colors.red() + self.reportexploitable + tty_colors.default())

                    elif not g_ignore_frame_pointer and bp_inconsistent_with_sp(self.bp,self.sp):
                        self.is_exploitable = True
                        print(tty_colors.red()+"Exploitable = %s"%self.is_exploitable+tty_colors.default())
                        self.reportexploitable="Presumed exploitable based on the discrepancy between the stack pointer and base pointer registers."
                        print(tty_colors.red() + self.reportexploitable + tty_colors.default())
                    else:
                        print(tty_colors.red()+"Exploitable = %s"%self.is_exploitable+tty_colors.default())

                        if self.access_type in ("read", "write"):
                            self.reportexploitable = "Crash "+self.access_type+"'g invalid address."
                        else:
                            self.reportexploitable = "Crash accessing invalid address."
                        print(tty_colors.red() + self.reportexploitable + tty_colors.default())

            elif self.exception=="EXC_BAD_INSTRUCTION":
                self.is_exploitable = True
                print(tty_colors.red()+"Exploitable = %s"%self.is_exploitable+tty_colors.default())
                self.reportexploitable="Illegal instruction at %s, probably a exploitable issue unless the crash was in libdispatch/xpc."%self.pc
                print(tty_colors.blue() + self.reportexploitable + tty_colors.default())

            elif self.exception=="EXC_ARITHMETIC":
                self.is_exploitable = False
                print(tty_colors.red()+"Exploitable = %s"%self.is_exploitable+tty_colors.default())
                self.reportexploitable="Arithmetic exception at %s, probably not exploitable."%self.pc
                print(tty_colors.blue() + self.reportexploitable + tty_colors.default())

            elif self.exception=="EXC_SOFTWARE":
                self.is_exploitable=False
                print(tty_colors.red()+"Exploitable = %s"%self.is_exploitable+tty_colors.default())
                self.reportexploitable="Software exception, probably not exploitable."
                print(tty_colors.blue() + self.reportexploitable + tty_colors.default())

            elif self.exception=="EXC_BREAKPOINT":
                self.is_exploitable=False
                print(tty_colors.red()+"Exploitable = %s"%self.is_exploitable+tty_colors.default())
                self.reportexploitable="Software breakpoint, probably not exploitable."
                print(tty_colors.blue() + self.reportexploitable + tty_colors.default())
            
            elif self.exc_address=="EXC_CRASH":
                self.is_exploitable= False
                print(tty_colors.red()+"Exploitable = %s"%self.is_exploitable+tty_colors.default())


    def is_stack_suspicious(self):
        exc_address = self.exc_address
        exception   = self.exception
        backtrace   = self.backtrace

        suspicious_functions = """__chk_fail __stack_chk_fail szone_error CFRelease CFRetain
            _CFRelease _CFRetain malloc calloc realloc objc_msgSend szone_free free_small
            tiny_free_list_add_ptr tiny_free_list_remove_ptr small_free_list_add_ptr
            small_free_list_remove_ptr large_entries_free_no_lock large_free_no_lock
            szone_batch_free szone_destroy free CSMemDisposeHandle CSMemDisposePtr append_int
            release_file_streams_for_task __guard_setup _CFStringAppendFormatAndArgumentsAux
            WTF::fastFree WTF::fastMalloc WTF::FastCalloc WTF::FastRealloc WTF::tryFastCalloc
            WTF::tryFastMalloc WTF::tryFastRealloc WTF::TCMalloc_Central_FreeList GMfree
            GMmalloc_zone_free GMrealloc GMmalloc_zone_realloc""".split()

        if exc_address=="0xbbadbeef":
            # WebCore functions call CRASH() in various assertions or if the amount to allocate was too big. CRASH writes a null byte to 0xbbadbeef.
            self.is_exploitable=False
            print(tty_colors.red()+"Exploitable = %s"%self.is_exploitable+tty_colors.default())
            self.reportexploitable = "Not exploitable. Seems to be a safe crash. Calls to CRASH() function writes a null byte to 0xbbadbeef"
            print(tty_colors.red() + self.reportexploitable + tty_colors.default())
            return 

        if "0   ???" in backtrace:
            self.is_exploitable = True
            print(tty_colors.red()+"Exploitable = %s"%self.is_exploitable+tty_colors.default())
            self.reportexploitable="This crash is suspected to be exploitable because the crashing instruction is outside of a known function, i.e. in dynamically generated code"
            print(tty_colors.red() + self.reportexploitable + tty_colors.default())
            return

        for i in suspicious_functions:
            if i in backtrace:
                if exception == "EXC_BREAKPOINT" and i in ("CFRelease", "CFRetain"):
                    self.is_exploitable = "no"
                    return
                elif i=="_CFRelease" or i=="CFRelease" and "CGContextDelegateFinalize" in backtrace:
                    return
                elif i=="objc_msgSend" and exc_address<<PAGE_SIZE:
                    continue
                else:
                    self.is_exploitable = True
                    print(tty_colors.red()+"Exploitable = %s"%self.is_exploitable+tty_colors.default())
                    self.reportexploitable="The crash is suspected to be an exploitable issue due to the suspicious function in the stack trace of the crashing thread."
                    print(tty_colors.red() + self.reportexploitable + tty_colors.default())
                    return

def exploitable(debugger,cmd,res,dict):
    """checks if the crash is exploitable"""
    lisa_=Lisa(debugger,res,dict)

class ShellStorm:
    def __init__(self):
        pass

    def searchShellcode(self, keyword):
        try:
            print("Connecting to shell-storm.org...")
            s = requests.get("http://shell-storm.org/api/?s=%s"%(keyword))
            res = s.text
            data_l = res.split('\n')
        except:
            print("Cannot connect to shell-storm.org")
            return None

        data_dl = []
        for data in data_l:
            try:
                desc = data.split("::::")
                try:
                    dico = {
                             'ScAuthor': desc[0],
                             'ScArch': desc[1],
                             'ScTitle': desc[2],
                             'ScId': desc[3],
                             'ScUrl': desc[4],
                             'ScSize': int(''.join(x for x in desc[2][-10:-5] if x.isdigit()))
                           }
                except Exception:
                    dico = {
                             'ScAuthor': desc[0],
                             'ScArch': desc[1],
                             'ScTitle': desc[2],
                             'ScId': desc[3],
                             'ScUrl': desc[4],
                             'ScSize': 0
                           }


                data_dl.append(dico)
            except:
                pass

        try:
            return sorted(data_dl, key=lambda x: x['ScSize'], reverse=True)
        except Exception:
            print("Could not sort by size")

        return data_dl

    def displayShellcode(self, shellcodeId):
        if shellcodeId is None:
            return None

        try:
            s = requests.get("http://shell-storm.org/shellcode/files/shellcode-%s.php"%(shellcodeId))
            res = s.text
            data = res.split("<pre>")[1].split("<body>")[0]
        except:
            print("Failed to download shellcode from shell-storm.org")
            return None

        data = data.replace("&quot;", "\"")
        data = data.replace("&amp;", "&")
        data = data.replace("&lt;", "<")
        data = data.replace("&gt;", ">")

        return data

    @staticmethod
    def version():
        print("shell-storm API - v0.1")
        print("Search and display all shellcodes in shell-storm database")
        print("Jonathan Salwan - @JonathanSalwan - 2012")
        print("http://shell-storm.org")
        return

def extractFromUniversalBinary(debugger,command,result,dict):
    """Uses lipo to extract a given architecture from a Universal binary
        Syntax: extract x86_64 <input file> <output file>
        Ex: extract x86_64 /usr/lib/system/libsystem_kernel.dylib ./libsystem_kernel.dylib

    """
    args = shlex.split(command)
    if len(args)==3:
        architecture, intputfile, outputfile = args
        subprocess.check_output(['lipo', intputfile, '-extract', architecture, '-output', outputfile], shell=False)
    else:
        print("Syntax: extract x86_64 /usr/lib/system/libsystem_kernel.dylib ./libsystem_kernel.dylib")

def shellcode(debugger, command, result, dict):
    """Searches shell-storm for shellcode
       Syntax:shellcode"""
    mod = shlex.split(command)
    if len(mod)!=2:
        print("Syntax:   shellcode <option> <arg>\n")
        print("Options:  -search <keyword>")
        print("          -display <shellcode id>")
        print("          -save <shellcode id>")
        return

    mod, arg = mod
    if mod not in ("-search", "-display", "-save"):
        syntax()
        return

    if mod == "-search":
        api = ShellStorm()
        res_dl = api.searchShellcode(arg)
        if not res_dl:
            print("Shellcode not found")
            sys.exit(0)

        
        print(tty_colors.red()+"Found %d shellcodes" % len(res_dl)+tty_colors.default())
        print(tty_colors.green()+"%s\t%s %s" %("ScId", "Size", "Title")+tty_colors.default())
        for data_d in res_dl:
            if data_d['ScSize'] == 0:
                print("[%s]\tn/a  %s - %s"%(data_d['ScId'], data_d['ScArch'], data_d['ScTitle']))
            else:
                print("[%s]\t%s%s - %s"%(data_d['ScId'], str(data_d['ScSize']).ljust(5), data_d['ScArch'], data_d['ScTitle']))

    elif mod == "-display":
        res = ShellStorm().displayShellcode(arg)
        if not res:
            print("Shellcode id not found")
            return
        print(tty_colors.red()+res+tty_colors.default())

    elif mod == "-save":
        res = ShellStorm().displayShellcode(arg)

        if not res:
            print("Shellcode id not found")
            return
        filename = 'shellcode_'+str(time.time())+'.c'
        with open(filename,'w') as f:
            f.write(res)    
        print(tty_colors.red()+"Written to file shellcode_"+filename+'.c'+tty_colors.default())
    return


def coredump(debugger,command,result,dict):
    """
        dump entire process memory
    """
    binary_name = lldb.target.executable.basename
    execute(debugger, 'process save-core "%s.core"'%(binary_name),result,dict)
    return

def dump(debugger,args,result,dict):
    """Dump's Memory of the process in a given address range
       Syntax: dump outfile 0x6080000fe680 0x6080000fe680+1000
            dump will not read over 1024 bytes of data. To overwride this use -f
       Syntax: dump -o outfile -s 0x6080000fe680 -e 0x6080000fe680+1000 -f"""

    args=shlex.split(args)

    parser = argparse.ArgumentParser(prog="dump memory in the memory given range");
    parser.add_argument("-s", "--start",  required=True, help="start address");
    parser.add_argument("-e", "--end", required=True, help="end address");
    parser.add_argument("-o", "--outfile", help="file to save the dump to");
    parser.add_argument("-f", "--force", default=0, type=int, help="dump will not read over 1024 bytes of data. To overwride this use -f. 0(false) or 1(true)");

    args = parser.parse_args(args)
    start_range = args.start
    end_range   = args.end

    if args.force==1:
        if args.outfile:
            output, error=executeReturnOutput(debugger,"memory read -b --force --outfile %s %s %s"%(args.outfile, start_range, end_range),result,dict)    
        else:
            output, error=executeReturnOutput(debugger,"memory read -b %s %s"%(start_range, end_range),result,dict)    
    else:
        if args.outfile:
            output, error=executeReturnOutput(debugger,"memory read -b --force --outfile %s %s %s"%(args.outfile, start_range, end_range),result,dict)    
        else:
            output, error=executeReturnOutput(debugger,"memory read -b %s %s"%(start_range, end_range),result,dict)    

    if not error:
        if not args.outfile:
            print(output)

    else:
        if "--force" in error:
            print("dump will not read over 1024 bytes of data. To overwride this use -f.")
            print("Syntax: dump outfile 0x6080000fe680 0x6080000fe680+1000 -f")
        else:
            print(error)

def symbols(debugger, string, result, dict):
    results = return_symbols(debugger, string)
    if results:
        for i in results:
            print(i)
        return

    print('Nothing found')

def return_symbols(debugger, string):
    module_to_search = None
    result = []
    if string:
        if "`" in string:
            module_to_search, string_to_search = string.split("`", 1)
        else:
            string_to_search = string

        modules = debugger.GetSelectedTarget().modules
        
        for module in modules:
            if module_to_search and not fnmatch.fnmatch(module.file.basename.lower(), module_to_search.lower()):
                continue

            prefix = module.file.basename + "`"
            for i in module.symbols:
                if fnmatch.fnmatch((i.name).lower(), string_to_search.lower()):
                    result.append('%s '%(prefix+"%s"%i))
        return result
    return None

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:
    CPU_TYPE_I386               = 0x7
    CPU_TYPE_X86_64             = (CPU_TYPE_I386 | 0x1000000)
    CPU_TYPE_MIPS               = 0x8
    CPU_TYPE_ARM                = 12
    CPU_TYPE_SPARC              = 14
    CPU_TYPE_POWERPC            = 18
    CPU_TYPE_POWERPC64          = (CPU_TYPE_POWERPC | 0x1000000)
    LC_SEGMENT                  = 0x1
    LC_SEGMENT_64               = 0x19
    S_ATTR_SOME_INSTRUCTIONS    = 0x00000400
    S_ATTR_PURE_INSTRUCTIONS    = 0x80000000

class MachOHeader(Structure):

    _fields_ = [

        ("magic", c_uint),
        ("cputype", c_uint),
        ("cpusubtype", c_uint),
        ("filetype", c_uint),
        ("ncmds", c_uint),
        ("sizeofcmds", c_uint),
        ("flags", c_uint)

    ]

""" This class parses the Mach-O """
class MACHO:
    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.nsects, base[56:], 32)

            elif command.cmd == MACHOFlags.LC_SEGMENT_64:
                segment = SEGMENT_COMMAND64.from_buffer_copy(base)
                self.__setSections(segment.nsects, base[72:], 64)

            base = base[command.cmdsize:]

    def __setSections(self, sectionsNumber, base, sizeHeader):
        for i in range(sectionsNumber):
            if sizeHeader == 32:
                section = SECTION.from_buffer_copy(base)
                base = base[68:]
                self.__sections_l += [section]
            elif sizeHeader == 64:
                section = SECTION64.from_buffer_copy(base)
                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" : str(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_MIPS:
            return CS_ARCH_MIPS
        else:
            print("[Error] MACHO.getArch() - Architecture not supported")
            return None
            
    def getArchMode(self):
        if self.__machHeader.magic == 0xfeedface: 
            return 4
        elif self.__machHeader.magic == 0xfeedfacf:
            return 8
        else:
            print("[Error] MACHO.getArchMode() - Bad Arch size")
            return None
        pass

    def getFormat(self):
        return "Mach-O"

class FAT_HEADER(BigEndianStructure):
    _fields_ = [
                ("magic",           c_uint),
                ("nfat_arch",       c_uint)
               ]

class FAT_ARC(BigEndianStructure):
    _fields_ = [
                ("cputype",         c_uint),
                ("cpusubtype",      c_uint),
                ("offset",          c_uint),
                ("size",            c_uint),
                ("align",           c_uint)
               ]

class MACHOFlags:
    CPU_TYPE_I386               = 0x7
    CPU_TYPE_X86_64             = (CPU_TYPE_I386 | 0x1000000)
    CPU_TYPE_MIPS               = 0x8
    CPU_TYPE_ARM                = 12
    CPU_TYPE_SPARC              = 14
    CPU_TYPE_POWERPC            = 18
    CPU_TYPE_POWERPC64          = (CPU_TYPE_POWERPC | 0x1000000)
    LC_SEGMENT                  = 0x1
    LC_SEGMENT_64               = 0x19
    S_ATTR_SOME_INSTRUCTIONS    = 0x00000400
    S_ATTR_PURE_INSTRUCTIONS    = 0x80000000

""" This class parses the Universal binary """
class UNIVERSAL:
    def __init__(self, binary):
        self.__binary = bytearray(binary)
        self.__machoBinaries = []

        self.__fatHeader    = None
        self.__rawLoadCmd   = None
        self.__sections_l   = []

        self.__setHeader()
        self.__setBinaries()

    def __setHeader(self):
        self.__fatHeader = FAT_HEADER.from_buffer_copy(self.__binary)

    def __setBinaries(self):
        offset = 8
        for i in xrange(self.__fatHeader.nfat_arch):
            header = FAT_ARC.from_buffer_copy(self.__binary[offset:])
            rawBinary = self.__binary[header.offset:header.offset+header.size]
            if rawBinary[:4] == unhexlify(b"cefaedfe") or rawBinary[:4] == unhexlify(b"cffaedfe"):
                self.__machoBinaries.append(MACHO(rawBinary))
            else:
                print("[Error] Binary #"+str(i+1)+" in Universal binary has an unsupported format")
            offset += sizeof(header)

    def getExecSections(self):
        ret = []
        for binary in self.__machoBinaries:
            ret += binary.getExecSections()
        return ret

    def getDataSections(self):
        ret = []
        for binary in self.__machoBinaries:
            ret += binary.getDataSections()
        return ret

    def getFormat(self):
        return "Universal"

    # TODO: These three will just return whatever is in the first binary.
    # Perhaps the rest of ROPgadget should support loading multiple binaries?
    def getEntryPoint(self):
        for binary in self.__machoBinaries:
            return binary.getEntryPoint()

    def getArch(self):
        for binary in self.__machoBinaries:
            return binary.getArch()
            
    def getArchMode(self):
        for binary in self.__machoBinaries:
            return binary.getArchMode()



def deleteDuplicateGadgets(currentGadgets):
    gadgets_content_set = set()
    unique_gadgets = []
    for gadget in currentGadgets:
        gad = gadget["gadget"]
        if gad in gadgets_content_set:
            continue
        gadgets_content_set.add(gad)
        unique_gadgets += [gadget]
    return unique_gadgets

def alphaSortgadgets(currentGadgets):
    return sorted(currentGadgets, key=lambda key : key["gadget"]) 


class Options:
    def __init__(self, options, binary, gadgets):
        self.__options = options
        self.__gadgets = gadgets
        self.__binary  = binary 

        if options.filter:   self.__filterOption()
        if options.only:     self.__onlyOption()
        if options.range:    self.__rangeOption()
        if options.badbytes: self.__deleteBadBytes()

    def __filterOption(self):
        new = []
        if not self.__options.filter:
            return 
        filt = self.__options.filter.split("|")
        if not len(filt):
            return 
        for gadget in self.__gadgets:
            flag = 0
            insts = gadget["gadget"].split(" ; ")
            for ins in insts:
                if ins.split(" ")[0] in filt:
                    flag = 1
                    break
            if not flag:
                new += [gadget]
        self.__gadgets = new

    def __onlyOption(self):
        new = []
        if not self.__options.only:
            return 
        only = self.__options.only.split("|")
        if not len(only):
            return 
        for gadget in self.__gadgets:
            flag = 0
            insts = gadget["gadget"].split(" ; ")
            for ins in insts:
                if ins.split(" ")[0] not in only:
                    flag = 1
                    break
            if not flag:
                new += [gadget]
        self.__gadgets = new

    def __rangeOption(self):
        new = []
        rangeS = int(self.__options.range.split('-')[0], 16)
        rangeE = int(self.__options.range.split('-')[1], 16)
        if rangeS == 0 and rangeE == 0:
            return 
        for gadget in self.__gadgets:
            vaddr = gadget["vaddr"]
            if vaddr >= rangeS and vaddr <= rangeE:
                new += [gadget]
        self.__gadgets = new

    def __deleteBadBytes(self):
        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 = [bb.decode('hex') for bb in self.__options.badbytes.split("|") if bb]
        archMode = self.__binary.getArchMode()
        for gadget in self.__gadgets:
            gadAddr = pack("<L", gadget["vaddr"]) if archMode == CS_MODE_32 else pack("<Q", gadget["vaddr"])
            try:
                for x in bbytes:
                    if x in gadAddr: raise
                new += [gadget]
            except:
                pass
        self.__gadgets = new

    def getGadgets(self):
        return self.__gadgets


class ROPMakerX86:
    def __init__(self, binary, gadgets, liboffset=0x0):
        self.__binary  = binary
        self.__gadgets = gadgets

        # If it's a library, we have the option to add an offset to the addresses
        self.__liboffset = liboffset

        self.__generate()


    def __lookingForWrite4Where(self, gadgetsAlreadyTested):
        for gadget in self.__gadgets:
            if gadget in gadgetsAlreadyTested:
                continue
            f = gadget["gadget"].split(" ; ")[0]
            # regex -> mov dword ptr [r32], r32
            regex = re.search("mov dword ptr \[(?P<dst>([(eax)|(ebx)|(ecx)|(edx)|(esi)|(edi)]{3}))\], (?P<src>([(eax)|(ebx)|(ecx)|(edx)|(esi)|(edi)]{3}))$", f)
            if regex:
                lg = gadget["gadget"].split(" ; ")[1:]
                try:
                    for g in lg:
                        if g.split()[0] != "pop" and g.split()[0] != "ret":
                            raise
                        # we need this to filterout 'ret' instructions with an offset like 'ret 0x6', because they ruin the stack pointer
                        if g != "ret":
                            if g.split()[0] == "ret" and g.split()[1] != "":
                                raise
                    print("\t[+] Gadget found: 0x%x %s" %(gadget["vaddr"], gadget["gadget"]))
                    return [gadget, regex.group("dst"), regex.group("src")]
                except:
                    continue
        return None

    def __lookingForSomeThing(self, something):
        for gadget in self.__gadgets:
            lg = gadget["gadget"].split(" ; ")
            if lg[0] == something:
                try:
                    for g in lg[1:]:
                        if g.split()[0] != "pop" and g.split()[0] != "ret":
                            raise
                        # we need this to filterout 'ret' instructions with an offset like 'ret 0x6', because they ruin the stack pointer
                        if g != "ret":
                            if g.split()[0] == "ret" and g.split()[1] != "":
                                raise
                    print("\t[+] Gadget found: 0x%x %s" %(gadget["vaddr"], gadget["gadget"]))
                    return gadget
                except:
                    continue
        return None

    def __padding(self, gadget, regAlreadSetted):
        lg = gadget["gadget"].split(" ; ")
        for g in lg[1:]:
            if g.split()[0] == "pop":
                reg = g.split()[1]
                try:
                    print("\tp += pack('<I', 0x%08x) # padding without overwrite %s" %(regAlreadSetted[reg], reg))
                except KeyError:
                    print("\tp += pack('<I', 0x41414141) # padding")

    def __buildRopChain(self, write4where, popDst, popSrc, xorSrc, xorEax, incEax, popEbx, popEcx, popEdx, syscall):

        sects = self.__binary.getDataSections()
        dataAddr = None
        for s in sects:
            if s["name"] == ".data":
                dataAddr = s["vaddr"] + self.__liboffset
        if dataAddr == None:
            print("\n[-] Error - Can't find a writable section")
            return

        print("\t#!/usr/bin/env python22")
        print("\t# execve generated by ROPgadget\n" )
        print("\tfrom struct import pack\n")

        print("\t# Padding goes here")
        print("\tp = ''\n")

        print("\tp += pack('<I', 0x%08x) # %s" %(popDst["vaddr"], popDst["gadget"]))
        print("\tp += pack('<I', 0x%08x) # @ .data" %(dataAddr))
        self.__padding(popDst, {})

        print("\tp += pack('<I', 0x%08x) # %s" %(popSrc["vaddr"], popSrc["gadget"]))
        print("\tp += '/bin'")
        self.__padding(popSrc, {popDst["gadget"].split()[1]: dataAddr}) # Don't overwrite reg dst

        print("\tp += pack('<I', 0x%08x) # %s" %(write4where["vaddr"], write4where["gadget"]))
        self.__padding(write4where, {})

        print("\tp += pack('<I', 0x%08x) # %s" %(popDst["vaddr"], popDst["gadget"]))
        print("\tp += pack('<I', 0x%08x) # @ .data + 4" %(dataAddr + 4))
        self.__padding(popDst, {})

        print("\tp += pack('<I', 0x%08x) # %s" %(popSrc["vaddr"], popSrc["gadget"]))
        print("\tp += '//sh'")
        self.__padding(popSrc, {popDst["gadget"].split()[1]: dataAddr + 4}) # Don't overwrite reg dst

        print("\tp += pack('<I', 0x%08x) # %s" %(write4where["vaddr"], write4where["gadget"]))
        self.__padding(write4where, {})

        print("\tp += pack('<I', 0x%08x) # %s" %(popDst["vaddr"], popDst["gadget"]))
        print("\tp += pack('<I', 0x%08x) # @ .data + 8" %(dataAddr + 8))
        self.__padding(popDst, {})

        print("\tp += pack('<I', 0x%08x) # %s" %(xorSrc["vaddr"], xorSrc["gadget"]))
        self.__padding(xorSrc, {})

        print("\tp += pack('<I', 0x%08x) # %s" %(write4where["vaddr"], write4where["gadget"]))
        self.__padding(write4where, {})

        print("\tp += pack('<I', 0x%08x) # %s" %(popEbx["vaddr"], popEbx["gadget"]))
        print("\tp += pack('<I', 0x%08x) # @ .data" %(dataAddr))
        self.__padding(popEbx, {})

        print("\tp += pack('<I', 0x%08x) # %s" %(popEcx["vaddr"], popEcx["gadget"]))
        print("\tp += pack('<I', 0x%08x) # @ .data + 8" %(dataAddr + 8))
        self.__padding(popEcx, {"ebx": dataAddr}) # Don't overwrite ebx

        print("\tp += pack('<I', 0x%08x) # %s" %(popEdx["vaddr"], popEdx["gadget"]))
        print("\tp += pack('<I', 0x%08x) # @ .data + 8" %(dataAddr + 8))
        self.__padding(popEdx, {"ebx": dataAddr, "ecx": dataAddr + 8}) # Don't overwrite ebx and ecx

        print("\tp += pack('<I', 0x%08x) # %s" %(xorEax["vaddr"], xorEax["gadget"]))
        self.__padding(xorEax, {"ebx": dataAddr, "ecx": dataAddr + 8}) # Don't overwrite ebx and ecx

        for i in range(11):
            print("\tp += pack('<I', 0x%08x) # %s" %(incEax["vaddr"], incEax["gadget"]))
            self.__padding(incEax, {"ebx": dataAddr, "ecx": dataAddr + 8}) # Don't overwrite ebx and ecx

        print("\tp += pack('<I', 0x%08x) # %s" %(syscall["vaddr"], syscall["gadget"]))

    def __generate(self):

        # To find the smaller gadget
        self.__gadgets.reverse()

        print("\nROP chain generation\n===========================================================")

        print("\n- Step 1 -- Write-what-where gadgets\n")

        gadgetsAlreadyTested = []
        while True:
            write4where = self.__lookingForWrite4Where(gadgetsAlreadyTested)
            if not write4where:
                print("\t[-] Can't find the 'mov dword ptr [r32], r32' gadget")
                return

            popDst = self.__lookingForSomeThing("pop %s" %(write4where[1]))
            if not popDst:
                print("\t[-] Can't find the 'pop %s' gadget. Try with another 'mov [reg], reg'\n" %(write4where[1]))
                gadgetsAlreadyTested += [write4where[0]]
                continue

            popSrc = self.__lookingForSomeThing("pop %s" %(write4where[2]))
            if not popSrc:
                print("\t[-] Can't find the 'pop %s' gadget. Try with another 'mov [reg], reg'\n" %(write4where[2]))
                gadgetsAlreadyTested += [write4where[0]]
                continue

            xorSrc = self.__lookingForSomeThing("xor %s, %s" %(write4where[2], write4where[2]))
            if not xorSrc:
                print("\t[-] Can't find the 'xor %s, %s' gadget. Try with another 'mov [r], r'\n" %(write4where[2], write4where[2]))
                gadgetsAlreadyTested += [write4where[0]]
                continue
            else:
                break

        print("\n- Step 2 -- Init syscall number gadgets\n")

        xorEax = self.__lookingForSomeThing("xor eax, eax")
        if not xorEax:
            print("\t[-] Can't find the 'xor eax, eax' instuction")
            return

        incEax = self.__lookingForSomeThing("inc eax")
        if not incEax:
            print("\t[-] Can't find the 'inc eax' instuction")
            return

        print("\n- Step 3 -- Init syscall arguments gadgets\n")

        popEbx = self.__lookingForSomeThing("pop ebx")
        if not popEbx:
            print("\t[-] Can't find the 'pop ebx' instruction")
            return

        popEcx = self.__lookingForSomeThing("pop ecx")
        if not popEcx:
            print("\t[-] Can't find the 'pop ecx' instruction")
            return

        popEdx = self.__lookingForSomeThing("pop edx")
        if not popEdx:
            print("\t[-] Can't find the 'pop edx' instruction")
            return

        print("\n- Step 4 -- Syscall gadget\n")

        syscall = self.__lookingForSomeThing("int 0x80")
        if not syscall:
            print("\t[-] Can't find the 'syscall' instruction")
            return

        print("\n- Step 5 -- Build the ROP chain\n")

        self.__buildRopChain(write4where[0], popDst, popSrc, xorSrc, xorEax, incEax, popEbx, popEcx, popEdx, syscall)

class ROPMakerX64:
    def __init__(self, binary, gadgets, liboffset=0x0):
        self.__binary  = binary
        self.__gadgets = gadgets

        # If it's a library, we have the option to add an offset to the addresses
        self.__liboffset = liboffset

        self.__generate()


    def __lookingForWrite4Where(self, gadgetsAlreadyTested):
        for gadget in self.__gadgets:
            if gadget in gadgetsAlreadyTested:
                continue
            f = gadget["gadget"].split(" ; ")[0]
            regex = re.search("mov .* ptr \[(?P<dst>([(rax)|(rbx)|(rcx)|(rdx)|(rsi)|(rdi)|(r9)|(r10)|(r11)|(r12)|(r13)|(r14)|(r15)]{3}))\], (?P<src>([(rax)|(rbx)|(rcx)|(rdx)|(rsi)|(rdi)|(r9)|(r10)|(r11)|(r12)|(r13)|(r14)|(r15)]{3}))$", f)
            if regex:
                lg = gadget["gadget"].split(" ; ")[1:]
                try:
                    for g in lg:
                        if g.split()[0] != "pop" and g.split()[0] != "ret":
                            raise
                        # we need this to filterout 'ret' instructions with an offset like 'ret 0x6', because they ruin the stack pointer
                        if g != "ret":
                            if g.split()[0] == "ret" and g.split()[1] != "":
                                raise
                    print("\t[+] Gadget found: 0x%x %s" %(gadget["vaddr"], gadget["gadget"]))
                    return [gadget, regex.group("dst"), regex.group("src")]
                except:
                    continue
        return None

    def __lookingForSomeThing(self, something):
        for gadget in self.__gadgets:
            lg = gadget["gadget"].split(" ; ")
            if lg[0] == something:
                try:
                    for g in lg[1:]:
                        if g.split()[0] != "pop" and g.split()[0] != "ret":
                            raise
                        if g != "ret":
                        # we need this to filterout 'ret' instructions with an offset like 'ret 0x6', because they ruin the stack pointer
                            if g.split()[0] == "ret" and g.split()[1] != "":
                                raise
                    print("\t[+] Gadget found: 0x%x %s" %(gadget["vaddr"], gadget["gadget"]))
                    return gadget
                except:
                    continue
        return None

    def __padding(self, gadget, regAlreadSetted):
        lg = gadget["gadget"].split(" ; ")
        for g in lg[1:]:
            if g.split()[0] == "pop":
                reg = g.split()[1]
                try:
                    print("\tp += pack('<Q', 0x%016x) # padding without overwrite %s" %(regAlreadSetted[reg], reg))
                except KeyError:
                    print("\tp += pack('<Q', 0x4141414141414141) # padding")

    def __buildRopChain(self, write4where, popDst, popSrc, xorSrc, xorRax, incRax, popRdi, popRsi, popRdx, syscall):

        sects = self.__binary.getDataSections()
        dataAddr = None
        for s in sects:
            if s["name"] == ".data":
                dataAddr = s["vaddr"] + self.__liboffset
        if dataAddr is None:
            print("\n[-] Error - Can't find a writable section")
            return

        print("\t#!/usr/bin/env python22")
        print("\t# execve generated by ROPgadget\n")
        print("\tfrom struct import pack\n")

        print("\t# Padding goes here")
        print("\tp = ''\n")

        print("\tp += pack('<Q', 0x%016x) # %s" %(popDst["vaddr"], popDst["gadget"]))
        print("\tp += pack('<Q', 0x%016x) # @ .data" %(dataAddr))
        self.__padding(popDst, {})

        print("\tp += pack('<Q', 0x%016x) # %s" %(popSrc["vaddr"], popSrc["gadget"]))
        print("\tp += '/bin//sh'")
        self.__padding(popSrc, {popDst["gadget"].split()[1]: dataAddr}) # Don't overwrite reg dst

        print("\tp += pack('<Q', 0x%016x) # %s" %(write4where["vaddr"], write4where["gadget"]))
        self.__padding(write4where, {})

        print("\tp += pack('<Q', 0x%016x) # %s" %(popDst["vaddr"], popDst["gadget"]))
        print("\tp += pack('<Q', 0x%016x) # @ .data + 8" %(dataAddr + 8))
        self.__padding(popDst, {})

        print("\tp += pack('<Q', 0x%016x) # %s" %(xorSrc["vaddr"], xorSrc["gadget"]))
        self.__padding(xorSrc, {})

        print("\tp += pack('<Q', 0x%016x) # %s" %(write4where["vaddr"], write4where["gadget"]))
        self.__padding(write4where, {})

        print("\tp += pack('<Q', 0x%016x) # %s" %(popRdi["vaddr"], popRdi["gadget"]))
        print("\tp += pack('<Q', 0x%016x) # @ .data" %(dataAddr))
        self.__padding(popRdi, {})

        print("\tp += pack('<Q', 0x%016x) # %s" %(popRsi["vaddr"], popRsi["gadget"]))
        print("\tp += pack('<Q', 0x%016x) # @ .data + 8" %(dataAddr + 8))
        self.__padding(popRsi, {"rdi": dataAddr}) # Don't overwrite rdi

        print("\tp += pack('<Q', 0x%016x) # %s" %(popRdx["vaddr"], popRdx["gadget"]))
        print("\tp += pack('<Q', 0x%016x) # @ .data + 8" %(dataAddr + 8))
        self.__padding(popRdx, {"rdi": dataAddr, "rsi": dataAddr + 8}) # Don't overwrite rdi and rsi

        print("\tp += pack('<Q', 0x%016x) # %s" %(xorRax["vaddr"], xorRax["gadget"]))
        self.__padding(xorRax, {"rdi": dataAddr, "rsi": dataAddr + 8}) # Don't overwrite rdi and rsi

        for i in range(59):
            print("\tp += pack('<Q', 0x%016x) # %s" %(incRax["vaddr"], incRax["gadget"]))
            self.__padding(incRax, {"rdi": dataAddr, "rsi": dataAddr + 8}) # Don't overwrite rdi and rsi

        print("\tp += pack('<Q', 0x%016x) # %s" %(syscall["vaddr"], syscall["gadget"]))

    def __generate(self):

        # To find the smaller gadget
        self.__gadgets.reverse()

        print("\nROP chain generation\n===========================================================")

        print("\n- Step 1 -- Write-what-where gadgets\n")

        gadgetsAlreadyTested = []
        while True:
            write4where = self.__lookingForWrite4Where(gadgetsAlreadyTested)
            if not write4where:
                print("\t[-] Can't find the 'mov qword ptr [r64], r64' gadget")
                return

            popDst = self.__lookingForSomeThing("pop %s" %(write4where[1]))
            if not popDst:
                print("\t[-] Can't find the 'pop %s' gadget. Try with another 'mov [reg], reg'\n" %(write4where[1]))
                gadgetsAlreadyTested += [write4where[0]]
                continue

            popSrc = self.__lookingForSomeThing("pop %s" %(write4where[2]))
            if not popSrc:
                print("\t[-] Can't find the 'pop %s' gadget. Try with another 'mov [reg], reg'\n" %(write4where[2]))
                gadgetsAlreadyTested += [write4where[0]]
                continue

            xorSrc = self.__lookingForSomeThing("xor %s, %s" %(write4where[2], write4where[2]))
            if not xorSrc:
                print("\t[-] Can't find the 'xor %s, %s' gadget. Try with another 'mov [reg], reg'\n" %(write4where[2], write4where[2]))
                gadgetsAlreadyTested += [write4where[0]]
                continue
            else:
                break

        print("\n- Step 2 -- Init syscall number gadgets\n")

        xorRax = self.__lookingForSomeThing("xor rax, rax")
        if not xorRax:
            print("\t[-] Can't find the 'xor rax, rax' instuction")
            return

        incRax = self.__lookingForSomeThing("inc rax")
        incEax = self.__lookingForSomeThing("inc eax")
        incAx = self.__lookingForSomeThing("inc al")
        addRax = self.__lookingForSomeThing("add rax, 1")
        addEax = self.__lookingForSomeThing("add eax, 1")
        addAx = self.__lookingForSomeThing("add al, 1")

        instr = [incRax, incEax, incAx, addRax, addEax, addAx]

        if all(v is None for v in instr):
            print("\t[-] Can't find the 'inc rax' or 'add rax, 1' instuction")
            return

        for i in instr:
            if i is not None:
                incRax = i
                break

        print("\n- Step 3 -- Init syscall arguments gadgets\n")

        popRdi = self.__lookingForSomeThing("pop rdi")
        if not popRdi:
            print("\t[-] Can't find the 'pop rdi' instruction")
            return

        popRsi = self.__lookingForSomeThing("pop rsi")
        if not popRsi:
            print("\t[-] Can't find the 'pop rsi' instruction")
            return

        popRdx = self.__lookingForSomeThing("pop rdx")
        if not popRdx:
            print("\t[-] Can't find the 'pop rdx' instruction")
            return

        print("\n- Step 4 -- Syscall gadget\n")

        syscall = self.__lookingForSomeThing("syscall")
        if not syscall:
            print("\t[-] Can't find the 'syscall' instruction")
            return

        print("\n- Step 5 -- Build the ROP chain\n")

        self.__buildRopChain(write4where[0], popDst, popSrc, xorSrc, xorRax, incRax, popRdi, popRsi, popRdx, syscall)


class ROPMaker:
    def __init__(self, binary, gadgets, offset):
        self.__binary  = binary
        self.__gadgets = gadgets
        self.__offset  = offset

        self.__handlerArch()

    def __handlerArch(self):

        if self.__binary.getArch() == CS_ARCH_X86           \
            and self.__binary.getArchMode() == CS_MODE_32   \
            and self.__binary.getFormat() == "ELF":
            ROPMakerX86(self.__binary, self.__gadgets, self.__offset)

        elif self.__binary.getArch() == CS_ARCH_X86         \
            and self.__binary.getArchMode() == CS_MODE_64   \
            and self.__binary.getFormat() == "ELF":
            ROPMakerX64(self.__binary, self.__gadgets, self.__offset)

        else:
            print("\n[Error] ROPMaker.__handlerArch - Arch not supported yet for the rop chain generation")


class Gadgets:
    def __init__(self, binary, options, offset):
        self.__binary  = binary
        self.__options = options
        self.__offset  = offset


    def __checkInstructionBlackListedX86(self, insts):
        bl = ["db", "int3"]
        for inst in insts:
            for b in bl:
                if inst.split(" ")[0] == b:
                    return True
        return False

    def __checkMultiBr(self, insts, br):
        count = 0
        for inst in insts:
            if inst.split()[0] in br:
                count += 1
        return count

    def __passCleanX86(self, gadgets, multibr=False):
        new = []
        br = ["ret", "retf", "int", "sysenter", "jmp", "call", "syscall"]
        for gadget in gadgets:
            insts = gadget["gadget"].split(" ; ")
            if len(insts) == 1 and insts[0].split(" ")[0] not in br:
                continue
            if insts[-1].split(" ")[0] not in br:
                continue
            if self.__checkInstructionBlackListedX86(insts):
                continue
            if not multibr and self.__checkMultiBr(insts, br) > 1:
                continue
            if len([m.start() for m in re.finditer("ret", gadget["gadget"])]) > 1:
                continue
            new += [gadget]
        return new

    def __gadgetsFinding(self, section, gadgets, arch, mode):

        C_OP    = 0
        C_SIZE  = 1
        C_ALIGN = 2

        ret = []
        md = Cs(arch, mode)
        for gad in gadgets:
            
            allRefRet = [m.start() for m in re.finditer(gad[C_OP], section["opcodes"])]
            for ref in allRefRet:
                for i in range(self.__options.depth):
                    if (section["vaddr"]+ref-(i*gad[C_ALIGN])) % gad[C_ALIGN] == 0:
                        off = self.__offset
                        decodes = md.disasm(section["opcodes"][ref-(i*gad[C_ALIGN]):ref+gad[C_SIZE]], section["vaddr"]+ref)
                        gadget = ""
                        for decode in decodes:
                            gadget += (decode.mnemonic + " " + decode.op_str + " ; ").replace("  ", " ")
                        if len(gadget) > 0:
                            gadget = gadget[:-3]
                            off = self.__offset
                            ret += [{"vaddr" :  off+section["vaddr"]+ref-(i*gad[C_ALIGN]), "gadget" : gadget, "decodes" : decodes, "bytes": section["opcodes"][ref-(i*gad[C_ALIGN]):ref+gad[C_SIZE]]}]
        return ret

    def addROPGadgets(self, section):

        arch = self.__binary.getArch()
        arch_mode = self.__binary.getArchMode()

        if arch == CS_ARCH_X86:
            gadgets = [
                            [b"\xc3", 1, 1],               # ret
                            [b"\xc2[\x00-\xff]{2}", 3, 1], # ret <imm>
                            [b"\xcb", 1, 1],               # retf
                            [b"\xca[\x00-\xff]{2}", 3, 1]  # retf <imm>
                       ]

        elif arch == CS_ARCH_MIPS:   gadgets = []            # MIPS doesn't contains RET instruction set. Only JOP gadgets
        elif arch == CS_ARCH_PPC:
            gadgets = [
                            [b"\x4e\x80\x00\x20", 4, 4] # blr
                       ]
            arch_mode = arch_mode + CS_MODE_BIG_ENDIAN

        elif arch == CS_ARCH_SPARC:
            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
                       ]
            arch_mode = CS_MODE_BIG_ENDIAN

        elif arch == CS_ARCH_ARM:    gadgets = []            # ARM doesn't contains RET instruction set. Only JOP gadgets
        elif arch == CS_ARCH_ARM64:
            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)
        return gadgets


    def addJOPGadgets(self, section):
        arch = self.__binary.getArch()
        arch_mode = self.__binary.getArchMode()



        if arch  == CS_ARCH_X86:
            gadgets = [
                               [b"\xff[\x20\x21\x22\x23\x26\x27]{1}", 2, 1],     # jmp  [reg]
                               [b"\xff[\xe0\xe1\xe2\xe3\xe4\xe6\xe7]{1}", 2, 1], # jmp  [reg]
                               [b"\xff[\x10\x11\x12\x13\x16\x17]{1}", 2, 1],     # jmp  [reg]
                               [b"\xff[\xd0\xd1\xd2\xd3\xd4\xd6\xd7]{1}", 2, 1]  # call [reg]
                      ]


        elif arch == CS_ARCH_MIPS:
            gadgets = [
                               [b"\x09\xf8\x20\x03[\x00-\xff]{4}", 8, 4], # jrl $t9
                               [b"\x08\x00\x20\x03[\x00-\xff]{4}", 8, 4], # jr  $t9
                               [b"\x08\x00\xe0\x03[\x00-\xff]{4}", 8, 4]  # jr  $ra
                      ]
        elif arch == CS_ARCH_PPC:    gadgets = [] # PPC architecture doesn't contains reg branch instruction
        elif arch == CS_ARCH_SPARC:
            gadgets = [
                               [b"\x81\xc0[\x00\x40\x80\xc0]{1}\x00", 4, 4]  # jmp %g[0-3]
                      ]
            arch_mode = CS_MODE_BIG_ENDIAN
        elif arch == CS_ARCH_ARM64:
            gadgets = [
                               [b"[\x00\x20\x40\x60\x80\xa0\xc0\xe0]{1}[\x00\x02]{1}\x1f\xd6", 4, 4],     # br  reg
                               [b"[\x00\x20\x40\x60\x80\xa0\xc0\xe0]{1}[\x00\x02]{1}\x5C\x3f\xd6", 4, 4]  # blr reg
                      ]
            arch_mode = CS_MODE_ARM
        elif arch == CS_ARCH_ARM:
            if self.__options.thumb or self.__options.rawMode == "thumb":
                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:
                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]{1}\x80\xbd\xe8", 4, 4]       # pop {,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)
        return gadgets


    def addSYSGadgets(self, section):

        arch = self.__binary.getArch()
        arch_mode = self.__binary.getArchMode()

        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:
            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] # svc
                          ]
                arch_mode = CS_MODE_THUMB
            else:
                gadgets = [
                               [b"\x00-\xff]{3}\xef", 4, 4] # 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)
        return []


    def passClean(self, gadgets, multibr):

        arch = self.__binary.getArch()
        if   arch == CS_ARCH_X86:    return self.__passCleanX86(gadgets, multibr)
        elif arch == CS_ARCH_MIPS:   return gadgets
        elif arch == CS_ARCH_PPC:    return gadgets
        elif arch == CS_ARCH_SPARC:  return gadgets
        elif arch == CS_ARCH_ARM:    return gadgets
        elif arch == CS_ARCH_ARM64:  return gadgets
        else:
            print("Gadgets().passClean() - Architecture not supported")
            return None


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:
            return False
        return True


    def __getAllgadgets(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:
            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)

        # Pass clean single instruction and unknown instructions
        self.__gadgets = G.passClean(self.__gadgets, self.__options.multibr)

        # Delete duplicate gadgets
        if not self.__options.all:
            self.__gadgets = deleteDuplicateGadgets(self.__gadgets)

        # Applicate some Options
        self.__gadgets = Options(self.__options, self.__binary, self.__gadgets).getGadgets()

        # Sorted alphabetically
        self.__gadgets = alphaSortgadgets(self.__gadgets)

        return True


    def __lookingForGadgets(self):

        if self.__checksBeforeManipulations() == False:
            return False

        arch = self.__binary.getArchMode()
        print("Gadgets information\n============================================================")
        for gadget in self.__gadgets:
            vaddr = gadget["vaddr"]
            insts = gadget["gadget"]
            bytes = gadget["bytes"]
            bytesStr = " // " + bytes.encode('hex') if self.__options.dump else ""

            print(("0x%08x" %(vaddr) if arch == CS_MODE_32 else "0x%016x" %(vaddr)) + " : %s" %(insts) + bytesStr)

        print("\nUnique gadgets found: %d" %(len(self.__gadgets)))
        return True


    def __lookingForAString(self, string):

        if self.__checksBeforeManipulations() == False:
            return False

        dataSections = self.__binary.getDataSections()
        arch = self.__binary.getArchMode()
        print("Strings information\n============================================================")
        for section in dataSections:
            allRef = [m.start() for m in re.finditer(string, section["opcodes"])]
            for ref in allRef:
                vaddr  = self.__offset + section["vaddr"] + ref
                string = section["opcodes"][ref:ref+len(string)]
                rangeS = int(self.__options.range.split('-')[0], 16)
                rangeE = int(self.__options.range.split('-')[1], 16)
                if (rangeS == 0 and rangeE == 0) or (vaddr >= rangeS and vaddr <= rangeE):
                    print(("0x%08x" %(vaddr) if arch == CS_MODE_32 else "0x%016x" %(vaddr)) + " : %s" %(string))
        return True


    def __lookingForOpcodes(self, opcodes):

        if self.__checksBeforeManipulations() == False:
            return False

        execSections = self.__binary.getExecSections()
        arch = self.__binary.getArchMode()
        print("Opcodes information\n============================================================")
        for section in execSections:
            allRef = [m.start() for m in re.finditer(opcodes.decode("hex"), section["opcodes"])]
            for ref in allRef:
                vaddr  = self.__offset + section["vaddr"] + ref
                rangeS = int(self.__options.range.split('-')[0], 16)
                rangeE = int(self.__options.range.split('-')[1], 16)
                if (rangeS == 0 and rangeE == 0) or (vaddr >= rangeS and vaddr <= rangeE):
                    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

        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:
                    allRef = [m.start() for m in re.finditer(char, section["opcodes"])]
                    for ref in allRef:
                        vaddr  = self.__offset + section["vaddr"] + ref
                        rangeS = int(self.__options.range.split('-')[0], 16)
                        rangeE = int(self.__options.range.split('-')[1], 16)
                        if (rangeS == 0 and rangeE == 0) or (vaddr >= rangeS and vaddr <= rangeE):
                            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.__getAllgadgets()
            self.__lookingForGadgets()
            if self.__options.ropchain:
                ROPMaker(self.__binary, self.__gadgets, self.__offset)
            return True


    def gadgets(self):
        return self.__gadgets




    # Console methods  ============================================

    def do_binary(self, s, silent=False):
        # Do not split the filename with spaces since it might contain 
        # whitespaces
        if len(s) == 0:
            if not silent:
                return self.help_binary()
            return False

        binary = s

        self.__options.binary = binary
        self.__binary = Binary(self.__options)
        if self.__checksBeforeManipulations() == False:
            return False

        if not silent:
            print("[+] Binary loaded")


    def help_binary(self):
        print("Syntax: binary <file> -- Load a binary")
        return False


    def do_EOF(self, s, silent=False):
        return self.do_quit(s, silent)

    def do_quit(self, s, silent=False):
        return True


    def help_quit(self):
        print("Syntax: quit -- Terminates the application")
        return False


    def do_load(self, s, silent=False):

        if self.__binary == None:
            if not silent:
                print("[-] No binary loaded.")
            return False

        if not silent:
            print("[+] Loading gadgets, please wait...")
        self.__getAllgadgets()

        if not silent:
            print("[+] Gadgets loaded !")

        
    def help_load(self):
        print("Syntax: load -- Load all gadgets")
        return False


    def do_display(self, s, silent=False):
        self.__lookingForGadgets()


    def help_display(self):
        print("Syntax: display -- Display all gadgets loaded")
        return False


    def do_depth(self, s, silent=False):
        try:
            depth = int(s.split()[0])
        except:
            if not silent:
                return self.help_depth()
            return False
        if depth <= 0:
            if not silent:
                print("[-] The depth value must be > 0")
            return False
        self.__options.depth = int(depth)

        if not silent:
            print("[+] Depth updated. You have to reload gadgets")


    def help_depth(self):
        print("Syntax: depth <value> -- Set the depth search engine")
        return False


    def do_badbytes(self, s, silent=False):
        try:
            bb = s.split()[0]
        except:
            if not silent:
                return self.help_badbytes()
            else:
                return False
        self.__options.badbytes = bb

        if not silent:
            print("[+] Bad bytes updated. You have to reload gadgets")


    def help_badbytes(self):
        print("Syntax: badbytes <badbyte1|badbyte2...> -- ")
        return False


    def __withK(self, listK, gadget):
        if len(listK) == 0:
            return True
        for a in listK:
            if a not in gadget:
                return False
        return True
        
    def __withoutK(self, listK, gadget):
        for a in listK:
            if a in gadget:
                return False
        return True

    def do_search(self, s, silent=False):
        args = s.split()
        if not len(args):
            return self.help_search()
        withK, withoutK = [], []
        for a in args:
            if a[0:1] == "!":
                withoutK += [a[1:]]
            else:
                withK += [a]
        if self.__checksBeforeManipulations() == False:
            if not silent:
                print("[-] You have to load a binary")
            return False
        arch = self.__binary.getArchMode()
        for gadget in self.__gadgets:
            vaddr = gadget["vaddr"]
            insts = gadget["gadget"]
            if self.__withK(withK, insts) and self.__withoutK(withoutK, insts):
                # What to do if silent = True?
                print(("0x%08x" %(vaddr) if arch == CS_MODE_32 else "0x%016x" %(vaddr)) + " : %s" %(insts))


    def help_search(self):
        print("Syntax: search <keyword1 keyword2 keyword3...> -- Filter with or without keywords")
        print("keyword  = with")
        print("!keyword = witout")
        return False


    def count(self):
        return len(self.__gadgets)

    def do_count(self, s, silent=False):
        if not silent:
            print("[+] %d loaded gadgets." % self.count())


    def help_count(self):
        print("Shows the number of loaded gadgets.")
        return False


    def do_filter(self, s, silent=False):
        try:
            self.__options.filter = s.split()[0]
        except:
            if not silent:
                return self.help_filter()
            return False

        if not silent:
            print("[+] Filter setted. You have to reload gadgets")


    def help_filter(self):
        print("Syntax: filter <filter1|filter2|...> - Suppress specific instructions")
        return False


    def do_only(self, s, silent=False):
        try:
            self.__options.only = s.split()[0]
        except:
            if not silent:
                return self.help_only()
            return False

        if not silent:
            print("[+] Only setted. You have to reload gadgets")


    def help_only(self):
        print("Syntax: only <only1|only2|...> - Only show specific instructions")
        return False


    def do_range(self, s, silent=False):
            try:
                rangeS = int(s.split('-')[0], 16)
                rangeE = int(s.split('-')[1], 16)
                self.__options.range = s.split()[0]
            except:
                if not silent:
                    return self.help_range()
                return False

            if rangeS > rangeE:
                if not silent:
                    print("[-] The start value must be greater than the end value")
                return False

            if not silent:
                print("[+] Range setted. You have to reload gadgets")


    def help_range(self):
        print("Syntax: range <start-and> - Search between two addresses (0x...-0x...)")
        return False


    def do_settings(self, s, silent=False):
        print("All:         %s" %(self.__options.all))
        print("Badbytes:    %s" %(self.__options.badbytes))
        print("Binary:      %s" %(self.__options.binary))
        print("Depth:       %s" %(self.__options.depth))
        print("Filter:      %s" %(self.__options.filter))
        print("Memstr:      %s" %(self.__options.memstr))
        print("MultiBr:     %s" %(self.__options.multibr))
        print("NoJOP:       %s" %(self.__options.nojop))
        print("NoROP:       %s" %(self.__options.norop))
        print("NoSYS:       %s" %(self.__options.nosys))
        print("Offset:      %s" %(self.__options.offset))
        print("Only:        %s" %(self.__options.only))
        print("Opcode:      %s" %(self.__options.opcode))
        print("ROPchain:    %s" %(self.__options.ropchain))
        print("Range:       %s" %(self.__options.range))
        print("RawArch:     %s" %(self.__options.rawArch))
        print("RawMode:     %s" %(self.__options.rawMode))
        print("String:      %s" %(self.__options.string))
        print("Thumb:       %s" %(self.__options.thumb))

    def help_settings(self):
        print("Display setting's environment")
        return False


    def do_nojop(self, s, silent=False):
        try:
            arg = s.split()[0]
        except:
            return self.help_nojop()

        if arg == "enable":
            self.__options.nojop = True
            if not silent:
                print("[+] NoJOP enable. You have to reload gadgets")

        elif arg == "disable":
            self.__options.nojop = False
            if not silent:
                print("[+] NoJOP disable. You have to reload gadgets")

        else:
            if not silent:
                return self.help_nojop()
            return False


    def help_nojop(self):
        print("Syntax: nojop <enable|disable> - Disable JOP search engin")
        return False


    def do_norop(self, s, silent=False):
        try:
            arg = s.split()[0]
        except:
            return self.help_norop()

        if arg == "enable":
            self.__options.norop = True
            if not silent:
                print("[+] NoROP enable. You have to reload gadgets")

        elif arg == "disable":
            self.__options.norop = False
            if not silent:
                print("[+] NoROP disable. You have to reload gadgets")

        else:
            if not silent:
                return self.help_norop()
            return False


    def help_norop(self):
        print("Syntax: norop <enable|disable> - Disable ROP search engin")
        return False


    def do_nosys(self, s, silent=False):
        try:
            arg = s.split()[0]
        except:
            return self.help_nosys()

        if arg == "enable":
            self.__options.nosys = True
            if not silent:
                print("[+] NoSYS enable. You have to reload gadgets")

        elif arg == "disable":
            self.__options.nosys = False
            if not silent:
                print("[+] NoSYS disable. You have to reload gadgets")

        else:
            if not silent:
                return self.help_nosys()

            return False


    def help_nosys(self):
        print("Syntax: nosys <enable|disable> - Disable SYS search engin")
        return False


    def do_thumb(self, s, silent=False):
        try:
            arg = s.split()[0]
        except:
            return self.help_thumb()

        if arg == "enable":
            self.__options.thumb = True
            if not silent:
                print("[+] Thumb enable. You have to reload gadgets")

        elif arg == "disable":
            self.__options.thumb = False
            if not silent:
                print("[+] Thumb disable. You have to reload gadgets")

        else:
            if not silent:
                return self.help_thumb()
            return False


    def help_thumb(self):
        print("Syntax: thumb <enable|disable> - Use the thumb mode for the search engine (ARM only)")
        return False


    def do_all(self, s, silent=False):
        if s == "enable":
            self.__options.all = True
            if not silent:
                print("[+] Showing all gadgets enabled. You have to reload gadgets")

        elif s == "disable":
            self.__options.all = False
            if not silent:
                print("[+] Showing all gadgets disabled. You have to reload gadgets")

        else:
            if not silent:
                return self.help_all()

            return False


    def help_multibr(self):
        print("Syntax: multibr <enable|disable> - Enable/Disable multiple branch gadgets")
        return False


    def do_multibr(self, s, silent=False):
        if s == "enable":
            self.__options.multibr = True
            if not silent:
                print("[+] Multiple branch gadgets enabled. You have to reload gadgets")

        elif s == "disable":
            self.__options.multibr = False
            if not silent:
                print("[+] Multiple branch gadgets disabled. You have to reload gadgets")

        else:
            if not silent:
                return self.help_all()

            return False


    def help_all(self):
        print("Syntax: all <enable|disable - Show all gadgets (disable removing duplice gadgets)")
        return False

class Binary:
    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 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)

    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 getFormat(self):
        return self.__binary.getFormat()
class Args:
    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 = args
          custom_arguments_provided = False

        sys.argv=arguments
        self.__parse(arguments, custom_arguments_provided)

    def __parse(self, arguments, custom_arguments_provided=False):
        parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter,
                                         description="""description:
  rop(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:
  rop --binary ./test-suite-binaries/elf-Linux-x86 
  rop --binary ./test-suite-binaries/elf-Linux-x86 --ropchain
  rop --binary ./test-suite-binaries/elf-Linux-x86 --depth 3
  rop --binary ./test-suite-binaries/elf-Linux-x86 --string "main"
  rop --binary ./test-suite-binaries/elf-Linux-x86 --string "m..n"
  rop --binary ./test-suite-binaries/elf-Linux-x86 --opcode c9c3
  rop --binary ./test-suite-binaries/elf-Linux-x86 --only "mov|ret"
  rop --binary ./test-suite-binaries/elf-Linux-x86 --only "mov|pop|xor|ret"
  rop --binary ./test-suite-binaries/elf-Linux-x86 --filter "xchg|add|sub"
  rop --binary ./test-suite-binaries/elf-Linux-x86 --norop --nosys
  rop --binary ./test-suite-binaries/elf-Linux-x86 --range 0x08041000-0x08042000
  rop --binary ./test-suite-binaries/elf-Linux-x86 --string main --range 0x080c9aaa-0x080c9aba
  rop --binary ./test-suite-binaries/elf-Linux-x86 --memstr "/bin/sh"
  rop --binary ./test-suite-binaries/elf-Linux-x86 --console
  rop --binary ./test-suite-binaries/elf-Linux-x86 --badbytes "00|7f|42"
  rop --binary ./test-suite-binaries/Linux_lib64.so --offset 0xdeadbeef00000000
  rop --binary ./test-suite-binaries/elf-ARMv7-ls --depth 5
  rop --binary ./test-suite-binaries/elf-ARM64-bash --depth 5
  rop --binary ./test-suite-binaries/raw-x86.raw --rawArch=x86 --rawMode=32""")

        parser.add_argument("--binary",             type=str, metavar="<binary>",     help="Specify a binary filename to analyze")
        parser.add_argument("--opcode",             type=str, metavar="<opcodes>",    help="Search opcode in executable segment")
        parser.add_argument("--string",             type=str, metavar="<string>",     help="Search string in readable segment")
        parser.add_argument("--memstr",             type=str, metavar="<string>",     help="Search each byte in all readable segment")
        parser.add_argument("--depth",              type=int, metavar="<nbyte>",      default=10, help="Depth for search engine (default 10)")
        parser.add_argument("--only",               type=str, metavar="<key>",        help="Only show specific instructions")
        parser.add_argument("--filter",             type=str, metavar="<key>",        help="Suppress specific instructions")
        parser.add_argument("--range",              type=str, metavar="<start-end>",  default="0x0-0x0", help="Search between two addresses (0x...-0x...)")
        parser.add_argument("--badbytes",           type=str, metavar="<byte>",       help="Rejects specific bytes in the gadget's address")
        parser.add_argument("--rawArch",            type=str, metavar="<arch>",       help="Specify an arch for a raw file")
        parser.add_argument("--rawMode",            type=str, metavar="<mode>",       help="Specify a mode for a raw file")
        parser.add_argument("--offset",             type=str, metavar="<hexaddr>",    help="Specify an offset for gadget addresses")
        parser.add_argument("--ropchain",           action="store_true",              help="Enable the ROP chain generation")
        parser.add_argument("--thumb"  ,            action="store_true",              help="Use the thumb mode for the search engine (ARM only)")
        parser.add_argument("--console",            action="store_true",              help="Use an interactive console for search engine")
        parser.add_argument("--norop",              action="store_true",              help="Disable ROP search engine")
        parser.add_argument("--nojop",              action="store_true",              help="Disable JOP search engine")
        parser.add_argument("--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("--dump",               action="store_true",              help="Outputs the gadget bytes")
        
        self.__args = parser.parse_args(arguments)

        if self.__args.depth < 2:
            print("[Error] The depth must be >= 2")
            sys.exit(-1)

        elif not custom_arguments_provided and not self.__args.binary and not self.__args.console:
            print("[Error] Need a binary filename (--binary/--console or --help)")
            sys.exit(-1)

        elif self.__args.range:
            try:
                rangeS = int(self.__args.range.split('-')[0], 16)
                rangeE = int(self.__args.range.split('-')[1], 16)
            except:
                print("[Error] A range must be set in hexadecimal. Ex: 0x08041000-0x08042000")
                sys.exit(-1)
            if rangeS > rangeE:
                print("[Error] The start value must be greater than end value")
                sys.exit(-1)

    def __printVersion(self):
        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

def rop(debugger,args,result,dict):
    args=shlex.split(args)
    
    if args:
        Core(Args(arguments=args).getArgs()).analyze()
    else:
        print("""description:
  rop(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:
  rop --binary ./test-suite-binaries/elf-Linux-x86 
  rop --binary ./test-suite-binaries/elf-Linux-x86 --ropchain
  rop --binary ./test-suite-binaries/elf-Linux-x86 --depth 3
  rop --binary ./test-suite-binaries/elf-Linux-x86 --string "main"
  rop --binary ./test-suite-binaries/elf-Linux-x86 --string "m..n"
  rop --binary ./test-suite-binaries/elf-Linux-x86 --opcode c9c3
  rop --binary ./test-suite-binaries/elf-Linux-x86 --only "mov|ret"
  rop --binary ./test-suite-binaries/elf-Linux-x86 --only "mov|pop|xor|ret"
  rop --binary ./test-suite-binaries/elf-Linux-x86 --filter "xchg|add|sub"
  rop --binary ./test-suite-binaries/elf-Linux-x86 --norop --nosys
  rop --binary ./test-suite-binaries/elf-Linux-x86 --range 0x08041000-0x08042000
  rop --binary ./test-suite-binaries/elf-Linux-x86 --string main --range 0x080c9aaa-0x080c9aba
  rop --binary ./test-suite-binaries/elf-Linux-x86 --memstr "/bin/sh"
  rop --binary ./test-suite-binaries/elf-Linux-x86 --console
  rop --binary ./test-suite-binaries/elf-Linux-x86 --badbytes "00|7f|42"
  rop --binary ./test-suite-binaries/Linux_lib64.so --offset 0xdeadbeef00000000
  rop --binary ./test-suite-binaries/elf-ARMv7-ls --depth 5
  rop --binary ./test-suite-binaries/elf-ARM64-bash --depth 5
  rop --binary ./test-suite-binaries/raw-x86.raw --rawArch=x86 --rawMode=32""")

def launch(debugger, com, result, dict):
    process_to_launch = com
    for i in glob.glob("/Applications/*.app"):
        application = os.path.basename(i).replace(".app","")
        
        if fnmatch.fnmatch(application.lower(),process_to_launch.lower()):
            execute(debugger, "target create '%s'"%(i), result, dict)
            run = raw_input(tty_colors.green()+"Shall i run %s?y/n : "%(i)+tty_colors.default())
            if run=="y":
                execute(debugger, "run", result, dict)

            return

    print("Nothing found")

def backtrace(debugger, command, result, dict):
    bt = executeReturnOutput(debugger, "thread backtrace", result, dict)[0].split("\n")
    thread = bt[0]
    print(thread)

    for i in bt[1:]:
        
        if i.startswith("  * frame"):
            print(tty_colors.red()+i+tty_colors.default())
            pc = getregvalue(debugger,"pc",result,dict)
            disassemble = executeReturnOutput(debugger, "disassemble --start-address=" + pc + " --count=4", result, dict)[0].split("\n")
            print(tty_colors.blue()+"\t[disassembly]"+tty_colors.default())
            print(tty_colors.green()+"\t\t%s"%(disassemble[0])+tty_colors.default())
            print(tty_colors.red()+"\t\t%s"%(disassemble[1])+tty_colors.default())
            for i in disassemble[2:]:
                print(tty_colors.green()+"\t\t%s"%(i)+tty_colors.default())

            print(tty_colors.blue()+"\t[/disassembly]"+tty_colors.default())
        else:
            print(i)

def __lldb_init_module(debugger, dict):

    debugger.HandleCommand('settings set target.x86-disassembly-flavor intel')

    debugger.HandleCommand('command script add --function lisa.stepnInstructions sf')
    debugger.HandleCommand('command script add --function lisa.context ct')
    debugger.HandleCommand('command script add --function lisa.s s')
    debugger.HandleCommand('command script add --function lisa.si si')
    debugger.HandleCommand('command script add --function lisa.so so')
    debugger.HandleCommand('command script add --function lisa.backtrace pbt')    
    debugger.HandleCommand('command script add --function lisa.banner banner')
    debugger.HandleCommand('command script add --function lisa.patternCreate patterncreate')
    debugger.HandleCommand('command script add --function lisa.patternOffset patternoffset')
    debugger.HandleCommand('command script add --function lisa.checkIfCyclic checkifcyclic')    
    debugger.HandleCommand('command script add --function lisa.exploitable exploitable')
    debugger.HandleCommand('command script add --function lisa.setMallocDebug setmalloc')
    debugger.HandleCommand('command script add --function lisa.shellcode shellcode')
    debugger.HandleCommand('command script add --function lisa.rop rop')
    debugger.HandleCommand('command script add --function lisa.extractFromUniversalBinary extract')
    debugger.HandleCommand('command script add --function lisa.dump dump')
    debugger.HandleCommand('command script add --function lisa.coredump coredump')
    debugger.HandleCommand('command script add --function lisa.searchMem search')
    debugger.HandleCommand('command script add --function lisa.symbols symbol')
    debugger.HandleCommand('command script add --function lisa.vtable vtable')
    debugger.HandleCommand('command script add --function lisa.launch launch')
    debugger.HandleCommand('command script add --function lisa.shell shell')

    debugger.HandleCommand('banner')

    res = lldb.SBCommandReturnObject()
    executeReturnOutput(debugger, 'target stop-hook add -o ct', res, dict)

tty_colors = TerminalColors (True)
