mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-09 14:49:32 +00:00
421 lines
14 KiB
Java
421 lines
14 KiB
Java
/*
|
|
* Autopsy Forensic Browser
|
|
*
|
|
* Copyright 2013 Basis Technology Corp.
|
|
* Contact: carrier <at> sleuthkit <dot> org
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
package org.sleuthkit.autopsy.sevenzip;
|
|
|
|
import java.io.BufferedOutputStream;
|
|
import java.io.File;
|
|
import java.io.FileNotFoundException;
|
|
import java.io.FileOutputStream;
|
|
import java.io.IOException;
|
|
import java.io.OutputStream;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.logging.Level;
|
|
import javax.swing.JPanel;
|
|
import net.sf.sevenzipjbinding.ISequentialOutStream;
|
|
import net.sf.sevenzipjbinding.ISevenZipInArchive;
|
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
|
import org.sleuthkit.autopsy.ingest.IngestModuleAbstractFile;
|
|
import org.sleuthkit.autopsy.ingest.IngestModuleInit;
|
|
import org.sleuthkit.autopsy.ingest.IngestServices;
|
|
import org.sleuthkit.datamodel.AbstractFile;
|
|
import org.sleuthkit.datamodel.SleuthkitCase;
|
|
import net.sf.sevenzipjbinding.SevenZip;
|
|
import net.sf.sevenzipjbinding.SevenZipException;
|
|
import net.sf.sevenzipjbinding.SevenZipNativeInitializationException;
|
|
import net.sf.sevenzipjbinding.simple.ISimpleInArchive;
|
|
import net.sf.sevenzipjbinding.simple.ISimpleInArchiveItem;
|
|
import org.sleuthkit.autopsy.casemodule.Case;
|
|
import org.sleuthkit.autopsy.casemodule.services.FileManager;
|
|
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
|
|
import org.sleuthkit.datamodel.DerivedFile;
|
|
import org.sleuthkit.datamodel.ReadContentInputStream;
|
|
import org.sleuthkit.datamodel.TskCoreException;
|
|
|
|
/**
|
|
* 7Zip ingest module Extracts supported archives, adds extracted DerivedFiles,
|
|
* reschedules extracted DerivedFiles for ingest.
|
|
*
|
|
* Updates datamodel / directory tree with new files.
|
|
*/
|
|
public final class SevenZipIngestModule implements IngestModuleAbstractFile {
|
|
|
|
private static final Logger logger = Logger.getLogger(SevenZipIngestModule.class.getName());
|
|
public static final String MODULE_NAME = "7Zip";
|
|
public static final String MODULE_DESCRIPTION = "Extracts archive files, add new files, reschedules them to current ingest and populates directory tree with new files.";
|
|
final public static String MODULE_VERSION = "1.0";
|
|
private String args;
|
|
private IngestServices services;
|
|
private volatile int messageID = 0;
|
|
private boolean processedFiles;
|
|
private SleuthkitCase caseHandle = null;
|
|
private boolean initialized = false;
|
|
private static SevenZipIngestModule instance = null;
|
|
//TODO use content type detection instead of extensions
|
|
static final String[] SUPPORTED_EXTENSIONS = {"zip", "rar",};
|
|
private String unpackDir; //relative to the case, to store in db
|
|
private String unpackDirPath; //absolute, to extract to
|
|
private FileManager fileManager;
|
|
|
|
//private constructor to ensure singleton instance
|
|
private SevenZipIngestModule() {
|
|
}
|
|
|
|
/**
|
|
* Returns singleton instance of the module, creates one if needed
|
|
*
|
|
* @return instance of the module
|
|
*/
|
|
public static synchronized SevenZipIngestModule getDefault() {
|
|
if (instance == null) {
|
|
instance = new SevenZipIngestModule();
|
|
}
|
|
return instance;
|
|
}
|
|
|
|
@Override
|
|
public void init(IngestModuleInit initContext) {
|
|
logger.log(Level.INFO, "init()");
|
|
services = IngestServices.getDefault();
|
|
initialized = false;
|
|
|
|
final Case currentCase = Case.getCurrentCase();
|
|
|
|
unpackDir = currentCase.getModulesOutputDirectory() + File.separator + MODULE_NAME;
|
|
unpackDirPath = currentCase.getModulesOutputDirectoryPath() + File.separator + MODULE_NAME;
|
|
|
|
fileManager = currentCase.getServices().getFileManager();
|
|
|
|
File unpackDirPathFile = new File(unpackDirPath);
|
|
if (!unpackDirPathFile.exists()) {
|
|
try {
|
|
unpackDirPathFile.mkdirs();
|
|
} catch (SecurityException e) {
|
|
logger.log(Level.SEVERE, "Error initializing output dir: " + unpackDirPath, e);
|
|
MessageNotifyUtil.Notify.error("Error initializing " + MODULE_NAME, "Error initializing output dir: " + unpackDirPath + ": " + e.getMessage());
|
|
return;
|
|
}
|
|
}
|
|
|
|
try {
|
|
SevenZip.initSevenZipFromPlatformJAR();
|
|
String platform = SevenZip.getUsedPlatform();
|
|
logger.log(Level.INFO, "7-Zip-JBinding library was initialized on supported platform: " + platform);
|
|
} catch (SevenZipNativeInitializationException e) {
|
|
logger.log(Level.SEVERE, "Error initializing 7-Zip-JBinding library", e);
|
|
MessageNotifyUtil.Notify.error("Error initializing " + MODULE_NAME, "Could not initialize 7-ZIP library");
|
|
return;
|
|
}
|
|
|
|
|
|
initialized = true;
|
|
}
|
|
|
|
@Override
|
|
public ProcessResult process(AbstractFile abstractFile) {
|
|
|
|
if (initialized == false) //error initializing indexing/Solr
|
|
{
|
|
logger.log(Level.WARNING, "Skipping processing, module not initialized, file: " + abstractFile.getName());
|
|
return ProcessResult.OK;
|
|
}
|
|
|
|
if (abstractFile.isFile() == false || !isSupported(abstractFile)) {
|
|
//do not process dirs and files that are not supported
|
|
return ProcessResult.OK;
|
|
}
|
|
|
|
//check if already has derived files, skip
|
|
try {
|
|
if (abstractFile.hasChildren()) {
|
|
logger.log(Level.INFO, "File already has been processed as it has children, skipping: " + abstractFile.getName());
|
|
return ProcessResult.OK;
|
|
}
|
|
} catch (TskCoreException e) {
|
|
logger.log(Level.INFO, "Error checking if file already has been processed, skipping: " + abstractFile.getName());
|
|
return ProcessResult.OK;
|
|
}
|
|
|
|
|
|
logger.log(Level.INFO, "Processing with 7ZIP: " + abstractFile.getName());
|
|
|
|
|
|
List<DerivedFile> unpacked = unpack(abstractFile);
|
|
//TODO send event to dir tree
|
|
|
|
//TODO reschedule unpacked file for ingest
|
|
|
|
//process, return error if occurred
|
|
|
|
return ProcessResult.OK;
|
|
}
|
|
|
|
/**
|
|
* Unpack the file to local folder and return a list of derived files
|
|
*
|
|
* @param file file to unpack
|
|
* @return list of unpacked derived files
|
|
*/
|
|
private List<DerivedFile> unpack(AbstractFile file) {
|
|
final List<DerivedFile> unpacked = new ArrayList<DerivedFile>();
|
|
|
|
ISevenZipInArchive inArchive = null;
|
|
SevenZipContentReadStream stream = null;
|
|
try {
|
|
stream = new SevenZipContentReadStream(new ReadContentInputStream(file));
|
|
inArchive = SevenZip.openInArchive(null, // autodetect archive type
|
|
stream);
|
|
|
|
logger.log(Level.INFO, "Count of items in archive: " + file.getName() + ": "
|
|
+ inArchive.getNumberOfItems());
|
|
|
|
final ISimpleInArchive simpleInArchive = inArchive.getSimpleInterface();
|
|
AbstractFile currentParent = file;
|
|
for (ISimpleInArchiveItem item : simpleInArchive.getArchiveItems()) {
|
|
final boolean isDir = item.isFolder();
|
|
|
|
final String extractedPath = item.getPath();
|
|
|
|
final boolean isEncrypted = item.isEncrypted(); //TODO handle this
|
|
if (isEncrypted) {
|
|
logger.log(Level.WARNING, "Skipping encrypted file in archive: " + extractedPath);
|
|
continue;
|
|
}
|
|
|
|
logger.log(Level.INFO, "Extracted item path: " + extractedPath);
|
|
|
|
int lastSep = extractedPath.lastIndexOf("/\\");
|
|
String fileName;
|
|
if (lastSep != -1) {
|
|
fileName = extractedPath.substring(lastSep);
|
|
} else {
|
|
fileName = extractedPath;
|
|
}
|
|
final String localFileRelPath = file.getId() + File.separator + extractedPath; //TODO check if ok using ID of parent file
|
|
final String localRelPath = unpackDir + File.separator + localFileRelPath;
|
|
final String localAbsPath = unpackDirPath + File.separator + localFileRelPath;
|
|
|
|
File f = new java.io.File(localAbsPath);
|
|
if (!f.exists()) {
|
|
try {
|
|
if (isDir) {
|
|
f.mkdirs();
|
|
} else {
|
|
f.getParentFile().mkdirs();
|
|
try {
|
|
f.createNewFile();
|
|
} catch (IOException ex) {
|
|
logger.log(Level.SEVERE, "Error creating extracted file: " + f.getAbsolutePath(), ex);
|
|
}
|
|
}
|
|
} catch (SecurityException e) {
|
|
logger.log(Level.SEVERE, "Error setting up output path for unpacked file: " + extractedPath);
|
|
//TODO consider bail out / msg to the user
|
|
}
|
|
}
|
|
|
|
long size = item.getSize();
|
|
try {
|
|
DerivedFile df = fileManager.addDerivedFile(fileName, localRelPath, size, !isDir, currentParent, "", "", "", "");
|
|
unpacked.add(df);
|
|
|
|
if (isDir) {
|
|
//set the new parent for the next unzipped file
|
|
//assuming 7zip unzips top down TODO check
|
|
currentParent = df;
|
|
}
|
|
|
|
|
|
} catch (TskCoreException ex) {
|
|
logger.log(Level.SEVERE, "Error adding a derived file to db", ex);
|
|
break;
|
|
}
|
|
|
|
//unpack
|
|
UnpackStream unpackStream = new UnpackStream(localAbsPath);
|
|
item.extractSlow(unpackStream);
|
|
unpackStream.close();
|
|
|
|
|
|
//TODO handle directories / hierarchy in unzipped file
|
|
|
|
|
|
|
|
}
|
|
|
|
} catch (SevenZipException ex) {
|
|
logger.log(Level.SEVERE, "Error unpacking file: " + file, ex);
|
|
} finally {
|
|
if (inArchive != null) {
|
|
try {
|
|
inArchive.close();
|
|
} catch (SevenZipException e) {
|
|
logger.log(Level.SEVERE, "Error closing archive: " + file, e);
|
|
}
|
|
}
|
|
|
|
if (stream != null) {
|
|
try {
|
|
stream.close();
|
|
} catch (IOException ex) {
|
|
logger.log(Level.SEVERE, "Error closing stream after unpacking archive: " + file, ex);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
return unpacked;
|
|
}
|
|
|
|
@Override
|
|
public void complete() {
|
|
logger.log(Level.INFO, "complete()");
|
|
if (initialized == false) {
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
@Override
|
|
public void stop() {
|
|
logger.log(Level.INFO, "stop()");
|
|
|
|
|
|
}
|
|
|
|
@Override
|
|
public String getName() {
|
|
return MODULE_NAME;
|
|
}
|
|
|
|
@Override
|
|
public String getDescription() {
|
|
return MODULE_DESCRIPTION;
|
|
}
|
|
|
|
@Override
|
|
public String getVersion() {
|
|
return MODULE_VERSION;
|
|
}
|
|
|
|
@Override
|
|
public String getArguments() {
|
|
return args;
|
|
}
|
|
|
|
@Override
|
|
public void setArguments(String args) {
|
|
this.args = args;
|
|
}
|
|
|
|
@Override
|
|
public ModuleType getType() {
|
|
return ModuleType.AbstractFile;
|
|
}
|
|
|
|
@Override
|
|
public boolean hasBackgroundJobsRunning() {
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public boolean hasSimpleConfiguration() {
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public boolean hasAdvancedConfiguration() {
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public void saveSimpleConfiguration() {
|
|
}
|
|
|
|
@Override
|
|
public void saveAdvancedConfiguration() {
|
|
}
|
|
|
|
@Override
|
|
public JPanel getSimpleConfiguration() {
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public JPanel getAdvancedConfiguration() {
|
|
return null;
|
|
}
|
|
|
|
public boolean isSupported(AbstractFile file) {
|
|
String fileNameLower = file.getName().toLowerCase();
|
|
int dotI = fileNameLower.lastIndexOf(".");
|
|
if (dotI == -1 || dotI == fileNameLower.length() - 1) {
|
|
return false; //no extension
|
|
}
|
|
final String extension = fileNameLower.substring(dotI + 1);
|
|
for (int i = 0; i < SUPPORTED_EXTENSIONS.length; ++i) {
|
|
if (extension.equals(SUPPORTED_EXTENSIONS[i])) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Stream used to unpack the archive to local file
|
|
*/
|
|
private static class UnpackStream implements ISequentialOutStream {
|
|
|
|
private OutputStream output;
|
|
private String localAbsPath;
|
|
|
|
UnpackStream(String localAbsPath) {
|
|
try {
|
|
output = new BufferedOutputStream(new FileOutputStream(localAbsPath));
|
|
} catch (FileNotFoundException ex) {
|
|
logger.log(Level.SEVERE, "Error writing extracted file: " + localAbsPath, ex);
|
|
}
|
|
|
|
}
|
|
|
|
@Override
|
|
public int write(byte[] bytes) throws SevenZipException {
|
|
try {
|
|
output.write(bytes);
|
|
} catch (IOException ex) {
|
|
throw new SevenZipException("Error writing unpacked file to: " + localAbsPath, ex);
|
|
}
|
|
return bytes.length;
|
|
}
|
|
|
|
public void close() {
|
|
if (output != null) {
|
|
try {
|
|
output.flush();
|
|
output.close();
|
|
} catch (IOException e) {
|
|
logger.log(Level.SEVERE, "Error closing unpack stream for file: " + localAbsPath);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|