diff --git a/bindings/java/jni/dataModel_SleuthkitJNI.cpp b/bindings/java/jni/dataModel_SleuthkitJNI.cpp index 093ea3ea1994d3bda08f02bb3d4a1ee1cb00760c..d1b5ce24381e9173943d41741fec9f0566c9ae26 100644 --- a/bindings/java/jni/dataModel_SleuthkitJNI.cpp +++ b/bindings/java/jni/dataModel_SleuthkitJNI.cpp @@ -278,6 +278,8 @@ JNIEXPORT jint JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_hashDBLookup } } + env->ReleaseStringUTFChars(hash, (const char *) md5); + return (int) file_known; } @@ -338,7 +340,7 @@ JNIEXPORT void JNICALL //change to true when autopsy needs the block table. tskAuto->createBlockMap(false); //change to false if hashes aren't needed - tskAuto->hashFiles(true); + tskAuto->hashFiles(false); diff --git a/bindings/java/src/org/sleuthkit/datamodel/BlackboardArtifact.java b/bindings/java/src/org/sleuthkit/datamodel/BlackboardArtifact.java index 26a5f79892b073f2dba56e389d890d34c83d5674..9a7bb338b11dbf122ec91ffbcc6aa5133350f36e 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/BlackboardArtifact.java +++ b/bindings/java/src/org/sleuthkit/datamodel/BlackboardArtifact.java @@ -98,7 +98,7 @@ static public ARTIFACT_TYPE fromID(int ID) { throw new IllegalArgumentException("No ARTIFACT_TYPE matching type: " + ID); } - String getDisplayName() { + public String getDisplayName() { return this.displayName; } } diff --git a/bindings/java/src/org/sleuthkit/datamodel/BlackboardAttribute.java b/bindings/java/src/org/sleuthkit/datamodel/BlackboardAttribute.java index 8be9e73046eab856a36f1931ec5feccf84031707..1a6c130c8f16c5fc87c0a49fde6e851c4862f562 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/BlackboardAttribute.java +++ b/bindings/java/src/org/sleuthkit/datamodel/BlackboardAttribute.java @@ -158,7 +158,7 @@ static public ATTRIBUTE_TYPE fromLabel(String label) { throw new IllegalArgumentException("No ATTRIBUTE_TYPE matching type: " + label); } - String getDisplayName() { + public String getDisplayName() { return this.displayName; } } diff --git a/bindings/java/src/org/sleuthkit/datamodel/Hash.java b/bindings/java/src/org/sleuthkit/datamodel/Hash.java new file mode 100644 index 0000000000000000000000000000000000000000..cb34e2e30c3d90b73f95c8724c4f1b53155cc944 --- /dev/null +++ b/bindings/java/src/org/sleuthkit/datamodel/Hash.java @@ -0,0 +1,65 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2011 Basis Technology Corp. + * Contact: carrier <at> sleuthkit <dot> org + * + * 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.datamodel; + +import java.io.IOException; +import java.math.BigInteger; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.io.InputStream; + +class Hash { + + private final static int BUFFER_SIZE = 8192; + + /** + * generate the md5 hash for the given content + * + * @param content Content object whose md5 hash we want to calculate + * @return md5 of the given Content object + */ + static String calculateMd5(Content content) { + String hashText = ""; + InputStream in = new ReadContentInputStream(content); + Logger logger = Logger.getLogger(Hash.class.getName()); + try { + MessageDigest md = MessageDigest.getInstance("md5"); + byte[] buffer = new byte[BUFFER_SIZE]; + int len = in.read(buffer); + while (len != -1) { + md.update(buffer, 0, len); + len = in.read(buffer); + } + byte[] hash = md.digest(); + BigInteger bigInt = new BigInteger(1, hash); + hashText = bigInt.toString(16); + // zero padding + while (hashText.length() < 32) { + hashText = "0" + hashText; + } + } catch (NoSuchAlgorithmException ex) { + logger.log(Level.SEVERE, "No algorithm known as 'md5'", ex); + } catch (IOException ex) { + logger.log(Level.SEVERE, "Error reading content", ex); + } + return hashText; + } +} diff --git a/bindings/java/src/org/sleuthkit/datamodel/ReadContentInputStream.java b/bindings/java/src/org/sleuthkit/datamodel/ReadContentInputStream.java new file mode 100644 index 0000000000000000000000000000000000000000..d1e69e69c9933f1dcfb8b1734c803380b9dee8ac --- /dev/null +++ b/bindings/java/src/org/sleuthkit/datamodel/ReadContentInputStream.java @@ -0,0 +1,88 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2011 Basis Technology Corp. + * Contact: carrier <at> sleuthkit <dot> org + * + * 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.datamodel; + +import java.io.IOException; +import java.io.InputStream; + +/** + * InputStream to read bytes from a Content object's data + */ +public class ReadContentInputStream extends InputStream { + + private long position; + private long length; + private Content content; + + public ReadContentInputStream(Content content) { + this.content = content; + this.position = 0; + this.length = content.getSize(); + } + + @Override + public int read() throws IOException { + byte[] buff = new byte[1]; + return (read(buff) != -1) ? buff[0] : -1; + } + + @Override + public int read(byte[] b) throws IOException { + return read(b, 0, b.length); + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + + // must return 0 for zero-length arrays + if (b.length == 0) { + return 0; + } + + // will get an error from TSK if we try to read an empty file + if (this.length == 0) { + return -1; + } + + if (position < length) { + // data remains to be read + + int lenToRead = (int) Math.min(len, length - position); + + try { + byte[] buff = content.read(position, lenToRead); + int lenRead = buff.length; + + if (lenRead == 0) { + // TSK could not read the whole file, ending partway + return -1; + } else { + System.arraycopy(buff, 0, b, off, lenRead); + position += lenRead; + return lenRead; + } + } catch (TskException ex) { + throw new IOException(ex); + } + } else { + // at end of file + return -1; + } + } +} \ No newline at end of file diff --git a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java old mode 100644 new mode 100755 index 1667a7ccf37be6dbd5e07ad1713c21f99f2534e2..ca6651aafc29324fbe141e6f03d7fd0e37640f29 --- a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java +++ b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java @@ -31,6 +31,7 @@ import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; import org.sleuthkit.datamodel.SleuthkitJNI.CaseDbHandle.AddImageProcess; +import org.sleuthkit.datamodel.TskData.FileKnown; import org.sleuthkit.datamodel.TskData.TSK_FS_META_TYPE_ENUM; /** @@ -402,6 +403,49 @@ public List<Content> getRootObjects() throws TskException { } } + /** + * Get all blackboard artifacts of a given type + * @param artifactTypeID artifact type id (must exist in database) + * @return list of blackboard artifacts + */ + public ArrayList<BlackboardArtifact> getBlackboardArtifacts(int artifactTypeID) throws TskException { + String artifactTypeName = this.getArtifactTypeString(artifactTypeID); + try { + ArrayList<BlackboardArtifact> artifacts = new ArrayList<BlackboardArtifact>(); + Statement s = con.createStatement(); + ResultSet rs = s.executeQuery("SELECT artifact_id, obj_id FROM blackboard_artifacts WHERE artifact_type_id = " + artifactTypeID); + + while (rs.next()) { + artifacts.add(new BlackboardArtifact(this, rs.getLong(1), rs.getLong(2), + artifactTypeID, artifactTypeName, ARTIFACT_TYPE.fromID(artifactTypeID).getDisplayName())); + } + s.close(); + return artifacts; + } catch (SQLException ex) { + throw new TskException("Error getting or creating a blackboard artifact. " + ex.getMessage(), ex); + } + } + + /** + * Get all blackboard artifact types + * @return list of blackboard artifact types + */ + public ArrayList<BlackboardArtifact.ARTIFACT_TYPE> getBlackboardArtifactTypes() throws TskException { + try { + ArrayList<BlackboardArtifact.ARTIFACT_TYPE> artifact_types = new ArrayList<BlackboardArtifact.ARTIFACT_TYPE>(); + Statement s = con.createStatement(); + ResultSet rs = s.executeQuery("SELECT artifact_type_id FROM blackboard_artifact_types"); + + while (rs.next()) { + artifact_types.add(BlackboardArtifact.ARTIFACT_TYPE.fromID(rs.getInt(1))); + } + s.close(); + return artifact_types; + } catch (SQLException ex) { + throw new TskException("Error getting artifact types. " + ex.getMessage(), ex); + } + } + /** * helper method to get all artifacts matching the type id name and object id * @param artifactTypeID artifact type id @@ -458,7 +502,7 @@ public ArrayList<BlackboardArtifact> getBlackboardArtifacts(int artifactTypeID, public ArrayList<BlackboardArtifact> getBlackboardArtifacts(ARTIFACT_TYPE artifactType, long obj_id) throws TskException { return getArtifactsHelper(artifactType.getTypeID(), artifactType.getLabel(), obj_id); } - + /** * Get the blackboard artifact with the given artifact id * @param artifactID artifact ID @@ -1370,4 +1414,121 @@ public void close() { "Error freeing case handle.", ex); } } -} + + /** + * Update the given hash and known status of the object in the DB denoted by id + * + * @param id The object's unique ID in the database + * @param md5Hash The object's calculated md5 hash + * @param fileKnown The object's known status + * @throws SQLException + */ + private void updateHashAndKnown(long id, String md5Hash, FileKnown fileKnown) throws SQLException{ + Statement s = con.createStatement(); + s.executeUpdate("UPDATE tsk_files " + + "SET known='" + fileKnown.toLong() + "', md5='" + md5Hash + "' " + + "WHERE obj_id=" + id); + s.close(); + } + +// Useful if we want to queue sql updates for performance reasons +// /** +// * Update the given hash and known status of the objects in the DB denoted by id +// * +// * @param ids The objects' unique IDs in the database +// * @param md5Hashes The objects' calculated md5 hashes +// * @param knowns The objects' known statuses +// * @throws SQLException +// */ +// private void updateHashesAndKnowns(List<Long> ids, List<String> md5Hashes, List<Long> knowns) throws SQLException{ +// int idsSize = ids.size(); +// int md5sSize = md5Hashes.size(); +// int knownsSize = knowns.size(); +// if(idsSize == md5sSize && md5sSize == knownsSize && knownsSize == idsSize){ +// StringBuilder query = new StringBuilder("UPDATE tsk_files SET known = CASE obj_id"); +// for(int i = 0; i<idsSize; i++){ +// // " WHEN id THEN known" +// query.append(" WHEN ").append(ids.get(i)) +// .append(" THEN ").append(knowns.get(i)); +// } +// query.append(" END, md5 = CASE obj_id"); +// for(int i = 0; i<idsSize; i++){ +// // " WHEN id THEN hash" +// query.append(" WHEN ").append(ids.get(i)) +// .append(" THEN '").append(md5Hashes.get(i)).append("'"); +// } +// query.append(" END WHERE id in ("); +// for(int i = 0; i<idsSize; i++){ +// // "1,2,3,4," +// query.append(ids.get(i)).append(","); +// } +// // remove the last unnecessary comma +// query.deleteCharAt(query.length()-1); +// query.append(")"); +// Statement s = con.createStatement(); +// s.executeUpdate(query.toString()); +// s.close(); +// }else{ +// throw new IllegalArgumentException("Lists must be of equal length!"); +// } +// } + + /** + * Calculate the given Content object's md5 hash, look it up in the + * known databases, and then update the case database with both hash and + * known status + * + * @param cont The content whose md5 you want to look up + * @return The content's known status from the databases + * @throws TskException + */ + public String lookupFileMd5(Content cont) throws TskException{ + Logger logger = Logger.getLogger(SleuthkitCase.class.getName()); + try{ + long contId = cont.getId(); + String md5Hash = Hash.calculateMd5(cont); + FileKnown fileKnown = SleuthkitJNI.lookupHash(md5Hash); + updateHashAndKnown(contId, md5Hash, fileKnown); + return fileKnown.getName(); + } catch (TskException ex) { + logger.log(Level.SEVERE, "Error looking up known status", ex); + } catch(SQLException ex) { + logger.log(Level.SEVERE, "Error updating SQL database", ex); + } + throw new TskException("Error analyzing file"); + } + +// Useful if we want to queue sql updates for performance reasons +// /** +// * Calculate the given Content objects' md5 hashes, look them up in the +// * known databases, and then update the case database with both hash and +// * known status +// * +// * @param cont The list of contents whose md5s you want to look up +// * @return The contents' known statuses from the databases +// * @throws TskException +// */ +// public List<Long> lookupFilesMd5(List<? extends Content> cont) throws TskException{ +// List<Long> ids = new ArrayList<Long>(); +// List<String> md5Hashes = new ArrayList<String>(); +// List<Long> knowns = new ArrayList<Long>(); +// +// try{ +// for(Content c : cont){ +// ids.add(c.getId()); +// String md5Hash = Hash.calculateMd5(c); +// md5Hashes.add(md5Hash); +// knowns.add(SleuthkitJNI.lookupHash(md5Hash).toLong()); +// } +// updateHashesAndKnowns(ids, md5Hashes, knowns); +// return knowns; +// } catch (TskException ex) { +// Logger.getLogger(SleuthkitCase.class.getName()).log(Level.SEVERE, +// "Error looking up known status", ex); +// } catch(SQLException ex) { +// Logger.getLogger(SleuthkitCase.class.getName()).log(Level.SEVERE, +// "Error updating SQL database", ex); +// } +// throw new TskException("Error analyzing files"); +// } +} diff --git a/tsk3/auto/auto_db.cpp b/tsk3/auto/auto_db.cpp index 5f0445ca137ed440cb594faf3f9bfc22c27634b4..2b9b1909eeb15c17c8e13a1e929e19dc69d94a40 100644 --- a/tsk3/auto/auto_db.cpp +++ b/tsk3/auto/auto_db.cpp @@ -28,7 +28,7 @@ TskAutoDb::TskAutoDb(TskDbSqlite * a_db, TSK_HDB_INFO * a_NSRLDb, TSK_HDB_INFO * m_curFsId = 0; m_curVsId = 0; m_blkMapFlag = false; - m_fileHashFlag = true; + m_fileHashFlag = false; m_vsFound = false; m_volFound = false; m_stopped = false; @@ -511,7 +511,7 @@ TskAutoDb::processAttribute(TSK_FS_FILE * fs_file, TSK_AUTO_CASE_KNOWN_FILE_ENUM file_known = TSK_AUTO_CASE_FILE_KNOWN_UNKNOWN; - if (m_fileHashFlag && isFile(fs_file)) { + if (m_fileHashFlag && isFile(fs_file)) { if (md5HashAttr(hash, fs_attr)) { return TSK_ERR; }