diff --git a/CoreLibs/nbproject/project.xml b/CoreLibs/nbproject/project.xml index 33c3e28eeb194552c889899877a7704fc0d7c736..2dd56aa5cb8cc433134bae908c6635793b86650f 100644 --- a/CoreLibs/nbproject/project.xml +++ b/CoreLibs/nbproject/project.xml @@ -973,7 +973,7 @@ </class-path-extension> <class-path-extension> <runtime-relative-path>ext/dd-plist-1.20.jar</runtime-relative-path> - <binary-origin>C:\Users\kelly\Workspace\autopsy\CoreLibs\release\modules\ext\dd-plist-1.20.jar</binary-origin> + <binary-origin>release/modules/ext/dd-plist-1.20.jar</binary-origin> </class-path-extension> <class-path-extension> <runtime-relative-path>ext/dom4j-1.6.1.jar</runtime-relative-path> diff --git a/RecentActivity/ivy.xml b/RecentActivity/ivy.xml index 290c8371eae064e2c558862744a7107569a06a7a..ca95f14a981e828a78da5828c554a99d2c25dd33 100644 --- a/RecentActivity/ivy.xml +++ b/RecentActivity/ivy.xml @@ -6,4 +6,7 @@ <conf name="recent-activity"/> </configurations> + <dependencies> + <dependency conf="recent-activity->default" org="com.googlecode.plist" name="dd-plist" rev="1.20"/> + </dependencies> </ivy-module> diff --git a/RecentActivity/nbproject/project.properties b/RecentActivity/nbproject/project.properties index 9736070e535c751845709a9d241ec4b2a13a5faa..4071f4a54e42ca590d3262ac2cf8b4620b0e1b68 100644 --- a/RecentActivity/nbproject/project.properties +++ b/RecentActivity/nbproject/project.properties @@ -1,3 +1,4 @@ +file.reference.dd-plist-1.20.jar=release/modules/ext/dd-plist-1.20.jar javac.source=1.8 javac.compilerargs=-Xlint -Xlint:-serial license.file=../LICENSE-2.0.txt diff --git a/RecentActivity/nbproject/project.xml b/RecentActivity/nbproject/project.xml index 87619a83562c3fb03faf6788142f610f9d98d025..9584170602fd5b39350d8eba584200fc76ccb469 100644 --- a/RecentActivity/nbproject/project.xml +++ b/RecentActivity/nbproject/project.xml @@ -74,6 +74,10 @@ </dependency> </module-dependencies> <public-packages/> + <class-path-extension> + <runtime-relative-path>ext/dd-plist-1.20.jar</runtime-relative-path> + <binary-origin>release/modules/ext/dd-plist-1.20.jar</binary-origin> + </class-path-extension> </data> </configuration> </project> diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/BinaryCookieReader.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/BinaryCookieReader.java new file mode 100755 index 0000000000000000000000000000000000000000..3e24a1fe5d940e4e9451eddb806d4331b7555c9d --- /dev/null +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/BinaryCookieReader.java @@ -0,0 +1,434 @@ +/* + * + * Autopsy Forensic Browser + * + * Copyright 2019 Basis Technology Corp. + * + * 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. + */ +package org.sleuthkit.autopsy.recentactivity; + +import java.io.DataInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.logging.Level; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.recentactivity.BinaryCookieReader.Cookie; + +/** + * The binary cookie reader encapsulates all the knowledge of how to read the mac + * .binarycookie files into one class. + * + * The binarycookie file has a header which describes how many pages of cookies + * and where they are located. Each cookie page has a header and a list of + * cookies. + * + */ +public final class BinaryCookieReader implements Iterable<Cookie> { + + private static final int MAGIC_SIZE = 4; + private static final int SIZEOF_INT_BYTES = 4; + private static final int PAGE_HEADER_VALUE = 256; + + private static final String COOKIE_MAGIC = "cook"; //NON-NLS + + private static final int MAC_EPOC_FIX = 978307200; + + private final int[] pageSizeArray; + private final File cookieFile; + + private static final Logger LOG = Logger.getLogger(BinaryCookieReader.class.getName()); + + /** + * The binary cookie reader encapsulates all the knowledge of how to read the mac + * .binarycookie files into one class. + * + */ + private BinaryCookieReader(File cookieFile, int[] pageSizeArray) { + this.cookieFile = cookieFile; + this.pageSizeArray = pageSizeArray; + } + + /** + * initalizeReader opens the given file, reads the header and checks that + * the file is a binarycookie file. This function does not keep the file + * open. + * + * @param file binarycookie file + * @return An instance of the reader + * @throws FileNotFoundException + * @throws IOException + */ + public static BinaryCookieReader initalizeReader(File cookieFile) throws FileNotFoundException, IOException { + BinaryCookieReader reader = null; + try (DataInputStream dataStream = new DataInputStream(new FileInputStream(cookieFile))) { + + byte[] magic = new byte[MAGIC_SIZE]; + if (dataStream.read(magic) != MAGIC_SIZE) { + throw new IOException("Failed to read header, invalid file size (" + cookieFile.getName() + ")"); //NON-NLS + } + + if (!(new String(magic)).equals(COOKIE_MAGIC)) { + throw new IOException(cookieFile.getName() + " is not a cookie file"); //NON-NLS + } + + int[] sizeArray = null; + int pageCount = dataStream.readInt(); + if (pageCount != 0) { + sizeArray = new int[pageCount]; + + for (int cnt = 0; cnt < pageCount; cnt++) { + sizeArray[cnt] = dataStream.readInt(); + } + + LOG.log(Level.INFO, "No cookies found in {0}", cookieFile.getName()); //NON-NLS + } + + reader = new BinaryCookieReader(cookieFile, sizeArray); + } + + return reader; + } + + /** + * Creates and returns a instance of CookiePageIterator. + * + * @return CookiePageIterator + */ + @Override + public Iterator<Cookie> iterator() { + return new CookiePageIterator(); + } + + /** + * The cookiePageIterator iterates the binarycookie file by page. + */ + private class CookiePageIterator implements Iterator<Cookie> { + + int pageIndex = 0; + CookiePage currentPage = null; + Iterator<Cookie> currentIterator = null; + DataInputStream dataStream = null; + + /** + * The cookiePageIterator iterates the binarycookie file by page. + */ + CookiePageIterator() { + if(pageSizeArray == null || pageSizeArray.length == 0) { + return; + } + + try { + dataStream = new DataInputStream(new FileInputStream(cookieFile)); + // skip to the first page + dataStream.skipBytes((2 * SIZEOF_INT_BYTES) + (pageSizeArray.length * SIZEOF_INT_BYTES)); + } catch (IOException ex) { + + String errorMessage = String.format("An error occurred creating an input stream for %s", cookieFile.getName()); + LOG.log(Level.WARNING, errorMessage, ex); //NON-NLS + closeStream(); // Just incase the error was from skip + } + } + + /** + * Returns true if there are more cookies in the binarycookie file. + * + * @return True if there are more cookies + */ + @Override + public boolean hasNext() { + + if (dataStream == null) { + return false; + } + + if (currentIterator == null || !currentIterator.hasNext()) { + try { + + if (pageIndex < pageSizeArray.length) { + byte[] nextPage = new byte[pageSizeArray[pageIndex]]; + dataStream.read(nextPage); + + currentPage = new CookiePage(nextPage); + currentIterator = currentPage.iterator(); + } else { + closeStream(); + return false; + } + + pageIndex++; + } catch (IOException ex) { + closeStream(); + String errorMessage = String.format("A read error occured for file %s (pageIndex = %d)", cookieFile.getName(), pageIndex); + LOG.log(Level.WARNING, errorMessage, ex); //NON-NLS + return false; + } + } + + return currentIterator.hasNext(); + } + + /** + * Get the next cookie from the current CookieIterator. + * + * @return The next cookie + */ + @Override + public Cookie next() { + // Just in case someone uses next without hasNext, this check will + // make sure there are more elements and that we iterate properly + // through the pages. + if (!hasNext()) { + throw new NoSuchElementException(); + } + return currentIterator.next(); + } + + /** + * Close the DataInputStream + */ + private void closeStream() { + if (dataStream != null) { + try { + dataStream.close(); + dataStream = null; + } catch (IOException ex) { + String errorMessage = String.format("An error occurred trying to close stream for file %s", cookieFile.getName()); + LOG.log(Level.WARNING, errorMessage, ex); //NON-NLS + } + } + } + } + + /** + * Wrapper class for an instance of a CookiePage in the binarycookie file. + */ + private class CookiePage implements Iterable<Cookie> { + + int[] cookieOffSets; + ByteBuffer pageBuffer; + + /** + * Setup the CookiePage object. Calidates that the page bytes are in the + * correct format by checking for the header value of 0x0100. + * + * @param page byte array representing a cookie page + * @throws IOException + */ + CookiePage(byte[] page) throws IOException { + if (page == null || page.length == 0) { + throw new IllegalArgumentException("Invalid value for page passed to CookiePage constructor"); //NON-NLS + } + + pageBuffer = ByteBuffer.wrap(page); + + if (pageBuffer.getInt() != PAGE_HEADER_VALUE) { + pageBuffer = null; + throw new IOException("Invalid file format, bad page head value found"); //NON-NLS + } + + pageBuffer.order(ByteOrder.LITTLE_ENDIAN); + int count = pageBuffer.getInt(); + cookieOffSets = new int[count]; + + for (int cnt = 0; cnt < count; cnt++) { + cookieOffSets[cnt] = pageBuffer.getInt(); + } + + pageBuffer.getInt(); // All 0, not needed + } + + /** + * Returns an instance of a CookieIterator. + * + * @return CookieIterator + */ + @Override + public Iterator<Cookie> iterator() { + return new CookieIterator(); + } + + /** + * Implements Iterator to iterate over the cookies in the page. + */ + private class CookieIterator implements Iterator<Cookie> { + + int index = 0; + + /** + * Checks to see if there are more cookies. + * + * @return True if there are more cookies, false if there are not + */ + @Override + public boolean hasNext() { + if (pageBuffer == null) { + return false; + } + + return index < cookieOffSets.length; + } + + /** + * Gets the next cookie from the page. + * + * @return Next cookie + */ + @Override + public Cookie next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + + int offset = cookieOffSets[index]; + int size = pageBuffer.getInt(offset); + byte[] cookieBytes = new byte[size]; + pageBuffer.get(cookieBytes, 0, size); + index++; + + return new Cookie(cookieBytes); + } + } + } + + /** + * Represents an instance of a cookie from the binarycookie file. + */ + public class Cookie { + + private final static int COOKIE_HEAD_SKIP = 16; + + private final double expirationDate; + private final double creationDate; + + private final String name; + private final String url; + private final String path; + private final String value; + + /** + * Creates a cookie object from the given array of bytes. + * + * @param cookieBytes Byte array for the cookie + */ + protected Cookie(byte[] cookieBytes) { + if (cookieBytes == null || cookieBytes.length == 0) { + throw new IllegalArgumentException("Invalid value for cookieBytes passed to Cookie constructor"); //NON-NLS + } + + ByteBuffer byteBuffer = ByteBuffer.wrap(cookieBytes); + byteBuffer.order(ByteOrder.LITTLE_ENDIAN); + + // Skip past the four int values that we are not interested in + byteBuffer.position(byteBuffer.position() + COOKIE_HEAD_SKIP); + + int urlOffset = byteBuffer.getInt(); + int nameOffset = byteBuffer.getInt(); + int pathOffset = byteBuffer.getInt(); + int valueOffset = byteBuffer.getInt(); + byteBuffer.getLong(); // 8 bytes of not needed + + expirationDate = byteBuffer.getDouble(); + creationDate = byteBuffer.getDouble(); + + url = decodeString(cookieBytes, urlOffset); + name = decodeString(cookieBytes, nameOffset); + path = decodeString(cookieBytes, pathOffset); + value = decodeString(cookieBytes, valueOffset); + } + + /** + * Returns the expiration date of the cookie represented by this cookie + * object. + * + * @return Cookie expiration date in milliseconds with java epoch + */ + public final Long getExpirationDate() { + return ((long)expirationDate) + MAC_EPOC_FIX; + } + + /** + * Returns the creation date of the cookie represented by this cookie + * object. + * + * @return Cookie creation date in milliseconds with java epoch + */ + public final Long getCreationDate() { + return ((long)creationDate) + MAC_EPOC_FIX; + } + + /** + * Returns the url of the cookie represented by this cookie object. + * + * @return the cookie URL + */ + public final String getURL() { + return url; + } + + /** + * Returns the name of the cookie represented by this cookie object. + * + * @return The cookie name + */ + public final String getName() { + return name; + } + + /** + * Returns the path of the cookie represented by this cookie object. + * + * @return The cookie path + */ + public final String getPath() { + return path; + } + + /** + * Returns the value of the cookie represented by this cookie object. + * + * @return The cookie value + */ + public final String getValue() { + return value; + } + + /** + * Creates an ascii string from the bytes in byteArray starting at + * offset ending at the first null terminator found. + * + * @param byteArray Array of bytes + * @param offset starting offset in the array + * @return String with bytes converted to ascii + */ + private String decodeString(byte[] byteArray, int offset) { + byte[] stringBytes = new byte[byteArray.length - offset]; + for (int index = 0; index < stringBytes.length; index++) { + byte nibble = byteArray[offset + index]; + if (nibble != '\0') { //NON-NLS + stringBytes[index] = nibble; + } else { + break; + } + } + + return new String(stringBytes); + } + } +} diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Extract.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Extract.java index 7ab22b4850799296b5622767bd63c1511434d198..b4f04c79cd02b382033cbd6ca47245e80236c889 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Extract.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Extract.java @@ -22,10 +22,18 @@ */ package org.sleuthkit.autopsy.recentactivity; +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; import java.util.logging.Level; import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; @@ -35,9 +43,17 @@ import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.coreutils.SQLiteDBConnect; +import org.sleuthkit.autopsy.datamodel.ContentUtils; import org.sleuthkit.autopsy.ingest.IngestJobContext; import org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException; -import org.sleuthkit.datamodel.*; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.BlackboardAttribute; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.TskException; + abstract class Extract { @@ -216,4 +232,207 @@ public boolean foundData() { protected void setFoundData(boolean foundData){ dataFound = foundData; } + + /** + * Returns the current case instance + * @return Current case instance + */ + protected Case getCurrentCase(){ + return this.currentCase; + } + + /** + * Creates a list of attributes for a history artifact. + * + * @param url + * @param accessTime Time url was accessed + * @param referrer referred url + * @param title title of the page + * @param programName module name + * @param domain domain of the url + * @param user user that accessed url + * @return List of BlackboardAttributes for giving attributes + * @throws TskCoreException + */ + protected Collection<BlackboardAttribute> createHistoryAttribute(String url, Long accessTime, + String referrer, String title, String programName, String domain, String user) throws TskCoreException { + + Collection<BlackboardAttribute> bbattributes = new ArrayList<>(); + bbattributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_URL, + RecentActivityExtracterModuleFactory.getModuleName(), + (url != null) ? url : "")); //NON-NLS + + if (accessTime != null) { + bbattributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED, + RecentActivityExtracterModuleFactory.getModuleName(), accessTime)); + } + + bbattributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_REFERRER, + RecentActivityExtracterModuleFactory.getModuleName(), + (referrer != null) ? referrer : "")); //NON-NLS + + bbattributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TITLE, + RecentActivityExtracterModuleFactory.getModuleName(), + (title != null) ? title : "")); //NON-NLS + + bbattributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME, + RecentActivityExtracterModuleFactory.getModuleName(), + (programName != null) ? programName : "")); //NON-NLS + + bbattributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN, + RecentActivityExtracterModuleFactory.getModuleName(), + (domain != null) ? domain : "")); //NON-NLS + + bbattributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_USER_NAME, + RecentActivityExtracterModuleFactory.getModuleName(), + (user != null) ? user : "")); //NON-NLS + + return bbattributes; + } + + /** + * Creates a list of attributes for a cookie. + * + * @param url cookie url + * @param creationTime cookie creation time + * @param name cookie name + * @param value cookie value + * @param programName Name of the module creating the attribute + * @param domain Domain of the URL + * @return List of BlackboarAttributes for the passed in attributes + */ + protected Collection<BlackboardAttribute> createCookieAttributes(String url, + Long creationTime, String name, String value, String programName, String domain) { + + Collection<BlackboardAttribute> bbattributes = new ArrayList<>(); + bbattributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_URL, + RecentActivityExtracterModuleFactory.getModuleName(), + (url != null) ? url : "")); //NON-NLS + + if (creationTime != null) { + bbattributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, + RecentActivityExtracterModuleFactory.getModuleName(), creationTime)); + } + + bbattributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME, + RecentActivityExtracterModuleFactory.getModuleName(), + (name != null) ? name : "")); //NON-NLS + + bbattributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_VALUE, + RecentActivityExtracterModuleFactory.getModuleName(), + (value != null) ? value : "")); //NON-NLS + + bbattributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME, + RecentActivityExtracterModuleFactory.getModuleName(), + (programName != null) ? programName : "")); //NON-NLS + + bbattributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN, + RecentActivityExtracterModuleFactory.getModuleName(), + (domain != null) ? domain : "")); //NON-NLS + + return bbattributes; + } + + /** + * Creates a list of bookmark attributes from the passed in parameters. + * + * @param url Bookmark url + * @param title Title of the bookmarked page + * @param creationTime Date & time at which the bookmark was created + * @param programName Name of the module creating the attribute + * @param domain The domain of the bookmark's url + * @return A collection of bookmark attributes + */ + protected Collection<BlackboardAttribute> createBookmarkAttributes(String url, String title, Long creationTime, String programName, String domain) { + Collection<BlackboardAttribute> bbattributes = new ArrayList<>(); + + bbattributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_URL, + RecentActivityExtracterModuleFactory.getModuleName(), + (url != null) ? url : "")); //NON-NLS + + bbattributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TITLE, + RecentActivityExtracterModuleFactory.getModuleName(), + (title != null) ? title : "")); //NON-NLS + + if (creationTime != null) { + bbattributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED, + RecentActivityExtracterModuleFactory.getModuleName(), creationTime)); + } + + bbattributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME, + RecentActivityExtracterModuleFactory.getModuleName(), + (programName != null) ? programName : "")); //NON-NLS + + bbattributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN, + RecentActivityExtracterModuleFactory.getModuleName(), + (domain != null) ? domain : "")); //NON-NLS + + return bbattributes; + } + + /** + * Creates a list of the attributes of a downloaded file + * + * @param path + * @param url URL of the downloaded file + * @param accessTime Time the download occurred + * @param domain Domain of the URL + * @param programName Name of the module creating the attribute + * @return A collection of attributed of a downloaded file + */ + protected Collection<BlackboardAttribute> createDownloadAttributes(String path, Long pathID, String url, Long accessTime, String domain, String programName) { + Collection<BlackboardAttribute> bbattributes = new ArrayList<>(); + + bbattributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH, + RecentActivityExtracterModuleFactory.getModuleName(), + (path != null) ? path : "")); //NON-NLS + + if (pathID != null && pathID != -1) { + bbattributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID, + RecentActivityExtracterModuleFactory.getModuleName(), + pathID)); + } + + bbattributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_URL, + RecentActivityExtracterModuleFactory.getModuleName(), + (url != null) ? url : "")); //NON-NLS + + if (accessTime != null) { + bbattributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED, + RecentActivityExtracterModuleFactory.getModuleName(), accessTime)); + } + + bbattributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN, + RecentActivityExtracterModuleFactory.getModuleName(), + (domain != null) ? domain : "")); //NON-NLS + + bbattributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME, + RecentActivityExtracterModuleFactory.getModuleName(), + (programName != null) ? programName : "")); //NON-NLS + + return bbattributes; + } + + /** + * Create temporary file for the given AbstractFile. The new file will be + * created in the temp directory for the module with a unique file name. + * + * @param context + * @param file + * @return Newly created copy of the AbstractFile + * @throws IOException + */ + protected File createTemporaryFile(IngestJobContext context, AbstractFile file) throws IOException{ + Path tempFilePath = Paths.get(RAImageIngestModule.getRATempPath( + getCurrentCase(), getName()), file.getName() + file.getId() + file.getNameExtension()); + java.io.File tempFile = tempFilePath.toFile(); + + try { + ContentUtils.writeToFile(file, tempFile, context::dataSourceIngestIsCancelled); + } catch (IOException ex) { + throw new IOException("Error writingToFile: " + file, ex); //NON-NLS + } + + return tempFile; + } } diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractEdge.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractEdge.java index 2264a8385078af7622e286d108bf255fccc4edec..ff793703b1db4dda9c7d5c94525222c10c99813f 100755 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractEdge.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractEdge.java @@ -48,7 +48,6 @@ import org.sleuthkit.autopsy.ingest.ModuleDataEvent; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; -import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.TskCoreException; @@ -698,180 +697,7 @@ private BlackboardArtifact getBookmarkArtifact(AbstractFile origFile, List<Strin this.getName(), NetworkUtils.extractDomain(url))); return bbart; } - - /** - * Creates a list of attributes for a history artifact. - * - * @param url - * @param accessTime Time url was accessed - * @param referrer referred url - * @param title title of the page - * @param programName module name - * @param domain domain of the url - * @param user user that accessed url - * @return List of BlackboardAttributes for giving attributes - * @throws TskCoreException - */ - private Collection<BlackboardAttribute> createHistoryAttribute(String url, Long accessTime, - String referrer, String title, String programName, String domain, String user) throws TskCoreException { - - Collection<BlackboardAttribute> bbattributes = new ArrayList<>(); - bbattributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_URL, - RecentActivityExtracterModuleFactory.getModuleName(), - (url != null) ? url : "")); - - if (accessTime != null) { - bbattributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED, - RecentActivityExtracterModuleFactory.getModuleName(), accessTime)); - } - - bbattributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_REFERRER, - RecentActivityExtracterModuleFactory.getModuleName(), - (referrer != null) ? referrer : "")); - - bbattributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TITLE, - RecentActivityExtracterModuleFactory.getModuleName(), - (title != null) ? title : "")); - - bbattributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME, - RecentActivityExtracterModuleFactory.getModuleName(), - (programName != null) ? programName : "")); - - bbattributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN, - RecentActivityExtracterModuleFactory.getModuleName(), - (domain != null) ? domain : "")); - - bbattributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_USER_NAME, - RecentActivityExtracterModuleFactory.getModuleName(), - (user != null) ? user : "")); - - return bbattributes; - } - - /** - * Creates a list of attributes for a cookie. - * - * @param url cookie url - * @param creationTime cookie creation time - * @param name cookie name - * @param value cookie value - * @param programName Name of the module creating the attribute - * @param domain Domain of the URL - * @return List of BlackboarAttributes for the passed in attributes - */ - private Collection<BlackboardAttribute> createCookieAttributes(String url, - Long creationTime, String name, String value, String programName, String domain) { - - Collection<BlackboardAttribute> bbattributes = new ArrayList<>(); - bbattributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_URL, - RecentActivityExtracterModuleFactory.getModuleName(), - (url != null) ? url : "")); - - if (creationTime != null) { - bbattributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, - RecentActivityExtracterModuleFactory.getModuleName(), creationTime)); - } - - bbattributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME, - RecentActivityExtracterModuleFactory.getModuleName(), - (name != null) ? name : "")); - - bbattributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_VALUE, - RecentActivityExtracterModuleFactory.getModuleName(), - (value != null) ? value : "")); - - bbattributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME, - RecentActivityExtracterModuleFactory.getModuleName(), - (programName != null) ? programName : "")); - - bbattributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN, - RecentActivityExtracterModuleFactory.getModuleName(), - (domain != null) ? domain : "")); - - return bbattributes; - } - - /** - * Creates a list of the attributes of a downloaded file - * - * @param path - * @param url URL of the downloaded file - * @param accessTime Time the download occurred - * @param domain Domain of the URL - * @param programName Name of the module creating the attribute - * @return A collection of attributed of a downloaded file - */ - private Collection<BlackboardAttribute> createDownloadAttributes(String path, String url, Long accessTime, String domain, String programName) { - Collection<BlackboardAttribute> bbattributes = new ArrayList<>(); - - bbattributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH, - RecentActivityExtracterModuleFactory.getModuleName(), - (path != null) ? path : "")); - - long pathID = Util.findID(dataSource, path); - if (pathID != -1) { - bbattributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID, - RecentActivityExtracterModuleFactory.getModuleName(), - pathID)); - } - - bbattributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_URL, - RecentActivityExtracterModuleFactory.getModuleName(), - (url != null) ? url : "")); - - if (accessTime != null) { - bbattributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED, - RecentActivityExtracterModuleFactory.getModuleName(), accessTime)); - } - - bbattributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN, - RecentActivityExtracterModuleFactory.getModuleName(), - (domain != null) ? domain : "")); - - bbattributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME, - RecentActivityExtracterModuleFactory.getModuleName(), - (programName != null) ? programName : "")); - - return bbattributes; - } - - /** - * Creates a list of bookmark attributes from the passed in parameters. - * - * @param url Bookmark url - * @param title Title of the bookmarked page - * @param creationTime Date & time at which the bookmark was created - * @param programName Name of the module creating the attribute - * @param domain The domain of the bookmark's url - * @return A collection of bookmark attributes - */ - private Collection<BlackboardAttribute> createBookmarkAttributes(String url, String title, Long creationTime, String programName, String domain) { - Collection<BlackboardAttribute> bbattributes = new ArrayList<>(); - - bbattributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_URL, - RecentActivityExtracterModuleFactory.getModuleName(), - (url != null) ? url : "")); - - bbattributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TITLE, - RecentActivityExtracterModuleFactory.getModuleName(), - (title != null) ? title : "")); - - if (creationTime != null) { - bbattributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED, - RecentActivityExtracterModuleFactory.getModuleName(), creationTime)); - } - - bbattributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME, - RecentActivityExtracterModuleFactory.getModuleName(), - (programName != null) ? programName : "")); - - bbattributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN, - RecentActivityExtracterModuleFactory.getModuleName(), - (domain != null) ? domain : "")); - - return bbattributes; - } - + /** * Converts a space separated string of hex values to ascii characters. * diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractSafari.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractSafari.java new file mode 100755 index 0000000000000000000000000000000000000000..afb52654b2012170d618df289ddfa0b4517c2d41 --- /dev/null +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractSafari.java @@ -0,0 +1,633 @@ +/* + * + * Autopsy Forensic Browser + * + * Copyright 2019 Basis Technology Corp. + * + * 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. + */ +package org.sleuthkit.autopsy.recentactivity; + +import com.dd.plist.NSArray; +import com.dd.plist.NSDate; +import com.dd.plist.NSDictionary; +import com.dd.plist.NSObject; +import com.dd.plist.NSString; +import com.dd.plist.PropertyListFormatException; +import com.dd.plist.PropertyListParser; +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.logging.Level; +import javax.xml.parsers.ParserConfigurationException; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.casemodule.services.FileManager; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.NetworkUtils; +import org.sleuthkit.autopsy.datamodel.ContentUtils; +import org.sleuthkit.autopsy.ingest.IngestJobContext; +import org.sleuthkit.autopsy.ingest.IngestServices; +import org.sleuthkit.autopsy.ingest.ModuleDataEvent; +import org.sleuthkit.autopsy.recentactivity.BinaryCookieReader.Cookie; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.TskCoreException; +import org.xml.sax.SAXException; + +/** + * Extract the bookmarks, cookies, downloads and history from Safari + * + */ +final class ExtractSafari extends Extract { + + private final IngestServices services = IngestServices.getInstance(); + + // visit_time uses an epoch of Jan 1, 2001 thus the addition of 978307200 + private static final String HISTORY_QUERY = "SELECT url, title, visit_time + 978307200 as time FROM 'history_items' JOIN history_visits ON history_item = history_items.id;"; //NON-NLS + + private static final String HISTORY_FILE_NAME = "History.db"; //NON-NLS + private static final String BOOKMARK_FILE_NAME = "Bookmarks.plist"; //NON-NLS + private static final String DOWNLOAD_FILE_NAME = "Downloads.plist"; //NON-NLS + private static final String COOKIE_FILE_NAME = "Cookies.binarycookies"; //NON-NLS + private static final String COOKIE_FOLDER = "Cookies"; + private static final String SAFARI_FOLDER = "Safari"; + + private static final String HEAD_URL = "url"; //NON-NLS + private static final String HEAD_TITLE = "title"; //NON-NLS + private static final String HEAD_TIME = "time"; //NON-NLS + + private static final String PLIST_KEY_CHILDREN = "Children"; //NON-NLS + private static final String PLIST_KEY_URL = "URLString"; //NON-NLS + private static final String PLIST_KEY_URI = "URIDictionary"; //NON-NLS + private static final String PLIST_KEY_TITLE = "title"; //NON-NLS + private static final String PLIST_KEY_DOWNLOAD_URL = "DownloadEntryURL"; //NON-NLS + private static final String PLIST_KEY_DOWNLOAD_DATE = "DownloadEntryDateAddedKey"; //NON-NLS + private static final String PLIST_KEY_DOWNLOAD_PATH = "DownloadEntryPath"; //NON-NLS + private static final String PLIST_KEY_DOWNLOAD_HISTORY = "DownloadHistory"; //NON-NLS + + private static final Logger LOG = Logger.getLogger(ExtractSafari.class.getName()); + + @Messages({ + "ExtractSafari_Module_Name=Safari", + "ExtractSafari_Error_Getting_History=An error occurred while processing Safari history files.", + "ExtractSafari_Error_Parsing_Bookmark=An error occured while processing Safari Bookmark files", + "ExtractSafari_Error_Parsing_Cookies=An error occured while processing Safari Cookies files", + }) + + /** + * Extract the bookmarks, cookies, downloads and history from Safari. + * + */ + ExtractSafari() { + + } + + @Override + protected String getName() { + return Bundle.ExtractSafari_Module_Name(); + } + + @Override + void process(Content dataSource, IngestJobContext context) { + setFoundData(false); + + try { + processHistoryDB(dataSource, context); + + } catch (IOException | TskCoreException ex) { + this.addErrorMessage(Bundle.ExtractSafari_Error_Getting_History()); + LOG.log(Level.SEVERE, "Exception thrown while processing history file: {0}", ex); //NON-NLS + } + + try { + processBookmarkPList(dataSource, context); + } catch (IOException | TskCoreException | SAXException | PropertyListFormatException | ParseException | ParserConfigurationException ex) { + this.addErrorMessage(Bundle.ExtractSafari_Error_Parsing_Bookmark()); + LOG.log(Level.SEVERE, "Exception thrown while parsing Safari Bookmarks file: {0}", ex); //NON-NLS + } + + try { + processDownloadsPList(dataSource, context); + } catch (IOException | TskCoreException | SAXException | PropertyListFormatException | ParseException | ParserConfigurationException ex) { + this.addErrorMessage(Bundle.ExtractSafari_Error_Parsing_Bookmark()); + LOG.log(Level.SEVERE, "Exception thrown while parsing Safari Download.plist file: {0}", ex); //NON-NLS + } + + try { + processBinaryCookieFile(dataSource, context); + } catch (IOException | TskCoreException ex) { + this.addErrorMessage(Bundle.ExtractSafari_Error_Parsing_Cookies()); + LOG.log(Level.SEVERE, "Exception thrown while processing Safari cookies file: {0}", ex); //NON-NLS + } + } + + /** + * Finds the all of the history.db files in the case looping through them to + * find all of the history artifacts. + * + * @throws TskCoreException + * @throws IOException + */ + private void processHistoryDB(Content dataSource, IngestJobContext context) throws TskCoreException, IOException { + FileManager fileManager = getCurrentCase().getServices().getFileManager(); + + List<AbstractFile> historyFiles = fileManager.findFiles(dataSource, HISTORY_FILE_NAME, SAFARI_FOLDER); + + if (historyFiles == null || historyFiles.isEmpty()) { + return; + } + + setFoundData(true); + + for (AbstractFile historyFile : historyFiles) { + if (context.dataSourceIngestIsCancelled()) { + break; + } + + getHistory(context, historyFile); + } + } + + /** + * Finds all Bookmark.plist files and looks for bookmark entries. + * @param dataSource + * @param context + * @throws TskCoreException + * @throws IOException + * @throws SAXException + * @throws PropertyListFormatException + * @throws ParseException + * @throws ParserConfigurationException + */ + private void processBookmarkPList(Content dataSource, IngestJobContext context) throws TskCoreException, IOException, SAXException, PropertyListFormatException, ParseException, ParserConfigurationException { + FileManager fileManager = getCurrentCase().getServices().getFileManager(); + + List<AbstractFile> files = fileManager.findFiles(dataSource, BOOKMARK_FILE_NAME, SAFARI_FOLDER); + + if (files == null || files.isEmpty()) { + return; + } + + setFoundData(true); + + for (AbstractFile file : files) { + if (context.dataSourceIngestIsCancelled()) { + break; + } + + getBookmarks(context, file); + } + } + + /** + * Process the safari download.plist file. + * + * @param dataSource + * @param context + * @throws TskCoreException + * @throws IOException + * @throws SAXException + * @throws PropertyListFormatException + * @throws ParseException + * @throws ParserConfigurationException + */ + private void processDownloadsPList(Content dataSource, IngestJobContext context) throws TskCoreException, IOException, SAXException, PropertyListFormatException, ParseException, ParserConfigurationException { + FileManager fileManager = getCurrentCase().getServices().getFileManager(); + + List<AbstractFile> files = fileManager.findFiles(dataSource, DOWNLOAD_FILE_NAME, SAFARI_FOLDER); + + if (files == null || files.isEmpty()) { + return; + } + + setFoundData(true); + + for (AbstractFile file : files) { + if (context.dataSourceIngestIsCancelled()) { + break; + } + + getDownloads(dataSource, context, file); + } + } + + /** + * Process the Safari Cookie file. + * @param dataSource + * @param context + * @throws TskCoreException + * @throws IOException + */ + private void processBinaryCookieFile(Content dataSource, IngestJobContext context) throws TskCoreException, IOException { + FileManager fileManager = getCurrentCase().getServices().getFileManager(); + + List<AbstractFile> files = fileManager.findFiles(dataSource, COOKIE_FILE_NAME, COOKIE_FOLDER); + + if (files == null || files.isEmpty()) { + return; + } + + setFoundData(true); + + for (AbstractFile file : files) { + if (context.dataSourceIngestIsCancelled()) { + break; + } + getCookies(context, file); + } + } + + /** + * Creates a temporary copy of historyFile and creates a list of + * BlackboardArtifacts for the history information in the file. + * + * @param historyFile AbstractFile version of the history file from the case + * @throws TskCoreException + * @throws IOException + */ + private void getHistory(IngestJobContext context, AbstractFile historyFile) throws TskCoreException, IOException { + if (historyFile.getSize() == 0) { + return; + } + + File tempHistoryFile = createTemporaryFile(context, historyFile); + + try { + ContentUtils.writeToFile(historyFile, tempHistoryFile, context::dataSourceIngestIsCancelled); + } catch (IOException ex) { + throw new IOException("Error writingToFile: " + historyFile, ex); //NON-NLS + } + + try { + Collection<BlackboardArtifact> bbartifacts = getHistoryArtifacts(historyFile, tempHistoryFile.toPath()); + if (!bbartifacts.isEmpty()) { + services.fireModuleDataEvent(new ModuleDataEvent( + RecentActivityExtracterModuleFactory.getModuleName(), + BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY, bbartifacts)); + } + } finally { + tempHistoryFile.delete(); + } + } + + /** + * Creates a temporary bookmark file from the AbstractFile and creates + * BlackboardArtifacts for the any bookmarks found. + * + * @param context IngestJobContext object + * @param file AbstractFile from case + * @throws TskCoreException + * @throws IOException + * @throws SAXException + * @throws PropertyListFormatException + * @throws ParseException + * @throws ParserConfigurationException + */ + private void getBookmarks(IngestJobContext context, AbstractFile file) throws TskCoreException, IOException, SAXException, PropertyListFormatException, ParseException, ParserConfigurationException { + if (file.getSize() == 0) { + return; + } + + File tempFile = createTemporaryFile(context, file); + + try { + Collection<BlackboardArtifact> bbartifacts = getBookmarkArtifacts(file, tempFile); + if (!bbartifacts.isEmpty()) { + services.fireModuleDataEvent(new ModuleDataEvent( + RecentActivityExtracterModuleFactory.getModuleName(), + BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_BOOKMARK, bbartifacts)); + } + } finally { + tempFile.delete(); + } + + } + + /** + * Creates a temporary downloads file from the AbstractFile and creates + * BlackboardArtifacts for the any downloads found. + * + * @param context IngestJobContext object + * @param file AbstractFile from case + * @throws TskCoreException + * @throws IOException + * @throws SAXException + * @throws PropertyListFormatException + * @throws ParseException + * @throws ParserConfigurationException + */ + private void getDownloads(Content dataSource, IngestJobContext context, AbstractFile file) throws TskCoreException, IOException, SAXException, PropertyListFormatException, ParseException, ParserConfigurationException { + if (file.getSize() == 0) { + return; + } + + File tempFile = createTemporaryFile(context, file); + + try { + Collection<BlackboardArtifact> bbartifacts = getDownloadArtifacts(dataSource, file, tempFile); + if (!bbartifacts.isEmpty()) { + services.fireModuleDataEvent(new ModuleDataEvent( + RecentActivityExtracterModuleFactory.getModuleName(), + BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, bbartifacts)); + } + } finally { + if (tempFile != null) { + tempFile.delete(); + } + } + + } + + /** + * Creates a temporary copy of the Cookie file and creates a list of cookie + * BlackboardArtifacts. + * + * @param context IngetstJobContext + * @param file Original Cookie file from the case + * @throws TskCoreException + * @throws IOException + */ + private void getCookies(IngestJobContext context, AbstractFile file) throws TskCoreException, IOException { + if (file.getSize() == 0) { + return; + } + + File tempFile = null; + + try { + tempFile = createTemporaryFile(context, file); + + Collection<BlackboardArtifact> bbartifacts = getCookieArtifacts(file, tempFile); + + if (!bbartifacts.isEmpty()) { + services.fireModuleDataEvent(new ModuleDataEvent( + RecentActivityExtracterModuleFactory.getModuleName(), + BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_COOKIE, bbartifacts)); + } + } finally { + if (tempFile != null) { + tempFile.delete(); + } + } + } + + /** + * Queries the history db for the history information creating a list of + * BlackBoardArtifact for each row returned from the db. + * + * @param origFile AbstractFile of the history file from the case + * @param tempFilePath Path to temporary copy of the history db + * @return Blackboard Artifacts for the history db or null if there are no + * history artifacts + * @throws TskCoreException + */ + private Collection<BlackboardArtifact> getHistoryArtifacts(AbstractFile origFile, Path tempFilePath) throws TskCoreException { + List<HashMap<String, Object>> historyList = this.dbConnect(tempFilePath.toString(), HISTORY_QUERY); + + if (historyList == null || historyList.isEmpty()) { + return null; + } + + Collection<BlackboardArtifact> bbartifacts = new ArrayList<>(); + for (HashMap<String, Object> row : historyList) { + String url = row.get(HEAD_URL).toString(); + String title = row.get(HEAD_TITLE).toString(); + Long time = (Double.valueOf(row.get(HEAD_TIME).toString())).longValue(); + + BlackboardArtifact bbart = origFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY); + bbart.addAttributes(createHistoryAttribute(url, time, null, title, + this.getName(), NetworkUtils.extractDomain(url), null)); + bbartifacts.add(bbart); + } + + return bbartifacts; + } + + /** + * Parses the temporary version of bookmarks.plist and creates + * + * @param origFile The origFile Bookmark.plist file from the case + * @param tempFile The temporary local version of Bookmark.plist + * @return Collection of BlackboardArtifacts for the bookmarks in origFile + * @throws IOException + * @throws PropertyListFormatException + * @throws ParseException + * @throws ParserConfigurationException + * @throws SAXException + * @throws TskCoreException + */ + private Collection<BlackboardArtifact> getBookmarkArtifacts(AbstractFile origFile, File tempFile) throws IOException, PropertyListFormatException, ParseException, ParserConfigurationException, SAXException, TskCoreException { + Collection<BlackboardArtifact> bbartifacts = new ArrayList<>(); + + try { + NSDictionary root = (NSDictionary) PropertyListParser.parse(tempFile); + + parseBookmarkDictionary(bbartifacts, origFile, root); + } catch (PropertyListFormatException ex) { + PropertyListFormatException plfe = new PropertyListFormatException(origFile.getName() + ": " + ex.getMessage()); + plfe.setStackTrace(ex.getStackTrace()); + throw plfe; + } catch (ParseException ex) { + ParseException pe = new ParseException(origFile.getName() + ": " + ex.getMessage(), ex.getErrorOffset()); + pe.setStackTrace(ex.getStackTrace()); + throw pe; + } catch (ParserConfigurationException ex) { + ParserConfigurationException pce = new ParserConfigurationException(origFile.getName() + ": " + ex.getMessage()); + pce.setStackTrace(ex.getStackTrace()); + throw pce; + } catch (SAXException ex) { + SAXException se = new SAXException(origFile.getName() + ": " + ex.getMessage()); + se.setStackTrace(ex.getStackTrace()); + throw se; + } + + return bbartifacts; + } + + /** + * Finds the download entries in the tempFile and creates a list of artifacts from them. + * + * @param origFile Download.plist file from case + * @param tempFile Temporary copy of download.plist file + * @return Collection of BlackboardArtifacts for the downloads in origFile + * @throws IOException + * @throws PropertyListFormatException + * @throws ParseException + * @throws ParserConfigurationException + * @throws SAXException + * @throws TskCoreException + */ + private Collection<BlackboardArtifact> getDownloadArtifacts(Content dataSource, AbstractFile origFile, File tempFile)throws IOException, PropertyListFormatException, ParseException, ParserConfigurationException, SAXException, TskCoreException { + Collection<BlackboardArtifact> bbartifacts = null; + + try { + while(true){ + NSDictionary root = (NSDictionary)PropertyListParser.parse(tempFile); + + if(root == null) + break; + + NSArray nsArray = (NSArray)root.get(PLIST_KEY_DOWNLOAD_HISTORY); + + if(nsArray == null) + break; + + NSObject[] objectArray = nsArray.getArray(); + bbartifacts = new ArrayList<>(); + + for(NSObject obj: objectArray){ + if(obj instanceof NSDictionary){ + bbartifacts.add(parseDownloadDictionary(dataSource, origFile, (NSDictionary)obj)); + } + } + break; + } + + } catch (PropertyListFormatException ex) { + PropertyListFormatException plfe = new PropertyListFormatException(origFile.getName() + ": " + ex.getMessage()); + plfe.setStackTrace(ex.getStackTrace()); + throw plfe; + } catch (ParseException ex) { + ParseException pe = new ParseException(origFile.getName() + ": " + ex.getMessage(), ex.getErrorOffset()); + pe.setStackTrace(ex.getStackTrace()); + throw pe; + } catch (ParserConfigurationException ex) { + ParserConfigurationException pce = new ParserConfigurationException(origFile.getName() + ": " + ex.getMessage()); + pce.setStackTrace(ex.getStackTrace()); + throw pce; + } catch (SAXException ex) { + SAXException se = new SAXException(origFile.getName() + ": " + ex.getMessage()); + se.setStackTrace(ex.getStackTrace()); + throw se; + } + + return bbartifacts; + } + + /** + * Finds the cookies in the tempFile creating a list of BlackboardArtifacts + * each representing one cookie. + * + * @param origFile Original Cookies.binarycookie file from case + * @param tempFile Temporary copy of the cookies file + * @return List of Blackboard Artifacts, one for each cookie + * @throws TskCoreException + * @throws IOException + */ + private Collection<BlackboardArtifact> getCookieArtifacts(AbstractFile origFile, File tempFile) throws TskCoreException, IOException { + Collection<BlackboardArtifact> bbartifacts = null; + BinaryCookieReader reader = BinaryCookieReader.initalizeReader(tempFile); + + if (reader != null) { + bbartifacts = new ArrayList<>(); + + Iterator<Cookie> iter = reader.iterator(); + while (iter.hasNext()) { + Cookie cookie = iter.next(); + + BlackboardArtifact bbart = origFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_COOKIE); + bbart.addAttributes(createCookieAttributes(cookie.getURL(), cookie.getCreationDate(), cookie.getName(), cookie.getValue(), this.getName(), NetworkUtils.extractDomain(cookie.getURL()))); + bbartifacts.add(bbart); + } + } + + return bbartifacts; + } + + /** + * Parses the plist object to find the bookmark child objects, then creates + * an artifact with the bookmark information. + * + * @param bbartifacts BlackboardArtifact list to add new the artifacts to + * @param origFile The origFile Bookmark.plist file from the case + * @param root NSDictionary object to parse + * @throws TskCoreException + */ + private void parseBookmarkDictionary(Collection<BlackboardArtifact> bbartifacts, AbstractFile origFile, NSDictionary root) throws TskCoreException { + if (root.containsKey(PLIST_KEY_CHILDREN)) { + NSArray children = (NSArray) root.objectForKey(PLIST_KEY_CHILDREN); + + if (children != null) { + for (NSObject obj : children.getArray()) { + parseBookmarkDictionary(bbartifacts, origFile, (NSDictionary) obj); + } + } + } else if (root.containsKey(PLIST_KEY_URL)) { + String url = null; + String title = null; + + NSString nsstr = (NSString) root.objectForKey(PLIST_KEY_URL); + if (nsstr != null) { + url = nsstr.toString(); + } + + NSDictionary dic = (NSDictionary) root.get(PLIST_KEY_URI); + + nsstr = (NSString) root.objectForKey(PLIST_KEY_TITLE); + + if (nsstr != null) { + title = ((NSString) dic.get(PLIST_KEY_TITLE)).toString(); + } + + if (url != null || title != null) { + BlackboardArtifact bbart = origFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_BOOKMARK); + bbart.addAttributes(createBookmarkAttributes(url, title, null, getName(), NetworkUtils.extractDomain(url))); + bbartifacts.add(bbart); + } + } + } + + /** + * Parse the NSDictionary object that represents one download. + * + * @param origFile Download.plist file from the case + * @param entry One NSDictionary Object that represents one download + * instance + * @return a Blackboard Artifact for the download. + * @throws TskCoreException + */ + private BlackboardArtifact parseDownloadDictionary(Content dataSource, AbstractFile origFile, NSDictionary entry) throws TskCoreException { + String url = null; + String path = null; + Long time = null; + Long pathID = null; + + NSString nsstring = (NSString) entry.get(PLIST_KEY_DOWNLOAD_URL); + if (nsstring != null) { + url = nsstring.toString(); + } + + nsstring = (NSString) entry.get(PLIST_KEY_DOWNLOAD_PATH); + if (nsstring != null) { + path = nsstring.toString(); + pathID = Util.findID(dataSource, path); + } + + NSDate date = (NSDate) entry.get(PLIST_KEY_DOWNLOAD_DATE); + if (date != null) { + time = date.getDate().getTime(); + } + + BlackboardArtifact bbart = origFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD); + bbart.addAttributes(this.createDownloadAttributes(path, pathID, url, time, NetworkUtils.extractDomain(url), getName())); + + return bbart; + } +} diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/RAImageIngestModule.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/RAImageIngestModule.java index 7e959660f492a5ec8b049fc8c7ff97e73b0e6268..434cf968ded361373ed5f33ecee4d198cc4a7553 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/RAImageIngestModule.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/RAImageIngestModule.java @@ -74,11 +74,13 @@ public void startUp(IngestJobContext context) throws IngestModuleException { Extract SEUQA = new SearchEngineURLQueryAnalyzer(); Extract osExtract = new ExtractOs(); Extract dataSourceAnalyzer = new DataSourceUsageAnalyzer(); + Extract safari = new ExtractSafari(); extractors.add(chrome); extractors.add(firefox); extractors.add(iexplore); extractors.add(edge); + extractors.add(safari); extractors.add(recentDocuments); extractors.add(SEUQA); // this needs to run after the web browser modules extractors.add(registry); // this should run after quicker modules like the browser modules and needs to run before the DataSourceUsageAnalyzer @@ -89,6 +91,7 @@ public void startUp(IngestJobContext context) throws IngestModuleException { browserExtractors.add(firefox); browserExtractors.add(iexplore); browserExtractors.add(edge); + browserExtractors.add(safari); for (Extract extractor : extractors) { extractor.init();