"""Read Recycle Bin

Author: Matt Gardenghi, Computer Security, Bob Jones University
Date: 10/2007
email: mtgarden@bju.edu

Thanks to David Lowry, System Administrator, Bob Jones University for starting
me onto Python and for tweaking my code.

This program is free for distribution.  It is my hope that investigators
will be able to use this utility to expedite cases.  If you have any
suggestions or modifications, please send them to me and I will incorporate
them into a future version.  As this is my first utility, I am certain that
the community will have plenty of suggestions.  Please feel free to send me
an email and let me know how I can improve this tool (or talk about
anything else for that matter).
"""

#Imports
from optparse import OptionParser
from datetime import timedelta,datetime 
import os, struct, time

#Process the CL arguments.
parser = OptionParser()
parser.add_option("-i", dest="input", default="error", help="Input the Recycle Bin Location")
parser.add_option("-o", dest="report", default="output.txt", help="Optional output filename/location. Defaults to output.txt")
parser.add_option("-s", dest="skew", default=0, type=int, help="Enter the hours from GMT of the suspect's timezone. e.g. EST is -5 from GMT, ergo \'-s -5\'.")
(options, args) = parser.parse_args()

if options.input == "error":#I need a '-i' option.
    parser.print_help()
    exit()

#Global Variables
cwd = options.input
output = options.report
skew = options.skew

def eval_directory(cwd, skew): #Determine whether the input was valid.
    file = open(output, 'wb') #Prepare the final output file
    cwd_ls = os.listdir(cwd) #Dump the directory contents to determine where you're at.
    if '$Recycle.Bin' in cwd_ls: #If I find the Recycle.Bin, then I need to move into it.
        cwd = cwd + '\\$Recycle.Bin'
        cwd_ls = os.listdir(cwd)
    if 'Recycler' in cwd_ls: #Same as above, but this is for pre-Vista systems.
        cwd = cwd + '\\Recycler'
        cwd_ls = os.listdir(cwd)
    for index in range(len(cwd_ls)): #Cycle through the directory for the SIDs.  Gotta have them.
        if 'S-1' not in cwd_ls[index]:  #Error handling
            print "\nCannot find SIDs...exiting\n"
            parser.print_help()
            exit()
        else: #Process each SID individually.
            cwd_temp = os.path.abspath(cwd) + "/" + cwd_ls[index]+"/" #Temporarily add the SID to the current directory.
            proc_dir(cwd_temp,skew,file) #Send the directoiry to be processed.
    #Rinse and repeat for each SID.
    file.close()#Close the output file.  You're all done. Go home.

def proc_dir(cwd_temp,skew,file): #Time to process the SID directory
    cwd_ls = []  
    cwd_ls = os.listdir(cwd_temp)#Let's get a list of the files in the directory.
    if 'INFO2' in cwd_ls or 'INFO' in cwd_ls:return() #Handle pre-Vista Recyclers
    a = [] #Store the original filenames of the '$I' crowd
    b = [] #Store the original filenames of the '$R' bunch
    c = [] #Store a[:] minus the '$I'
    d = [] #Store b[:} minus the '$R'
    #c[:] & d[:] allow us to easily compare the files in the directories.
    for index in range(len(cwd_ls)): #Select each file in the directory and process it.
        if cwd_ls[index].startswith('$I'): a.append(cwd_ls[index]) #if the file begins with $I, then put it in list a
        elif cwd_ls[index].startswith('$R'): b.append(cwd_ls[index]) #else if the file begins with $R, put it in list b
    #The next four lines copy each filename from the original list to its secondary list minus the starting code.
    c = a[:]
    d = b[:]
    for index in range(len(a)):c[index] = a[index].strip('$I')
    for index in range(len(b)):d[index] = b[index].strip('$R')
    for x in c: #Now we determine if the deleted file has been located.  Sometimes the index exists, but not the deleted file.
        if x in d: found = 1 #Mark it as having found the deleted file.
        else: found = 0 #Mark it as not having the deleted file any longer.
        proc_file(cwd_temp, skew, file, found, a[c.index(x)]) #Now we process the index file (i.e. $I files)
    return()

def proc_datestamp(datestamp):
    """The next line is arguably the most confusing.  Since the time stamp
    is in Windows time, we must extract it as a Long Int.  That's the
    struct.unpack("=Q") part. Then we must convert it to Unix time via the
    division and subtraction.  From there, we convert the long int into
    a time tuple standardized on gmtime.  Since the time.gmtime function is
    not compatible with timedelta option, we now convert it the datetime
    format using the fromtimestamp option.  Now we can add the skew to the
    time for use with investigator's timelines.  If you want seconds, I can
    add it later, just not doing it without a real call."""
    datestamp = datetime.fromtimestamp(time.mktime(time.gmtime(((struct.unpack\
                ("=Q",datestamp)[0])/10000000)-11644473600)))\
                    + timedelta(hours = skew)
    return(datestamp)


def proc_file(cwd_temp, skew, file, found, x):#Let's extract that data!
    f = (cwd_temp + "%s\\" % x).strip('\\')#makes this program useful on Linux!
    f = open(f, "rb")#Open the file in question.  Must use "rb" else some files won't open properly.
    f.seek(8) #Seek the first pertinent set of data
    z = ["", "", ""] #Make a list to hold the data
    """struct.unpack pulls the data and turns it from hex to an integer.
    We convert that to a str for later use. These four bytes: 0x08-0x0b
    hold the deleted file size."""
    z[0] = str(struct.unpack("=I",f.read(4))[0]) 
    f.seek(16)#Skip to the next section of data.
    z[1] = f.read(8)#Lets read the next 8 bytes - the time stamp.
    z[1] = proc_datestamp(z[1])
    #Only thing left is the filename and we don't know how long it is.
    z[2] = str(f.read())#Now we grab the rest of the file.  
    z[2] = z[2].strip(unichr(0))#Strip out all the trailing zeros.
    z[2] = z[2].replace("\00", "").strip() #There were blanks between each letter.  This fixes it.
    f.close()# We don't need that file open any longer.
    #If we found both sides of the pair, then we grab the first line.
    #If not, grab line two and throw a '*' in to indicate that the original file is missing.
    if found == 1: temp = (x + "\t" + z[0] + "\t" + time.strftime("%a, %d, %b, %Y %H:%M:%S",datetime.timetuple(z[1])) + "\t" + z[2] + "\n")
    elif found == 0: temp = ("*" + x + "\t" + z[0] + "\t" + time.strftime("%a, %d, %b, %Y %H:%M:%S",datetime.timetuple(z[1])) + "\t" + z[2] + "\n")
    file.write(str(temp)) #Now we write it to the output file. And we repeat for each file.

eval_directory(cwd,skew)#Start the program

#We're outta here.
exit()


