Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • irt/sleuthkit
1 result
Show changes
Showing
with 6031 additions and 0 deletions
/*! \page mod_dspage Data Sources, Hosts, and Persons
\section ds_overview Overview
This page outlines some of the core concepts around data sources and how they are organized.
\section ds_ds Data Sources
A org.sleuthkit.datamodel.DataSource represents a set of data that has been added to a case. Examples of data sources include:
- A disk or phone image
- A set of logical files
- A report from another forensics tool
The objects in the case database are generally organized in a tree structure. The data sources are often the set of top-most items in the tree.
You can call org.sleuthkit.datamodel.SleuthkitCase.getDataSources() to get all of data sources in a case. From there you can call getChildren() to go down the tree. For example, you can go from the disk image to volumes, then to file systems, and finally to files and subfolders.
You can add data sources by various SleuthkitCase methods, such as org.sleuthkit.datamodel.SleuthkitCase.makeAddImageProcess().
\section ds_hosts Hosts
All data sources must be associated with a org.sleuthkit.datamodel.Host. A host represents the device that the data source came from. Some hosts will have only a single data source, for example when analyzing a computer with one hard drive. Other hosts may have multiple data sources, for example when analyzing a phone with an image of the handset and another image of a media card.
If you later learn that two data sources are from the same device, you can merge the hosts.
Hosts are managed from org.sleuthkit.datamodel.HostManager.
NOTE: Hosts are different from org.sleuthkit.datamodel.HostAddress. A Host is for devices that were seized and added to the case. A HostAddress is for an address of any external host that was found during the analysis of a data source. For example, a HostAddress for "www.sleuthkit.org" could be created based on web history artifacts.
\section ds_person Persons
You can optionally associate a host with a org.sleuthkit.datamodel.Person. This can allow you to more easily organize data in a large case. The concept is that you have multiple data sources representing different devices that are all owned or used by a given person. You can group that person's data sources together.
Persons are managed from org.sleuthkit.datamodel.PersonManager.
*/
gnore everything in this directory
*
# Except this file
!.gitignore
<hr/>
<p><i>Copyright &#169; 2011-2021 Brian Carrier. (carrier -at- sleuthkit -dot- org)<br/>
This work is licensed under a
<a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/us/">Creative Commons Attribution-Share Alike 3.0 United States License</a>.
</i></p>
</body>
</html>
bindings/java/doxygen/images/associated_object.png

23 KiB

bindings/java/doxygen/images/json_attribute.png

55.9 KiB

/*! \page insert_and_update_database_page Inserting Data
\section types_of_databases_available Inserting Into the Database
This page is for people who are developing Sleuth Kit code and need to place items into the database with SQL statements. If you are simply writing modules that read from the database (such as Autopsy ingest modules), there is nothing for you here.
The Sleuth Kit currently allows either SQLite or PostgreSQL as the back-end database system for a case. Any code you write could be used with either as the backend database, at the user's discretion. Be sure to test your work with both platforms.
- For SQLite compatibility, use SQL statements supported by SQLite 3
- For PostgreSQL compatibility, use SQL statements supported by PostgreSQL 9.4
\section insert_pitfalls_to_avoid How to Avoid Pitfalls When INSERTing into the Database
- Do not use INSERT OR REPLACE INTO. It does not exist in PostgreSQL.
- Do not use INSERT OR IGNORE INTO. It does not exist in PostgreSQL.
- Do not insert [NUL characters](http://en.wikipedia.org/wiki/Null_character) into the database as UTF-8 (NUL characters are not NULL fields). Translate NUL characters to the [SUB character](http://en.wikipedia.org/wiki/Substitute_character) with the following instead:
\code{.java}
private String replaceNulls(String text);
\endcode
*/
/*! \mainpage The Sleuth Kit Java Bindings Developer's Guide and API Reference
<h3>Overview</h3>
These classes allow Java programs to access data extracted by The Sleuth Kit.
The Sleuth Kit is primarily a C/C++ library and set of command line tools. These classes allow programs to obtain the data that TSK can produce. The typical steps would be to use JNI to cause the TSK library to create and populate a SQLite or PostgreSQL database. The Java classes then directly open the database and perform queries on it.
\section main_classes Types of Classes
There are three broad types of classes in this package:
- org.sleuthkit.datamodel.SleuthkitCase contains all of the code that deals with the backend database.
- org.sleuthkit.datamodel.SleuthkitJNI deals with the JNI bindings with the C/C++ code (that primarily populate the database or allow file content to be read)
- Lots of classes that store information about specific files or volumes. Nearly all of them implement the org.sleuthkit.datamodel.Content interface. Files from file systems or carved files will extend org.sleuthkit.datamodel.AbstractFile.
\section main_workflow Basic Workflow
\subsection basics_add Adding Data to Case
To get data into the database (which is needed before you get it into a Java object), you need to call some org.sleuthkit.datamodel.SleuthkitCase methods.
To open or create a case, call org.sleuthkit.datamodel.SleuthkitCase.newCase() or org.sleuthkit.datamodel.SleuthkitCase.openCase().
To add a <b>disk image</b> to the case, use org.sleuthkit.datamodel.SleuthkitCase.makeAddImageProcess() to get a org.sleuthkit.datamodel.SleuthkitJNI.CaseDbHandle.AddImageProcess object that allows you to populate the database in the scope of a transaction and get feedback on its update process.
To add a <b>local file</b> (logical file) you can use methods such as org.sleuthkit.datamodel.SleuthkitCase.addLocalFile().
\subsection basics_analyzing Analyzing Data in Case
You can either access files directly using methods such as org.sleuthkit.datamodel.SleuthkitCase.findFiles() or org.sleuthkit.datamodel.SleuthkitCase.getAbstractFileById().
You can also access the data in its tree form by starting with org.sleuthkit.datamodel.SleuthkitCase.getImages() and then calling getChildren() on each of the returned objects. See the section below on basics of the datamodel structure.
\section main_other Other Topics
- \subpage mod_dspage describes data source organization
- \subpage mod_os_accounts_page
- \subpage mod_bbpage is where analysis modules (such as those in Autopsy) can post and save their results.
- The \subpage artifact_catalog_page gives a list of the current artifacts and attributes used on \ref mod_bbpage.
- \subpage mod_compage is where analysis modules can store and retrieve communications-related data.
\section main_db Database Topics
The Sleuth Kit has its own database schema that is shared with Autopsy and other tools. The primary way it gets populated is via the Java code.
- Database Schema Documentation:
- \subpage db_schema_9_1_page
- \subpage db_schema_page "Older schemas"
- Refer to \subpage query_database_page if you are going to use one of the SleuthkitCase methods that requires you to specify a query.
- Refer to \subpage insert_and_update_database_page if you are a Sleuth Kit developer and want to avoid database issues.
*/
/*! \page mod_os_accounts_page OS Accounts and Realms
\section os_acct_overview Overview
This page outlines some of the core concepts around OS accounts and realms and how they are stored.
OS accounts are unique data types in the TSK datamodel and have more complexity than other types because
we often will not fully understand the details when creating the OS accounts early in the processing and will
need to update them at various points as analysis continues.
\section os_acct_basics Basic Terminology
- An <b>OS account</b> allows a person to do some action or access some resource on a device.
- A <b>realm</b> is the scope in which the OS account is defined. A realm can be scoped to a single host (i.e., for accounts that exist only on a single host) or to a network domain (such as Windows domain accounts).
\section os_acct_challenges OS Account Challenges
A key challenge with OS accounts is that we do not know the account information until we have started to parse files, and the more detailed information will only come from OS configuration files. It is also possible that we may never know the details if we have only a media card.
As a user adds a disk image to the case, we may learn about addresses from the files. But, we won't yet know the account name or if it is domain-scoped or local-scoped. So, the basic properties of the realm and account may change as more data is ingested and analyzed. This could even result in needing to merge realms and accounts.
Another difference from other data types in the TSK data model is that OS accounts may span multiple data sources if they are domain accounts. Therefore, they are not "children" of a data source and exist outside of the usual tree model in TSK.
\section os_acct_realm OS Account Realms
An org.sleuthkit.datamodel.OsAccountRealm represents the scope of a set of OS accounts. A realm's scope is defined by org.sleuthkit.datamodel.OsAccountRealm.RealmScope. By default, the scope is set to host-level and the org.sleuthkit.datamodel.OsAccountRealm.ScopeConfidence is set to inferred. As more is learned, the confidence and scope can be made more specific.
A realm has two core fields:
- Address that the OS uses internally, such as part of a Windows SID
- Name that is what users more often see
A local realm also defines the single host that the realm works on.
When searching for realms, the address has priority over the name. Often times with Windows systems, we may have a realm address from SIDs but not a specific realm name.
Realms are managed by org.sleuthkit.datamodel.OsAccountRealmManager.
\section os_acct_acct OS Accounts
An org.sleuthkit.datamodel.OsAccount represents an account that was configured in an operating system. It must be defined within the scope of an OsAccountRealm.
An OS account has two core fields:
- Login name that the user enters (such as jdoe)
- Address that the operating system uses internally (such as a UID of 0 or a Windows SID)
OS accounts also have other properties, such as full name, creation date, etc., that can be set after the account is created.
OS accounts are managed by org.sleuthkit.datamodel.OsAccountManager.
\subsection os_acct_acct_inst OS Account Instances
An OS Account can exist on multiple systems for several reasons, including:
- It's a domain account and the user logged into several systems
- It's a local account, but there was a reference to the account in an event log or registry setting.
Therefore, the database stories each instance the account was seen. An account instance shows that there was a reference to the account on a given host. The instance types are defined in org.sleuthkit.datamodel.OsAccountInstance.OsAccountInstanceType.
When writing modules, you should record each instance that you see the account.
\subsection os_acct_acct_os Supported Operating Systems
At this point, APIs exist for only Windows accounts, such as:
- org.sleuthkit.datamodel.OsAccountManager.newWindowsOsAccount()
- org.sleuthkit.datamodel.OsAccountManager.getWindowsOsAccount()
The underlying database schema supports other operating systems, but the utility APIs do not exist to populate them other than with Windows SIDs. These methods may be added in the future.
\section os_account_storing Storing Original Account Data in Other Tables
We recommend that the OS account addresses or names that were parsed from the data source be saved alongside any references to OsAccount objects when making new tables. For example, the case database stores the UID or SID that was stored in a file system for a file in addition to the reference to the OsAccount object that is associated with that address. This helps to ensure the original data is preserved in case an Os account can't be created, gets deleted, or is incorrectly merged.
\section os_acct_example Example Creation & Update Code
There are three unique things to keep in mind when creating or updating OS accounts:
<ol>
<li><b>Transactions.</b> To avoid duplicates, OS Accounts are often created and merged outside of a transaction. With single-user cases, you can get into deadlocks when you mix transaction-based API calls and non-transaction API calls.
This means that if you want to use a CaseDbTransation to add a lot of files or artifacts associated with OS accounts at once, you'll need to:
<ol type="a">
<li>Pre-process the data to identify what OS accounts you need to create or look up
<li>Look up or create the OS accounts in individual transactions
<li>Start a new transaction and add the files or artifacts with the references to the OS accounts
</ol>
<li><b>Updates.</b> When you come accross OS Account data, you may have more info then what already exists in the DB and you should therefore try to upate it. e.g maybe the realm name was unknown. You should call an "update" command just in case you have new data.
<li><b>Instances.</b> You need to record that an OS account was referenced on a given data source because OS accounts are stored in parallel to data sources and are not children of them. Some methods, such as 'addFile()', will automatically record the instance.
</ol>
Here are some examples.
\subsection os_acct_ex_get Adding a File or Data Artifact
The various addFile() methods allow you to pass in an OsAccount and the database will make the association and record the occurence. All you need to do is get the account. You can do that with org.sleuthkit.datamodel.OsAccountManager.getWindowsOsAccount(). Note that sometimes that call will fail if the SID associated with the file is for a group, for example, if the OS account has admin rights.
If you get an OsAccount, you can try to update it if you think you may have new information.
Here is example pseudo-code:
\code
OsAccount osAcct = null;
try {
Optional<OsAccount> osAcctOpt = getWindowsOsAccount("S-....", "jdoe", "ACME", host);
if (osAcctOpt.isPresent(()) {
osAcct = osAcctOpt.get();
updateWindowsOsAccount(osAccount, "S-.....", "jdoe", "ACME", host);
}
else {
osAcct = newWindowsOsAccount("S-....", "jdoe", "ACME", host)
}
}
catch (NotUserSIDException ex) {
// Ignore this SID
}
// Pass in osAcct when making artifacts and files
\endcode
\subsection os_acct_ex_update Parsing OS Configuration Data
When parsing the Windows registry or other OS Configuration file, you may find updated information about OS accounts. You can call various org.sleuthkit.datamodel.OsAccountManager methods to get and update the accounts. When adding extended attributes, you can choose to limit the scope of the attribute to the single host being parsed or to the domain-level.
You should make sure to call org.sleuthkit.datamodel.OsAccountManager.newOsAccountInstance() to ensure it is recorded that there was at least some reference to account on that data source. Otherwise, it will not be associated with the data source unless there were also files or artifacts that were mapped to the OS account.
*/
/*! \page query_database_page Query the Database
\section types_of_databases Database Queries
This page is for people who are developing their own Autopsy plugin modules that require SQL queries. If you are not developing a module requiring SQL queries, you can skip this page.
Autopsy currently allows either SQLite or PostgreSQL as the back-end database system for a case. Any module you write could be used with either as the backend database, at the user's discretion.
If you are writing code actually for Autopsy, not just an Autopsy module, you may need to be able to INSERT and UPDATE into the database as well. Please see \subpage insert_and_update_database_page.
<br>
\subsection which_db Which Database is my Module Accessing?
In an Autopsy Module, you can check the database type currently in use with the following code snippet:
\code{.java}
Case currentCase = Case.getCurrentCase();
if (currentCase.getCaseType() == Case.CaseType.MULTI_USER_CASE)
{
// PostgreSQL in use
}
else
{
// SQLite in use
}
\endcode
<br>
\section db_user_calls TSK methods to Query the Database With User-Supplied SQL
The following SleuthkitCase methods are available for the user to supply all of, or a portion of, a SQL query.
\code{.java}
ArrayList<BlackboardAttribute> getMatchingAttributes(String whereClause)
ArrayList<BlackboardArtifact> getMatchingArtifacts(String whereClause)
long countFilesWhere(String sqlWhereClause)
List<AbstractFile> findAllFilesWhere(String sqlWhereClause)
List<Long> findAllFileIdsWhere(String sqlWhereClause)
CaseDbQuery executeQuery(String query)
List<FsContent> findFilesWhere(String sqlWhereClause) [deprecated]
ResultSet runQuery(String query) [deprecated]
void closeRunQuery(ResultSet resultSet) [deprecated]
\endcode
The majority of them only allow the user to specify a WHERE clause, determining which records to SELECT.
<br>
<br>
The following example finds all the .txt files in the case:
\code{.java}
List<AbstractFile> files = sk.findAllFilesWhere("LOWER(name) LIKE '%.txt'");
\endcode
<br>
\section db_pitfalls_to_avoid How to Avoid Pitfalls When Using the Query Methods
Because there are multiple backend databases, care must be taken to use strict SQL. When things must be different between database types, use \ref which_db to determine which database type is currently in use and create the proper SQL statements. Be sure to test your module with both types of databases. They behave differently and will give you different resulting output order.
<br>
<br>
\subsection general_items WHERE Clause Syntax
- Do not use backticks. PostgreSQL does not use them like SQLite does.
<br>
<br>
- Use only single quotes to quote values. Do not use double quotes for this. Quoting values is not required.
<br>
\code{.java}
SELECT * FROM tsk_files WHERE has_path = "1" // Bad example
SELECT * FROM tsk_files WHERE has_path = '1' // Good example
SELECT * FROM tsk_files WHERE has_path = 1 // Good example
\endcode
<br>
- Use only double quotes to quote column names. Do not use single quotes for this. Quoting column names is not required.
<br>
\code{.java}
SELECT 'obj_id' FROM tsk_files WHERE has_path = 1 // Bad example
SELECT "obj_id" FROM tsk_files WHERE has_path = 1 // Good example
SELECT obj_id FROM tsk_files WHERE has_path = 1 // Good example
\endcode
<br>
- Do not use || and && to connect logical clauses. This does not exist in PostgreSQL. Use OR and AND instead.
\code{.java}
SELECT COUNT(*) FROM tsk_files WHERE dir_type = '5' && md5 IS NULL || size > '0' // Bad Example
SELECT COUNT(*) FROM tsk_files WHERE dir_type = '5' AND md5 IS NULL OR size > '0' // Good Example
\endcode
<br>
- PostgreSQL compares are case-sensitive. Always specify what type of compare you want. UPPER() and LOWER() can help with that.
\code{.java}
SELECT * from people WHERE first_name LIKE '%somename%' // Will be case sensitive in PostgreSQL, not in SQLite
SELECT * from people WHERE first_name ILIKE '%somename%' // Works in PostgreSQL, does not exist in SQLite
SELECT * from people WHERE LOWER(first_name) LIKE LOWER('%somename%') // Not case sensitive in either database
\endcode
<br>
- When generating WHERE queries via code, some folks include an AND(1) or OR(0) clause in the query as a placeholder that does not effect the outcome of the query but simplifies the query-generation logic. PostgreSQL does not allow true or false comparisons with integers. The PostgreSql syntax is AND(true) or OR(false). SQLite does not allow the PostgreSQL syntax and PostgreSQL does not allow the SQLite syntax. Do not use this trick to generate queries. Instead, have your code handle the edge cases of if there are no entries for the AND or OR portion of a clause.
\code{.java}
WHERE id=12 AND(1) // SQLite example, will not work in PostgreSQL
WHERE id=12 AND(true) // PostgreSQL example, will not work in SQLite
WHERE id=12 // Will work in both, just a bit harder to handle all the cases in query-generation code
\endcode
<br>
- SQLite allows non-standard usage of the IS keyword. Standard usage of IS checks if something IS NULL or IS NOT NULL. It does not compare against specific values. Remember when comparing values to use = instead of the IS keyword. If you want to check for NULL, then IS NULL is the right tool. Example:
\code{.java}
WHERE value IS '4' // Bad example. Works in SQLite, does not work in PostgreSQL
WHERE value = '4' // Good example. Works in both SQLite and PostgreSQL
WHERE value != '4' // Good example. Works in both SQLite and PostgreSQL
WHERE value IS NULL // Good example. Works in both SQLite and PostgreSQL
WHERE value IS NOT NULL // Good example. Works in both SQLite and PostgreSQL
\endcode
<br>
<br>
\subsection order_by How to ORDER BY Consistently
- SQLite and PostgreSQL have different default sort orders for returned records, so you want to fully specify ORDER BY clauses for both database types. Example:
\code{.java}
Case currentCase = Case.getCurrentCase();
String orderByClause;
if (currentCase.getCaseType() == Case.CaseType.MULTI_USER_CASE)
{
orderByClause = "ORDER BY att.value_text, ASC NULLS FIRST"; //PostgreSQL
}
else
{
orderByClause = "ORDER BY att.value_text ASC"; //SQLite
}
\endcode
<br>
- Do not use COLLATE NOCASE to order output. This does not exist in PostgreSQL. Use LOWER() or UPPER() instead.
\code{.java}
ORDER BY tsk_files.dir_type, tsk_files.name COLLATE NOCASE // Bad Example
ORDER BY tsk_files.dir_type, LOWER(tsk_files.name) // Good Example
\endcode
<br>
- In ORDER BY clauses, PostgreSQL ignores leading dashes. Given the following data, you will see the following two sort orders for the different databases.
<br>
| Data | PostgreSQL sort order | SQLite sort order|
|:--------:|:------------------------:|:------------------:|
|Alpha | Alpha | -Bravo |
|-Bravo | -Bravo | Alpha |
|Charlie | Charlie | Charlie |
<br>
To force PostgreSQL to not ignore leading dashes, convert strings to SQL_ASCII before sorting by them. This is done with convert_to(), but it only exists in PostgreSQL.
<br>
\code{.java}
ORDER BY some_value // Bad example
ORDER BY convert_to(some_value, 'SQL_ASCII') // Good example
\endcode
<br>
With the code above, using SQL_ASCII encoding, the following results are seen:
<br>
| Data | PostgreSQL sort order | SQLite sort order|
|:--------:|:------------------------:|:------------------:|
|Alpha | -Bravo | -Bravo |
|-Bravo | Alpha | Alpha |
|Charlie | Charlie | Charlie |
<br>
- PostgreSQL sorts NULLs last for ASC and first for DESC. SQLite does the opposite. PostgreSQL allows you to control the NULL sort order with NULLS FIRST or NULLS LAST
\code{.java}
ORDER BY att.value_text ASC // SQLite example, will give different ordering in PostgreSQL
ORDER BY convert_to(att.value_text, 'SQL_ASCII') ASC NULLS FIRST // PostgreSQL example. The command NULLS FIRST does not exist in SQLite, but SQLite will sort nulls first by default.
\endcode
<br>
*/
/*! \page db_schema_8_6_page TSK & Autopsy Database Schema (Schema version 8.6)
[TOC]
# Introduction
This page outlines version 8.6 the database that is used by The Sleuth Kit and Autopsy. The goal of this page is to provide short descriptions for each table and column and not focus on foreign key requirements, etc. If you want that level of detail, then refer to the actual schema in addition to this.
You can find a basic graphic of some of the table relationships <a href="https://docs.google.com/drawings/d/1omR_uUAp1fQt720oJ-kk8C48BXmVa3PNjPZCDdT0Tb4/edit?usp#sharing">here</a>
Some general notes on this schema:
- Nearly every type of data is assigned a unique ID, called the Object ID
- The objects form a hierarchy, that shows where data came from. A child comes from its parent.
- For example, disk images are the root, with a volume system below it, then a file system, and then files and directories.
- This schema has been designed to store data beyond the file system data that The Sleuth Kit supports. It can store carved files, a folder full of local files, etc.
- The Blackboard is used to store artifacts, which contain attributes (name/value pairs). Artifacts are used to store data types that do not have more formal tables. Module writers can make whatever artifact types they want. See \ref mod_bbpage for more details.
- The Sleuth Kit will make virtual files to span the unallocated space. They will have a naming format of 'Unalloc_[PARENT-OBJECT-ID]_[BYTE-START]_[BYTE-END]'.
# Schema Information
- <b>Autopsy versions: </b> Autopsy 4.18
- <b>Changes from version 8.5:</b>
- New column for SHA-256 hash in tsk_files
# General Information Tables
## tsk_db_info
Metadata about the database.
- **schema_ver** - Major version number of the current database schema
- **tsk_ver** - Version of TSK used to create database
- **schema_minor_version** - Minor version number of the current database schema
## tsk_db_info_extended
Name & Value pair table to store any information about the database. For example, which schema it was created with. etc.
- **name** - Any string name
- **value** - Any string value
# Object Tables
## tsk_objects
Every object (image, volume system, file, etc.) has an entry in this table. This table allows you to find the parent of a given object and allows objects to be tagged and have children. This table provides items with a unique object id. The details of the object are in other tables.
- **obj_id** - Unique id
- **par_obj_id** - The object id of the parent object (NULL for root objects). The parent of a volume system is an image, the parent of a directory is a directory or filesystem, the parent of a filesystem is a volume or an image, etc.
- **type** - Object type (as org.sleuthkit.datamodel.TskData.ObjectType enum)
# Data Source/Device Tables
## data_source_info
Contains information about a data source, which could be an image. This is where we group data sources into devices (based on device ID).
- **obj_id** - Id of image/data source in tsk_objects
- **device_id** - Unique ID (GUID) for the device that contains the data source
- **time_zone** - Timezone that the data source was originally located in
- **acquisition_details** - Notes on the acquisition of the data source
# Disk Image Tables
## tsk_image_info
Contains information about each set of images that is stored in the database.
- **obj_id** - Id of image in tsk_objects
- **type** - Type of disk image format (as org.sleuthkit.datamodel.TskData.TSK_IMG_TYPE_ENUM)
- **ssize** - Sector size of device in bytes
- **tzone** - Timezone where image is from (the same format that TSK tools want as input)
- **size** - Size of the original image (in bytes)
- **md5** - MD5 hash of the image (for compressed data such as E01, the hashes are of the decompressed image, not the E01 itself)
- **sha1** - SHA-1 hash of the image
- **sha256** - SHA-256 hash of the image
- **display_name** - Display name of the image
## tsk_image_names
Stores path(s) to file(s) on disk that make up an image set.
- **obj_id** - Id of image in tsk_objects
- **name** - Path to location of image file on disk
- **sequence** - Position in sequence of image parts
# Volume System Tables
## tsk_vs_info
Contains one row for every volume system found in the images.
- **obj_id** - Id of volume system in tsk_objects
- **vs_type** - Type of volume system / media management (as org.sleuthkit.datamodel.TskData.TSK_VS_TYPE_ENUM)
- **img_offset** - Byte offset where VS starts in disk image
- **block_size** - Size of blocks in bytes
## tsk_vs_parts
Contains one row for every volume / partition in the images.
- **obj_id** - Id of volume in tsk_objects
- **addr** - Address of the partition
- **start** - Sector offset of start of partition
- **length** - Number of sectors in partition
- **desc** - Description of partition (volume system type-specific)
- **flags** - Flags for partition (as org.sleuthkit.datamodel.TskData.TSK_VS_PART_FLAG_ENUM)
## tsk_pool_info
Contains information about pools (for APFS, logical disk management, etc.)
- **obj_id** - Id of pool in tsk_objects
- **pool_type** - Type of pool (as org.sleuthkit.datamodel.TskData.TSK_POOL_TYPE_ENUM)
# File System Tables
## tsk_fs_info
Contains one for for every file system in the images.
- **obj_id** - Id of filesystem in tsk_objects
- **data_source_obj_id** - Id of the data source for the file system
- **img_offset** - Byte offset that filesystem starts at
- **fs_type** - Type of file system (as org.sleuthkit.datamodel.TskData.TSK_FS_TYPE_ENUM)
- **block_size** - Size of each block (in bytes)
- **block_count** - Number of blocks in filesystem
- **root_inum** - Metadata address of root directory
- **first_inum** - First valid metadata address
- **last_inum** - Last valid metadata address
- **display_name** - Display name of file system (could be volume label)
## tsk_files
Contains one for for every file found in the images. Has the basic metadata for the file.
- **obj_id** - Id of file in tsk_objects
- **fs_obj_id** - Id of filesystem in tsk_objects (NULL if file is not located in a file system -- carved in unpartitioned space, etc.)
- **data_source_obj_id** - Id of the data source for the file
- **attr_type** - Type of attribute (as org.sleuthkit.datamodel.TskData.TSK_FS_ATTR_TYPE_ENUM)
- **attr_id** - Id of attribute
- **name** - Name of attribute. Will be NULL if attribute doesn't have a name. Must not have any slashes in it.
- **meta_addr** - Address of the metadata structure that the name points to
- **meta_seq** - Sequence of the metadata address
- **type** - Type of file: filesystem, carved, etc. (as org.sleuthkit.datamodel.TskData.TSK_DB_FILES_TYPE_ENUM enum)
- **has_layout** - True if file has an entry in tsk_file_layout
- **has_path** - True if file has an entry in tsk_files_path
- **dir_type** - File type information: directory, file, etc. (as org.sleuthkit.datamodel.TskData.TSK_FS_NAME_TYPE_ENUM)
- **meta_type** - File type (as org.sleuthkit.datamodel.TskData.TSK_FS_META_TYPE_ENUM)
- **dir_flags** - Flags that describe allocation status etc. (as org.sleuthkit.datamodel.TskData.TSK_FS_NAME_FLAG_ENUM)
- **meta_flags** - Flags for the file for its allocation status etc. (as org.sleuthkit.datamodel.TskData.TSK_FS_META_FLAG_ENUM)
- **size** - File size in bytes
- **ctime** - Last file / metadata status change time (stored in number of seconds since Jan 1, 1970 UTC)
- **crtime** - Created time
- **atime** - Last file content accessed time
- **mtime** - Last file content modification time
- **mode** - Unix-style permissions (as org.sleuthkit.datamodel.TskData.TSK_FS_META_MODE_ENUM)
- **uid** - Owner id
- **gid** - Group id
- **md5** - MD5 hash of file contents
- **sha256** - SHA-256 hash of file contents
- **known** - Known status of file (as org.sleuthkit.datamodel.TskData.FileKnown)
- **parent_path** - Full path of parent folder. Must begin and end with a '/' (Note that a single '/' is valid)
- **mime_type** - MIME type of the file content, if it has been detected.
- **extension** - File extension
## tsk_file_layout
Stores the layout of a file within the image. A file will have one or more rows in this table depending on how fragmented it was. All file types use this table (file system, carved, unallocated blocks, etc.).
- **obj_id** - Id of file in tsk_objects
- **sequence** - Position of the run in the file (0-based and the obj_id and sequence pair will be unique in the table)
- **byte_start** - Byte offset of fragment relative to the start of the image file
- **byte_len** - Length of fragment in bytes
## tsk_files_path
If a "locally-stored" file has been imported into the database for analysis, then this table stores its path. Used for derived files and other files that are not directly in the image file.
- **obj_id** - Id of file in tsk_objects
- **path** - Path to where the file is locally stored in a file system
- **encoding_type** - Method used to store the file on the disk
## file_encoding_types
Methods that can be used to store files on local disks to prevent them from being quarantined by antivirus
- **encoding_type** - ID of method used to store data. See org.sleuthkit.datamodel.TskData.EncodingType enum
- **name** - Display name of technique
## tsk_files_derived_method
Derived files are those that result from analyzing another file. For example, files that are extracted from a ZIP file will be considered derived. This table keeps track of the derivation techniques that were used to make the derived files.
NOTE: This table is not used in any code.
- **derived_id** - Unique id for the derivation method.
- **tool_name** - Name of derivation method/tool
- **tool_version** - Version of tool used in derivation method
- **other** - Other details
## tsk_files_derived
Each derived file has a row that captures the information needed to re-derive it
NOTE: This table is not used in any code.
- **obj_id** - Id of file in tsk_objects
- **derived_id** - Id of derivation method in tsk_files_derived_method
- **rederive** - Details needed to re-derive file (will be specific to the derivation method)
# Blackboard Tables
The \ref mod_bbpage "Blackboard" is used to store results from analysis modules.
## blackboard_artifacts
Stores artifacts associated with objects.
- **artifact_id** - Id of the artifact (assigned by the database)
- **obj_id** - Id of the associated object
- **artifact_type_id** - Id for the type of artifact (can be looked up in the blackboard_artifact_types table)
- **data_source_obj_id** - Id of the data source for the artifact
- **artifact_type_id** - Type of artifact (references artifact_type_id in blackboard_artifact_types)
- **review_status_id** - Review status (references review_status_id in review_statuses)
## blackboard_attributes
Stores name value pairs associated with an artifact. Only one of the value columns should be populated.
- **artifact_id** - Id of the associated artifact
- **artifact_type_id** - Artifact type of the associated artifact
- **source** - Source string, should be module name that created the entry
- **context** - Additional context string
- **attribute_type_id** - Id for the type of attribute (can be looked up in the blackboard_attribute_types)
- **value_type** - The type of the value (see org.sleuthkit.datamodel.BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE)
- **value_byte** - A blob of binary data (should be NULL unless the value type is byte)
- **value_text** - A string of text (should be NULL unless the value type is string)
- **value_int32** - An integer (should be NULL unless the value type is int)
- **value_int64** - A long integer / timestamp (should be NULL unless the value type is long)
- **value_double** - A double (should be NULL unless the value type is double)
## blackboard_artifact_types
Types of artifacts
- **artifact_type_id** - Id for the type (this is used by the blackboard_artifacts table)
- **type_name** - A string identifier for the type (unique)
- **display_name** - A display name for the type (not unique, should be human readable)
## blackboard_attribute_types
Types of attribute
- **attribute_type_id** - Id for the type (this is used by the blackboard_attributes table)
- **type_name** - A string identifier for the type (unique)
- **display_name** - A display name for the type (not unique, should be human readable)
- **value_type** - Expected type of data for the attribute type (see blackboard_attributes)
## review_statuses
Review status of an artifact. Should mirror the org.sleuthkit.datamodel.BlackboardArtifact.ReviewStatus enum.
- **review_status_id** - Id of the status
- **review_status_name** - Internal name of the status
- **display_name** - Display name (should be human readable)
# Communication Accounts
Stores data related to communications between two parties. It is highly recommended to use
the org.sleuthkit.datamodel.CommunicationsManager API to create/access this type of data
(see the \ref mod_compage page).
## accounts
Stores communication accounts (email, phone number, etc.). Note that this does not include OS accounts.
- **account_id** - Id for the account within the scope of the database (i.e. Row Id) (used in the account_relationships table)
- **account_type_id** - The type of account (must match an account_type_id entry from the account_types table)
- **account_unique_identifier** - The phone number/email/other identifier associated with the account that is unique within the Account Type
## account_types
Types of accounts and service providers (Phone, email, Twitter, Facebook, etc.)
- **account_type_id** - Id for the type (this is used by the accounts table)
- **type_name** - A string identifier for the type (unique)
- **display_name** - A display name for the type (not unique, should be human readable)
## account_relationships
Stores non-directional relationships between two accounts if they communicated or had references to each other (such as contact book)
- **relationship_id** - Id for the relationship
- **account1_id** - Id of the first participant (from account_id column in accounts table)
- **account2_id** - Id of the second participant (from account_id column in accounts table)
- **relationship_source_obj_id** - Id of the artifact this relationship was derived from (artifact_id column from the blackboard_artifacts)
- **date_time** - Time the communication took place, stored in number of seconds since Jan 1, 1970 UTC (NULL if unknown)
- **relationship_type** - The type of relationship (as org.sleuthkit.datamodel.Relationship.Type)
- **data_source_obj_id** - Id of the data source this relationship came from (from obj_id in data_source_info)
# Timeline
Stores data used to populate various timelines. Two tables are used to reduce data duplication. It is highly recommended to use
the org.sleuthkit.datamodel.TimelineManager API to create/access this type of data.
## tsk_event_types
Stores the types for events. The super_type_id column is used to arrange the types into a tree.
- **event_type_id** - Id for the type
- **display_name** - Display name for the type (unique, should be human readable)
- **super_type_id** - Parent type for the type (used for building heirarchy; references the event_type_id in this table)
## tsk_event_descriptions
Stores descriptions of an event. This table exists to reduce duplicate data that is common to events. For example, a file will have only one row in tsk_event_descriptions, but could have 4+ rows in tsk_events that all refer to the same description. Note that the combination of the full_description, content_obj_id, and artifact_id columns must be unique.
- **event_description_id** - Id for the event description
- **full_description** - Full length description of the event (required). For example, the full file path including file name.
- **med_description** - Medium length description of the event (may be null). For example, a file may have only the first three folder names.
- **short_description** - Short length description of the event (may be null). For example, a file may have only its first folder name.
- **data_source_obj_id** - Object id of the data source for the event source (references obj_id column in data_source_info)
- **content_obj_id** - If the event is from a non-artifact, then this is the object id from that source. If the event is from an artifact, then this is the object id of the artifact's source. (references obj_id column in tsk_objects)
- **artifact_id** - If the event is from a non-artifact, this is null. If the event is from an artifact, then this is the id of the artifact (references artifact_id column in blackboard_artifacts) (may be null)
- **hash_hit** - 1 if the file associated with the event has a hash set hit, 0 otherwise
- **tagged** - 1 if the direct source of the event has been tagged, 0 otherwise
## tsk_events
Stores each event. A file, artifact, or other type of content can have several rows in this table. One for each time stamp.
- **event_id** - Id for the event
- **event_type_id** - Event type id (references event_type_id column in tsk_event_types)
- **event_description_id** - Event description id (references event_description_id column in tsk_event_descriptions)
- **time** - Time the event occurred, in seconds from the UNIX epoch
# Examiners and Reports
## tsk_examiners
Encapsulates the concept of an examiner associated with a case.
- **examiner_id** - Id for the examiner
- **login_name** - Login name for the examiner (must be unique)
- **display_name** - Display name for the examiner (may be null)
## reports
Stores information on generated reports.
- **obj_id** - Id of the report
- **path** - Full path to the report (including file name)
- **crtime** - Time the report was created, in seconds from the UNIX epoch
- **src_module_name** - Name of the module that created the report
- **report_name** - Name of the report (can be empty string)
# Tags
## tag_names
Defines what tag names the user has created and can therefore be applied.
- **tag_name_id** - Unique ID for each tag name
- **display_name** - Display name of tag
- **description** - Description (can be empty string)
- **color** - Color choice for tag (can be empty string)
- **knownStatus** - Stores whether a tag is notable/bad (as org.sleuthkit.datamodel.TskData.FileKnown enum)
- **tag_set_id** - Id of the tag set the tag name belongs to (references tag_set_id in tsk_tag_sets, may be null)
- **rank** - Used to order the tag names for a given tag set for display purposes
## tsk_tag_sets
Used to group entries from the tag_names table. An object can have only one tag from a tag set at a time.
- **tag_set_id** - Id of the tag set
- **name** - Name of the tag set (unique, should be human readable)
## content_tags
One row for each file tagged.
- **tag_id** - unique ID
- **obj_id** - object id of Content that has been tagged
- **tag_name_id** - Tag name that was used
- **comment** - optional comment
- **begin_byte_offset** - optional byte offset into file that was tagged
- **end_byte_offset** - optional byte ending offset into file that was tagged
- **examiner_id** - Examiner that tagged the artifact (references examiner_id in tsk_examiners)
## blackboard_artifact_tags
One row for each artifact that is tagged.
- **tag_id** - unique ID
- **artifact_id** - Artifact ID of artifact that was tagged
- **tag_name_id** - Tag name that was used
- **comment** - Optional comment
- **examiner_id** - Examiner that tagged the artifact (references examiner_id in tsk_examiners)
# Ingest Module Status
These tables keep track in Autopsy which modules were run on the data sources.
## ingest_module_types
Defines the types of ingest modules supported. Must exactly match the names and ordering in the org.sleuthkit.datamodel.IngestModuleInfo.IngestModuleType enum.
- **type_id** - Id for the ingest module type
- **type_name** - Internal name for the ingest module type
## ingest_modules
Defines which modules were installed and run on at least one data source. One row for each module.
- **ingest_module_id** - Id of the ingest module
- **display_name** - Display name for the ingest module (should be human readable)
- **unique_name** - Unique name for the ingest module
- **type_id** - Type of ingest module (references type_id from ingest_module_types)
- **version** - Version of the ingest module
## ingest_job_status_types
Defines the status options for ingest jobs. Must match the names and ordering in the org.sleuthkit.datamodel.IngestJobInfo.IngestJobStatusType enum.
- **type_id** - Id for the ingest job status type
- **type_name** - Internal name for the ingest job status type
## ingest_jobs
One row is created each time ingest is started, which is a set of modules in a pipeline.
- **ingest_job_id** - Id of the ingest job
- **obj_id** - Id of the data source ingest is being run on
- **host_name** - Name of the host that is running the ingest job
- **start_date_time** - Time the ingest job started (stored in number of milliseconds since Jan 1, 1970 UTC)
- **end_date_time** - Time the ingest job finished (stored in number of milliseconds since Jan 1, 1970 UTC)
- **status_id** - Ingest job status (references type_id from ingest_job_status_types)
- **settings_dir** - Directory of the job's settings (may be an empty string)
## ingest_job_modules
Defines the order of the modules in a given pipeline (i.e. ingest_job).
- **ingest_job_id** - Id for the ingest job (references ingest_job_id in ingest_jobs)
- **ingest_module_id** - Id of the ingest module (references ingest_module_id in ingest_modules)
- **pipeline_position** - Order that the ingest module was run
*/
/*! \page db_schema_9_0_page TSK & Autopsy Database Schema (Schema version 9.0)
Schema 9.0 is not associated with a released version of Autopsy and is almost the same as schema 9.1.
Please see the \ref db_schema_9_1_page page for all changes from schema 8.6 to schema 9.0.
*/
/*! \page db_schema_9_1_page TSK & Autopsy Database Schema (Schema version 9.1)
[TOC]
# Introduction
This page outlines version 9.1 the database that is used by The Sleuth Kit and Autopsy. The goal of this page is to provide short descriptions for each table and column and not focus on foreign key requirements, etc. If you want that level of detail, then refer to the actual schema in addition to this.
Each Autopsy release is associated with a schema version with a major and minor version number. If a case with an older schema version is opened in a new version of Autopsy, the case will automatically be updated to the current schema. Going the other direction (opening a case that was created with a newer version of Autopsy), two things may happen:
- If the case database has the same major number as the version of Autopsy being used, the case should generally be able to be opened and used.
- If the case database has a higher major number than the version of Autopsy being used, an error will be displayed when attempting to open the case.
You can find more of a description in the org.sleuthkit.datamodel.CaseDbSchemaVersionNumber class description.
You can find a basic graphic of some of the table relationships <a href="https://docs.google.com/drawings/d/1omR_uUAp1fQt720oJ-kk8C48BXmVa3PNjPZCDdT0Tb4/edit?usp#sharing">here</a>
Some general notes on this schema:
- Nearly every type of data is assigned a unique ID, called the Object ID
- The objects form a hierarchy, that shows where data came from. A child comes from its parent.
- For example, disk images are the root, with a volume system below it, then a file system, and then files and directories.
- This schema has been designed to store data beyond the file system data that The Sleuth Kit supports. It can store carved files, a folder full of local files, etc.
- The Blackboard is used to store artifacts, which contain attributes (name/value pairs). Artifacts are used to store data types that do not have more formal tables. Module writers can make whatever artifact types they want. See \ref mod_bbpage for more details.
- The Sleuth Kit will make virtual files to span the unallocated space. They will have a naming format of 'Unalloc_[PARENT-OBJECT-ID]_[BYTE-START]_[BYTE-END]'.
# Schema Information
This was a big change. Tables were added to support analysis results, OS accounts, hosts and person structure of data sources, and host addresses (IPs, DNS, etc.). The major component of the version number has been incremented because there are new org.sleuthkit.datamodel.TskData.ObjectType enum types (OsAccount and HostAddress). More information on how to use these new objects can be found on the \ref mod_dspage and \ref mod_os_accounts_page pages.
<ul>
<li><b>Autopsy versions: </b> Autopsy 4.19
<li><b>Changes from version 8.6:</b>
<ul>
<li> New columns:
<ul>
<li>host_id, added_date_time, acquisition_tool_settings, acquisition_tool_name, acquisition_tool_version in data_source_info
<li>category_type in artifact_types
<li>owner_uid, os_account_obj_id in tsk_files
</ul>
<li> New tables:
<ul>
<li>tsk_aggregate_score
<li>tsk_analysis_results
<li>tsk_data_artifacts
<li>tsk_file_attributes
<li>tsk_hosts
<li>tsk_host_addresses
<li>tsk_host_address_dns_ip_map
<li>tsk_host_address_usage
<li>tsk_os_accounts
<li>tsk_os_account_attributes
<li>tsk_os_account_instances
<li>tsk_os_account_realms
<li>tsk_persons
</ul>
</ul>
</ul>
# General Information Tables
## tsk_db_info
Metadata about the database.
- **schema_ver** - Major version number of the current database schema
- **tsk_ver** - Version of TSK used to create database
- **schema_minor_version** - Minor version number of the current database schema
## tsk_db_info_extended
Name & Value pair table to store any information about the database. For example, which schema it was created with. etc.
- **name** - Any string name
- **value** - Any string value
# Object Tables
## tsk_objects
Every object (image, volume system, file, etc.) has an entry in this table. This table allows you to find the parent of a given object and allows objects to be tagged and have children. This table provides items with a unique object id. The details of the object are in other tables.
- **obj_id** - Unique id
- **par_obj_id** - The object id of the parent object (NULL for root objects). The parent of a volume system is an image, the parent of a directory is a directory or filesystem, the parent of a filesystem is a volume or an image, etc.
- **type** - Object type (as org.sleuthkit.datamodel.TskData.ObjectType enum)
# Hosts / Persons
Stores data related to hosts and persons, which can help organize data sources.
Persons are optional, but hosts are required. When persons are defined, they are associated with one or more hosts.
The person and host tree is in parallel to the data source and file tree.
- A host is associated with a person
- A data source is associated with a host (but not a child of it)
## tsk_persons
Stores persons for the case. A peron is someone who owns or used a data source in the case.
- **id** - Id of the person
- **name** - Name of the person (should be human readable)
## tsk_hosts
Stores hosts that have a data source in the case. Each data source must be associated with a host. These are NOT created for a reference to an external host (such as a web domain).
- **id** - Id of the host
- **name** - Name of the host (should be human readable)
- **db_status** - Status of the host (active/merged/deleted as org.sleuthkit.datamodel.Host.HostDbStatus)
- **person_id** - Optional id of associated person
- **merged_into** - Stores the host ID that this host was merged into
# Data Source / Device, Disk Image Tables
A data source is the top-level container added to the database. All files and artifacts must be children of a data source. There are different kinds of data sources and some will also have data in tsk_image_info and others will not. The data sources are the root of the object hierarchy.
## data_source_info
Contains information about a data source, which could be an image or logical folder. The device_id concept allows multiple data source to be grouped together (if they share the same ID).
The code will go to both tsk_image_info (for disk images) and tsk_files (for other types) for additional information.
- **obj_id** - Id of image/data source in tsk_objects
- **device_id** - Unique ID (GUID) for the device that contains the data source
- **time_zone** - Timezone that the data source was originally located in
- **acquisition_details** - Notes on the acquisition of the data source
- **added_date_time** - Timestamp of when the data source was added
- **acquisition_tool_name** - Name of the tool used to acquire the image
- **acquisition_tool_settings** - Specific settings used by the tool to acquire the image
- **acquisition_tool_version** - Version of the acquisition tool
- **host_id** - Host associated with this image (must be set)
## tsk_image_info
Contains additional data source information if it is a disk image. These rows use the same object ID as data_source_info.
- **obj_id** - Id of image in tsk_objects
- **type** - Type of disk image format (as org.sleuthkit.datamodel.TskData.TSK_IMG_TYPE_ENUM)
- **ssize** - Sector size of device in bytes
- **tzone** - Timezone where image is from (the same format that TSK tools want as input)
- **size** - Size of the original image (in bytes)
- **md5** - MD5 hash of the image (for compressed data such as E01, the hashes are of the decompressed image, not the E01 itself)
- **sha1** - SHA-1 hash of the image
- **sha256** - SHA-256 hash of the image
- **display_name** - Display name of the image
## tsk_image_names
Stores path(s) to file(s) on disk that make up an image set.
- **obj_id** - Id of image in tsk_objects
- **name** - Path to location of image file on disk
- **sequence** - Position in sequence of image parts
# Volume System Tables
The parent of a volume system is often a disk image / data source.
## tsk_vs_info
Contains one row for every volume system found in the images.
- **obj_id** - Id of volume system in tsk_objects
- **vs_type** - Type of volume system / media management (as org.sleuthkit.datamodel.TskData.TSK_VS_TYPE_ENUM)
- **img_offset** - Byte offset where VS starts in disk image
- **block_size** - Size of blocks in bytes
## tsk_vs_parts
Contains one row for every volume / partition in the images.
- **obj_id** - Id of volume in tsk_objects
- **addr** - Address of the partition
- **start** - Sector offset of start of partition
- **length** - Number of sectors in partition
- **desc** - Description of partition (volume system type-specific)
- **flags** - Flags for partition (as org.sleuthkit.datamodel.TskData.TSK_VS_PART_FLAG_ENUM)
## tsk_pool_info
Contains information about pools (for APFS, logical disk management, etc.)
- **obj_id** - Id of pool in tsk_objects
- **pool_type** - Type of pool (as org.sleuthkit.datamodel.TskData.TSK_POOL_TYPE_ENUM)
# File System Tables
The parent of a file system is often either a partition or a disk image. These tables form together to create a parent / child structure of a root folder, subfolders, and files.
## tsk_fs_info
Contains one for for every file system in the images.
- **obj_id** - Id of filesystem in tsk_objects
- **data_source_obj_id** - Id of the data source for the file system
- **img_offset** - Byte offset that filesystem starts at
- **fs_type** - Type of file system (as org.sleuthkit.datamodel.TskData.TSK_FS_TYPE_ENUM)
- **block_size** - Size of each block (in bytes)
- **block_count** - Number of blocks in filesystem
- **root_inum** - Metadata address of root directory
- **first_inum** - First valid metadata address
- **last_inum** - Last valid metadata address
- **display_name** - Display name of file system (could be volume label)
## tsk_files
Contains one for for every file found in the images. Has the basic metadata for the file.
- **obj_id** - Id of file in tsk_objects
- **fs_obj_id** - Id of filesystem in tsk_objects (NULL if file is not located in a file system -- carved in unpartitioned space, etc.)
- **data_source_obj_id** - Id of the data source for the file
- **attr_type** - Type of attribute (as org.sleuthkit.datamodel.TskData.TSK_FS_ATTR_TYPE_ENUM)
- **attr_id** - Id of attribute
- **name** - Name of attribute. Will be NULL if attribute doesn't have a name. Must not have any slashes in it.
- **meta_addr** - Address of the metadata structure that the name points to
- **meta_seq** - Sequence of the metadata address
- **type** - Type of file: filesystem, carved, etc. (as org.sleuthkit.datamodel.TskData.TSK_DB_FILES_TYPE_ENUM enum)
- **has_layout** - True if file has an entry in tsk_file_layout
- **has_path** - True if file has an entry in tsk_files_path
- **dir_type** - File type information: directory, file, etc. (as org.sleuthkit.datamodel.TskData.TSK_FS_NAME_TYPE_ENUM)
- **meta_type** - File type (as org.sleuthkit.datamodel.TskData.TSK_FS_META_TYPE_ENUM)
- **dir_flags** - Flags that describe allocation status etc. (as org.sleuthkit.datamodel.TskData.TSK_FS_NAME_FLAG_ENUM)
- **meta_flags** - Flags for the file for its allocation status etc. (as org.sleuthkit.datamodel.TskData.TSK_FS_META_FLAG_ENUM)
- **size** - File size in bytes
- **ctime** - Last file / metadata status change time (stored in number of seconds since Jan 1, 1970 UTC)
- **crtime** - Created time
- **atime** - Last file content accessed time
- **mtime** - Last file content modification time
- **mode** - Unix-style permissions (as org.sleuthkit.datamodel.TskData.TSK_FS_META_MODE_ENUM)
- **uid** - Owner id
- **gid** - Group id
- **md5** - MD5 hash of file contents
- **sha256** - SHA-256 hash of file contents
- **known** - Known status of file (as org.sleuthkit.datamodel.TskData.FileKnown)
- **parent_path** - Full path of parent folder. Must begin and end with a '/' (Note that a single '/' is valid)
- **mime_type** - MIME type of the file content, if it has been detected.
- **extension** - File extension
- **owner_uid** - Unique ID of the owner (SID in Windows)
- **os_account_obj_id** - ID of optional associated OS account
## tsk_file_layout
Stores the layout of a file within the image. A file will have one or more rows in this table depending on how fragmented it was. All file types use this table (file system, carved, unallocated blocks, etc.).
- **obj_id** - Id of file in tsk_objects
- **sequence** - Position of the run in the file (0-based and the obj_id and sequence pair will be unique in the table)
- **byte_start** - Byte offset of fragment relative to the start of the image file
- **byte_len** - Length of fragment in bytes
## tsk_files_path
If a "locally-stored" file has been imported into the database for analysis, then this table stores its path. Used for derived files and other files that are not directly in the image file.
- **obj_id** - Id of file in tsk_objects
- **path** - Path to where the file is locally stored in a file system
- **encoding_type** - Method used to store the file on the disk
## file_encoding_types
Methods that can be used to store files on local disks to prevent them from being quarantined by antivirus
- **encoding_type** - ID of method used to store data. See org.sleuthkit.datamodel.TskData.EncodingType enum
- **name** - Display name of technique
## tsk_file_attributes
Stores extended attributes for a particular file that do not have a column in tsk_files. Custom BlackboardAttribute types can be defined.
- **id** - Id of the attribute
- **obj_id** - File this attribute is associated with (references tsk_files)
- **attribute_type_id** - Id for the type of attribute (can be looked up in the blackboard_attribute_types)
- **value_type** - The type of the value (see org.sleuthkit.datamodel.BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE)
- **value_byte** - A blob of binary data (should be NULL unless the value type is byte)
- **value_text** - A string of text (should be NULL unless the value type is string)
- **value_int32** - An integer (should be NULL unless the value type is int)
- **value_int64** - A long integer / timestamp (should be NULL unless the value type is long)
- **value_double** - A double (should be NULL unless the value type is double)
## tsk_files_derived_method
Derived files are those that result from analyzing another file. For example, files that are extracted from a ZIP file will be considered derived. This table keeps track of the derivation techniques that were used to make the derived files.
NOTE: This table is not used in any code.
- **derived_id** - Unique id for the derivation method.
- **tool_name** - Name of derivation method/tool
- **tool_version** - Version of tool used in derivation method
- **other** - Other details
## tsk_files_derived
Each derived file has a row that captures the information needed to re-derive it
NOTE: This table is not used in any code.
- **obj_id** - Id of file in tsk_objects
- **derived_id** - Id of derivation method in tsk_files_derived_method
- **rederive** - Details needed to re-derive file (will be specific to the derivation method)
# Blackboard Tables
The \ref mod_bbpage "Blackboard" is used to store results and derived data from analysis modules.
## blackboard_artifacts
Stores artifacts associated with objects.
- **artifact_id** - Id of the artifact (assigned by the database)
- **obj_id** - Id of the associated object
- **artifact_obj_id** - Object id of the artifact
- **artifact_type_id** - Id for the type of artifact (can be looked up in the blackboard_artifact_types table)
- **data_source_obj_id** - Id of the data source for the artifact
- **artifact_type_id** - Type of artifact (references artifact_type_id in blackboard_artifact_types)
- **review_status_id** - Review status (references review_status_id in review_statuses)
## tsk_analysis_results
Additional information for artifacts that are analysis results
- **artifact_obj_id** - Object id of the associated artifact (artifact_obj_id column in blackboard_artifacts)
- **significance** - Significance to show if the result shows the object is relevant (as org.sleuthkit.datamodel.Score.Significance enum)
- **method_category** - Category of the analysis method used (as org.sleuthkit.datamodel.Score.MethodCategory enum)
- **conclusion** - Optional, text description of the conclusion of the analysis method.
- **configuration** - Otional, text description of the analysis method configuration (such as what hash set or keyword list was used)
- **justification** - Optional, text description of justification of the conclusion and significance.
- **ignore_score** - True (1) if score should be ignored when calculating aggregate score, false (0) otherwise. This allows users to ignore a false positive.
## tsk_data_artifacts
Additional information for artifacts that store extracted data.
- **artifact_obj_id** - Object id of the associated artifact (artifact_obj_id column in blackboard_artifacts)
- **os_account_obj_id** - Object id of the associated OS account
## blackboard_artifact_types
Types of artifacts
- **artifact_type_id** - Id for the type (this is used by the blackboard_artifacts table)
- **type_name** - A string identifier for the type (unique)
- **display_name** - A display name for the type (not unique, should be human readable)
- **category_type** - Indicates whether this is a data artifact or an analysis result
## blackboard_attributes
Stores name value pairs associated with an artifact. Only one of the value columns should be populated.
- **artifact_id** - Id of the associated artifact
- **artifact_type_id** - Artifact type of the associated artifact
- **source** - Source string, should be module name that created the entry
- **context** - Additional context string
- **attribute_type_id** - Id for the type of attribute (can be looked up in the blackboard_attribute_types)
- **value_type** - The type of the value (see org.sleuthkit.datamodel.BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE)
- **value_byte** - A blob of binary data (should be NULL unless the value type is byte)
- **value_text** - A string of text (should be NULL unless the value type is string)
- **value_int32** - An integer (should be NULL unless the value type is int)
- **value_int64** - A long integer / timestamp (should be NULL unless the value type is long)
- **value_double** - A double (should be NULL unless the value type is double)
## blackboard_attribute_types
Types of attribute
- **attribute_type_id** - Id for the type (this is used by the blackboard_attributes table)
- **type_name** - A string identifier for the type (unique)
- **display_name** - A display name for the type (not unique, should be human readable)
- **value_type** - Expected type of data for the attribute type (see blackboard_attributes)
## review_statuses
Review status of an artifact. Should mirror the org.sleuthkit.datamodel.BlackboardArtifact.ReviewStatus enum.
- **review_status_id** - Id of the status
- **review_status_name** - Internal name of the status
- **display_name** - Display name (should be human readable)
## tsk_aggregate_score
Stores the score of an object that is a combination of the various analysis result scores
- **obj_id** - Id of the object that corresponds to this score
- **data_source_obj_id** - Id of the data source the object belongs to
- **significance** - Significance (as org.sleuthkit.datamodel.Score.Significance enum)
- **method_category** - Category of the method used (as org.sleuthkit.datamodel.Score.MethodCategory enum)
# Host Addresses
Host addresses are various forms of identifiers assigned to a computer, such as host names or MAC addresses. These tables store data that is also stored in the data artifacts, but these tables allow for correlation and scoring of specific hosts.
## tsk_host_addresses
One entry is created in this table for each host address found in the data source. Examples include domain names (www.sleuthkit.org), IP addresses, and BlueTooth MAC addresses.
- **id** - Id of the host address
- **address_type** - Type of address (as org.sleuthkit.datamodel.HostAddress.HostAddressType enum)
- **address** - Address (must be unique within the scope of address_type).
## tsk_host_address_dns_ip_map
Stores data if host names and IP addresses were resolved between each other.
- **id** - Id of the mapping
- **dns_address_id** - Id of the DNS address in tsk_host_addresses
- **ip_address_id** - Id of the IP address in tsk_host_addresses
- **source_obj_id** - Id of the object used to determine this mapping (references tsk_objects)
- **time** - Timestamp when this mapping was recorded
## tsk_host_address_usage
Tracks which artifacts and files had a reference to a given host address. This is used to show what other artifacts used the same address.
- **id** - Id of the usage
- **addr_obj_id** - Id of the host address
- **obj_id** - Id of the object that had a reference/usage to the address (references tsk_objects)
- **data_source_obj_id** - Id of the data source associated with the usage
# Operating System Accounts
Stores data related to operating system accounts. Communication-related accounts (such as email or social media) are stored in other tables (see Communication Acccounts below).
## tsk_os_account_realms
Every OS Account must belong to a realm, which defines the scope of the account. Realms can be local to a given computer or domain-based.
- **realm_name** - Display bame of the realm (realm_name or realm_addr must be set)
- **realm_addr** - Address/ID of the realm (realm_name or realm_addr must be set)
- **realm_signature** - Used internally for unique clause. realm_addr if it is set. Otherwise, realm_name.
- **scope_host_id** - Optional host that this realm is scoped to. By default, realms are scoped to a given host.
- **scope_confidence** - Confidence of the scope of the realm (as org.sleuthkit.datamodel.OsAccountRealm.ScopeConfidence enum)
- **db_status** - Status of this realm in the database (as org.sleuthkit.datamodel.OsAccountRealm.RealmDbStatus enum)
- **merged_into** - For merged realms, set to the id of the realm they were merged in to.
## tsk_os_accounts
Stores operating system accounts
- **os_account_obj_id** - Id of the OS account
- **realm_id** - Id of the associated realm (references tsk_os_account_realms)
- **login_name** - Login name (login name or addr must be present)
- **addr** - Address/ID of account (login name or addr must be present)
- **signature** - Used internally for unique clause
- **full_name** - Full name
- **status** - Status of the account (as org.sleuthkit.datamodel.OsAccount.OsAccountStatus enum)
- **type** - Type of account (as org.sleuthkit.datamodel.OsAccount.OsAccountType enum)
- **created_date** - Timestamp of account creation
- **db_status** - Status of this account in the database (active/merged/deleted)
- **merged_into** - For merged accounts, set to the id of the account they were merged in to.
## tsk_os_account_attributes
Stores additional attributes for an OS account. Similar to blackboard_attributes. Attributes can either be specific to a host or domain-scoped.
- **id** - Id of the attribute
- **os_account_obj_id** - Id of the associated OS account
- **host_id** - Host Id if the attribute is scoped to the host. NULL if the attribute is domain-scoped.
- **source_obj_id** - Optional object id of where the attribute data was derived from (such as a registry hive) (references tsk_objects)
- **attribute_type_id** - Type of attribute (see org.sleuthkit.datamodel.BlackboardAttribute.BlackboardAttribute.Type)
- **value_type** - The type of the value (see org.sleuthkit.datamodel.BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE)
- **value_byte** - A blob of binary data (should be NULL unless the value type is byte)
- **value_text** - A string of text (should be NULL unless the value type is string)
- **value_int32** - An integer (should be NULL unless the value type is int)
- **value_int64** - A long integer / timestamp (should be NULL unless the value type is long)
- **value_double** - A double (should be NULL unless the value type is double)
## tsk_os_account_instances
Records that an OS account is associated with a specific data source. For example, the account logged in, accessed data, etc.
- **id** - Id of the OS account instance
- **os_account_obj_id** - Id of the OS account that was referenced
- **data_source_obj_id** - Id of the data source
- **instance_type** - Type of instance (as org.sleuthkit.datamodel.OsAccountInstance.OsAccountInstanceType enum)
# Communication Accounts
Stores data related to communications between two parties. It is highly recommended to use
the org.sleuthkit.datamodel.CommunicationsManager API to create/access this type of data
(see the \ref mod_compage page).
## accounts
Stores communication accounts (email, phone number, etc.). Note that this does not include OS accounts.
- **account_id** - Id for the account within the scope of the database (i.e. Row Id) (used in the account_relationships table)
- **account_type_id** - The type of account (must match an account_type_id entry from the account_types table)
- **account_unique_identifier** - The phone number/email/other identifier associated with the account that is unique within the Account Type
## account_types
Types of accounts and service providers (Phone, email, Twitter, Facebook, etc.)
- **account_type_id** - Id for the type (this is used by the accounts table)
- **type_name** - A string identifier for the type (unique)
- **display_name** - A display name for the type (not unique, should be human readable)
## account_relationships
Stores non-directional relationships between two accounts if they communicated or had references to each other (such as contact book)
- **relationship_id** - Id for the relationship
- **account1_id** - Id of the first participant (from account_id column in accounts table)
- **account2_id** - Id of the second participant (from account_id column in accounts table)
- **relationship_source_obj_id** - Id of the artifact this relationship was derived from (artifact_id column from the blackboard_artifacts)
- **date_time** - Time the communication took place, stored in number of seconds since Jan 1, 1970 UTC (NULL if unknown)
- **relationship_type** - The type of relationship (as org.sleuthkit.datamodel.Relationship.Type)
- **data_source_obj_id** - Id of the data source this relationship came from (from obj_id in data_source_info)
# Timeline
Stores data used to populate various timelines. Two tables are used to reduce data duplication. It is highly recommended to use
the org.sleuthkit.datamodel.TimelineManager API to create/access this type of data.
## tsk_event_types
Stores the types for events. The super_type_id column is used to arrange the types into a tree.
- **event_type_id** - Id for the type
- **display_name** - Display name for the type (unique, should be human readable)
- **super_type_id** - Parent type for the type (used for building heirarchy; references the event_type_id in this table)
## tsk_event_descriptions
Stores descriptions of an event. This table exists to reduce duplicate data that is common to events. For example, a file will have only one row in tsk_event_descriptions, but could have 4+ rows in tsk_events that all refer to the same description. Note that the combination of the full_description, content_obj_id, and artifact_id columns must be unique.
- **event_description_id** - Id for the event description
- **full_description** - Full length description of the event (required). For example, the full file path including file name.
- **med_description** - Medium length description of the event (may be null). For example, a file may have only the first three folder names.
- **short_description** - Short length description of the event (may be null). For example, a file may have only its first folder name.
- **data_source_obj_id** - Object id of the data source for the event source (references obj_id column in data_source_info)
- **content_obj_id** - If the event is from a non-artifact, then this is the object id from that source. If the event is from an artifact, then this is the object id of the artifact's source. (references obj_id column in tsk_objects)
- **artifact_id** - If the event is from a non-artifact, this is null. If the event is from an artifact, then this is the id of the artifact (references artifact_id column in blackboard_artifacts) (may be null)
- **hash_hit** - 1 if the file associated with the event has a hash set hit, 0 otherwise
- **tagged** - 1 if the direct source of the event has been tagged, 0 otherwise
## tsk_events
Stores each event. A file, artifact, or other type of content can have several rows in this table. One for each time stamp.
- **event_id** - Id for the event
- **event_type_id** - Event type id (references event_type_id column in tsk_event_types)
- **event_description_id** - Event description id (references event_description_id column in tsk_event_descriptions)
- **time** - Time the event occurred, in seconds from the UNIX epoch
# Examiners and Reports
## tsk_examiners
Encapsulates the concept of an examiner associated with a case.
- **examiner_id** - Id for the examiner
- **login_name** - Login name for the examiner (must be unique)
- **display_name** - Display name for the examiner (may be null)
## reports
Stores information on generated reports.
- **obj_id** - Id of the report
- **path** - Full path to the report (including file name)
- **crtime** - Time the report was created, in seconds from the UNIX epoch
- **src_module_name** - Name of the module that created the report
- **report_name** - Name of the report (can be empty string)
# Tags
## tag_names
Defines what tag names the user has created and can therefore be applied.
- **tag_name_id** - Unique ID for each tag name
- **display_name** - Display name of tag
- **description** - Description (can be empty string)
- **color** - Color choice for tag (can be empty string)
- **knownStatus** - Stores whether a tag is notable/bad (as org.sleuthkit.datamodel.TskData.FileKnown enum)
- **tag_set_id** - Id of the tag set the tag name belongs to (references tag_set_id in tsk_tag_sets, may be null)
- **rank** - Used to order the tag names for a given tag set for display purposes
## tsk_tag_sets
Used to group entries from the tag_names table. An object can have only one tag from a tag set at a time.
- **tag_set_id** - Id of the tag set
- **name** - Name of the tag set (unique, should be human readable)
## content_tags
One row for each file tagged.
- **tag_id** - unique ID
- **obj_id** - object id of Content that has been tagged
- **tag_name_id** - Tag name that was used
- **comment** - optional comment
- **begin_byte_offset** - optional byte offset into file that was tagged
- **end_byte_offset** - optional byte ending offset into file that was tagged
- **examiner_id** - Examiner that tagged the artifact (references examiner_id in tsk_examiners)
## blackboard_artifact_tags
One row for each artifact that is tagged.
- **tag_id** - unique ID
- **artifact_id** - Artifact ID of artifact that was tagged
- **tag_name_id** - Tag name that was used
- **comment** - Optional comment
- **examiner_id** - Examiner that tagged the artifact (references examiner_id in tsk_examiners)
# Ingest Module Status
These tables keep track in Autopsy which modules were run on the data sources.
## ingest_module_types
Defines the types of ingest modules supported. Must exactly match the names and ordering in the org.sleuthkit.datamodel.IngestModuleInfo.IngestModuleType enum.
- **type_id** - Id for the ingest module type
- **type_name** - Internal name for the ingest module type
## ingest_modules
Defines which modules were installed and run on at least one data source. One row for each module.
- **ingest_module_id** - Id of the ingest module
- **display_name** - Display name for the ingest module (should be human readable)
- **unique_name** - Unique name for the ingest module
- **type_id** - Type of ingest module (references type_id from ingest_module_types)
- **version** - Version of the ingest module
## ingest_job_status_types
Defines the status options for ingest jobs. Must match the names and ordering in the org.sleuthkit.datamodel.IngestJobInfo.IngestJobStatusType enum.
- **type_id** - Id for the ingest job status type
- **type_name** - Internal name for the ingest job status type
## ingest_jobs
One row is created each time ingest is started, which is a set of modules in a pipeline.
- **ingest_job_id** - Id of the ingest job
- **obj_id** - Id of the data source ingest is being run on
- **host_name** - Name of the host that is running the ingest job
- **start_date_time** - Time the ingest job started (stored in number of milliseconds since Jan 1, 1970 UTC)
- **end_date_time** - Time the ingest job finished (stored in number of milliseconds since Jan 1, 1970 UTC)
- **status_id** - Ingest job status (references type_id from ingest_job_status_types)
- **settings_dir** - Directory of the job's settings (may be an empty string)
## ingest_job_modules
Defines the order of the modules in a given pipeline (i.e. ingest_job).
- **ingest_job_id** - Id for the ingest job (references ingest_job_id in ingest_jobs)
- **ingest_module_id** - Id of the ingest module (references ingest_module_id in ingest_modules)
- **pipeline_position** - Order that the ingest module was run
*/
/*! \page db_schema_page TSK & Autopsy Database Schemas
This page contians links to the documention for selected versions of the TSK & Autopsy database schema.
- Current Schema
- \subpage db_schema_9_1_page
- Older Schemas
- \subpage db_schema_9_0_page
- \subpage db_schema_8_6_page
- <a href="https://wiki.sleuthkit.org/index.php?title=Database_v7.2_Schema">Schema version 7.2</a>
- <a href="https://wiki.sleuthkit.org/index.php?title=SQLite_Database_v6_Schema">Schema version 6</a>
- <a href="https://wiki.sleuthkit.org/index.php?title=SQLite_Database_v3_Schema">Schema version 3</a>
- <a href="https://wiki.sleuthkit.org/index.php?title=SQLite_Database_v2_Schema">Schema version 2</a>
*/
\ No newline at end of file
<ivy-module version="2.0">
<info organisation="org.sleuthkit" module="datamodel"/>
<dependencies>
<dependency org="joda-time" name="joda-time" rev="2.4" />
<dependency org="com.google.guava" name="guava" rev="31.1-jre"/>
<dependency org="org.apache.commons" name="commons-lang3" rev="3.0"/>
<dependency org="commons-validator" name="commons-validator" rev="1.6"/>
<dependency org="com.google.code.gson" name="gson" rev="2.8.5"/>
<dependency org="junit" name="junit" rev="4.8.2"/>
<dependency org="com.googlecode.java-diff-utils" name="diffutils" rev="1.2.1"/>
<!-- NOTE: When SQLITE version is changed, also change the version number in
debian/sleuthkit-java.install so that it gets packaged correctly on Linux -->
<dependency org="org.xerial" name="sqlite-jdbc" rev="3.42.0.1"/>
<dependency org="org.postgresql" name="postgresql" rev="42.3.5" >
<artifact name="postgresql" type="jar" />
</dependency>
<dependency conf="default" org="com.mchange" name="c3p0" rev="0.9.5.5" />
<dependency conf="default" org="com.mchange" name="mchange-commons-java" rev="0.2.20"/>
<dependency org="com.zaxxer" name="SparseBitSet" rev="1.1" />
</dependencies>
</ivy-module>
<ivysettings>
<settings defaultResolver="default"/>
<resolvers>
<chain name="default">
<ibiblio name="central" root="https://repo1.maven.org/maven2" m2compatible="true"/>
<ibiblio name="ibiblio" m2compatible="true"/>
</chain>
</resolvers>
</ivysettings>
-kr -psl -nce -ip2 -nlp -nut
AM_CPPFLAGS = -I../../.. -I$(srcdir)/../../.. $(JNI_CPPFLAGS)
AM_CXXFLAGS += -Wno-unused-command-line-argument -Wno-overloaded-virtual
EXTRA_DIST = .indent.pro
lib_LTLIBRARIES = libtsk_jni.la
libtsk_jni_la_SOURCES = dataModel_SleuthkitJNI.cpp dataModel_SleuthkitJNI.h auto_db_java.h auto_db_java.cpp
libtsk_jni_la_LIBADD = ../../../tsk/libtsk.la
indent:
indent *.cpp *.h
clean-local:
-rm -f *.c~ *.h~
/*
** The Sleuth Kit
**
** Brian Carrier [carrier <at> sleuthkit [dot] org]
** Copyright (c) 2020 Brian Carrier. All Rights reserved
**
** This software is distributed under the Common Public License 1.0
**
*/
/**
* \file auto_db_java.cpp
* Contains code to populate database with volume and file system information from a specific image.
*/
#include "auto_db_java.h"
#include "jni.h"
#include "tsk/img/img_writer.h"
#if HAVE_LIBEWF
#include "tsk/img/ewf.h"
#include "tsk/img/tsk_img_i.h"
#endif
#include <string.h>
#include <algorithm>
#include <sstream>
using std::stringstream;
using std::for_each;
/**
*/
TskAutoDbJava::TskAutoDbJava()
{
m_curImgId = 0;
m_curVsId = 0;
m_curVolId = 0;
m_curFsId = 0;
m_curFileId = 0;
m_curUnallocDirId = 0;
m_curDirAddr = 0;
m_curDirPath = "";
m_vsFound = false;
m_volFound = false;
m_poolFound = false;
m_stopped = false;
m_foundStructure = false;
m_attributeAdded = false;
m_addFileSystems = true;
m_noFatFsOrphans = false;
m_addUnallocSpace = false;
m_minChunkSize = -1;
m_maxChunkSize = -1;
m_jniEnv = NULL;
tsk_init_lock(&m_curDirPathLock);
}
TskAutoDbJava::~TskAutoDbJava()
{
closeImage();
tsk_deinit_lock(&m_curDirPathLock);
}
/**
* Look up all callback method IDs
* @param jniEnv pointer to java environment this was called from
* @param jobj the TskCaseDbBridge object this was called from
*/
TSK_RETVAL_ENUM
TskAutoDbJava::initializeJni(JNIEnv * jniEnv, jobject jobj) {
m_jniEnv = jniEnv;
m_javaDbObj = m_jniEnv->NewGlobalRef(jobj);
jclass localCallbackClass = m_jniEnv->FindClass("org/sleuthkit/datamodel/TskCaseDbBridge");
if (localCallbackClass == NULL) {
return TSK_ERR;
}
m_callbackClass = (jclass)m_jniEnv->NewGlobalRef(localCallbackClass);
m_addImageMethodID = m_jniEnv->GetMethodID(m_callbackClass, "addImageInfo", "(IJLjava/lang/String;JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;)J");
if (m_addImageMethodID == NULL) {
return TSK_ERR;
}
m_addAcquisitionDetailsMethodID = m_jniEnv->GetMethodID(m_callbackClass, "addAcquisitionDetails", "(JLjava/lang/String;)V");
if (m_addAcquisitionDetailsMethodID == NULL) {
return TSK_ERR;
}
m_addVolumeSystemMethodID = m_jniEnv->GetMethodID(m_callbackClass, "addVsInfo", "(JIJJ)J");
if (m_addVolumeSystemMethodID == NULL) {
return TSK_ERR;
}
m_addVolumeMethodID = m_jniEnv->GetMethodID(m_callbackClass, "addVolume", "(JJJJLjava/lang/String;J)J");
if (m_addVolumeMethodID == NULL) {
return TSK_ERR;
}
m_addPoolMethodID = m_jniEnv->GetMethodID(m_callbackClass, "addPool", "(JI)J");
if (m_addPoolMethodID == NULL) {
return TSK_ERR;
}
m_addFileSystemMethodID = m_jniEnv->GetMethodID(m_callbackClass, "addFileSystem", "(JJIJJJJJ)J");
if (m_addFileSystemMethodID == NULL) {
return TSK_ERR;
}
m_addFileMethodID = m_jniEnv->GetMethodID(m_callbackClass, "addFile", "(JJJIIILjava/lang/String;JJIIIIJJJJJIIILjava/lang/String;Ljava/lang/String;JJJLjava/lang/String;)J");
if (m_addFileMethodID == NULL) {
return TSK_ERR;
}
m_addUnallocParentMethodID = m_jniEnv->GetMethodID(m_callbackClass, "addUnallocFsBlockFilesParent", "(JLjava/lang/String;)J");
if (m_addUnallocParentMethodID == NULL) {
return TSK_ERR;
}
m_addLayoutFileMethodID = m_jniEnv->GetMethodID(m_callbackClass, "addLayoutFile", "(JJJILjava/lang/String;J)J");
if (m_addLayoutFileMethodID == NULL) {
return TSK_ERR;
}
m_addLayoutFileRangeMethodID = m_jniEnv->GetMethodID(m_callbackClass, "addLayoutFileRange", "(JJJJ)J");
if (m_addLayoutFileRangeMethodID == NULL) {
return TSK_ERR;
}
return TSK_OK;
}
/**
* Cache a database object for later use. Should be called on image, volume system, volume,
* pool, and file system.
* @param objId The object ID of the new object
* @param parObjId The object ID of the new object's parent
* @param type The type of object
*/
void
TskAutoDbJava::saveObjectInfo(int64_t objId, int64_t parObjId, TSK_DB_OBJECT_TYPE_ENUM type) {
TSK_DB_OBJECT objectInfo;
objectInfo.objId = objId;
objectInfo.parObjId = parObjId;
objectInfo.type = type;
m_savedObjects.push_back(objectInfo);
}
/**
* Get a previously cached database object.
* @param objId The object ID of the object being loaded
*/
TSK_RETVAL_ENUM
TskAutoDbJava::getObjectInfo(int64_t objId, TSK_DB_OBJECT** obj_info) {
for (vector<TSK_DB_OBJECT>::iterator itObjs = m_savedObjects.begin();
itObjs != m_savedObjects.end(); ++itObjs) {
TSK_DB_OBJECT* tskDbObj = &(*itObjs);
if (tskDbObj->objId == objId) {
*obj_info = tskDbObj;
return TSK_OK;
}
}
// Object not found
return TSK_ERR;
}
/**
* Adds image details to the existing database tables. Object ID for new image stored in objId.
*
* @param type Image type
* @param ssize Size of device sector in bytes (or 0 for default)
* @param objId The object id assigned to the image (out param)
* @param timeZone The timezone the image is from
* @param size The size of the image in bytes.
* @param md5 MD5 hash of the image
* @param sha1 SHA1 hash of the image
* @param sha256 SHA256 hash of the image
* @param deviceId An ASCII-printable identifier for the device associated with the data source that is intended to be unique across multiple cases (e.g., a UUID).
* @param collectionDetails collection details
* @returns TSK_ERR on error, TSK_OK on success
*/
TSK_RETVAL_ENUM
TskAutoDbJava::addImageInfo(int type, TSK_OFF_T ssize, int64_t & objId, const string & timezone, TSK_OFF_T size, const string &md5,
const string& sha1, const string& sha256, const string& deviceId, const string& collectionDetails,
char** img_ptrs, int num_imgs) {
const char *tz_cstr = timezone.c_str();
jstring tzj = m_jniEnv->NewStringUTF(tz_cstr);
const char *md5_cstr = md5.c_str();
jstring md5j = m_jniEnv->NewStringUTF(md5_cstr);
const char *sha1_cstr = sha1.c_str();
jstring sha1j = m_jniEnv->NewStringUTF(sha1_cstr);
const char *sha256_cstr = sha256.c_str();
jstring sha256j = m_jniEnv->NewStringUTF(sha256_cstr);
const char *devId_cstr = deviceId.c_str();
jstring devIdj = m_jniEnv->NewStringUTF(devId_cstr);
const char *coll_cstr = collectionDetails.c_str();
jstring collj = m_jniEnv->NewStringUTF(coll_cstr);
jobjectArray imgNamesj = (jobjectArray)m_jniEnv->NewObjectArray(
num_imgs,
m_jniEnv->FindClass("java/lang/String"),
m_jniEnv->NewStringUTF(""));
for (int i = 0; i < num_imgs; i++) {
m_jniEnv->SetObjectArrayElement(
imgNamesj, i, m_jniEnv->NewStringUTF(img_ptrs[i]));
}
jlong objIdj = m_jniEnv->CallLongMethod(m_javaDbObj, m_addImageMethodID,
type, ssize, tzj, size, md5j, sha1j, sha256j, devIdj, collj, imgNamesj);
objId = (int64_t)objIdj;
if (objId < 0) {
return TSK_ERR;
}
saveObjectInfo(objId, 0, TSK_DB_OBJECT_TYPE_IMG);
return TSK_OK;
}
void
TskAutoDbJava::addAcquisitionDetails(int64_t imgId, const string& collectionDetails) {
const char *coll_cstr = collectionDetails.c_str();
jstring collj = m_jniEnv->NewStringUTF(coll_cstr);
m_jniEnv->CallLongMethod(m_javaDbObj, m_addAcquisitionDetailsMethodID,
imgId, collj);
}
/**
* Adds volume system to database. Object ID for new vs stored in objId.
*
* @param vs_info Struct containing info for this volume system
* @param parObjId Parent object ID for the volume system
* @param objId Object ID of new volume system
* @returns TSK_ERR on error, TSK_OK on success
*/
TSK_RETVAL_ENUM
TskAutoDbJava::addVsInfo(const TSK_VS_INFO* vs_info, int64_t parObjId, int64_t& objId) {
jlong objIdj = m_jniEnv->CallLongMethod(m_javaDbObj, m_addVolumeSystemMethodID,
parObjId, vs_info->vstype, vs_info->offset, (uint64_t)vs_info->block_size);
objId = (int64_t)objIdj;
if (objId < 0) {
return TSK_ERR;
}
// Save the vs info to use for unallocated blocks later
TSK_DB_VS_INFO vs_db;
vs_db.objId = objId;
vs_db.offset = vs_info->offset;
vs_db.vstype = vs_info->vstype;
vs_db.block_size = vs_info->block_size;
m_savedVsInfo.push_back(vs_db);
saveObjectInfo(objId, parObjId, TSK_DB_OBJECT_TYPE_VS);
return TSK_OK;
}
/**
* Adds pool and pool volume system to database. Object ID for new pool vs stored in objId.
*
* @param pool_info Struct containing info for this pool
* @param parObjId Parent object ID for the pool
* @param objId Object ID of new pool volume system
* @returns TSK_ERR on error, TSK_OK on success
*/
TSK_RETVAL_ENUM
TskAutoDbJava::addPoolInfoAndVS(const TSK_POOL_INFO *pool_info, int64_t parObjId, int64_t& objId) {
// Add the pool
jlong poolObjIdj = m_jniEnv->CallLongMethod(m_javaDbObj, m_addPoolMethodID,
parObjId, pool_info->ctype);
int64_t poolObjId = (int64_t)poolObjIdj;
if (poolObjId < 0) {
return TSK_ERR;
}
saveObjectInfo(poolObjId, parObjId, TSK_DB_OBJECT_TYPE_POOL);
// Add the pool volume
jlong objIdj = m_jniEnv->CallLongMethod(m_javaDbObj, m_addVolumeSystemMethodID,
poolObjIdj, TSK_VS_TYPE_APFS, pool_info->img_offset, (uint64_t)pool_info->block_size);
objId = (int64_t)objIdj;
saveObjectInfo(objId, poolObjId, TSK_DB_OBJECT_TYPE_VS);
return TSK_OK;
}
/**
* Adds a pool volume to database. Object ID for new pool volume stored in objId.
*
* @param pool_vol Struct containing info for this pool volume
* @param parObjId Parent object ID
* @param objId Object ID of new pool volume
* @returns TSK_ERR on error, TSK_OK on success
*/
TSK_RETVAL_ENUM
TskAutoDbJava::addPoolVolumeInfo(const TSK_POOL_VOLUME_INFO* pool_vol,
int64_t parObjId, int64_t& objId) {
jstring descj = m_jniEnv->NewStringUTF(pool_vol->desc);
jlong objIdj = m_jniEnv->CallLongMethod(m_javaDbObj, m_addVolumeMethodID,
parObjId, (int64_t)pool_vol->index, pool_vol->block, pool_vol->num_blocks,
descj, pool_vol->flags);
objId = (int64_t)objIdj;
if (objId < 0) {
return TSK_ERR;
}
saveObjectInfo(objId, parObjId, TSK_DB_OBJECT_TYPE_VOL);
return TSK_OK;
}
/**
* Adds a volume to database. Object ID for new volume stored in objId.
*
* @param vs_part Struct containing info for this volume
* @param parObjId Parent object ID
* @param objId Object ID of new volume
* @returns TSK_ERR on error, TSK_OK on success
*/
TSK_RETVAL_ENUM
TskAutoDbJava::addVolumeInfo(const TSK_VS_PART_INFO* vs_part,
int64_t parObjId, int64_t& objId) {
jstring descj = m_jniEnv->NewStringUTF(vs_part->desc);
jlong objIdj = m_jniEnv->CallLongMethod(m_javaDbObj, m_addVolumeMethodID,
parObjId, (uint64_t)vs_part->addr, vs_part->start, vs_part->len,
descj, vs_part->flags);
objId = (int64_t)objIdj;
if (objId < 0) {
return TSK_ERR;
}
// Save the volume info for creating unallocated blocks later
TSK_DB_VS_PART_INFO vs_part_db;
vs_part_db.objId = objId;
vs_part_db.addr = vs_part->addr;
vs_part_db.start = vs_part->start;
vs_part_db.len = vs_part->len;
strncpy(vs_part_db.desc, vs_part->desc, TSK_MAX_DB_VS_PART_INFO_DESC_LEN - 1);
vs_part_db.flags = vs_part->flags;
m_savedVsPartInfo.push_back(vs_part_db);
saveObjectInfo(objId, parObjId, TSK_DB_OBJECT_TYPE_VOL);
return TSK_OK;
}
/**
* Adds a file system to database. Object ID for new file system stored in objId.
*
* @param fs_info Struct containing info for this file system
* @param parObjId Parent object ID
* @param objId Object ID of new file system
* @returns TSK_ERR on error, TSK_OK on success
*/
TSK_RETVAL_ENUM
TskAutoDbJava::addFsInfo(const TSK_FS_INFO* fs_info, int64_t parObjId,
int64_t& objId) {
jlong objIdj = m_jniEnv->CallLongMethod(m_javaDbObj, m_addFileSystemMethodID,
parObjId, fs_info->offset, (int)fs_info->ftype, (uint64_t)fs_info->block_size,
fs_info->block_count, fs_info->root_inum, fs_info->first_inum,
fs_info->last_inum);
objId = (int64_t)objIdj;
if (objId < 0) {
return TSK_ERR;
}
// Save the file system info for creating unallocated blocks later
TSK_DB_FS_INFO fs_info_db;
fs_info_db.objId = objId;
fs_info_db.imgOffset = fs_info->offset;
fs_info_db.fType = fs_info->ftype;
fs_info_db.block_size = fs_info->block_size;
fs_info_db.block_count = fs_info->block_count;
fs_info_db.root_inum = fs_info->root_inum;
fs_info_db.first_inum = fs_info->first_inum;
fs_info_db.last_inum = fs_info->last_inum;
m_savedFsInfo.push_back(fs_info_db);
saveObjectInfo(objId, parObjId, TSK_DB_OBJECT_TYPE_FS);
return TSK_OK;
}
/**
* Adds a file to database. Object ID for new file stored in objId.
*
* @param fs_file
* @param fs_attr
* @param path File path
* @param parObjId Parent object ID
* @param fsObjId Object ID of the file system
* @param objId Object ID of new file
* @param dataSourceObjId Object ID of the data source
* @returns TSK_ERR on error, TSK_OK on success
*/
TSK_RETVAL_ENUM
TskAutoDbJava::addFsFile(TSK_FS_FILE* fs_file,
const TSK_FS_ATTR* fs_attr, const char* path,
int64_t fsObjId, int64_t& objId, int64_t dataSourceObjId) {
if (fs_file->name == NULL)
return TSK_ERR;
// The object id for the parent folder. Will stay as zero if not the root folder
int64_t parObjId = 0;
// Root directory's parent should be the file system object.
// Make sure it doesn't have a name, so that we don't pick up ".." entries
if ((fs_file->fs_info->root_inum == fs_file->name->meta_addr) &&
((fs_file->name->name == NULL) || (strlen(fs_file->name->name) == 0))) {
// File is in the root directory
parObjId = fsObjId;
}
// Add the file to the database
return addFile(fs_file, fs_attr, path, fsObjId, parObjId, dataSourceObjId);
}
/**
* Extract the extension from the given file name and store it in the supplied string.
* @param name A file name
* @param extension The file name extension will be extracted to extension.
*/
void extractExtension(char *name, char *extension) {
char *ext = strrchr(name, '.');
//if ext is not null and is not the entire filename...
if (ext && (name != ext)) {
size_t extLen = strlen(ext);
//... and doesn't only contain the '.' and isn't too long to be a real extension.
if ((1 < extLen) && (extLen < 15)) {
strncpy(extension, ext + 1, extLen - 1);
//normalize to lower case, only works for ascii
for (int i = 0; extension[i]; i++) {
extension[i] = tolower(extension[i]);
}
}
}
}
/**
* Convert a sequence of characters to a jstring object.
* We first convert the character sequence to UTF16 and then
* use the JNI NewString() method to create the jstring.
* We do it this way because we encountered data that contained
* 4 byte (or more) UTF8 encoded characters and the JNI NewStringUTF()
* method does not handle 4 byte UTF8 encoding.
*
* @param input The sequence of characters to be turned into a jstring.
* @param newJString The new jstring object created from the input.
* @returns TSK_ERR on error, TSK_OK on success
*/
TSK_RETVAL_ENUM TskAutoDbJava::createJString(const char * input, jstring & newJString) {
size_t input_len = strlen(input) + 1;
UTF16 * utf16_input;
if ((utf16_input = (UTF16 *)tsk_malloc(input_len * sizeof(UTF16))) == NULL) {
return TSK_ERR;
}
UTF8 * source = (UTF8 *)input;
UTF16 * target = utf16_input;
if (tsk_UTF8toUTF16((const UTF8 **)&source, (const UTF8 *)&source[input_len], &target, &target[input_len], TSKlenientConversion) != TSKconversionOK) {
free(utf16_input);
// use default JNI method as fallback, fixes https://github.com/sleuthkit/sleuthkit/issues/2723
newJString = m_jniEnv->NewStringUTF(input);
return TSK_OK;
}
/*
* To determine the length of the new string we we subtract the address
* of the start of the UTF16 buffer from the address at the end of the
* UTF16 buffer (target is advanced in the call to the conversion routine
* above).
*/
newJString = m_jniEnv->NewString(utf16_input, (target - utf16_input) - 1);
free(utf16_input);
return TSK_OK;
}
/**
* Adds a file and its associated slack file to database.
* Does not learn object ID for new files, and files may
* not be added to the database immediately.
*
* @param fs_file
* @param fs_attr
* @param path File path
* @param fsObjId Object ID of the file system
* @param parObjId Parent object ID if known, 0 otherwise
* @param dataSourceObjId Object ID of the data source
* @returns TSK_ERR on error, TSK_OK on success
*/
TSK_RETVAL_ENUM
TskAutoDbJava::addFile(TSK_FS_FILE* fs_file,
const TSK_FS_ATTR* fs_attr, const char* path,
int64_t fsObjId, int64_t parObjId,
int64_t dataSourceObjId)
{
time_t mtime = 0;
time_t crtime = 0;
time_t ctime = 0;
time_t atime = 0;
TSK_OFF_T size = 0;
int meta_type = 0;
int meta_flags = 0;
int meta_mode = 0;
int meta_seq = 0;
int gid = 0;
int uid = 0;
int type = TSK_FS_ATTR_TYPE_NOT_FOUND;
int idx = 0;
if (fs_file->name == NULL)
return TSK_OK;
if (fs_file->meta) {
mtime = fs_file->meta->mtime;
atime = fs_file->meta->atime;
ctime = fs_file->meta->ctime;
crtime = fs_file->meta->crtime;
meta_type = fs_file->meta->type;
meta_flags = fs_file->meta->flags;
meta_mode = fs_file->meta->mode;
gid = fs_file->meta->gid;
uid = fs_file->meta->uid;
meta_seq = fs_file->meta->seq;
}
size_t attr_nlen = 0;
if (fs_attr) {
type = fs_attr->type;
idx = fs_attr->id;
size = fs_attr->size;
if (fs_attr->name) {
if ((fs_attr->type != TSK_FS_ATTR_TYPE_NTFS_IDXROOT) ||
(strcmp(fs_attr->name, "$I30") != 0)) {
attr_nlen = strlen(fs_attr->name);
}
}
}
// sanity check
if (size < 0) {
size = 0;
}
// combine name and attribute name
size_t len = strlen(fs_file->name->name);
char * name;
size_t nlen = len + attr_nlen + 11; // Extra space for possible colon and '-slack'
if ((name = (char *)tsk_malloc(nlen)) == NULL) {
return TSK_ERR;
}
strncpy(name, fs_file->name->name, nlen);
char extension[24] = "";
extractExtension(name, extension);
// Add the attribute name
if (attr_nlen > 0) {
strncat(name, ":", nlen - strlen(name));
if (fs_attr != NULL) {
strncat(name, fs_attr->name, nlen - strlen(name));
}
}
jstring namej;
if (createJString(name, namej) != TSK_OK) {
free(name);
return TSK_ERR;
}
// clean up path
// +2 = space for leading slash and terminating null
size_t path_len = strlen(path) + 2;
char* escaped_path;
if ((escaped_path = (char *)tsk_malloc(path_len)) == NULL) {
free(name);
return TSK_ERR;
}
strncpy(escaped_path, "/", path_len);
strncat(escaped_path, path, path_len - strlen(escaped_path));
jstring pathj;
if (createJString(escaped_path, pathj) != TSK_OK) {
free(name);
free(escaped_path);
return TSK_ERR;
}
// Escaped path is not needed beyond this point so free it.
free(escaped_path);
jstring extj;
if (createJString(extension, extj) != TSK_OK) {
free(name);
return TSK_ERR;
}
/* NTFS uses sequence, otherwise we hash the path. We do this to map to the
* correct parent folder if there are two from the root dir that eventually point to
* the same folder (one deleted and one allocated) or two hard links. */
jlong par_seqj;
if (TSK_FS_TYPE_ISNTFS(fs_file->fs_info->ftype))
{
par_seqj = fs_file->name->par_seq;
}
else {
par_seqj = -1;
}
TSK_INUM_T par_meta_addr = fs_file->name->par_addr;
char *sid_str = NULL;
jstring sidj = NULL; // return null across JNI if sid is not available
if (tsk_fs_file_get_owner_sid(fs_file, &sid_str) == 0) {
if (createJString(sid_str, sidj) != TSK_OK) {
free(sid_str);
return TSK_ERR;
}
free(sid_str);
}
// Add the file to the database
jlong ret_val = m_jniEnv->CallLongMethod(m_javaDbObj, m_addFileMethodID,
parObjId, fsObjId,
dataSourceObjId,
TSK_DB_FILES_TYPE_FS,
type, idx, namej,
fs_file->name->meta_addr, (uint64_t)fs_file->name->meta_seq,
fs_file->name->type, meta_type, fs_file->name->flags, meta_flags,
size,
(unsigned long long)crtime, (unsigned long long)ctime, (unsigned long long) atime, (unsigned long long) mtime,
meta_mode, gid, uid,
pathj, extj,
(uint64_t)meta_seq, par_meta_addr, par_seqj, sidj);
if (ret_val < 0) {
free(name);
return TSK_ERR;
}
// Add entry for the slack space.
// Current conditions for creating a slack file:
// - File name is not empty, "." or ".."
// - Data is non-resident
// - The allocated size is greater than the initialized file size
// See github issue #756 on why initsize and not size.
// - The data is not compressed
if ((fs_attr != NULL)
&& ((strlen(name) > 0) && (!TSK_FS_ISDOT(name)))
&& (!(fs_file->meta->flags & TSK_FS_META_FLAG_COMP))
&& (fs_attr->flags & TSK_FS_ATTR_NONRES)
&& (fs_attr->nrd.allocsize > fs_attr->nrd.initsize)) {
strncat(name, "-slack", 6);
if (strlen(extension) > 0) {
strncat(extension, "-slack", 6);
}
jstring slackNamej;
if (createJString(name, slackNamej) != TSK_OK) {
free(name);
return TSK_ERR;
}
jstring slackExtj;
if (createJString(extension, slackExtj) != TSK_OK) {
free(name);
return TSK_ERR;
}
TSK_OFF_T slackSize = fs_attr->nrd.allocsize - fs_attr->nrd.initsize;
// Add slack file to database
jlong ret_val = m_jniEnv->CallLongMethod(m_javaDbObj, m_addFileMethodID,
parObjId, fsObjId,
dataSourceObjId,
TSK_DB_FILES_TYPE_SLACK,
type, idx, slackNamej,
fs_file->name->meta_addr, (uint64_t)fs_file->name->meta_seq,
TSK_FS_NAME_TYPE_REG, TSK_FS_META_TYPE_REG, fs_file->name->flags, meta_flags,
slackSize,
(unsigned long long)crtime, (unsigned long long)ctime, (unsigned long long) atime, (unsigned long long) mtime,
meta_mode, gid, uid, // md5TextPtr, known,
pathj, slackExtj,
(uint64_t)meta_seq, par_meta_addr, par_seqj, sidj);
if (ret_val < 0) {
free(name);
return TSK_ERR;
}
}
free(name);
return TSK_OK;
}
// Internal function object to check for range overlap
typedef struct _checkFileLayoutRangeOverlap {
const vector<TSK_DB_FILE_LAYOUT_RANGE> & ranges;
bool hasOverlap;
explicit _checkFileLayoutRangeOverlap(const vector<TSK_DB_FILE_LAYOUT_RANGE> & ranges)
: ranges(ranges), hasOverlap(false) {}
bool getHasOverlap() const { return hasOverlap; }
void operator() (const TSK_DB_FILE_LAYOUT_RANGE & range) {
if (hasOverlap)
return; //no need to check other
uint64_t start = range.byteStart;
uint64_t end = start + range.byteLen;
vector<TSK_DB_FILE_LAYOUT_RANGE>::const_iterator it;
for (it = ranges.begin(); it != ranges.end(); ++it) {
const TSK_DB_FILE_LAYOUT_RANGE * otherRange = &(*it);
if (&range == otherRange)
continue; //skip, it's the same range
uint64_t otherStart = otherRange->byteStart;
uint64_t otherEnd = otherStart + otherRange->byteLen;
if (start <= otherEnd && end >= otherStart) {
hasOverlap = true;
break;
}
}
}
} checkFileLayoutRangeOverlap;
/**
* Internal helper method to add unalloc, unused and carved files with layout ranges to db
* Generates file_name and populates tsk_files, tsk_objects and tsk_file_layout tables
* Adds a single entry to tsk_files table with an auto-generated file name, tsk_objects table, and one or more entries to tsk_file_layout table
* @param dbFileType Type of file
* @param parentObjId Id of the parent object in the database (fs, volume, or image)
* @param fsObjId parent fs, or NULL if the file is not associated with fs
* @param size Number of bytes in file
* @param ranges vector containing one or more TSK_DB_FILE_LAYOUT_RANGE layout ranges (in)
* @param objId object id of the file object created (output)
* @param dataSourceObjId The object ID for the data source
* @returns TSK_OK on success or TSK_ERR on error.
*/
TSK_RETVAL_ENUM
TskAutoDbJava::addFileWithLayoutRange(const TSK_DB_FILES_TYPE_ENUM dbFileType, const int64_t parentObjId,
const int64_t fsObjId, const uint64_t size,
vector<TSK_DB_FILE_LAYOUT_RANGE>& ranges, int64_t& objId,
int64_t dataSourceObjId) {
const size_t numRanges = ranges.size();
if (numRanges < 1) {
tsk_error_reset();
tsk_error_set_errno(TSK_ERR_AUTO_DB);
tsk_error_set_errstr("Error addFileWithLayoutRange() - no ranges present");
return TSK_ERR;
}
stringstream fileNameSs;
switch (dbFileType) {
case TSK_DB_FILES_TYPE_UNALLOC_BLOCKS:
fileNameSs << "Unalloc";
break;
case TSK_DB_FILES_TYPE_UNUSED_BLOCKS:
fileNameSs << "Unused";
break;
case TSK_DB_FILES_TYPE_CARVED:
fileNameSs << "Carved";
break;
default:
stringstream sserr;
tsk_error_reset();
tsk_error_set_errno(TSK_ERR_AUTO_DB);
sserr << "Error addFileWithLayoutRange() - unsupported file type for file layout range: ";
sserr << (int)dbFileType;
tsk_error_set_errstr("%s", sserr.str().c_str());
return TSK_ERR;
}
//ensure layout ranges are sorted (to generate file name and to be inserted in sequence order)
sort(ranges.begin(), ranges.end());
//dome some checking
//ensure there is no overlap and each range has unique byte range
const checkFileLayoutRangeOverlap & overlapRes =
for_each(ranges.begin(), ranges.end(), checkFileLayoutRangeOverlap(ranges));
if (overlapRes.getHasOverlap()) {
tsk_error_reset();
tsk_error_set_errno(TSK_ERR_AUTO_DB);
tsk_error_set_errstr("Error addFileWithLayoutRange() - overlap detected between ranges");
return TSK_ERR;
}
//construct filename with parent obj id, start byte of first range, end byte of last range
fileNameSs << "_" << parentObjId << "_" << ranges[0].byteStart;
fileNameSs << "_" << (ranges[numRanges - 1].byteStart + ranges[numRanges - 1].byteLen);
jstring namej = m_jniEnv->NewStringUTF(fileNameSs.str().c_str());
// Insert into tsk files and tsk objects
jlong objIdj = m_jniEnv->CallLongMethod(m_javaDbObj, m_addLayoutFileMethodID,
parentObjId, fsObjId, dataSourceObjId, dbFileType, namej, size);
objId = (int64_t)objIdj;
if (objId < 0) {
return TSK_ERR;
}
// Fill in fileObjId and insert ranges
for (vector<TSK_DB_FILE_LAYOUT_RANGE>::iterator it = ranges.begin();
it != ranges.end(); ++it) {
TSK_DB_FILE_LAYOUT_RANGE & range = *it;
range.fileObjId = objId;
if (-1 == m_jniEnv->CallLongMethod(m_javaDbObj, m_addLayoutFileRangeMethodID,
objId, range.byteStart, range.byteLen, (uint64_t)range.sequence)) {
return TSK_ERR;
}
}
return TSK_OK;
}
/**
* Adds information about a unallocated file with layout ranges into the database.
* Adds a single entry to tsk_files table with an auto-generated file name, tsk_objects table, and one or more entries to tsk_file_layout table
* @param parentObjId Id of the parent object in the database (fs, volume, or image)
* @param fsObjId parent fs, or NULL if the file is not associated with fs
* @param size Number of bytes in file
* @param ranges vector containing one or more TSK_DB_FILE_LAYOUT_RANGE layout ranges (in)
* @param objId object id of the file object created (output)
* @param dataSourceObjId The object ID for the data source
* @returns TSK_OK on success or TSK_ERR on error.
*/
TSK_RETVAL_ENUM
TskAutoDbJava::addUnallocBlockFile(const int64_t parentObjId, const int64_t fsObjId, const uint64_t size,
vector<TSK_DB_FILE_LAYOUT_RANGE>& ranges, int64_t& objId,
int64_t dataSourceObjId) {
return addFileWithLayoutRange(TSK_DB_FILES_TYPE_UNALLOC_BLOCKS, parentObjId, fsObjId, size, ranges, objId,
dataSourceObjId);
}
/**
* Adds information about a unused file with layout ranges into the database.
* Adds a single entry to tsk_files table with an auto-generated file name, tsk_objects table, and one or more entries to tsk_file_layout table
* @param parentObjId Id of the parent object in the database (fs, volume, or image)
* @param fsObjId parent fs, or NULL if the file is not associated with fs
* @param size Number of bytes in file
* @param ranges vector containing one or more TSK_DB_FILE_LAYOUT_RANGE layout ranges (in)
* @param objId object id of the file object created (output)
* @param dataSourceObjId The object ID for the data source
* @returns TSK_OK on success or TSK_ERR on error.
*/
TSK_RETVAL_ENUM
TskAutoDbJava::addUnusedBlockFile(const int64_t parentObjId, const int64_t fsObjId, const uint64_t size,
vector<TSK_DB_FILE_LAYOUT_RANGE>& ranges, int64_t& objId,
int64_t dataSourceObjId) {
return addFileWithLayoutRange(TSK_DB_FILES_TYPE_UNUSED_BLOCKS, parentObjId, fsObjId, size, ranges, objId,
dataSourceObjId);
}
/**
* Add a virtual dir to hold unallocated block files for this file system.
* @param fsObjId Object ID of the file system
* @param objId Object ID of the created virtual dir
* @param dataSourceObjId Object ID of the data source
*/
TSK_RETVAL_ENUM
TskAutoDbJava::addUnallocFsBlockFilesParent(const int64_t fsObjId, int64_t& objId,
int64_t dataSourceObjId) {
const char * const unallocDirName = "$Unalloc";
jstring namej = m_jniEnv->NewStringUTF(unallocDirName);
jlong objIdj = m_jniEnv->CallLongMethod(m_javaDbObj, m_addUnallocParentMethodID,
fsObjId, namej);
objId = (int64_t)objIdj;
if (objId < 0) {
return TSK_ERR;
}
return TSK_OK;
}
/**
* Adds a new volume that will hold the unallocated blocks for the pool.
*
* @param vol_index The index for the new volume (should be one higher than the number of pool volumes)
* @param parObjId The object ID of the parent volume system
* @param objId Will be set to the object ID of the new volume
*
* @returns TSK_ERR on error, TSK_OK on success
*/
TSK_RETVAL_ENUM
TskAutoDbJava::addUnallocatedPoolVolume(int vol_index, int64_t parObjId, int64_t& objId)
{
const char *desc = "Unallocated Blocks";
jstring descj = m_jniEnv->NewStringUTF(desc);
jlong objIdj = m_jniEnv->CallLongMethod(m_javaDbObj, m_addVolumeMethodID,
parObjId, vol_index, 0, 0,
descj, 0);
objId = (int64_t)objIdj;
if (objId < 0) {
return TSK_ERR;
}
return TSK_OK;
}
void TskAutoDbJava::close() {
if (m_jniEnv == NULL) {
return;
}
if (m_javaDbObj != NULL) {
m_jniEnv->DeleteGlobalRef(m_javaDbObj);
}
if (m_callbackClass != NULL) {
m_jniEnv->DeleteGlobalRef(m_callbackClass);
}
}
int64_t TskAutoDbJava::getImageID() {
return m_curImgId;
}
void TskAutoDbJava::closeImage() {
TskAuto::closeImage();
}
void TskAutoDbJava::setAddFileSystems(bool addFileSystems) {
m_addFileSystems = addFileSystems;
}
void TskAutoDbJava::setNoFatFsOrphans(bool noFatFsOrphans) {
m_noFatFsOrphans = noFatFsOrphans;
}
void TskAutoDbJava::setAddUnallocSpace(bool addUnallocSpace) {
setAddUnallocSpace(addUnallocSpace, -1);
}
void TskAutoDbJava::setAddUnallocSpace(bool addUnallocSpace, int64_t minChunkSize) {
m_addUnallocSpace = addUnallocSpace;
m_minChunkSize = minChunkSize;
m_maxChunkSize = -1;
}
void TskAutoDbJava::setAddUnallocSpace(int64_t minChunkSize, int64_t maxChunkSize) {
m_addUnallocSpace = true;
m_minChunkSize = minChunkSize;
m_maxChunkSize = maxChunkSize;
}
/**
* Adds an image to the database.
*
* @param a_num Number of image parts
* @param a_images Array of paths to the image parts
* @param a_type Image type
* @param a_ssize Size of device sector in bytes (or 0 for default)
* @param a_deviceId An ASCII-printable identifier for the device associated with the data source that is intended to be unique across multiple cases (e.g., a UUID).
* @return 0 for success, 1 for failure
*/
uint8_t
TskAutoDbJava::openImageUtf8(int a_num, const char *const a_images[],
TSK_IMG_TYPE_ENUM a_type, unsigned int a_ssize, const char* a_deviceId)
{
uint8_t retval =
TskAuto::openImageUtf8(a_num, a_images, a_type, a_ssize);
if (retval != 0) {
return retval;
}
if (addImageDetails(a_deviceId)) {
return 1;
}
return 0;
}
/**
* Adds an image to the database.
*
* @param a_num Number of image parts
* @param a_images Array of paths to the image parts
* @param a_type Image type
* @param a_ssize Size of device sector in bytes (or 0 for default)
* @param a_deviceId An ASCII-printable identifier for the device associated with the data source that is intended to be unique across multiple cases (e.g., a UUID).
* @return 0 for success, 1 for failure
*/
uint8_t
TskAutoDbJava::openImage(int a_num, const TSK_TCHAR * const a_images[],
TSK_IMG_TYPE_ENUM a_type, unsigned int a_ssize, const char* a_deviceId)
{
#ifdef TSK_WIN32
uint8_t retval = TskAuto::openImage(a_num, a_images, a_type, a_ssize);
if (retval != 0) {
return retval;
}
return (addImageDetails(a_deviceId));
#else
return openImageUtf8(a_num, a_images, a_type, a_ssize, a_deviceId);
#endif
}
/**
* Adds an image to the database. Requires that m_img_info is already initialized
*
* @param a_deviceId An ASCII-printable identifier for the device associated with the data source that is intended to be unique across multiple cases (e.g., a UUID).
* @return 0 for success, 1 for failure
*/
uint8_t
TskAutoDbJava::openImage(const char* a_deviceId)
{
if (m_img_info == NULL) {
return 1;
}
return(addImageDetails(a_deviceId));
}
/**
* Adds image details to the existing database tables.
*
* @param deviceId An ASCII-printable identifier for the device associated with the data source that is intended to be unique across multiple cases (e.g., a UUID).
* @return Returns 0 for success, 1 for failure
*/
uint8_t
TskAutoDbJava::addImageDetails(const char* deviceId)
{
string md5 = "";
string sha1 = "";
string collectionDetails = "";
#if HAVE_LIBEWF
if (m_img_info->itype == TSK_IMG_TYPE_EWF_EWF) {
// @@@ This should really probably be inside of a tsk_img_ method
IMG_EWF_INFO *ewf_info = (IMG_EWF_INFO *)m_img_info;
if (ewf_info->md5hash_isset) {
md5 = ewf_info->md5hash;
}
if (ewf_info->sha1hash_isset) {
sha1 = ewf_info->sha1hash;
}
collectionDetails = ewf_get_details(ewf_info);
}
#endif
// If the image has already been added to the database, update the acquisition details and return.
if (m_curImgId > 0) {
addAcquisitionDetails(m_curImgId, collectionDetails);
return 0;
}
string devId;
if (NULL != deviceId) {
devId = deviceId;
} else {
devId = "";
}
char **img_ptrs;
#ifdef TSK_WIN32
// convert image paths to UTF-8
img_ptrs = (char **)tsk_malloc(m_img_info->num_img * sizeof(char *));
if (img_ptrs == NULL) {
return 1;
}
for (int i = 0; i < m_img_info->num_img; i++) {
char * img2 = (char*)tsk_malloc(1024 * sizeof(char));
UTF8 *ptr8;
UTF16 *ptr16;
ptr8 = (UTF8 *)img2;
ptr16 = (UTF16 *)m_img_info->images[i];
uint8_t retval =
tsk_UTF16toUTF8_lclorder((const UTF16 **)&ptr16, (UTF16 *)
& ptr16[TSTRLEN(m_img_info->images[i]) + 1], &ptr8,
(UTF8 *)((uintptr_t)ptr8 + 1024), TSKlenientConversion);
if (retval != TSKconversionOK) {
tsk_error_reset();
tsk_error_set_errno(TSK_ERR_AUTO_UNICODE);
tsk_error_set_errstr("Error converting image to UTF-8\n");
return 1;
}
img_ptrs[i] = img2;
}
#else
img_ptrs = m_img_info->images;
#endif
if (TSK_OK != addImageInfo(m_img_info->itype, m_img_info->sector_size,
m_curImgId, m_curImgTZone, m_img_info->size, md5, sha1, "", devId, collectionDetails,
img_ptrs, m_img_info->num_img)) {
registerError();
return 1;
}
#ifdef TSK_WIN32
//cleanup
for (int i = 0; i < m_img_info->num_img; ++i) {
free(img_ptrs[i]);
}
free(img_ptrs);
#endif
return 0;
}
TSK_FILTER_ENUM
TskAutoDbJava::filterVs(const TSK_VS_INFO * vs_info)
{
m_vsFound = true;
if (TSK_OK != addVsInfo(vs_info, m_curImgId, m_curVsId)) {
registerError();
return TSK_FILTER_STOP;
}
return TSK_FILTER_CONT;
}
TSK_FILTER_ENUM
TskAutoDbJava::filterPool(const TSK_POOL_INFO * pool_info)
{
m_poolFound = true;
if (m_volFound && m_vsFound) {
// there's a volume system and volume
if (TSK_OK != addPoolInfoAndVS(pool_info, m_curVolId, m_curPoolVs)) {
registerError();
return TSK_FILTER_STOP;
}
// Save the parent obj ID for the pool
m_poolOffsetToParentId[pool_info->img_offset] = m_curVolId;
}
else {
// pool doesn't live in a volume, use image as parent
if (TSK_OK != addPoolInfoAndVS(pool_info, m_curImgId, m_curPoolVs)) {
registerError();
return TSK_FILTER_STOP;
}
// Save the parent obj ID for the pool
m_poolOffsetToParentId[pool_info->img_offset] = m_curImgId;
}
// Store the volume system object ID for later use
m_poolOffsetToVsId[pool_info->img_offset] = m_curPoolVs;
return TSK_FILTER_CONT;
}
/**
* Adds unallocated pool blocks to a new volume.
*
* @param numPool Will be updated with the number of pools processed
*
* @return Returns 0 for success, 1 for failure
*/
TSK_RETVAL_ENUM
TskAutoDbJava::addUnallocatedPoolBlocksToDb(size_t & numPool) {
for (size_t i = 0; i < m_poolInfos.size(); i++) {
const TSK_POOL_INFO * pool_info = m_poolInfos[i];
if (m_poolOffsetToVsId.find(pool_info->img_offset) == m_poolOffsetToVsId.end()) {
tsk_error_reset();
tsk_error_set_errno(TSK_ERR_AUTO_DB);
tsk_error_set_errstr("Error addUnallocatedPoolBlocksToDb() - could not find volume system object ID for pool at offset %lld", pool_info->img_offset);
return TSK_ERR;
}
int64_t curPoolVs = m_poolOffsetToVsId[pool_info->img_offset];
/* Make sure the pool_info is still allocated */
if (pool_info->tag != TSK_POOL_INFO_TAG) {
tsk_error_reset();
tsk_error_set_errno(TSK_ERR_AUTO_DB);
tsk_error_set_errstr("Error addUnallocatedPoolBlocksToDb() - pool_info is not allocated");
return TSK_ERR;
}
/* Only APFS pools are currently supported */
if (pool_info->ctype != TSK_POOL_TYPE_APFS) {
continue;
}
/* Increment the count of pools found */
numPool++;
/* Create the volume */
int64_t unallocVolObjId;
if (TSK_ERR == addUnallocatedPoolVolume(pool_info->num_vols, curPoolVs, unallocVolObjId)) {
tsk_error_reset();
tsk_error_set_errno(TSK_ERR_AUTO_DB);
tsk_error_set_errstr("Error addUnallocatedPoolBlocksToDb() - error createing unallocated space pool volume");
return TSK_ERR;
}
/* Create the unallocated space files */
TSK_FS_ATTR_RUN * unalloc_runs = tsk_pool_unallocated_runs(pool_info);
TSK_FS_ATTR_RUN * current_run = unalloc_runs;
while (current_run != NULL) {
if (addUnallocBlockFileInChunks(current_run->addr * pool_info->block_size, current_run->len * pool_info->block_size, unallocVolObjId, m_curImgId) == TSK_ERR) {
registerError();
tsk_fs_attr_run_free(unalloc_runs);
return TSK_ERR;
}
current_run = current_run->next;
}
tsk_fs_attr_run_free(unalloc_runs);
}
return TSK_OK;
}
TSK_FILTER_ENUM
TskAutoDbJava::filterPoolVol(const TSK_POOL_VOLUME_INFO * pool_vol)
{
if (TSK_OK != addPoolVolumeInfo(pool_vol, m_curPoolVs, m_curPoolVol)) {
registerError();
return TSK_FILTER_STOP;
}
return TSK_FILTER_CONT;
}
TSK_FILTER_ENUM
TskAutoDbJava::filterVol(const TSK_VS_PART_INFO * vs_part)
{
m_volFound = true;
m_foundStructure = true;
m_poolFound = false;
if (TSK_OK != addVolumeInfo(vs_part, m_curVsId, m_curVolId)) {
registerError();
return TSK_FILTER_STOP;
}
return TSK_FILTER_CONT;
}
TSK_FILTER_ENUM
TskAutoDbJava::filterFs(TSK_FS_INFO * fs_info)
{
TSK_FS_FILE *file_root;
m_foundStructure = true;
if (m_poolFound) {
// there's a pool
if (TSK_OK != addFsInfo(fs_info, m_curPoolVol, m_curFsId)) {
registerError();
return TSK_FILTER_STOP;
}
}
else if (m_volFound && m_vsFound) {
// there's a volume system and volume
if (TSK_OK != addFsInfo(fs_info, m_curVolId, m_curFsId)) {
registerError();
return TSK_FILTER_STOP;
}
}
else {
// file system doesn't live in a volume, use image as parent
if (TSK_OK != addFsInfo(fs_info, m_curImgId, m_curFsId)) {
registerError();
return TSK_FILTER_STOP;
}
}
// We won't hit the root directory on the walk, so open it now
if ((file_root = tsk_fs_file_open(fs_info, NULL, "/")) != NULL) {
processFile(file_root, "");
tsk_fs_file_close(file_root);
file_root = NULL;
}
// make sure that flags are set to get all files -- we need this to
// find parent directory
TSK_FS_DIR_WALK_FLAG_ENUM filterFlags = (TSK_FS_DIR_WALK_FLAG_ENUM)
(TSK_FS_DIR_WALK_FLAG_ALLOC | TSK_FS_DIR_WALK_FLAG_UNALLOC);
//check if to skip processing of FAT orphans
if (m_noFatFsOrphans
&& TSK_FS_TYPE_ISFAT(fs_info->ftype) ) {
filterFlags = (TSK_FS_DIR_WALK_FLAG_ENUM) (filterFlags | TSK_FS_DIR_WALK_FLAG_NOORPHAN);
}
setFileFilterFlags(filterFlags);
return TSK_FILTER_CONT;
}
/* Insert the file data into the file table.
* @param fs_file
* @param fs_attr
* @param path
* Returns TSK_ERR on error.
*/
TSK_RETVAL_ENUM
TskAutoDbJava::insertFileData(TSK_FS_FILE * fs_file,
const TSK_FS_ATTR * fs_attr, const char *path)
{
if (TSK_ERR == addFsFile(fs_file, fs_attr, path, m_curFsId, m_curFileId,
m_curImgId)) {
registerError();
return TSK_ERR;
}
return TSK_OK;
}
/**
* Analyzes the open image and adds image info to a database.
* Does not deal with transactions and such. Refer to startAddImage()
* for more control.
* @returns 1 if a critical error occurred (DB doesn't exist, no file system, etc.), 2 if errors occurred at some point adding files to the DB (corrupt file, etc.), and 0 otherwise. Errors will have been registered.
*/
uint8_t TskAutoDbJava::addFilesInImgToDb()
{
// @@@ This seems bad because we are overriding what the user may
// have set. We should remove the public API if we are going to
// override it -- presumably this was added so that we always have
// unallocated volume space...
setVolFilterFlags((TSK_VS_PART_FLAG_ENUM) (TSK_VS_PART_FLAG_ALLOC |
TSK_VS_PART_FLAG_UNALLOC));
uint8_t retVal = 0;
if (findFilesInImg()) {
// map the boolean return value from findFiles to the three-state return value we use
// @@@ findFiles should probably return this three-state enum too
if (m_foundStructure == false) {
retVal = 1;
}
else {
retVal = 2;
}
}
TSK_RETVAL_ENUM addUnallocRetval = TSK_OK;
if (m_addUnallocSpace)
addUnallocRetval = addUnallocSpaceToDb();
// findFiles return value trumps unalloc since it can return either 2 or 1.
if (retVal) {
return retVal;
}
else if (addUnallocRetval == TSK_ERR) {
return 2;
}
else {
return 0;
}
}
/**
* Start the process to add image/file metadata to database inside of a transaction.
* User must call either commitAddImage() to commit the changes,
* or revertAddImage() to revert them.
*
* @param numImg Number of image parts
* @param imagePaths Array of paths to the image parts
* @param imgType Image type
* @param sSize Size of device sector in bytes (or 0 for default)
* @param deviceId An ASCII-printable identifier for the device associated with the data source that is intended to be unique across multiple cases (e.g., a UUID)
* @return 0 for success, 1 for failure
*/
uint8_t
TskAutoDbJava::startAddImage(int numImg, const TSK_TCHAR * const imagePaths[],
TSK_IMG_TYPE_ENUM imgType, unsigned int sSize, const char* deviceId)
{
if (tsk_verbose)
tsk_fprintf(stderr, "TskAutoDbJava::startAddImage: Starting add image process\n");
if (openImage(numImg, imagePaths, imgType, sSize, deviceId)) {
tsk_error_set_errstr2("TskAutoDbJava::startAddImage");
registerError();
return 1;
}
if (m_imageWriterEnabled) {
tsk_img_writer_create(m_img_info, m_imageWriterPath);
}
if (m_addFileSystems) {
return addFilesInImgToDb();
} else {
return 0;
}
}
/**
* Start the process to add image/file metadata to database inside of a transaction.
* User must call either commitAddImage() to commit the changes,
* or revertAddImage() to revert them.
*
* @param img_info Previously initialized TSK_IMG_INFO object
* @param deviceId An ASCII-printable identifier for the device associated with the data source that is intended to be unique across multiple cases (e.g., a UUID)
* @return 0 for success, 1 for failure
*/
uint8_t
TskAutoDbJava::startAddImage(TSK_IMG_INFO * img_info, const char* deviceId)
{
openImageHandle(img_info);
if (m_img_info == NULL) {
return 1;
}
if (tsk_verbose)
tsk_fprintf(stderr, "TskAutoDbJava::startAddImage: Starting add image process\n");
if (openImage(deviceId)) {
tsk_error_set_errstr2("TskAutoDbJava::startAddImage");
registerError();
return 1;
}
if (m_imageWriterEnabled) {
if (tsk_img_writer_create(m_img_info, m_imageWriterPath)) {
registerError();
return 1;
}
}
if (m_addFileSystems) {
return addFilesInImgToDb();
}
else {
return 0;
}
}
#ifdef WIN32
/**
* Start the process to add image/file metadata to database inside of a transaction.
* Same functionality as addFilesInImgToDb(). Reverts
* all changes on error. User must call either commitAddImage() to commit the changes,
* or revertAddImage() to revert them.
*
* @param numImg Number of image parts
* @param imagePaths Array of paths to the image parts
* @param imgType Image type
* @param sSize Size of device sector in bytes (or 0 for default)
* @param deviceId An ASCII-printable identifier for the device associated with the data source that is intended to be unique across multiple cases (e.g., a UUID)
* @return 0 for success 1, for failure
*/
uint8_t
TskAutoDbJava::startAddImage(int numImg, const char *const imagePaths[],
TSK_IMG_TYPE_ENUM imgType, unsigned int sSize, const char* deviceId)
{
if (tsk_verbose)
tsk_fprintf(stderr, "TskAutoDbJava::startAddImage_utf8: Starting add image process\n");
if (openImageUtf8(numImg, imagePaths, imgType, sSize, deviceId)) {
tsk_error_set_errstr2("TskAutoDbJava::startAddImage");
registerError();
return 1;
}
if (m_imageWriterEnabled) {
tsk_img_writer_create(m_img_info, m_imageWriterPath);
}
if (m_addFileSystems) {
return addFilesInImgToDb();
} else {
return 0;
}
}
#endif
/**
* Cancel the running process. Will not be handled immediately.
*/
void
TskAutoDbJava::stopAddImage()
{
if (tsk_verbose)
tsk_fprintf(stderr, "TskAutoDbJava::stopAddImage: Stop request received\n");
m_stopped = true;
setStopProcessing();
// flag is checked every time processFile() is called
}
/**
* Set the current image's timezone
*/
void
TskAutoDbJava::setTz(string tzone)
{
m_curImgTZone = tzone;
}
/**
* Set the object ID for the data source
*/
void
TskAutoDbJava::setDatasourceObjId(int64_t img_id)
{
m_curImgId = img_id;
}
TSK_RETVAL_ENUM
TskAutoDbJava::processFile(TSK_FS_FILE * fs_file, const char *path)
{
// Check if the process has been canceled
if (m_stopped) {
if (tsk_verbose)
tsk_fprintf(stderr, "TskAutoDbJava::processFile: Stop request detected\n");
return TSK_STOP;
}
/* Update the current directory, which can be used to show
* progress. If we get a directory, then use its name. We
* do this so that when we are searching for orphan files, then
* we at least show $OrphanFiles as status. The secondary check
* is to grab the parent folder from files once we return back
* into a folder when we are doing our depth-first recursion. */
if (isDir(fs_file)) {
m_curDirAddr = fs_file->name->meta_addr;
tsk_take_lock(&m_curDirPathLock);
m_curDirPath = string(path) + fs_file->name->name;
tsk_release_lock(&m_curDirPathLock);
}
else if (m_curDirAddr != fs_file->name->par_addr) {
m_curDirAddr = fs_file->name->par_addr;
tsk_take_lock(&m_curDirPathLock);
m_curDirPath = path;
tsk_release_lock(&m_curDirPathLock);
}
/* process the attributes. The case of having 0 attributes can occur
* with virtual / sparse files and HFS directories.
* At some point, this can probably be cleaned
* up if TSK is more consistent about if there should always be an
* attribute or not. Sometimes, none of the attributes are added
* because of their type and we always want to add a reference to
* every file. */
TSK_RETVAL_ENUM retval = TSK_OK;
m_attributeAdded = false;
if (tsk_fs_file_attr_getsize(fs_file) > 0) {
retval = processAttributes(fs_file, path);
}
// insert a general row if we didn't add a specific attribute one
if ((retval == TSK_OK) && (m_attributeAdded == false)) {
retval = insertFileData(fs_file, NULL, path);
}
// reset the file id
m_curFileId = 0;
if (retval == TSK_STOP)
return TSK_STOP;
else
return TSK_OK;
}
// we return only OK or STOP -- errors are registered only and OK is returned.
TSK_RETVAL_ENUM
TskAutoDbJava::processAttribute(TSK_FS_FILE * fs_file,
const TSK_FS_ATTR * fs_attr, const char *path)
{
// add the file metadata for the default attribute type
if (isDefaultType(fs_file, fs_attr)) {
if (insertFileData(fs_attr->fs_file, fs_attr, path) == TSK_ERR) {
registerError();
return TSK_OK;
}
else {
m_attributeAdded = true;
}
}
return TSK_OK;
}
/**
* Callback invoked per every unallocated block in the filesystem
* Creates file ranges and file entries
* A single file entry per consecutive range of blocks
* @param a_block block being walked
* @param a_ptr a pointer to an UNALLOC_BLOCK_WLK_TRACK struct
* @returns TSK_WALK_CONT if continue, otherwise TSK_WALK_STOP if stop processing requested
*/
TSK_WALK_RET_ENUM TskAutoDbJava::fsWalkUnallocBlocksCb(const TSK_FS_BLOCK *a_block, void *a_ptr) {
UNALLOC_BLOCK_WLK_TRACK * unallocBlockWlkTrack = (UNALLOC_BLOCK_WLK_TRACK *) a_ptr;
if (unallocBlockWlkTrack->tskAutoDbJava.m_stopAllProcessing)
return TSK_WALK_STOP;
// initialize if this is the first block
if (unallocBlockWlkTrack->isStart) {
unallocBlockWlkTrack->isStart = false;
unallocBlockWlkTrack->curRangeStart = a_block->addr;
unallocBlockWlkTrack->prevBlock = a_block->addr;
unallocBlockWlkTrack->size = unallocBlockWlkTrack->fsInfo.block_size;
unallocBlockWlkTrack->nextSequenceNo = 0;
return TSK_WALK_CONT;
}
// We want to keep consecutive blocks in the same run, so simply update prevBlock and the size
// if this one is consecutive with the last call. But, if we have hit the max chunk
// size, then break up this set of consecutive blocks.
if ((a_block->addr == unallocBlockWlkTrack->prevBlock + 1) && ((unallocBlockWlkTrack->maxChunkSize <= 0) ||
(unallocBlockWlkTrack->size < unallocBlockWlkTrack->maxChunkSize))) {
unallocBlockWlkTrack->prevBlock = a_block->addr;
unallocBlockWlkTrack->size += unallocBlockWlkTrack->fsInfo.block_size;
return TSK_WALK_CONT;
}
// this block is not contiguous with the previous one or we've hit the maximum size; create and add a range object
const uint64_t rangeStartOffset = unallocBlockWlkTrack->curRangeStart * unallocBlockWlkTrack->fsInfo.block_size
+ unallocBlockWlkTrack->fsInfo.offset;
const uint64_t rangeSizeBytes = (1 + unallocBlockWlkTrack->prevBlock - unallocBlockWlkTrack->curRangeStart)
* unallocBlockWlkTrack->fsInfo.block_size;
unallocBlockWlkTrack->ranges.push_back(TSK_DB_FILE_LAYOUT_RANGE(rangeStartOffset, rangeSizeBytes, unallocBlockWlkTrack->nextSequenceNo++));
// Return (instead of adding this run) if we are going to:
// a) Make one big file with all unallocated space (minChunkSize == 0)
// or
// b) Only make an unallocated file once we have at least chunkSize bytes
// of data in our current run (minChunkSize > 0)
// In either case, reset the range pointers and add this block to the size
if ((unallocBlockWlkTrack->minChunkSize == 0) ||
((unallocBlockWlkTrack->minChunkSize > 0) &&
(unallocBlockWlkTrack->size < unallocBlockWlkTrack->minChunkSize))) {
unallocBlockWlkTrack->size += unallocBlockWlkTrack->fsInfo.block_size;
unallocBlockWlkTrack->curRangeStart = a_block->addr;
unallocBlockWlkTrack->prevBlock = a_block->addr;
return TSK_WALK_CONT;
}
// at this point we are either chunking and have reached the chunk limit
// or we're not chunking. Either way we now add what we've got to the DB
int64_t fileObjId = 0;
TskAutoDbJava & tskAutoDbJava = unallocBlockWlkTrack->tskAutoDbJava;
if (tskAutoDbJava.addUnallocBlockFile(tskAutoDbJava.m_curUnallocDirId,
unallocBlockWlkTrack->fsObjId, unallocBlockWlkTrack->size, unallocBlockWlkTrack->ranges, fileObjId, tskAutoDbJava.m_curImgId) == TSK_ERR) {
// @@@ Handle error -> Don't have access to registerError() though...
}
// reset
unallocBlockWlkTrack->curRangeStart = a_block->addr;
unallocBlockWlkTrack->prevBlock = a_block->addr;
unallocBlockWlkTrack->size = unallocBlockWlkTrack->fsInfo.block_size; // The current block is part of the new range
unallocBlockWlkTrack->ranges.clear();
unallocBlockWlkTrack->nextSequenceNo = 0;
//we don't know what the last unalloc block is in advance
//and will handle the last range in addFsInfoUnalloc()
return TSK_WALK_CONT;
}
/**
* Add unallocated space for the given file system to the database.
* Create files for consecutive unalloc block ranges.
* @param dbFsInfo fs to process
* @returns TSK_OK on success, TSK_ERR on error
*/
TSK_RETVAL_ENUM TskAutoDbJava::addFsInfoUnalloc(const TSK_DB_FS_INFO & dbFsInfo) {
// Unalloc space is handled separately for APFS
if (dbFsInfo.fType == TSK_FS_TYPE_APFS) {
return TSK_OK;
}
//open the fs we have from database
TSK_FS_INFO * fsInfo = tsk_fs_open_img(m_img_info, dbFsInfo.imgOffset, dbFsInfo.fType);
if (fsInfo == NULL) {
tsk_error_set_errstr2("TskAutoDbJava::addFsInfoUnalloc: error opening fs at offset %" PRIdOFF, dbFsInfo.imgOffset);
registerError();
return TSK_ERR;
}
//create a "fake" dir to hold the unalloc files for the fs
if (addUnallocFsBlockFilesParent(dbFsInfo.objId, m_curUnallocDirId, m_curImgId) == TSK_ERR) {
tsk_error_set_errstr2("addFsInfoUnalloc: error creating dir for unallocated space");
registerError();
return TSK_ERR;
}
//walk unalloc blocks on the fs and process them
//initialize the unalloc block walk tracking
UNALLOC_BLOCK_WLK_TRACK unallocBlockWlkTrack(*this, *fsInfo, dbFsInfo.objId, m_minChunkSize, m_maxChunkSize);
uint8_t block_walk_ret = tsk_fs_block_walk(fsInfo, fsInfo->first_block, fsInfo->last_block, (TSK_FS_BLOCK_WALK_FLAG_ENUM)(TSK_FS_BLOCK_WALK_FLAG_UNALLOC | TSK_FS_BLOCK_WALK_FLAG_AONLY),
fsWalkUnallocBlocksCb, &unallocBlockWlkTrack);
if (block_walk_ret == 1) {
stringstream errss;
tsk_fs_close(fsInfo);
errss << "TskAutoDbJava::addFsInfoUnalloc: error walking fs unalloc blocks, fs id: ";
errss << unallocBlockWlkTrack.fsObjId;
tsk_error_set_errstr2("%s", errss.str().c_str());
registerError();
return TSK_ERR;
}
if(m_stopAllProcessing) {
tsk_fs_close(fsInfo);
return TSK_OK;
}
// handle creation of the last range
// make range inclusive from curBlockStart to prevBlock
const uint64_t byteStart = unallocBlockWlkTrack.curRangeStart * fsInfo->block_size + fsInfo->offset;
const uint64_t byteLen = (1 + unallocBlockWlkTrack.prevBlock - unallocBlockWlkTrack.curRangeStart) * fsInfo->block_size;
unallocBlockWlkTrack.ranges.push_back(TSK_DB_FILE_LAYOUT_RANGE(byteStart, byteLen, unallocBlockWlkTrack.nextSequenceNo++));
int64_t fileObjId = 0;
if (addUnallocBlockFile(m_curUnallocDirId, dbFsInfo.objId, unallocBlockWlkTrack.size, unallocBlockWlkTrack.ranges, fileObjId, m_curImgId) == TSK_ERR) {
registerError();
tsk_fs_close(fsInfo);
return TSK_ERR;
}
//cleanup
tsk_fs_close(fsInfo);
return TSK_OK;
}
/**
* Process all unallocated space for this disk image and create "virtual" files with layouts
* @returns TSK_OK on success, TSK_ERR on error
*/
TSK_RETVAL_ENUM TskAutoDbJava::addUnallocSpaceToDb() {
if (m_stopAllProcessing) {
return TSK_OK;
}
size_t numVsP = 0;
size_t numFs = 0;
size_t numPool = 0;
TSK_RETVAL_ENUM retFsSpace = addUnallocFsSpaceToDb(numFs);
TSK_RETVAL_ENUM retVsSpace = addUnallocVsSpaceToDb(numVsP);
TSK_RETVAL_ENUM retPoolSpace = addUnallocatedPoolBlocksToDb(numPool);
//handle case when no fs and no vs partitions and no pools
TSK_RETVAL_ENUM retImgFile = TSK_OK;
if (numVsP == 0 && numFs == 0 && numPool == 0) {
retImgFile = addUnallocImageSpaceToDb();
}
if (retFsSpace == TSK_ERR || retVsSpace == TSK_ERR || retPoolSpace == TSK_ERR || retImgFile == TSK_ERR)
return TSK_ERR;
else
return TSK_OK;
}
/**
* Process each file system in the database and add its unallocated sectors to virtual files.
* @param numFs (out) number of filesystems found
* @returns TSK_OK on success, TSK_ERR on error (if some or all fs could not be processed)
*/
TSK_RETVAL_ENUM TskAutoDbJava::addUnallocFsSpaceToDb(size_t & numFs) {
if(m_stopAllProcessing) {
return TSK_OK;
}
numFs = m_savedFsInfo.size();
TSK_RETVAL_ENUM allFsProcessRet = TSK_OK;
for (vector<TSK_DB_FS_INFO>::iterator it = m_savedFsInfo.begin(); it!= m_savedFsInfo.end(); ++it) {
if (m_stopAllProcessing) {
break;
}
if (addFsInfoUnalloc(*it) == TSK_ERR)
allFsProcessRet = TSK_ERR;
}
//TODO set parent_path for newly created virt dir/file hierarchy for consistency
return allFsProcessRet;
}
/**
* Process each volume in the database and add its unallocated sectors to virtual files.
* @param numVsP (out) number of vs partitions found
* @returns TSK_OK on success, TSK_ERR on error
*/
TSK_RETVAL_ENUM TskAutoDbJava::addUnallocVsSpaceToDb(size_t & numVsP) {
numVsP = m_savedVsPartInfo.size();
//get fs infos to see if this vspart has fs
for (vector<TSK_DB_VS_PART_INFO>::const_iterator it = m_savedVsPartInfo.begin();
it != m_savedVsPartInfo.end(); ++it) {
if (m_stopAllProcessing) {
break;
}
const TSK_DB_VS_PART_INFO &vsPart = *it;
//interested in unalloc, meta, or alloc and no fs
if ( (vsPart.flags & (TSK_VS_PART_FLAG_UNALLOC | TSK_VS_PART_FLAG_META)) == 0 ) {
//check if vspart has no fs
bool hasFs = false;
for (vector<TSK_DB_FS_INFO>::const_iterator itFs = m_savedFsInfo.begin();
itFs != m_savedFsInfo.end(); ++itFs) {
const TSK_DB_FS_INFO & fsInfo = *itFs;
TSK_DB_OBJECT* fsObjInfo = NULL;
if (getObjectInfo(fsInfo.objId, &fsObjInfo) == TSK_ERR ) {
stringstream errss;
errss << "addUnallocVsSpaceToDb: error getting object info for fs from db, objId: " << fsInfo.objId;
tsk_error_set_errstr2("%s", errss.str().c_str());
registerError();
return TSK_ERR;
}
if (fsObjInfo->parObjId == vsPart.objId) {
hasFs = true;
break;
}
}
if (hasFs == true) {
//skip processing this vspart
continue;
}
// Check if the volume contains a pool
bool hasPool = false;
for (std::map<int64_t, int64_t>::iterator iter = m_poolOffsetToParentId.begin(); iter != m_poolOffsetToParentId.end(); ++iter) {
if (iter->second == vsPart.objId) {
hasPool = true;
}
}
if (hasPool) {
// Skip processing this vspart
continue;
}
}
// Get sector size and image offset from parent vs info
// Get parent id of this vs part
TSK_DB_OBJECT* vsPartObj = NULL;
if (getObjectInfo(vsPart.objId, &vsPartObj) == TSK_ERR) {
stringstream errss;
errss << "addUnallocVsSpaceToDb: error getting object info for vs part from db, objId: " << vsPart.objId;
tsk_error_set_errstr2("%s", errss.str().c_str());
registerError();
return TSK_ERR;
}
if (vsPartObj == NULL) {
return TSK_ERR;
}
TSK_DB_VS_INFO* vsInfo = NULL;
for (vector<TSK_DB_VS_INFO>::iterator itVs = m_savedVsInfo.begin();
itVs != m_savedVsInfo.end(); ++itVs) {
TSK_DB_VS_INFO* temp_vs_info = &(*itVs);
if (temp_vs_info->objId == vsPartObj->parObjId) {
vsInfo = temp_vs_info;
}
}
if (vsInfo == NULL ) {
stringstream errss;
errss << "addUnallocVsSpaceToDb: error getting volume system info from db, objId: " << vsPartObj->parObjId;
tsk_error_set_errstr2("%s", errss.str().c_str());
registerError();
return TSK_ERR;
}
// Create an unalloc file (or files) with unalloc part, with vs part as parent
const uint64_t byteStart = vsInfo->offset + vsInfo->block_size * vsPart.start;
const uint64_t byteLen = vsInfo->block_size * vsPart.len;
if (addUnallocBlockFileInChunks(byteStart, byteLen, vsPart.objId, m_curImgId) == TSK_ERR) {
registerError();
return TSK_ERR;
}
}
return TSK_OK;
}
/**
* Adds unalloc space for the image if there is no volumes and no file systems.
*
* @returns TSK_OK on success, TSK_ERR on error
*/
TSK_RETVAL_ENUM TskAutoDbJava::addUnallocImageSpaceToDb() {
const TSK_OFF_T imgSize = getImageSize();
if (imgSize == -1) {
tsk_error_set_errstr("addUnallocImageSpaceToDb: error getting current image size, can't create unalloc block file for the image.");
registerError();
return TSK_ERR;
}
else {
TSK_DB_FILE_LAYOUT_RANGE tempRange(0, imgSize, 0);
//add unalloc block file for the entire image
vector<TSK_DB_FILE_LAYOUT_RANGE> ranges;
ranges.push_back(tempRange);
int64_t fileObjId = 0;
if (TSK_ERR == addUnallocBlockFileInChunks(0, imgSize, m_curImgId, m_curImgId)) {
return TSK_ERR;
}
}
return TSK_OK;
}
/**
* Adds unallocated block files to the database, chunking if enabled.
*
* @returns TSK_OK on success, TSK_ERR on error
*/
TSK_RETVAL_ENUM TskAutoDbJava::addUnallocBlockFileInChunks(uint64_t byteStart, TSK_OFF_T totalSize, int64_t parentObjId, int64_t dataSourceObjId) {
if (m_maxChunkSize <= 0) {
// No chunking - write the entire file
TSK_DB_FILE_LAYOUT_RANGE tempRange(byteStart, totalSize, 0);
vector<TSK_DB_FILE_LAYOUT_RANGE> ranges;
ranges.push_back(tempRange);
int64_t fileObjId = 0;
return addUnallocBlockFile(parentObjId, 0, totalSize, ranges, fileObjId, dataSourceObjId);
}
// We will chunk into separate files with max size m_maxChunkSize
uint64_t maxChunkSize = (uint64_t)m_maxChunkSize;
uint64_t bytesLeft = (uint64_t)totalSize;
uint64_t startingOffset = byteStart;
uint64_t chunkSize;
vector<TSK_DB_FILE_LAYOUT_RANGE> ranges;
while (bytesLeft > 0) {
if (maxChunkSize >= bytesLeft) {
chunkSize = bytesLeft;
bytesLeft = 0;
}
else {
chunkSize = maxChunkSize;
bytesLeft -= maxChunkSize;
}
TSK_DB_FILE_LAYOUT_RANGE tempRange(startingOffset, chunkSize, 0);
ranges.push_back(tempRange);
int64_t fileObjId = 0;
TSK_RETVAL_ENUM retval = addUnallocBlockFile(parentObjId, 0, chunkSize, ranges, fileObjId, dataSourceObjId);
if (retval != TSK_OK) {
return retval;
}
ranges.clear();
startingOffset += chunkSize;
}
return TSK_OK;
}
/**
* Returns the directory currently being analyzed by processFile().
* Safe to use from another thread than processFile().
*
* @returns curDirPath string representing currently analyzed directory
*/
const std::string TskAutoDbJava::getCurDir() {
string curDirPath;
tsk_take_lock(&m_curDirPathLock);
curDirPath = m_curDirPath;
tsk_release_lock(&m_curDirPathLock);
return curDirPath;
}
/*
** The Sleuth Kit
**
** Brian Carrier [carrier <at> sleuthkit [dot] org]
** Copyright (c) 2020 Brian Carrier. All Rights reserved
**
** This software is distributed under the Common Public License 1.0
**
*/
/**
* \file auto_db_java.h
* Contains the class that creates a case-level database of file system
* data from the JNI code.
*/
#ifndef _AUTO_DB_JAVA_H
#define _AUTO_DB_JAVA_H
#include <map>
using std::map;
#include <string>
using std::string;
#include "tsk/auto/tsk_auto_i.h"
#include "tsk/auto/tsk_db.h"
#include "jni.h"
/** \internal
* C++ class that implements TskAuto to load file metadata into a database.
* This is used by the TskCaseDb class.
*/
class TskAutoDbJava :public TskAuto {
public:
TskAutoDbJava();
virtual ~TskAutoDbJava();
virtual uint8_t openImage(int, const TSK_TCHAR * const images[],
TSK_IMG_TYPE_ENUM, unsigned int a_ssize, const char* deviceId = NULL);
virtual uint8_t openImage(const char* a_deviceId = NULL);
virtual uint8_t openImageUtf8(int, const char *const images[],
TSK_IMG_TYPE_ENUM, unsigned int a_ssize, const char* deviceId = NULL);
virtual void closeImage();
void close();
virtual void setTz(string tzone);
virtual void setDatasourceObjId(int64_t img_id);
virtual TSK_FILTER_ENUM filterVs(const TSK_VS_INFO * vs_info);
virtual TSK_FILTER_ENUM filterVol(const TSK_VS_PART_INFO * vs_part);
virtual TSK_FILTER_ENUM filterPool(const TSK_POOL_INFO * pool_info);
virtual TSK_FILTER_ENUM filterPoolVol(const TSK_POOL_VOLUME_INFO * pool_vol);
virtual TSK_FILTER_ENUM filterFs(TSK_FS_INFO * fs_info);
virtual TSK_RETVAL_ENUM processFile(TSK_FS_FILE * fs_file,
const char *path);
const std::string getCurDir();
/**
* Sets whether or not the file systems for an image should be added when
* the image is added to the case database. The default value is true.
*/
void setAddFileSystems(bool addFileSystems);
/**
* Skip processing of orphans on FAT filesystems.
* This will make the loading of the database much faster
* but you will not have all deleted files. Default value is false.
* @param noFatFsOrphans flag set to true if to skip processing orphans on FAT fs
*/
virtual void setNoFatFsOrphans(bool noFatFsOrphans);
/**
* When enabled, records for unallocated file system space will be added to the database. Default value is false.
* @param addUnallocSpace If true, create records for contiguous unallocated file system sectors.
*/
virtual void setAddUnallocSpace(bool addUnallocSpace);
/**
* When enabled, records for unallocated file system space will be added to the database. Default value is false.
* @param addUnallocSpace If true, create records for contiguous unallocated file system sectors.
* @param minChunkSize the number of bytes to group unallocated data into. A value of 0 will create
* one large chunk and group only on volume boundaries. A value of -1 will group each consecutive
* chunk.
*/
virtual void setAddUnallocSpace(bool addUnallocSpace, int64_t minChunkSize);
/**
* When enabled, records for unallocated file system space will be added to the database with the given parameters.
* Automatically sets the flag to create records for contiguous unallocated file system sectors.
* @param minChunkSize the number of bytes to group unallocated data into. A value of 0 will create
* one large chunk and group only on volume boundaries. A value of -1 will group each consecutive
* chunk.
* @param maxChunkSize the maximum number of bytes in one record of unallocated data. A value of -1 will not
* split the records based on size
*/
virtual void setAddUnallocSpace(int64_t minChunkSize, int64_t maxChunkSize);
uint8_t addFilesInImgToDb();
/**
*
*/
uint8_t startAddImage(int numImg, const TSK_TCHAR * const imagePaths[],
TSK_IMG_TYPE_ENUM imgType, unsigned int sSize, const char* deviceId = NULL);
uint8_t startAddImage(TSK_IMG_INFO * img_info, const char* deviceId = NULL);
#ifdef WIN32
uint8_t startAddImage(int numImg, const char *const imagePaths[],
TSK_IMG_TYPE_ENUM imgType, unsigned int sSize, const char* deviceId = NULL);
#endif
void stopAddImage();
int64_t getImageID();
TSK_RETVAL_ENUM initializeJni(JNIEnv *, jobject);
private:
int64_t m_curImgId; ///< Object ID of image currently being processed
int64_t m_curVsId; ///< Object ID of volume system currently being processed
int64_t m_curVolId; ///< Object ID of volume currently being processed
int64_t m_curPoolVol; ///< Object ID of the pool volume currently being processed
int64_t m_curPoolVs; ///< Object ID of the pool volume system currently being processed
int64_t m_curFsId; ///< Object ID of file system currently being processed
int64_t m_curFileId; ///< Object ID of file currently being processed
TSK_INUM_T m_curDirAddr; ///< Meta address the directory currently being processed
int64_t m_curUnallocDirId;
string m_curDirPath; //< Path of the current directory being processed
tsk_lock_t m_curDirPathLock; //< protects concurrent access to m_curDirPath
string m_curImgTZone;
bool m_vsFound;
bool m_volFound;
bool m_poolFound;
bool m_stopped;
bool m_addFileSystems;
bool m_noFatFsOrphans;
bool m_addUnallocSpace;
int64_t m_minChunkSize; ///< -1 for no minimum, 0 for no chunking at all, greater than 0 to wait for that number of chunks before writing to the database
int64_t m_maxChunkSize; ///< Max number of unalloc bytes to process before writing to the database, even if there is no natural break. -1 for no chunking
bool m_foundStructure; ///< Set to true when we find either a volume or file system
bool m_attributeAdded; ///< Set to true when an attribute was added by processAttributes
// These are used to write unallocated blocks for pools at the end of the add image
// process. We can't load the pool_info objects directly from the database so we will
// store info about them here.
std::map<int64_t, int64_t> m_poolOffsetToParentId;
std::map<int64_t, int64_t> m_poolOffsetToVsId;
// JNI data
JNIEnv * m_jniEnv = NULL;
jclass m_callbackClass = NULL;
jobject m_javaDbObj = NULL;
jmethodID m_addImageMethodID = NULL;
jmethodID m_addImageNameMethodID = NULL;
jmethodID m_addAcquisitionDetailsMethodID = NULL;
jmethodID m_addVolumeSystemMethodID = NULL;
jmethodID m_addVolumeMethodID = NULL;
jmethodID m_addPoolMethodID = NULL;
jmethodID m_addFileSystemMethodID = NULL;
jmethodID m_addFileMethodID = NULL;
jmethodID m_addUnallocParentMethodID = NULL;
jmethodID m_addLayoutFileMethodID = NULL;
jmethodID m_addLayoutFileRangeMethodID = NULL;
// Cached objects
vector<TSK_DB_FS_INFO> m_savedFsInfo;
vector<TSK_DB_VS_INFO> m_savedVsInfo;
vector<TSK_DB_VS_PART_INFO> m_savedVsPartInfo;
vector<TSK_DB_OBJECT> m_savedObjects;
void saveObjectInfo(int64_t objId, int64_t parObjId, TSK_DB_OBJECT_TYPE_ENUM type);
TSK_RETVAL_ENUM getObjectInfo(int64_t objId, TSK_DB_OBJECT** obj_info);
TSK_RETVAL_ENUM createJString(const char * inputString, jstring & newJString);
// prevent copying until we add proper logic to handle it
TskAutoDbJava(const TskAutoDbJava&);
TskAutoDbJava & operator=(const TskAutoDbJava&);
//internal structure to keep track of temp. unalloc block range
typedef struct _UNALLOC_BLOCK_WLK_TRACK {
_UNALLOC_BLOCK_WLK_TRACK(TskAutoDbJava & tskAutoDbJava, const TSK_FS_INFO & fsInfo, const int64_t fsObjId, int64_t minChunkSize, int64_t maxChunkSize)
: tskAutoDbJava(tskAutoDbJava),fsInfo(fsInfo),fsObjId(fsObjId),curRangeStart(0), minChunkSize(minChunkSize), maxChunkSize(maxChunkSize), prevBlock(0), isStart(true), nextSequenceNo(0) {}
TskAutoDbJava & tskAutoDbJava;
const TSK_FS_INFO & fsInfo;
const int64_t fsObjId;
vector<TSK_DB_FILE_LAYOUT_RANGE> ranges;
TSK_DADDR_T curRangeStart;
int64_t size;
const int64_t minChunkSize;
const int64_t maxChunkSize;
TSK_DADDR_T prevBlock;
bool isStart;
uint32_t nextSequenceNo;
} UNALLOC_BLOCK_WLK_TRACK;
uint8_t addImageDetails(const char *);
TSK_RETVAL_ENUM insertFileData(TSK_FS_FILE * fs_file,
const TSK_FS_ATTR *, const char *path);
virtual TSK_RETVAL_ENUM processAttribute(TSK_FS_FILE *,
const TSK_FS_ATTR * fs_attr, const char *path);
TSK_RETVAL_ENUM addUnallocatedPoolBlocksToDb(size_t & numPool);
static TSK_WALK_RET_ENUM fsWalkUnallocBlocksCb(const TSK_FS_BLOCK *a_block, void *a_ptr);
TSK_RETVAL_ENUM addFsInfoUnalloc(const TSK_DB_FS_INFO & dbFsInfo);
TSK_RETVAL_ENUM addUnallocFsSpaceToDb(size_t & numFs);
TSK_RETVAL_ENUM addUnallocVsSpaceToDb(size_t & numVsP);
TSK_RETVAL_ENUM addUnallocImageSpaceToDb();
TSK_RETVAL_ENUM addUnallocSpaceToDb();
TSK_RETVAL_ENUM addUnallocBlockFileInChunks(uint64_t byteStart, TSK_OFF_T totalSize, int64_t parentObjId, int64_t dataSourceObjId);
// JNI methods
TSK_RETVAL_ENUM addImageInfo(int type, TSK_OFF_T ssize, int64_t & objId, const string & timezone, TSK_OFF_T size, const string &md5,
const string& sha1, const string& sha256, const string& deviceId, const string& collectionDetails, char** img_ptrs, int num_imgs);
void addAcquisitionDetails(int64_t imgId, const string& collectionDetails);
TSK_RETVAL_ENUM addVsInfo(const TSK_VS_INFO* vs_info, int64_t parObjId, int64_t& objId);
TSK_RETVAL_ENUM addPoolInfoAndVS(const TSK_POOL_INFO *pool_info, int64_t parObjId, int64_t& objId);
TSK_RETVAL_ENUM addPoolVolumeInfo(const TSK_POOL_VOLUME_INFO* pool_vol, int64_t parObjId, int64_t& objId);
TSK_RETVAL_ENUM addVolumeInfo(const TSK_VS_PART_INFO* vs_part, int64_t parObjId, int64_t& objId);
TSK_RETVAL_ENUM addFsInfo(const TSK_FS_INFO* fs_info, int64_t parObjId, int64_t& objId);
TSK_RETVAL_ENUM addFsFile(TSK_FS_FILE* fs_file,
const TSK_FS_ATTR* fs_attr, const char* path,
int64_t fsObjId, int64_t& objId, int64_t dataSourceObjId);
TSK_RETVAL_ENUM addFile(TSK_FS_FILE* fs_file,
const TSK_FS_ATTR* fs_attr, const char* path,
int64_t fsObjId, int64_t parObjId,
int64_t dataSourceObjId);
TSK_RETVAL_ENUM addFileWithLayoutRange(const TSK_DB_FILES_TYPE_ENUM dbFileType, const int64_t parentObjId,
const int64_t fsObjId, const uint64_t size,
vector<TSK_DB_FILE_LAYOUT_RANGE>& ranges, int64_t& objId,
int64_t dataSourceObjId);
TSK_RETVAL_ENUM addUnallocBlockFile(const int64_t parentObjId, const int64_t fsObjId, const uint64_t size,
vector<TSK_DB_FILE_LAYOUT_RANGE>& ranges, int64_t& objId,
int64_t dataSourceObjId);
TSK_RETVAL_ENUM addUnusedBlockFile(const int64_t parentObjId, const int64_t fsObjId, const uint64_t size,
vector<TSK_DB_FILE_LAYOUT_RANGE>& ranges, int64_t& objId,
int64_t dataSourceObjId);
TSK_RETVAL_ENUM addUnallocFsBlockFilesParent(const int64_t fsObjId, int64_t& objId, int64_t dataSourceObjId);
TSK_RETVAL_ENUM addUnallocatedPoolVolume(int vol_index, int64_t parObjId, int64_t& objId);
};
#endif
/*
** dataModel_SleuthkitJNI
** The Sleuth Kit
**
** Brian Carrier [carrier <at> sleuthkit [dot] org]
** Copyright (c) 2010-2018 Brian Carrier. All Rights reserved
**
** This software is distributed under the Common Public License 1.0
**
*/
#include "tsk/tsk_tools_i.h"
#include "tsk/auto/tsk_case_db.h"
#include "tsk/hashdb/tsk_hash_info.h"
#include "tsk/auto/tsk_is_image_supported.h"
#include "tsk/img/img_writer.h"
#include "tsk/img/raw.h"
#include "auto_db_java.h"
#if HAVE_LIBEWF
#include "tsk/img/ewf.h"
#include "tsk/img/tsk_img_i.h"
#endif
#include "jni.h"
#include "dataModel_SleuthkitJNI.h"
#include <locale.h>
#include <time.h>
#include <vector>
#include <map>
#include <string>
#include <algorithm>
#include <sstream>
using std::string;
using std::vector;
using std::map;
using std::stringstream;
static std::vector<TSK_HDB_INFO *> hashDbs;
/*
* JNI file handle structure encapsulates both
* TSK_FS_FILE file handle and TSK_FS_ATTR attribute
* to support multiple attributes for the same file.
* TSK_FS_FILE still needs be maintained for opening and closing.
*/
typedef struct {
uint32_t tag;
TSK_FS_FILE *fs_file;
TSK_FS_ATTR *fs_attr;
} TSK_JNI_FILEHANDLE;
#define TSK_JNI_FILEHANDLE_TAG 0x10101214
//stack-allocated buffer size for read method
#define FIXED_BUF_SIZE (16 * 1024)
/**
* Sets flag to throw an TskCoreException back up to the Java code with a specific message.
* Note: exception is thrown to Java code after the native function returns
* not when setThrowTskCoreError() is invoked - this must be addressed in the code following the exception
* @param the java environment to send the exception to
* @param msg message string
*/
static void
setThrowTskCoreError(JNIEnv * env, const char *msg)
{
jclass exception;
exception = env->FindClass("org/sleuthkit/datamodel/TskCoreException");
env->ThrowNew(exception, msg);
}
/**
* Sets flag to throw an TskCoreException back up to the Java code with the currently set error message.
* Note: exception is thrown to Java code after the native function returns
* not when setThrowTskCoreError() is invoked - this must be addressed in the code following the exception
* @param the java environment to send the exception to
*/
static void
setThrowTskCoreError(JNIEnv * env)
{
const char *msg = tsk_error_get();
setThrowTskCoreError(env, msg);
}
/**
* Sets flag to throw an TskDataException back up to the Java code with a specific message.
* Note: exception is thrown to Java code after the native function returns
* not when setThrowTskDataError() is invoked - this must be addressed in the code following the exception
* @param the java environment to send the exception to
* @param msg message string
*/
static void
setThrowTskDataError(JNIEnv * env, const char *msg)
{
jclass exception;
exception = env->FindClass("org/sleuthkit/datamodel/TskDataException");
env->ThrowNew(exception, msg);
}
#if 0
/**
* Sets flag to throw an TskDataException back up to the Java code with the currently set error message.
* Note: exception is thrown to Java code after the native function returns
* not when setThrowTskDataError() is invoked - this must be addressed in the code following the exception
* @param the java environment to send the exception to
*/
static void
setThrowTskDataError(JNIEnv * env)
{
const char *msg = tsk_error_get();
setThrowTskDataError(env, msg);
}
#endif
/***** Methods to cast from jlong to data type and check tags
They all throw an exception if the incorrect type is passed in. *****/
static TSK_IMG_INFO *
castImgInfo(JNIEnv * env, jlong ptr)
{
TSK_IMG_INFO *lcl = (TSK_IMG_INFO *) ptr;
if (!lcl || lcl->tag != TSK_IMG_INFO_TAG) {
setThrowTskCoreError(env, "Invalid IMG_INFO object");
return 0;
}
return lcl;
}
static TSK_VS_INFO *
castVsInfo(JNIEnv * env, jlong ptr)
{
TSK_VS_INFO *lcl = (TSK_VS_INFO *) ptr;
if (!lcl || lcl->tag != TSK_VS_INFO_TAG) {
setThrowTskCoreError(env, "Invalid VS_INFO object");
return 0;
}
// verify that image handle is still open
if (!castImgInfo(env, (jlong) lcl->img_info)) {
return 0;
}
return lcl;
}
static TSK_VS_PART_INFO *
castVsPartInfo(JNIEnv * env, jlong ptr)
{
TSK_VS_PART_INFO *lcl = (TSK_VS_PART_INFO *) ptr;
if (!lcl || lcl->tag != TSK_VS_PART_INFO_TAG) {
setThrowTskCoreError(env, "Invalid VS_PART_INFO object");
return 0;
}
// verify that all handles are still open
if (!castVsInfo(env, (jlong) lcl->vs)) {
return 0;
}
return lcl;
}
static TSK_POOL_INFO *
castPoolInfo(JNIEnv * env, jlong ptr)
{
TSK_POOL_INFO *lcl = (TSK_POOL_INFO *)ptr;
if (!lcl || lcl->tag != TSK_POOL_INFO_TAG) {
setThrowTskCoreError(env, "Invalid TSK_POOL_INFO object");
return 0;
}
return lcl;
}
static TSK_FS_INFO *
castFsInfo(JNIEnv * env, jlong ptr)
{
TSK_FS_INFO *lcl = (TSK_FS_INFO *) ptr;
if (!lcl || lcl->tag != TSK_FS_INFO_TAG) {
setThrowTskCoreError(env, "Invalid FS_INFO object");
return 0;
}
// verify that image handle is still open
if (!castImgInfo(env, (jlong) lcl->img_info)) {
return 0;
}
return lcl;
}
static TSK_FS_FILE *
castFsFile(JNIEnv * env, jlong ptr)
{
TSK_FS_FILE *lcl = (TSK_FS_FILE *)ptr;
if (lcl == NULL || lcl->tag != TSK_FS_FILE_TAG) {
setThrowTskCoreError(env, "Invalid FS_FILE object");
return 0;
}
// verify that file system handle is still open
if (!castFsInfo(env, (jlong)lcl->fs_info)) {
return 0;
}
return lcl;
}
static TSK_JNI_FILEHANDLE *
castJniFileHandle(JNIEnv * env, jlong ptr)
{
TSK_JNI_FILEHANDLE *lcl = (TSK_JNI_FILEHANDLE *) ptr;
if (!lcl || lcl->tag != TSK_JNI_FILEHANDLE_TAG) {
setThrowTskCoreError(env, "Invalid TSK_JNI_FILEHANDLE object");
return 0;
}
// verify that all handles are still open
if (!castFsFile(env, (jlong) lcl->fs_file)) {
return 0;
}
return lcl;
}
static TskCaseDb *
castCaseDb(JNIEnv * env, jlong ptr)
{
TskCaseDb *lcl = ((TskCaseDb *) ptr);
if (lcl == NULL || lcl->m_tag != TSK_CASE_DB_TAG) {
setThrowTskCoreError(env,
"Invalid TskCaseDb object");
return 0;
}
return lcl;
}
/**
* Convert a jstring (UTF-8) to a TCHAR to pass into TSK methods.
* @param buffer Buffer to store resulting string into
* @param size Length of buffer
* @param strJ string to convert
* @returns 1 on error
*/
static int
toTCHAR(JNIEnv * env, TSK_TCHAR * buffer, size_t size, jstring strJ)
{
jboolean isCopy;
char *str8 = (char *) env->GetStringUTFChars(strJ, &isCopy);
#ifdef TSK_WIN32
// Windows TCHAR is UTF16 in Windows, so convert
UTF16 *utf16 = (UTF16 *) buffer;
UTF8 *utf8 = (UTF8 *) str8;;
TSKConversionResult retval;
size_t lengthOfUtf8 = strlen(str8);
retval =
tsk_UTF8toUTF16((const UTF8 **) &utf8, &utf8[lengthOfUtf8],
&utf16, &utf16[size], TSKlenientConversion);
if (retval != TSKconversionOK) {
tsk_error_set_errno(TSK_ERR_IMG_CONVERT);
tsk_error_set_errstr
("toTCHAR: Error converting UTF8 %s to UTF16, error %d",
utf8, retval);
env->ReleaseStringUTFChars(strJ, str8);
return 1;
}
// "utf16" now points to last char. Need to NULL terminate the string.
*utf16 = '\0';
#else
// nothing to convert. Keep it as UTF8
strncpy((char *)&buffer[0], str8, size);
#endif
env->ReleaseStringUTFChars(strJ, str8);
return 0;
}
/**
* Opens an existing hash database.
* @param env Pointer to Java environment from which this method was called.
* @param obj The Java object from which this method was called.
* @param pathJ The path to the hash database.
* @return A handle for the hash database.
*/
JNIEXPORT jint JNICALL
Java_org_sleuthkit_datamodel_SleuthkitJNI_hashDbOpenNat(JNIEnv * env,
jclass obj, jstring pathJ)
{
TSK_TCHAR pathT[1024];
toTCHAR(env, pathT, 1024, pathJ);
TSK_HDB_INFO *db = tsk_hdb_open(pathT, TSK_HDB_OPEN_NONE);
if (!db)
{
setThrowTskCoreError(env, tsk_error_get_errstr());
return -1;
}
// The index of the pointer in the vector is used as a handle for the
// database.
hashDbs.push_back(db);
return (jint)hashDbs.size();
}
/**
* Creates a new hash database.
* @param env Pointer to Java environment from which this method was called.
* @param obj The Java object from which this method was called.
* @param pathJ The path to the hash database.
* @return A handle for the hash database.
*/
JNIEXPORT jint JNICALL
Java_org_sleuthkit_datamodel_SleuthkitJNI_hashDbNewNat(JNIEnv * env,
jclass obj, jstring pathJ)
{
TSK_TCHAR pathT[1024];
toTCHAR(env, pathT, 1024, pathJ);
if (tsk_hdb_create(pathT)) {
setThrowTskCoreError(env, tsk_error_get_errstr());
return -1;
}
TSK_HDB_INFO *db = tsk_hdb_open(pathT, TSK_HDB_OPEN_NONE);
if (!db) {
setThrowTskCoreError(env, tsk_error_get_errstr());
return -1;
}
// The index of the pointer in the vector is used as a handle for the
// database.
hashDbs.push_back(db);
return (jint)hashDbs.size();
}
/**
* Begins a hash database transaction.
* @param env Pointer to Java environment from which this method was called.
* @param obj The Java object from which this method was called.
* @param dbHandle A handle for the hash database.
* @return 1 on error and 0 on success.
*/
JNIEXPORT jint JNICALL
Java_org_sleuthkit_datamodel_SleuthkitJNI_hashDbBeginTransactionNat(
JNIEnv *env, jclass obj, jint dbHandle)
{
if((size_t)dbHandle > hashDbs.size()) {
setThrowTskCoreError(env, "Invalid database handle");
return 1;
}
TSK_HDB_INFO *db = hashDbs.at(dbHandle - 1);
if (!db) {
setThrowTskCoreError(env, "Invalid database handle");
return 1;
}
return tsk_hdb_begin_transaction(db);
}
/**
* Commits a hash database transaction.
* @param env Pointer to Java environment from which this method was called.
* @param obj The Java object from which this method was called.
* @param dbHandle A handle for the hash database.
* @return 1 on error and 0 on success.
*/
JNIEXPORT jint JNICALL
Java_org_sleuthkit_datamodel_SleuthkitJNI_hashDbCommitTransactionNat(
JNIEnv *env, jclass obj, jint dbHandle)
{
if((size_t)dbHandle > hashDbs.size()) {
setThrowTskCoreError(env, "Invalid database handle");
return 1;
}
TSK_HDB_INFO *db = hashDbs.at(dbHandle - 1);
if (!db) {
setThrowTskCoreError(env, "Invalid database handle");
return 1;
}
return tsk_hdb_commit_transaction(db);
}
/**
* Rolls back a hash database transaction.
* @param env Pointer to Java environment from which this method was called.
* @param obj The Java object from which this method was called.
* @param dbHandle A handle for the hash database.
* @return 1 on error and 0 on success.
*/
JNIEXPORT jint JNICALL
Java_org_sleuthkit_datamodel_SleuthkitJNI_hashDbRollbackTransactionNat(
JNIEnv *env, jclass obj, jint dbHandle)
{
if((size_t)dbHandle > hashDbs.size()) {
setThrowTskCoreError(env, "Invalid database handle");
return 1;
}
TSK_HDB_INFO *db = hashDbs.at(dbHandle-1);
if (!db) {
setThrowTskCoreError(env, "Invalid database handle");
return 1;
}
return tsk_hdb_rollback_transaction(db);
}
/**
* Adds data to a hash database.
* @param env Pointer to Java environment from which this method was called.
* @param obj The Java object from which this method was called.
* @param filenameJ Name of the file that was hashed (can be null).
* @param hashMd5J MD5 hash of file contents (can be null).
* @param hashSha1J SHA-1 hash of file contents (can be null).
* @param hashSha256J Text of SHA256 hash (can be null).
* @param dbHandle A handle for the hash database.
* @return 1 on error and 0 on success.
*/
JNIEXPORT jint JNICALL
Java_org_sleuthkit_datamodel_SleuthkitJNI_hashDbAddEntryNat(JNIEnv * env,
jclass obj, jstring filenameJ, jstring hashMd5J, jstring hashSha1J, jstring hashSha256J,
jstring commentJ, jint dbHandle)
{
if((size_t) dbHandle > hashDbs.size()) {
setThrowTskCoreError(env, "Invalid database handle");
return 1;
}
TSK_HDB_INFO * db = hashDbs.at(dbHandle-1);
if(!db) {
setThrowTskCoreError(env, "Invalid database handle");
return 1;
}
if(!db->accepts_updates()) {
setThrowTskCoreError(env, "Database does not accept updates");
return 1;
}
jboolean isCopy;
const char * name = filenameJ ? (const char *) env->GetStringUTFChars(filenameJ, &isCopy) : NULL;
const char * md5 = hashMd5J ? (const char *) env->GetStringUTFChars(hashMd5J, &isCopy) : NULL;
const char * sha1 = hashSha1J ? (const char *) env->GetStringUTFChars(hashSha1J, &isCopy) : NULL;
const char * sha256 = hashSha256J ? (const char *) env->GetStringUTFChars(hashSha256J, &isCopy) : NULL;
const char * comment = commentJ ? (const char *) env->GetStringUTFChars(commentJ, &isCopy) : NULL;
if (tsk_hdb_add_entry(db, name, md5, sha1, sha256, comment)) {
setThrowTskCoreError(env, tsk_error_get_errstr());
}
if (filenameJ) {
env->ReleaseStringUTFChars(filenameJ, (const char *) name);
}
if (hashMd5J) {
env->ReleaseStringUTFChars(hashMd5J, (const char *) md5);
}
if (hashSha1J) {
env->ReleaseStringUTFChars(hashSha1J, (const char *) sha1);
}
if (hashSha256J) {
env->ReleaseStringUTFChars(hashSha256J, (const char *) sha256);
}
if (commentJ) {
env->ReleaseStringUTFChars(commentJ, (const char *) comment);
}
return 0;
}
/**
* Queries whether or not a hash database accepts updates.
* @param env Pointer to Java environment from which this method was called.
* @param obj The Java object from which this method was called.
* @param dbHandle A handle for the hash database.
* @return True if hash database can be updated.
*/
JNIEXPORT jboolean JNICALL
Java_org_sleuthkit_datamodel_SleuthkitJNI_hashDbIsUpdateableNat(JNIEnv * env,
jclass obj, jint dbHandle)
{
if((size_t)dbHandle > hashDbs.size()) {
setThrowTskCoreError(env, "Invalid database handle");
return (jboolean)false;
}
TSK_HDB_INFO *db = hashDbs.at(dbHandle-1);
if (db == NULL) {
setThrowTskCoreError(env, "Invalid database handle");
return (jboolean)false;
}
return (jboolean)(tsk_hdb_accepts_updates(db) == static_cast<uint8_t>(1));
}
/**
* Queries whether or not a hash database can be re-indexed. Only text-format
* databases with external indexes can be re-indexed.
* @param env Pointer to Java environment from which this method was called.
* @param obj The Java object from which this method was called.
* @param dbHandle A handle for the hash database.
* @return True if hash database can be indexed.
*/
JNIEXPORT jboolean JNICALL
Java_org_sleuthkit_datamodel_SleuthkitJNI_hashDbIsReindexableNat(JNIEnv * env,
jclass obj, jint dbHandle)
{
if((size_t)dbHandle > hashDbs.size()) {
setThrowTskCoreError(env, "Invalid database handle");
return (jboolean)false;
}
TSK_HDB_INFO *db = hashDbs.at(dbHandle-1);
if (db == NULL) {
setThrowTskCoreError(env, "Invalid database handle");
return (jboolean)false;
}
return (jboolean)((tsk_hdb_uses_external_indexes(db) == 1) &&
(tsk_hdb_is_idx_only(db) == 0));
}
/**
* Gets the path of a hash database.
* @param env Pointer to Java environment from which this method was called.
* @param obj The Java object from which this method was called.
* @param dbHandle A handle for the hash database.
* @return Path to the hash database or "None" if no path is available.
*/
JNIEXPORT jstring JNICALL
Java_org_sleuthkit_datamodel_SleuthkitJNI_hashDbPathNat(JNIEnv * env,
jclass obj, jint dbHandle)
{
if((size_t)dbHandle > hashDbs.size()) {
setThrowTskCoreError(env, "Invalid database handle");
return NULL;
}
TSK_HDB_INFO *db = hashDbs.at(dbHandle-1);
if (db == NULL) {
setThrowTskCoreError(env, "Invalid database handle");
return NULL;
}
jstring jPath = NULL;
const TSK_TCHAR *dbPath = tsk_hdb_get_db_path(db);
if (NULL != dbPath) {
const size_t pathLength = TSTRLEN(dbPath);
char *cPath = (char*)tsk_malloc((pathLength + 1) * sizeof(char));
snprintf(cPath, pathLength + 1, "%" PRIttocTSK, dbPath);
jPath = env->NewStringUTF(cPath);
free(cPath);
}
else {
jPath = env->NewStringUTF("None");
}
return jPath;
}
/*
* Gets the path of the external MD5 hash index for a text-format database.
* @param env Pointer to Java environment from which this method was called.
* @param obj The Java object from which this method was called.
* @param dbHandle A handle for the hash database.
* @return Path to the requested index or "None" if no path is available.
*/
JNIEXPORT jstring JNICALL
Java_org_sleuthkit_datamodel_SleuthkitJNI_hashDbIndexPathNat(JNIEnv * env,
jclass obj, jint dbHandle)
{
if((size_t)dbHandle > hashDbs.size()) {
setThrowTskCoreError(env, "Invalid database handle");
return NULL;
}
TSK_HDB_INFO *db = hashDbs.at(dbHandle-1);
if (db == NULL) {
setThrowTskCoreError(env, "Invalid database handle");
return NULL;
}
// Currently only supporting md5 indexes through Java binding.
jstring jPath = NULL;
const TSK_TCHAR *indexPath = tsk_hdb_get_idx_path(db, TSK_HDB_HTYPE_MD5_ID);
if (NULL != indexPath) {
const size_t pathLength = TSTRLEN(indexPath);
char *cPath = (char*)tsk_malloc((pathLength + 1) * sizeof(char));
snprintf(cPath, pathLength + 1, "%" PRIttocTSK, indexPath);
jPath = env->NewStringUTF(cPath);
free(cPath);
}
else {
jPath = env->NewStringUTF("None");
}
return jPath;
}
/**
* Queries whether the hash database is actually an external index for a
* text-format database that is being used for simple yes/no look ups in
* place of the roginal hash database.
* @param env Pointer to Java environment from which this method was called.
* @param obj The Java object from which this method was called.
* @param dbHandle A handle for the hash database.
* @return True if the hash database is an external index serving as a
* database.
*/
JNIEXPORT jboolean JNICALL
Java_org_sleuthkit_datamodel_SleuthkitJNI_hashDbIsIdxOnlyNat(JNIEnv * env,
jclass obj, jint dbHandle)
{
if((size_t)dbHandle > hashDbs.size()) {
setThrowTskCoreError(env, "Invalid database handle");
return (jboolean)false;
}
TSK_HDB_INFO *db = hashDbs.at(dbHandle-1);
if (db == NULL) {
setThrowTskCoreError(env, "Invalid database handle");
return (jboolean)false;
}
return (jboolean)(tsk_hdb_is_idx_only(db) == static_cast<uint8_t>(1));
}
/**
* Gets the display name of a hash database.
* @param env Pointer to Java environment from which this method was called.
* @param obj The Java object from which this method was called.
* @param dbHandle A handle for the hash database.
* @return The display name.
*/
JNIEXPORT jstring JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_hashDbGetDisplayName
(JNIEnv * env, jclass obj, jint dbHandle)
{
if((size_t)dbHandle > hashDbs.size()) {
setThrowTskCoreError(env, "Invalid database handle");
return NULL;
}
TSK_HDB_INFO *db = hashDbs.at(dbHandle-1);
if (db == NULL) {
setThrowTskCoreError(env, "Invalid database handle");
return NULL;
}
jstring j_name = NULL;
const char *db_name = tsk_hdb_get_display_name(db);
if (NULL != db_name) {
j_name = env->NewStringUTF(db_name);
}
return j_name;
}
/**
* Closes all open hash databases.
* @param env Pointer to Java environment from which this method was called.
* @param obj The Java object from which this method was called.
* @param dbHandle A handle for the hash database.
*/
JNIEXPORT void JNICALL
Java_org_sleuthkit_datamodel_SleuthkitJNI_hashDbCloseAll(JNIEnv * env,
jclass obj)
{
for (std::vector<TSK_HDB_INFO *>::iterator it = hashDbs.begin(); it != hashDbs.end(); ++it) {
if (NULL != *it) {
tsk_hdb_close(*it);
}
}
hashDbs.clear();
}
/**
* Closes a hash database.
* @param env Pointer to Java environment from which this method was called.
* @param obj The Java object from which this method was called.
* @param dbHandle A handle for the hash database.
*/
JNIEXPORT void JNICALL
Java_org_sleuthkit_datamodel_SleuthkitJNI_hashDbClose(JNIEnv * env,
jclass obj, jint dbHandle)
{
if((size_t)dbHandle > hashDbs.size()) {
setThrowTskCoreError(env, "Invalid database handle");
return;
}
TSK_HDB_INFO *db = hashDbs.at(dbHandle-1);
if (db == NULL) {
setThrowTskCoreError(env, "Invalid database handle");
return;
}
tsk_hdb_close(db);
// Do NOT erase the element because that would shift the indices,
// messing up the existing handles.
hashDbs.at(dbHandle-1) = NULL;
}
/**
* Looks up a hash in a hash database.
* @param env Pointer to Java environment from which this method was called.
* @param obj The Java object from which this method was called.
* @param dbHandle A handle for the hash database.
* @return True if the hash is found in the hash database, false otherwise.
*/
JNIEXPORT jboolean JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_hashDbLookup
(JNIEnv * env, jclass obj, jstring hash, jint dbHandle)
{
if ((size_t)dbHandle > hashDbs.size()) {
setThrowTskCoreError(env, "Invalid database handle");
return (jboolean)false;
}
TSK_HDB_INFO *db = hashDbs.at(dbHandle-1);
if (db == NULL) {
setThrowTskCoreError(env, "Invalid database handle");
return (jboolean)false;
}
jboolean isCopy;
const char *cHashStr = (const char *) env->GetStringUTFChars(hash, &isCopy);
jboolean file_known = false;
int8_t retval = tsk_hdb_lookup_str(db, cHashStr, TSK_HDB_FLAG_QUICK, NULL, NULL);
if (retval == -1) {
setThrowTskCoreError(env, tsk_error_get_errstr());
}
else if (retval) {
file_known = true;
}
env->ReleaseStringUTFChars(hash, (const char *) cHashStr);
return file_known;
}
/**
* Looks up a hash in a hash database.
* @param env Pointer to Java environment from which this method was called.
* @param obj The Java object from which this method was called.
* @param dbHandle A handle for the hash database.
* @return A HashInfo object if the hash is found, NULL otherwise.
*/
JNIEXPORT jobject JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_hashDbLookupVerbose
(JNIEnv * env, jclass obj, jstring hash, jint dbHandle) {
if ((size_t)dbHandle > hashDbs.size()) {
setThrowTskCoreError(env, "Invalid database handle");
return NULL;
}
TSK_HDB_INFO *db = hashDbs.at(dbHandle-1);
if (db == NULL) {
setThrowTskCoreError(env, "Invalid database handle");
return NULL;
}
jboolean isCopy;
const char *inputHash = (const char *) env->GetStringUTFChars(hash, &isCopy);
TskHashInfo result;
int8_t returnCode = tsk_hdb_lookup_verbose_str(db, inputHash, (void*)&result);
env->ReleaseStringUTFChars(hash, (const char *) inputHash);
if (returnCode == -1) {
setThrowTskCoreError(env, tsk_error_get_errstr());
return NULL;
}
else if (returnCode == 0) {
return NULL;
}
// Convert the hashes from the hash database so they can be written into
// the Java version of a HashInfo object.
const char *md5 = result.hashMd5.c_str();
jstring md5j = env->NewStringUTF(md5);
const char *sha1 = result.hashSha1.c_str();
jstring sha1j = env->NewStringUTF(sha1);
const char *sha256 = result.hashSha2_256.c_str();
jstring sha256j = env->NewStringUTF(sha256);
// Create and return a Java HashInfo object.
jclass clazz;
clazz = env->FindClass("org/sleuthkit/datamodel/HashHitInfo");
jmethodID ctor = env->GetMethodID(clazz, "<init>", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
jmethodID addName = env->GetMethodID(clazz, "addName", "(Ljava/lang/String;)V");
jmethodID addComment = env->GetMethodID(clazz, "addComment", "(Ljava/lang/String;)V");
jobject hashInfo = env->NewObject(clazz, ctor, md5j, sha1j, sha256j);
for (std::vector<std::string>::iterator it = result.fileNames.begin(); it != result.fileNames.end(); ++it) {
jstring namej = env->NewStringUTF((*it).c_str());
env->CallVoidMethod(hashInfo, addName, namej);
}
for (std::vector<std::string>::iterator it = result.comments.begin(); it != result.comments.end(); ++it) {
jstring commentj = env->NewStringUTF((*it).c_str());
env->CallVoidMethod(hashInfo, addComment, commentj);
}
return hashInfo;
}
/*
* Initialize a process for adding an image to a case database.
*
* @param env Pointer to java environment.
* @param obj Pointer the Java class object.
* @param timeZone The time zone for the image.
* @param addUnallocSpace Pass true to create virtual files for unallocated space. Ignored if addFileSystems is false.
* @param skipFatFsOrphans Pass true to skip processing of orphan files for FAT file systems. Ignored if addFileSystems is false.
* @param hostId Id of the host (already in the database).
*
* @return A pointer to the process (TskAutoDbJava object) or NULL on error.
*/
JNIEXPORT jlong JNICALL
Java_org_sleuthkit_datamodel_SleuthkitJNI_initAddImgNat(JNIEnv * env,
jclass obj, jobject callbackObj, jstring timeZone, jboolean addUnallocSpace, jboolean skipFatFsOrphans) {
return Java_org_sleuthkit_datamodel_SleuthkitJNI_initializeAddImgNat(env, obj, callbackObj, timeZone, true, addUnallocSpace, skipFatFsOrphans);
}
/*
* Initialize a process for adding an image to a case database.
*
* @param env Pointer to java environment.
* @param obj Pointer the Java class object.
* @param timeZone The time zone for the image.
* @param addFileSystems Pass true to attempt to add file systems within the image to the case database.
* @param addUnallocSpace Pass true to create virtual files for unallocated space. Ignored if addFileSystems is false.
* @param skipFatFsOrphans Pass true to skip processing of orphan files for FAT file systems. Ignored if addFileSystems is false.
* @param hostId The ID of the host (already in database).
*
* @return A pointer to the process (TskAutoDbJava object) or NULL on error.
*/
JNIEXPORT jlong JNICALL
Java_org_sleuthkit_datamodel_SleuthkitJNI_initializeAddImgNat(JNIEnv * env, jclass obj,
jobject callbackObj, jstring timeZone, jboolean addFileSystems, jboolean addUnallocSpace, jboolean skipFatFsOrphans) {
jboolean isCopy;
if (env->GetStringUTFLength(timeZone) > 0) {
const char *tzstr = env->GetStringUTFChars(timeZone, &isCopy);
if (strlen(tzstr) > 64) {
env->ReleaseStringUTFChars(timeZone, tzstr);
stringstream ss;
ss << "Timezone is too long";
setThrowTskCoreError(env, ss.str().c_str());
return 0;
}
char envstr[70];
snprintf(envstr, 70, "TZ=%s", tzstr);
env->ReleaseStringUTFChars(timeZone, tzstr);
if (0 != putenv(envstr)) {
stringstream ss;
ss << "Error setting timezone environment, using: ";
ss << envstr;
setThrowTskCoreError(env, ss.str().c_str());
return 0;
}
/* we should be checking this somehow */
TZSET();
}
TskAutoDbJava *tskAutoJava = new TskAutoDbJava();
if (tskAutoJava == NULL) {
setThrowTskCoreError(env, "Error creating TskAutoDbJava");
return 0;
}
// set the options flags
tskAutoJava->setAddFileSystems(addFileSystems?true:false);
if (addFileSystems) {
if (addUnallocSpace) {
// Minimum size of unalloc files: 500 MB, maximum size: 1 GB
tskAutoJava->setAddUnallocSpace((int64_t)500 * 1024 * 1024, (int64_t)1024 * 1024 * 1024);
}
else {
tskAutoJava->setAddUnallocSpace(false);
}
tskAutoJava->setNoFatFsOrphans(skipFatFsOrphans?true:false);
} else {
tskAutoJava->setAddUnallocSpace(false);
tskAutoJava->setNoFatFsOrphans(true);
}
// Set up the callbacks
if (TSK_ERR == tskAutoJava->initializeJni(env, callbackObj)) {
setThrowTskCoreError(env, "Error initializing JNI callbacks");
return 0;
}
return (jlong)tskAutoJava;
}
/*
* Add an image to a database using a pre-created process, which can be cancelled.
* MUST call commitAddImg or revertAddImg afterwards once runAddImg returns. If there is an
* error, you do not need to call revert or commit and the 'process' handle will be deleted.
*
* @param env pointer to java environment this was called from
* @param obj the java object this was called from
* @param process the add-image process created by initAddImgNat
* @param deviceId An ASCII-printable identifier for the device associated with the data source that is intended to be unique across multiple cases (e.g., a UUID)
* @param paths array of strings from java, the paths to the image parts
* @param numImgs number of image parts
* @param timeZone the timezone the image is from
*/
JNIEXPORT void JNICALL
Java_org_sleuthkit_datamodel_SleuthkitJNI_runOpenAndAddImgNat(JNIEnv * env,
jclass obj, jlong process, jstring deviceId, jobjectArray paths, jint numImgs, jstring timeZone) {
TskAutoDbJava *tskAuto = ((TskAutoDbJava *) process);
if (!tskAuto || tskAuto->m_tag != TSK_AUTO_TAG) {
setThrowTskCoreError(env,
"runOpenAndAddImgNat: Invalid TskAutoDbJava object passed in");
return;
}
jboolean isCopy;
const char *device_id = NULL;
if (NULL != deviceId) {
device_id = (const char *) env->GetStringUTFChars(deviceId, &isCopy);
if (NULL == device_id) {
setThrowTskCoreError(env, "runOpenAndAddImgNat: Can't convert data source id string");
return;
}
}
// Get pointers to each of the image file names.
char **imagepaths8 = (char **) tsk_malloc(numImgs * sizeof(char *));
if (imagepaths8 == NULL) {
setThrowTskCoreError(env);
return;
}
for (int i = 0; i < numImgs; i++) {
jstring jsPath = (jstring) env->GetObjectArrayElement(paths,
i);
imagepaths8[i] =
(char *) env->
GetStringUTFChars(jsPath, &isCopy);
if (imagepaths8[i] == NULL) {
setThrowTskCoreError(env,
"runOpenAndAddImgNat: Can't convert path strings.");
// @@@ should cleanup here paths that have been converted in imagepaths8[i]
return;
}
}
// Set the time zone.
if (env->GetStringLength(timeZone) > 0) {
const char *time_zone = env->GetStringUTFChars(timeZone, &isCopy);
tskAuto->setTz(string(time_zone));
env->ReleaseStringUTFChars(timeZone, time_zone);
}
// Add the data source.
uint8_t ret = 0;
if ( (ret = tskAuto->startAddImage((int) numImgs, imagepaths8,
TSK_IMG_TYPE_DETECT, 0, device_id)) != 0) {
stringstream msgss;
msgss << "Errors occurred while ingesting image " << std::endl;
vector<TskAuto::error_record> errors = tskAuto->getErrorList();
for (size_t i = 0; i < errors.size(); i++) {
msgss << (i+1) << ". ";
msgss << (TskAuto::errorRecordToString(errors[i]));
msgss << " " << std::endl;
}
if (ret == 1) {
// Fatal error
setThrowTskCoreError(env, msgss.str().c_str());
}
else if (ret == 2) {
// Non-fatal error
setThrowTskDataError(env, msgss.str().c_str());
}
}
// @@@ SHOULD WE CLOSE HERE before we commit / revert etc.
//close image first before freeing the image paths
tskAuto->closeImage();
// Cleanup
for (int i = 0; i < numImgs; i++) {
jstring jsPath = (jstring)
env->GetObjectArrayElement(paths, i);
env->
ReleaseStringUTFChars(jsPath, imagepaths8[i]);
env->DeleteLocalRef(jsPath);
}
free(imagepaths8);
env->ReleaseStringUTFChars(deviceId, (const char *) device_id);
// // Must call finishAddImgNat to free the TskAutoDb
}
/*
* Add an image to a database using a pre-created process, which can be cancelled.
* MUST call commitAddImg or revertAddImg afterwards once runAddImg returns. If there is an
* error, you do not need to call revert or commit and the 'process' handle will be deleted.
*
* @param env pointer to java environment this was called from
* @param obj the java object this was called from
* @param process the add-image process created by initAddImgNat
* @param deviceId An ASCII-printable identifier for the device associated with the data source that is intended to be unique across multiple cases (e.g., a UUID)
* @param a_img_info image info object
* @param img_id The object ID of the image in the database
* @param timeZone the timezone the image is from
*/
JNIEXPORT void JNICALL
Java_org_sleuthkit_datamodel_SleuthkitJNI_runAddImgNat(JNIEnv * env,
jclass obj, jlong process, jstring deviceId, jlong a_img_info, jlong img_id, jstring timeZone, jstring imageWriterPathJ) {
TskAutoDbJava *tskAuto = ((TskAutoDbJava *)process);
if (!tskAuto || tskAuto->m_tag != TSK_AUTO_TAG) {
setThrowTskCoreError(env,
"runAddImgNat: Invalid TskAutoDbJava object passed in");
return;
}
jboolean isCopy;
const char *device_id = NULL;
if (NULL != deviceId) {
device_id = (const char *)env->GetStringUTFChars(deviceId, &isCopy);
if (NULL == device_id) {
setThrowTskCoreError(env, "runAddImgNat: Can't convert data source id string");
return;
}
}
// Set the data source object ID
tskAuto->setDatasourceObjId(img_id);
// Set the time zone.
if (env->GetStringLength(timeZone) > 0) {
const char *time_zone = env->GetStringUTFChars(timeZone, &isCopy);
tskAuto->setTz(string(time_zone));
env->ReleaseStringUTFChars(timeZone, time_zone);
}
// Set up the TSK_IMG_INFO object
TSK_IMG_INFO *img_info = castImgInfo(env, a_img_info);
// Set up image writer, if the output path is present
if (env->GetStringLength(imageWriterPathJ) > 0) {
const char *imageWriterPath = env->GetStringUTFChars(imageWriterPathJ, &isCopy);
if (TSK_OK != tskAuto->enableImageWriter(imageWriterPath)) {
env->ReleaseStringUTFChars(imageWriterPathJ, imageWriterPath);
setThrowTskCoreError(env,
"runAddImgNat: error enabling image writer.");
return;
}
env->ReleaseStringUTFChars(imageWriterPathJ, imageWriterPath);
}
else {
tskAuto->disableImageWriter();
}
// Add the data source.
uint8_t ret = 0;
if ((ret = tskAuto->startAddImage(img_info, device_id)) != 0) {
stringstream msgss;
msgss << "Errors occurred while ingesting image " << std::endl;
vector<TskAuto::error_record> errors = tskAuto->getErrorList();
for (size_t i = 0; i < errors.size(); i++) {
msgss << (i + 1) << ". ";
msgss << (TskAuto::errorRecordToString(errors[i]));
msgss << " " << std::endl;
}
if (ret == 1) {
// Fatal error
setThrowTskCoreError(env, msgss.str().c_str());
}
else if (ret == 2) {
// Non-fatal error
setThrowTskDataError(env, msgss.str().c_str());
}
}
// @@@ SHOULD WE CLOSE HERE before we commit / revert etc.
//close image first before freeing the image paths
tskAuto->closeImage();
// Cleanup
env->ReleaseStringUTFChars(deviceId, (const char *)device_id);
// Must call finishAddImgNat to free the TskAutoDb
}
/*
* Cancel the given add-image process.
* @param env pointer to java environment this was called from
* @param obj the java object this was called from
* @param process the add-image process created by initAddImgNat
*/
JNIEXPORT void JNICALL
Java_org_sleuthkit_datamodel_SleuthkitJNI_stopAddImgNat(JNIEnv * env,
jclass obj, jlong process) {
TskAutoDbJava *tskAuto = ((TskAutoDbJava *) process);
if (!tskAuto || tskAuto->m_tag != TSK_AUTO_TAG) {
setThrowTskCoreError(env,
"stopAddImgNat: Invalid TskAutoDbJava object passed in");
return;
}
tskAuto->stopAddImage();
}
/*
* Completes the given add-image process. Deletes the 'process' handle and
* returns the ID of the added image.
* @param env pointer to java environment this was called from
* @param obj the java object this was called from
* @param process the add-image process created by initAddImgNat
*/
JNIEXPORT jlong JNICALL
Java_org_sleuthkit_datamodel_SleuthkitJNI_finishAddImgNat(JNIEnv * env,
jclass obj, jlong process) {
TskAutoDbJava *tskAuto = ((TskAutoDbJava *)process);
if (!tskAuto || tskAuto->m_tag != TSK_AUTO_TAG) {
setThrowTskCoreError(env,
"commitAddImgNat: Invalid TskAutoDb object passed in");
return -1;
}
int64_t imgId = tskAuto->getImageID();
tskAuto->close();
delete tskAuto;
tskAuto = 0;
if (imgId == -1) {
setThrowTskCoreError(env);
return -1;
}
return imgId;
}
/*
* Open an image pointer for the given image.
* @return the created TSK_IMG_INFO pointer
* @param env pointer to java environment this was called from
* @param obj the java object this was called from
* @param paths the paths to the image parts
* @param num_imgs number of image parts
* @param sector_size the sector size (use '0' for autodetect)
*/
JNIEXPORT jlong JNICALL
Java_org_sleuthkit_datamodel_SleuthkitJNI_openImgNat(JNIEnv * env,
jclass obj, jobjectArray paths, jint num_imgs, jint sector_size) {
TSK_IMG_INFO *img_info;
jboolean isCopy;
// get pointers to each of the file names
char **imagepaths8 = (char **)tsk_malloc(num_imgs * sizeof(char *));
if (imagepaths8 == NULL) {
setThrowTskCoreError(env);
return 0;
}
for (int i = 0; i < num_imgs; i++) {
imagepaths8[i] =
(char *)env->
GetStringUTFChars((jstring)env->GetObjectArrayElement(paths,
i), &isCopy);
// @@@ Error check
}
// open the image
img_info =
tsk_img_open_utf8((int)num_imgs, imagepaths8, TSK_IMG_TYPE_DETECT,
sector_size);
if (img_info == NULL) {
setThrowTskCoreError(env, tsk_error_get());
}
// cleanup
for (int i = 0; i < num_imgs; i++) {
env->
ReleaseStringUTFChars((jstring)
env->GetObjectArrayElement(paths, i), imagepaths8[i]);
}
free(imagepaths8);
return (jlong) img_info;
}
/*
* Get the full list of paths associated with an image.
*/
JNIEXPORT jobjectArray JNICALL
Java_org_sleuthkit_datamodel_SleuthkitJNI_getPathsForImageNat(JNIEnv * env,
jclass obj, jlong a_img_info) {
TSK_IMG_INFO *img_info = castImgInfo(env, a_img_info);
if (img_info == 0) {
//exception already set
return 0;
}
char **img_ptrs;
#ifdef TSK_WIN32
// convert image paths to UTF-8
img_ptrs = (char **)tsk_malloc(img_info->num_img * sizeof(char *));
if (img_ptrs == NULL) {
return (jobjectArray)env->NewObjectArray(0, env->FindClass("java/lang/String"), env->NewStringUTF(""));
}
for (int i = 0; i < img_info->num_img; i++) {
char * img2 = (char*)tsk_malloc(1024 * sizeof(char));
UTF8 *ptr8;
UTF16 *ptr16;
ptr8 = (UTF8 *)img2;
ptr16 = (UTF16 *)img_info->images[i];
uint8_t retval =
tsk_UTF16toUTF8_lclorder((const UTF16 **)&ptr16, (UTF16 *)
& ptr16[TSTRLEN(img_info->images[i]) + 1], &ptr8,
(UTF8 *)((uintptr_t)ptr8 + 1024), TSKlenientConversion);
if (retval != TSKconversionOK) {
tsk_error_reset();
tsk_error_set_errno(TSK_ERR_AUTO_UNICODE);
tsk_error_set_errstr("Error converting image to UTF-8\n");
return (jobjectArray)env->NewObjectArray(0, env->FindClass("java/lang/String"), env->NewStringUTF(""));
}
img_ptrs[i] = img2;
}
#else
img_ptrs = img_info->images;
#endif
jobjectArray path_list = (jobjectArray)env->NewObjectArray(img_info->num_img, env->FindClass("java/lang/String"), env->NewStringUTF(""));
for (int i = 0; i < img_info->num_img; i++) {
env->SetObjectArrayElement(path_list, i, env->NewStringUTF(img_ptrs[i]));
}
return path_list;
}
/*
* Get the size of an image.
*/
JNIEXPORT jlong JNICALL
Java_org_sleuthkit_datamodel_SleuthkitJNI_getSizeForImageNat(JNIEnv * env,
jclass obj, jlong a_img_info) {
TSK_IMG_INFO *img_info = castImgInfo(env, a_img_info);
if (img_info == 0) {
//exception already set
return 0;
}
return img_info->size;
}
/*
* Get the type of an image.
*/
JNIEXPORT jlong JNICALL
Java_org_sleuthkit_datamodel_SleuthkitJNI_getTypeForImageNat(JNIEnv * env,
jclass obj, jlong a_img_info) {
TSK_IMG_INFO *img_info = castImgInfo(env, a_img_info);
if (img_info == 0) {
//exception already set
return 0;
}
return img_info->itype;
}
/*
* Get the computed sector size of an image.
*/
JNIEXPORT jlong JNICALL
Java_org_sleuthkit_datamodel_SleuthkitJNI_getSectorSizeForImageNat(JNIEnv * env,
jclass obj, jlong a_img_info) {
TSK_IMG_INFO *img_info = castImgInfo(env, a_img_info);
if (img_info == 0) {
//exception already set
return 0;
}
return img_info->sector_size;
}
/*
* Get the md5 hash of an image.
*/
JNIEXPORT jstring JNICALL
Java_org_sleuthkit_datamodel_SleuthkitJNI_getMD5HashForImageNat(JNIEnv * env,
jclass obj, jlong a_img_info) {
TSK_IMG_INFO *img_info = castImgInfo(env, a_img_info);
if (img_info == 0) {
//exception already set
return 0;
}
// env->NewStringUTF(img_ptrs[i])
#if HAVE_LIBEWF
if (img_info->itype == TSK_IMG_TYPE_EWF_EWF) {
IMG_EWF_INFO *ewf_info = (IMG_EWF_INFO *)img_info;
if (ewf_info->md5hash_isset) {
return env->NewStringUTF(ewf_info->md5hash);
}
}
#endif
return env->NewStringUTF("");
}
/*
* Get the sha1 hash of an image.
*/
JNIEXPORT jstring JNICALL
Java_org_sleuthkit_datamodel_SleuthkitJNI_getSha1HashForImageNat(JNIEnv * env,
jclass obj, jlong a_img_info) {
TSK_IMG_INFO *img_info = castImgInfo(env, a_img_info);
if (img_info == 0) {
//exception already set
return 0;
}
// env->NewStringUTF(img_ptrs[i])
#if HAVE_LIBEWF
if (img_info->itype == TSK_IMG_TYPE_EWF_EWF) {
IMG_EWF_INFO *ewf_info = (IMG_EWF_INFO *)img_info;
if (ewf_info->sha1hash_isset) {
return env->NewStringUTF(ewf_info->sha1hash);
}
}
#endif
return env->NewStringUTF("");
}
/*
* Get the collection details of an image.
*/
JNIEXPORT jstring JNICALL
Java_org_sleuthkit_datamodel_SleuthkitJNI_getCollectionDetailsForImageNat(JNIEnv * env,
jclass obj, jlong a_img_info) {
TSK_IMG_INFO *img_info = castImgInfo(env, a_img_info);
if (img_info == 0) {
//exception already set
return 0;
}
// env->NewStringUTF(img_ptrs[i])
#if HAVE_LIBEWF
if (img_info->itype == TSK_IMG_TYPE_EWF_EWF) {
IMG_EWF_INFO *ewf_info = (IMG_EWF_INFO *)img_info;
ewf_get_details(ewf_info);
}
#endif
return env->NewStringUTF("");
}
/*
* Open the volume system at the given offset
* @return the created TSK_VS_INFO pointer
* @param env pointer to java environment this was called from
* @param obj the java object this was called from
* @param a_img_info the pointer to the parent img object
* @param vsOffset the offset of the volume system in bytes
*/
JNIEXPORT jlong JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_openVsNat
(JNIEnv * env, jclass obj, jlong a_img_info, jlong vsOffset) {
TSK_IMG_INFO *img_info = castImgInfo(env, a_img_info);
if (img_info == 0) {
//exception already set
return 0;
}
TSK_VS_INFO *vs_info;
vs_info = tsk_vs_open(img_info, vsOffset, TSK_VS_TYPE_DETECT);
if (vs_info == NULL) {
setThrowTskCoreError(env, tsk_error_get());
}
return (jlong) vs_info;
}
/*
* Open volume with the given id from the given volume system
* @return the created TSK_VS_PART_INFO pointer
* @param env pointer to java environment this was called from
* @param obj the java object this was called from
* @param a_vs_info the pointer to the parent vs object
* @param vol_id the id of the volume to get
*/
JNIEXPORT jlong JNICALL
Java_org_sleuthkit_datamodel_SleuthkitJNI_openVolNat(JNIEnv * env,
jclass obj, jlong a_vs_info, jlong vol_id)
{
TSK_VS_INFO *vs_info = castVsInfo(env, a_vs_info);
if (vs_info == 0) {
//exception already set
return 0;
}
const TSK_VS_PART_INFO *vol_part_info;
vol_part_info = tsk_vs_part_get(vs_info, (TSK_PNUM_T) vol_id);
if (vol_part_info == NULL) {
setThrowTskCoreError(env, tsk_error_get());
}
return (jlong) vol_part_info;
}
/*
* Open pool with the given offset
* @return the created TSK_POOL_INFO pointer
* @param env pointer to java environment this was called from
* @param obj the java object this was called from
* @param a_img_info the pointer to the parent img object
* @param offset the offset in bytes to the pool
*/
JNIEXPORT jlong JNICALL
Java_org_sleuthkit_datamodel_SleuthkitJNI_openPoolNat(JNIEnv * env,
jclass obj, jlong a_img_info, jlong offset)
{
TSK_IMG_INFO *img_info = castImgInfo(env, a_img_info);
if (img_info == 0) {
//exception already set
return 0;
}
const TSK_POOL_INFO *pool = tsk_pool_open_img_sing(img_info, offset, TSK_POOL_TYPE_DETECT);
if (pool == NULL) {
tsk_error_print(stderr);
if (tsk_error_get_errno() == TSK_ERR_POOL_UNSUPTYPE)
tsk_pool_type_print(stderr);
setThrowTskCoreError(env, tsk_error_get());
}
return (jlong) pool;
}
/*
* Create new image info to use with a specific pool volume
* @return the created TSK_IMG_INFO pointer
* @param env pointer to java environment this was called from
* @param obj the java object this was called from
* @param a_pool_info the pointer to the pool object
* @param pool_block the block number of the pool volume
*/
JNIEXPORT jlong JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_getImgInfoForPoolNat
(JNIEnv * env, jclass obj, jlong a_pool_info, jlong pool_block) {
TSK_POOL_INFO *pool_info = castPoolInfo(env, a_pool_info);
if (pool_info == 0) {
//exception already set
return 0;
}
TSK_IMG_INFO *img_info = pool_info->get_img_info(pool_info, pool_block);
return (jlong)img_info;
}
/*
* Open file system with the given offset
* @return the created TSK_FS_INFO pointer
* @param env pointer to java environment this was called from
* @param obj the java object this was called from
* @param a_img_info the pointer to the parent img object
* @param fs_offset the offset in bytes to the file system
*/
JNIEXPORT jlong JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_openFsNat
(JNIEnv * env, jclass obj, jlong a_img_info, jlong fs_offset) {
TSK_IMG_INFO *img_info = castImgInfo(env, a_img_info);
if (img_info == 0) {
//exception already set
return 0;
}
TSK_FS_INFO *fs_info;
fs_info =
tsk_fs_open_img(img_info, (TSK_OFF_T) fs_offset,
TSK_FS_TYPE_DETECT);
if (fs_info == NULL) {
setThrowTskCoreError(env, tsk_error_get());
}
return (jlong) fs_info;
}
/*
* Open the file with the given id in the given file system
* @return the created TSK_JNI_FILEHANDLE pointer, set throw exception on error
* @param env pointer to java environment this was called from
* @param obj the java object this was called from
* @param a_fs_info the pointer to the parent file system object
* @param file_id id of the file to open
* @param attr_type type of the file attribute to open
* @param attr_id id of the file attribute to open
*/
JNIEXPORT jlong JNICALL
Java_org_sleuthkit_datamodel_SleuthkitJNI_openFileNat(JNIEnv * env,
jclass obj, jlong a_fs_info, jlong file_id, jint attr_type, jint attr_id)
{
TSK_FS_INFO *fs_info = castFsInfo(env, a_fs_info);
if (fs_info == 0) {
//exception already set
return 0;
}
TSK_FS_FILE *file_info;
//open file
file_info = tsk_fs_file_open_meta(fs_info, NULL, (TSK_INUM_T) file_id);
if (file_info == NULL) {
setThrowTskCoreError(env, tsk_error_get());
return 0;
}
//open attribute
const TSK_FS_ATTR * tsk_fs_attr =
tsk_fs_file_attr_get_type(file_info, (TSK_FS_ATTR_TYPE_ENUM)attr_type, (uint16_t)attr_id, 1);
if (tsk_fs_attr == NULL) {
tsk_fs_file_close(file_info);
setThrowTskCoreError(env, tsk_error_get());
return 0;
}
//allocate file handle structure to encapsulate file and attribute
TSK_JNI_FILEHANDLE * fileHandle =
(TSK_JNI_FILEHANDLE *) tsk_malloc(sizeof(TSK_JNI_FILEHANDLE));
if (fileHandle == NULL) {
tsk_fs_file_close(file_info);
setThrowTskCoreError(env, "Could not allocate memory for TSK_JNI_FILEHANDLE");
return 0;
}
fileHandle->tag = TSK_JNI_FILEHANDLE_TAG;
fileHandle->fs_file = file_info;
fileHandle->fs_attr = const_cast<TSK_FS_ATTR*>(tsk_fs_attr);
return (jlong)fileHandle;
}
/** move a local buffer into a new Java array.
* @param env JNI env
* @param buf Buffer to copy from
* @param len Length of bytes in buf
* @returns Pointer to newly created java byte array or NULL if there is an error
*/
#if 0
static jbyteArray
copyBufToByteArray(JNIEnv * env, const char *buf, ssize_t len)
{
jbyteArray return_array = env->NewByteArray(len);
if (return_array == NULL) {
setThrowTskCoreError(env, "NewByteArray returned error while getting an array to copy buffer into.");
return 0;
}
env->SetByteArrayRegion(return_array, 0, len, (jbyte*)buf);
return return_array;
}
#endif
/** move a local buffer into an existing Java array.
* @param env JNI env
* @param jbuf Buffer to copy to
* @param buf Buffer to copy from
* @param len Length of bytes in buf
* @returns number of bytes copied or -1 on error
*/
inline static ssize_t
copyBufToByteArray(JNIEnv * env, jbyteArray jbuf, const char *buf, ssize_t len)
{
env->SetByteArrayRegion(jbuf, 0, (jsize)len, (jbyte*)buf);
return len;
}
/*
* Read bytes from the given image
* @return number of bytes read from the image, -1 on error
* @param env pointer to java environment this was called from
* @param obj the java object this was called from
* @param a_img_info the pointer to the image object
* @param offset the offset in bytes to start at
* @param len number of bytes to read
*/
JNIEXPORT jint JNICALL
Java_org_sleuthkit_datamodel_SleuthkitJNI_readImgNat(JNIEnv * env,
jclass obj, jlong a_img_info, jbyteArray jbuf, jlong offset, jlong len)
{
//use fixed size stack-allocated buffer if possible
char fixed_buf [FIXED_BUF_SIZE];
char * buf = fixed_buf;
bool dynBuf = false;
if (len > FIXED_BUF_SIZE) {
dynBuf = true;
buf = (char *) tsk_malloc((size_t) len);
if (buf == NULL) {
setThrowTskCoreError(env);
return -1;
}
}
TSK_IMG_INFO *img_info = castImgInfo(env, a_img_info);
if (img_info == 0) {
if (dynBuf) {
free(buf);
}
//exception already set
return -1;
}
ssize_t bytesread =
tsk_img_read(img_info, (TSK_OFF_T) offset, buf, (size_t) len);
if (bytesread == -1) {
if (dynBuf) {
free(buf);
}
setThrowTskCoreError(env, tsk_error_get());
return -1;
}
// package it up for return
// adjust number bytes to copy
ssize_t copybytes = bytesread;
jsize jbuflen = env->GetArrayLength(jbuf);
if (jbuflen < copybytes)
copybytes = jbuflen;
ssize_t copiedbytes = copyBufToByteArray(env, jbuf, buf, copybytes);
if (dynBuf) {
free(buf);
}
if (copiedbytes == -1) {
setThrowTskCoreError(env, tsk_error_get());
}
return (jint)copiedbytes;
}
/*
* Read bytes from the given pool
* @return number of bytes read from the pool, -1 on error
* @param env pointer to java environment this was called from
* @param obj the java object this was called from
* @param a_pool_info the pointer to the pool object
* @param jbuf the buffer to write to
* @param offset the offset in bytes to start at
* @param len number of bytes to read
*/
JNIEXPORT jint JNICALL
Java_org_sleuthkit_datamodel_SleuthkitJNI_readPoolNat(JNIEnv * env,
jclass obj, jlong a_pool_info, jbyteArray jbuf, jlong offset, jlong len)
{
//use fixed size stack-allocated buffer if possible
char fixed_buf[FIXED_BUF_SIZE];
char * buf = fixed_buf;
bool dynBuf = false;
if (len > FIXED_BUF_SIZE) {
dynBuf = true;
buf = (char *)tsk_malloc((size_t)len);
if (buf == NULL) {
setThrowTskCoreError(env);
return -1;
}
}
TSK_POOL_INFO *pool_info = castPoolInfo(env, a_pool_info);
if (pool_info == 0) {
//exception already set
if (dynBuf) {
free(buf);
}
return -1;
}
ssize_t bytesread = tsk_pool_read(pool_info, (TSK_DADDR_T)offset, buf,
(size_t)len);
if (bytesread == -1) {
setThrowTskCoreError(env, tsk_error_get());
if (dynBuf) {
free(buf);
}
return -1;
}
// package it up for return
// adjust number bytes to copy
ssize_t copybytes = bytesread;
jsize jbuflen = env->GetArrayLength(jbuf);
if (jbuflen < copybytes)
copybytes = jbuflen;
ssize_t copiedbytes = copyBufToByteArray(env, jbuf, buf, copybytes);
if (dynBuf) {
free(buf);
}
if (copiedbytes == -1) {
setThrowTskCoreError(env, tsk_error_get());
}
return (jint)copiedbytes;
}
/*
* Read bytes from the given volume system
* @return number of bytes read from the volume system, -1 on error
* @param env pointer to java environment this was called from
* @param obj the java object this was called from
* @param a_vs_info the pointer to the volume system object
* @param offset the offset in bytes to start at
* @param len number of bytes to read
*/
JNIEXPORT jint JNICALL
Java_org_sleuthkit_datamodel_SleuthkitJNI_readVsNat(JNIEnv * env,
jclass obj, jlong a_vs_info, jbyteArray jbuf, jlong offset, jlong len)
{
//use fixed size stack-allocated buffer if possible
char fixed_buf [FIXED_BUF_SIZE];
char * buf = fixed_buf;
bool dynBuf = false;
if (len > FIXED_BUF_SIZE) {
dynBuf = true;
buf = (char *) tsk_malloc((size_t) len);
if (buf == NULL) {
setThrowTskCoreError(env);
return -1;
}
}
TSK_VS_INFO *vs_info = castVsInfo(env, a_vs_info);
if (vs_info == 0) {
//exception already set
if (dynBuf) {
free(buf);
}
return -1;
}
ssize_t bytesread = tsk_vs_read_block(vs_info, (TSK_DADDR_T) offset, buf,
(size_t) len);
if (bytesread == -1) {
setThrowTskCoreError(env, tsk_error_get());
if (dynBuf) {
free(buf);
}
return -1;
}
// package it up for return
// adjust number bytes to copy
ssize_t copybytes = bytesread;
jsize jbuflen = env->GetArrayLength(jbuf);
if (jbuflen < copybytes)
copybytes = jbuflen;
ssize_t copiedbytes = copyBufToByteArray(env, jbuf, buf, copybytes);
if (dynBuf) {
free(buf);
}
if (copiedbytes == -1) {
setThrowTskCoreError(env, tsk_error_get());
}
return (jint)copiedbytes;
}
/*
* Read bytes from the given volume
* @return number of bytes read from the volume or -1 on error
* @param env pointer to java environment this was called from
* @param obj the java object this was called from
* @param a_vol_info the pointer to the volume object
* @param offset the offset in bytes to start at
* @param len number of bytes to read
*/
JNIEXPORT jint JNICALL
Java_org_sleuthkit_datamodel_SleuthkitJNI_readVolNat(JNIEnv * env,
jclass obj, jlong a_vol_info, jbyteArray jbuf, jlong offset, jlong len)
{
//use fixed size stack-allocated buffer if possible
char fixed_buf [FIXED_BUF_SIZE];
char * buf = fixed_buf;
bool dynBuf = false;
if (len > FIXED_BUF_SIZE) {
dynBuf = true;
buf = (char *) tsk_malloc((size_t) len);
if (buf == NULL) {
setThrowTskCoreError(env);
return -1;
}
}
TSK_VS_PART_INFO *vol_part_info = castVsPartInfo(env, a_vol_info);
if (vol_part_info == 0) {
if (dynBuf) {
free(buf);
}
//exception already set
return -1;
}
ssize_t bytesread =
tsk_vs_part_read(vol_part_info, (TSK_OFF_T) offset, buf,
(size_t) len);
if (bytesread == -1) {
setThrowTskCoreError(env, tsk_error_get());
if (dynBuf) {
free(buf);
}
return -1;
}
// package it up for return
// adjust number bytes to copy
ssize_t copybytes = bytesread;
jsize jbuflen = env->GetArrayLength(jbuf);
if (jbuflen < copybytes)
copybytes = jbuflen;
ssize_t copiedbytes = copyBufToByteArray(env, jbuf, buf, copybytes);
if (dynBuf) {
free(buf);
}
if (copiedbytes == -1) {
setThrowTskCoreError(env, tsk_error_get());
}
return (jint)copiedbytes;
}
/*
* Read bytes from the given file system
* @return number of bytes read from the file system, -1 on error
* @param env pointer to java environment this was called from
* @param obj the java object this was called from
* @param a_fs_info the pointer to the file system object
* @param offset the offset in bytes to start at
* @param len number of bytes to read
*/
JNIEXPORT jint JNICALL
Java_org_sleuthkit_datamodel_SleuthkitJNI_readFsNat(JNIEnv * env,
jclass obj, jlong a_fs_info, jbyteArray jbuf, jlong offset, jlong len)
{
//use fixed size stack-allocated buffer if possible
char fixed_buf [FIXED_BUF_SIZE];
char * buf = fixed_buf;
bool dynBuf = false;
if (len > FIXED_BUF_SIZE) {
dynBuf = true;
buf = (char *) tsk_malloc((size_t) len);
if (buf == NULL) {
setThrowTskCoreError(env);
return -1;
}
}
TSK_FS_INFO *fs_info = castFsInfo(env, a_fs_info);
if (fs_info == 0) {
if (dynBuf) {
free(buf);
}
//exception already set
return -1;
}
ssize_t bytesread =
tsk_fs_read(fs_info, (TSK_OFF_T) offset, buf, (size_t) len);
if (bytesread == -1) {
if (dynBuf) {
free(buf);
}
setThrowTskCoreError(env, tsk_error_get());
return -1;
}
// package it up for return
// adjust number bytes to copy
ssize_t copybytes = bytesread;
jsize jbuflen = env->GetArrayLength(jbuf);
if (jbuflen < copybytes)
copybytes = jbuflen;
ssize_t copiedbytes = copyBufToByteArray(env, jbuf, buf, copybytes);
if (dynBuf) {
free(buf);
}
if (copiedbytes == -1) {
setThrowTskCoreError(env, tsk_error_get());
}
return (jint)copiedbytes;
}
/**
* Flag used by readFileNat to specify if the offset is relative to the start of the file
* or the start of the slack space
*/
typedef enum {
TSK_FS_FILE_READ_OFFSET_TYPE_START_OF_FILE = 0x00,
TSK_FS_FILE_READ_OFFSET_TYPE_START_OF_SLACK = 0x01,
} TSK_FS_FILE_READ_OFFSET_TYPE_ENUM;
/*
* Read bytes from the given file
* @return number of bytes read, or -1 on error
* @param env pointer to java environment this was called from
* @param obj the java object this was called from
* @param a_file_handle the pointer to the TSK_JNI_FILEHANDLE object
* @param jbuf jvm allocated buffer to read to
* @param offset the offset in bytes to start at
* @param len number of bytes to read
*/
JNIEXPORT jint JNICALL
Java_org_sleuthkit_datamodel_SleuthkitJNI_readFileNat(JNIEnv * env,
jclass obj, jlong a_file_handle, jbyteArray jbuf, jlong offset, jint offset_type, jlong len)
{
//use fixed size stack-allocated buffer if possible
char fixed_buf [FIXED_BUF_SIZE];
char * buf = fixed_buf;
bool dynBuf = false;
if (len > FIXED_BUF_SIZE) {
dynBuf = true;
buf = (char *) tsk_malloc((size_t) len);
if (buf == NULL) {
setThrowTskCoreError(env);
return -1;
}
}
const TSK_JNI_FILEHANDLE *file_handle = castJniFileHandle(env, a_file_handle);
if (file_handle == 0) {
if (dynBuf) {
free(buf);
}
//exception already set
return -1;
}
TSK_FS_ATTR * tsk_fs_attr = file_handle->fs_attr;
TSK_FS_FILE_READ_FLAG_ENUM readFlag = TSK_FS_FILE_READ_FLAG_NONE;
TSK_OFF_T readOffset = (TSK_OFF_T) offset;
if(offset_type == TSK_FS_FILE_READ_OFFSET_TYPE_START_OF_SLACK){
readFlag = TSK_FS_FILE_READ_FLAG_SLACK;
readOffset += tsk_fs_attr->nrd.initsize;
}
//read attribute
ssize_t bytesread = tsk_fs_attr_read(tsk_fs_attr, readOffset, buf, (size_t) len,
readFlag);
if (bytesread == -1) {
if (dynBuf) {
free(buf);
}
setThrowTskCoreError(env, tsk_error_get());
return -1;
}
// package it up for return
// adjust number bytes to copy
ssize_t copybytes = bytesread;
jsize jbuflen = env->GetArrayLength(jbuf);
if (jbuflen < copybytes)
copybytes = jbuflen;
ssize_t copiedbytes = copyBufToByteArray(env, jbuf, buf, copybytes);
if (dynBuf) {
free(buf);
}
if (copiedbytes == -1) {
setThrowTskCoreError(env, tsk_error_get());
}
return (jint)copiedbytes;
}
/**
* Runs istat on a given file and saves the output to a temp file.
*
* @returns -1 on error (and throws exception)
*/
JNIEXPORT jint JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_saveFileMetaDataTextNat
(JNIEnv *env, jclass obj, jlong a_file_handle, jstring a_tmp_path)
{
const TSK_JNI_FILEHANDLE *file_handle = castJniFileHandle(env, a_file_handle);
if (file_handle == 0) {
//exception already set
return -1;
}
// check the pointers
if (file_handle->fs_file == NULL || file_handle->fs_file->fs_info == NULL || file_handle->fs_file->meta == NULL) {
setThrowTskCoreError(env, "NULL pointers for istat file.");
return -1;
}
TSK_FS_INFO *fs_info = file_handle->fs_file->fs_info;
// open a file to write the details to
jboolean isCopy;
char *str8 = (char *) env->GetStringUTFChars(a_tmp_path, &isCopy);
FILE *hFile = fopen(str8, "w");
if (hFile == NULL) {
env->ReleaseStringUTFChars(a_tmp_path, str8);
setThrowTskCoreError(env, "Couldn't open istat temp file for writing.");
return -1;
}
env->ReleaseStringUTFChars(a_tmp_path, str8);
if (fs_info->istat(fs_info, TSK_FS_ISTAT_RUNLIST, hFile, file_handle->fs_file->meta->addr, 0, 0) != 0) {
fclose(hFile);
setThrowTskCoreError(env);
return -1;
}
fclose(hFile);
return 0;
}
/*
* Close the given image
* @param env pointer to java environment this was called from
* @param obj the java object this was called from
* @param a_img_info the pointer to the image object
*/
JNIEXPORT void JNICALL
Java_org_sleuthkit_datamodel_SleuthkitJNI_closeImgNat(JNIEnv * env,
jclass obj, jlong a_img_info)
{
TSK_IMG_INFO *img_info = castImgInfo(env, a_img_info);
if (img_info == 0) {
//exception already set
return;
}
tsk_img_close(img_info);
}
/*
* Close the given volume system
* @param env pointer to java environment this was called from
* @param obj the java object this was called from
* @param a_vs_info the pointer to the volume system object
*/
JNIEXPORT void JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_closeVsNat
(JNIEnv * env, jclass obj, jlong a_vs_info) {
TSK_VS_INFO *vs_info = castVsInfo(env, a_vs_info);
if (vs_info == 0) {
//exception already set
return;
}
tsk_vs_close(vs_info);
}
/*
* Close the given file system
* @param env pointer to java environment this was called from
* @param obj the java object this was called from
* @param a_fs_info the pointer to the file system object
*/
JNIEXPORT void JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_closeFsNat
(JNIEnv * env, jclass obj, jlong a_fs_info) {
TSK_FS_INFO *fs_info = castFsInfo(env, a_fs_info);
if (fs_info == 0) {
//exception already set
return;
}
tsk_fs_close(fs_info);
}
/*
* Close the given pool
* @param env pointer to java environment this was called from
* @param obj the java object this was called from
* @param a_pool_info the pointer to the pool object
*/
JNIEXPORT void JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_closePoolNat
(JNIEnv * env, jclass obj, jlong a_pool_info) {
TSK_POOL_INFO *pool_info = castPoolInfo(env, a_pool_info);
if (pool_info == 0) {
//exception already set
return;
}
tsk_pool_close(pool_info);
}
/*
* Close the given file
* @param env pointer to java environment this was called from
* @param obj the java object this was called from
* @param a_file_info the pointer to the file object
*/
JNIEXPORT void JNICALL
Java_org_sleuthkit_datamodel_SleuthkitJNI_closeFileNat(JNIEnv * env,
jclass obj, jlong a_file_info)
{
TSK_JNI_FILEHANDLE *file_handle = castJniFileHandle(env, a_file_info);
if (file_handle == 0) {
//exception already set
return;
}
TSK_FS_FILE * file_info = file_handle->fs_file;
tsk_fs_file_close(file_info); //also closes the attribute
file_handle->fs_file = NULL;
file_handle->fs_attr = NULL;
file_handle->tag = 0;
free (file_handle);
}
/*
* Get the current Sleuthkit version number
* @return the version string
* @param env pointer to java environment this was called from
* @param obj the java object this was called from
*/
JNIEXPORT jstring JNICALL
Java_org_sleuthkit_datamodel_SleuthkitJNI_getVersionNat(JNIEnv * env,
jclass obj)
{
const char *cversion = tsk_version_get_str();
jstring jversion = (*env).NewStringUTF(cversion);
return jversion;
}
/*
* Get the current directory being analyzed during AddImage
* @return the path of the current directory
*
*/
JNIEXPORT jstring JNICALL
Java_org_sleuthkit_datamodel_SleuthkitJNI_getCurDirNat
(JNIEnv * env,jclass obj, jlong dbHandle)
{
TskAutoDbJava *tskAuto = ((TskAutoDbJava *) dbHandle);
const std::string curDir = tskAuto->getCurDir();
jstring jdir = (*env).NewStringUTF(curDir.c_str());
return jdir;
}
/*
* Enable verbose logging and redirect stderr to the given log file.
* @param env pointer to java environment this was called from
* @param obj the java object this was called from
* @param logPath The log file to append to.
*/
JNIEXPORT void JNICALL
Java_org_sleuthkit_datamodel_SleuthkitJNI_startVerboseLoggingNat
(JNIEnv * env, jclass obj, jstring logPath)
{
jboolean isCopy;
char *str8 = (char *) env->GetStringUTFChars(logPath, &isCopy);
if (freopen(str8, "a", stderr) == NULL) {
env->ReleaseStringUTFChars(logPath, str8);
setThrowTskCoreError(env, "Couldn't open verbose log file for appending.");
return;
}
env->ReleaseStringUTFChars(logPath, str8);
tsk_verbose++;
}
/*
* Creates an MD5 index for a hash database.
* @param env Pointer to Java environment from which this method was called.
* @param obj The Java object from which this method was called.
* @param dbHandle A handle for the hash database.
*/
JNIEXPORT void JNICALL
Java_org_sleuthkit_datamodel_SleuthkitJNI_hashDbCreateIndexNat (JNIEnv * env,
jclass obj, jint dbHandle)
{
if((size_t)dbHandle > hashDbs.size()) {
setThrowTskCoreError(env, "Invalid database handle");
return;
}
TSK_HDB_INFO *db = hashDbs.at(dbHandle-1);
if (db == NULL) {
setThrowTskCoreError(env, "Invalid database handle");
return;
}
TSK_TCHAR idx_type[1024];
if(db->db_type == TSK_HDB_DBTYPE_MD5SUM_ID) {
TSNPRINTF(idx_type, 1024, _TSK_T("%") PRIcTSK, TSK_HDB_DBTYPE_MD5SUM_STR);
}
else if(db->db_type == TSK_HDB_DBTYPE_HK_ID) {
TSNPRINTF(idx_type, 1024, _TSK_T("%") PRIcTSK, TSK_HDB_DBTYPE_HK_STR);
}
else if(db->db_type == TSK_HDB_DBTYPE_ENCASE_ID) {
TSNPRINTF(idx_type, 1024, _TSK_T("%") PRIcTSK, TSK_HDB_DBTYPE_ENCASE_STR);
}
else {
// The Java bindings only support the generation of md5 indexes for
// an NSRL hash database.
TSNPRINTF(idx_type, 1024, _TSK_T("%") PRIcTSK, TSK_HDB_DBTYPE_NSRL_MD5_STR);
}
if (tsk_hdb_make_index(db, idx_type) != 0) {
setThrowTskCoreError(env, tsk_error_get_errstr());
}
}
/*
* Queries whether or not an index for MD5 look ups exists for a hash database.
* @param env Pointer to Java environment from which this method was called.
* @param obj The Java object from which this method was called.
* @param dbHandle A handle for the hash database.
* @return True if the index exists.
*/
JNIEXPORT jboolean JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_hashDbIndexExistsNat
(JNIEnv * env, jclass obj, jint dbHandle) {
if((size_t)dbHandle > hashDbs.size()) {
setThrowTskCoreError(env, "Invalid database handle");
return (jboolean)false;
}
TSK_HDB_INFO *db = hashDbs.at(dbHandle-1);
if (db == NULL) {
setThrowTskCoreError(env, "Invalid database handle");
return (jboolean)false;
}
return (jboolean)(db->has_index(db, TSK_HDB_HTYPE_MD5_ID) == 1);
}
/*
* Query and get size of the device (such as physical disk, or image) pointed by the path
* Might require elevated priviletes to work (otherwise will error)
* @param env pointer to java environment this was called from
* @param obj the java object this was called from
* @param devPathJ the device path
* @return size of device, set throw jni exception on error
*/
JNIEXPORT jlong JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_findDeviceSizeNat
(JNIEnv * env, jclass obj, jstring devPathJ) {
jlong devSize = 0;
const char* devPath = env->GetStringUTFChars(devPathJ, 0);
// open the image to get the size
TSK_IMG_INFO * img_info =
tsk_img_open_utf8_sing(devPath, TSK_IMG_TYPE_DETECT, 0);
if (img_info == NULL) {
setThrowTskCoreError(env, tsk_error_get());
env->ReleaseStringUTFChars(devPathJ , devPath);
return -1;
}
TSK_OFF_T imgSize = img_info->size;
devSize = imgSize;
//cleanup
tsk_img_close(img_info);
env->ReleaseStringUTFChars(devPathJ , devPath);
return devSize;
}
/*
* Test whether an image is supported
* @param env pointer to java environment this was called from
* @param obj the java object this was called from
* @param imagePathJ the image path
* @return true if the image can be processed, false otherwise
*/
JNIEXPORT jboolean JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_isImageSupportedNat
(JNIEnv * env, jclass obj, jstring imagePathJ) {
TskIsImageSupported tskIsImage;
TSK_TCHAR imagePathT[1024];
toTCHAR(env, imagePathT, 1024, imagePathJ);
// It seems like passing &imagePathT should work instead of making this new array,
// but it generated an EXCEPTION_ACCESS_VIOLATION during testing.
TSK_TCHAR ** imagePaths = (TSK_TCHAR**)tsk_malloc((1) * sizeof(TSK_TCHAR*));
bool result;
imagePaths[0] = imagePathT;
if (tskIsImage.openImage(1, imagePaths, TSK_IMG_TYPE_DETECT, 0)) {
result = false;
} else {
if (tskIsImage.findFilesInImg()) {
result = false;
} else {
if (tskIsImage.isImageSupported()) {
result = true;
}
else {
result = false;
}
}
}
// Cleanup
free(imagePaths);
return (jboolean) result;
}
/*
* Returns the current Sleuthkit version as a long
* @return the current version
*/
JNIEXPORT jlong JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_getSleuthkitVersionNat
(JNIEnv * env, jclass obj) {
return (jlong)TSK_VERSION_NUM;
}
/*
* Finish the image being created by image writer.
* @param env pointer to java environment this was called from
* @param obj the java object this was called from
* @param a_img_info the image info pointer
*/
JNIEXPORT jint JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_finishImageWriterNat
(JNIEnv * env, jclass obj, jlong a_img_info) {
// Set up the TSK_IMG_INFO object
TSK_IMG_INFO *img_info = castImgInfo(env, a_img_info);
return tsk_img_writer_finish(img_info);
}
/*
* Get the progess of the finishImage process as an integer from 0 to 100
*/
JNIEXPORT jint JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_getFinishImageProgressNat
(JNIEnv * env, jclass obj, jlong a_img_info) {
// Set up the TSK_IMG_INFO object
TSK_IMG_INFO *img_info = castImgInfo(env, a_img_info);
IMG_RAW_INFO *raw_info = (IMG_RAW_INFO*)img_info;
if (raw_info->img_writer != NULL) {
return (raw_info->img_writer->finishProgress);
}
return 0;
}
/*
* Cancel the finishImage process
*/
JNIEXPORT void JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_cancelFinishImageNat
(JNIEnv * env, jclass obj, jlong a_img_info) {
// Set up the TSK_IMG_INFO object
TSK_IMG_INFO *img_info = castImgInfo(env, a_img_info);
IMG_RAW_INFO *raw_info = (IMG_RAW_INFO*)img_info;
if (raw_info->img_writer != NULL) {
raw_info->img_writer->cancelFinish = 1;
}
return ;
}