19 package org.sleuthkit.autopsy.modules.embeddedfileextractor;
22 import java.io.FileOutputStream;
23 import java.io.IOException;
24 import java.nio.charset.Charset;
25 import java.util.ArrayList;
26 import java.util.Arrays;
27 import java.util.Collection;
28 import java.util.Collections;
29 import java.util.Date;
30 import java.util.HashMap;
31 import java.util.List;
33 import java.util.concurrent.ConcurrentHashMap;
34 import java.util.logging.Level;
35 import net.sf.sevenzipjbinding.ArchiveFormat;
36 import static net.sf.sevenzipjbinding.ArchiveFormat.RAR;
37 import net.sf.sevenzipjbinding.ExtractAskMode;
38 import net.sf.sevenzipjbinding.ExtractOperationResult;
39 import net.sf.sevenzipjbinding.IArchiveExtractCallback;
40 import net.sf.sevenzipjbinding.ICryptoGetTextPassword;
41 import net.sf.sevenzipjbinding.IInArchive;
42 import net.sf.sevenzipjbinding.ISequentialOutStream;
43 import net.sf.sevenzipjbinding.PropID;
44 import net.sf.sevenzipjbinding.SevenZip;
45 import net.sf.sevenzipjbinding.SevenZipException;
46 import net.sf.sevenzipjbinding.SevenZipNativeInitializationException;
47 import org.apache.tika.parser.txt.CharsetDetector;
48 import org.apache.tika.parser.txt.CharsetMatch;
49 import org.netbeans.api.progress.ProgressHandle;
50 import org.openide.util.NbBundle;
51 import org.openide.util.NbBundle.Messages;
67 import static org.
sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT;
69 import static org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT;
70 import static org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DESCRIPTION;
71 import static org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME;
79 class SevenZipExtractor {
81 private static final Logger logger = Logger.getLogger(SevenZipExtractor.class.getName());
83 private static final String MODULE_NAME = EmbeddedFileExtractorModuleFactory.getModuleName();
86 private static final String ENCRYPTION_FILE_LEVEL = NbBundle.getMessage(EmbeddedFileExtractorIngestModule.class,
87 "EmbeddedFileExtractorIngestModule.ArchiveExtractor.encryptionFileLevel");
88 private static final String ENCRYPTION_FULL = NbBundle.getMessage(EmbeddedFileExtractorIngestModule.class,
89 "EmbeddedFileExtractorIngestModule.ArchiveExtractor.encryptionFull");
92 private static final int MAX_DEPTH = 4;
93 private static final int MAX_COMPRESSION_RATIO = 600;
94 private static final long MIN_COMPRESSION_RATIO_SIZE = 500 * 1000000L;
95 private static final long MIN_FREE_DISK_SPACE = 1 * 1000 * 1000000L;
97 private IngestServices services = IngestServices.getInstance();
98 private final IngestJobContext context;
99 private final FileTypeDetector fileTypeDetector;
101 private String moduleDirRelative;
102 private String moduleDirAbsolute;
104 private Blackboard blackboard;
106 private ProgressHandle progress;
107 private int numItems;
108 private String currentArchiveName;
110 private String getLocalRootAbsPath(String uniqueArchiveFileName) {
111 return moduleDirAbsolute + File.separator + uniqueArchiveFileName;
126 XRAR(
"application/x-rar-compressed");
131 this.mimeType = mimeType;
136 return this.mimeType;
141 SevenZipExtractor(
IngestJobContext context,
FileTypeDetector fileTypeDetector, String moduleDirRelative, String moduleDirAbsolute)
throws SevenZipNativeInitializationException {
142 if (!SevenZip.isInitializedSuccessfully() && (SevenZip.getLastInitializationException() == null)) {
143 SevenZip.initSevenZipFromPlatformJAR();
145 this.context = context;
146 this.fileTypeDetector = fileTypeDetector;
147 this.moduleDirRelative = moduleDirRelative;
148 this.moduleDirAbsolute = moduleDirAbsolute;
159 boolean isSevenZipExtractionSupported(AbstractFile file) {
160 String fileMimeType = fileTypeDetector.getMIMEType(file);
161 for (SupportedArchiveExtractionFormats mimeType : SupportedArchiveExtractionFormats.values()) {
162 if (mimeType.toString().equals(fileMimeType)) {
192 private boolean isZipBombArchiveItemCheck(AbstractFile archiveFile, IInArchive inArchive,
int inArchiveItemIndex, ConcurrentHashMap<Long, Archive> depthMap, String escapedFilePath) {
202 if (archiveFile.isMetaFlagSet(TskData.TSK_FS_META_FLAG_ENUM.UNALLOC) || archiveFile.getMIMEType().equalsIgnoreCase(SupportedArchiveExtractionFormats.XGZIP.toString())) {
207 final Long archiveItemSize = (Long) inArchive.getProperty(
208 inArchiveItemIndex, PropID.SIZE);
211 if (archiveItemSize == null || archiveItemSize < MIN_COMPRESSION_RATIO_SIZE) {
215 final Long archiveItemPackedSize = (Long) inArchive.getProperty(
216 inArchiveItemIndex, PropID.PACKED_SIZE);
218 if (archiveItemPackedSize == null || archiveItemPackedSize <= 0) {
219 logger.log(Level.WARNING,
"Cannot getting compression ratio, cannot detect if zipbomb: {0}, item: {1}",
220 new Object[]{archiveFile.getName(), (String) inArchive.getProperty(inArchiveItemIndex, PropID.PATH)});
224 int cRatio = (int) (archiveItemSize / archiveItemPackedSize);
226 if (cRatio >= MAX_COMPRESSION_RATIO) {
227 Archive rootArchive = depthMap.get(depthMap.get(archiveFile.getId()).getRootArchiveId());
228 String details = NbBundle.getMessage(SevenZipExtractor.class,
229 "EmbeddedFileExtractorIngestModule.ArchiveExtractor.isZipBombCheck.warnDetails",
230 cRatio, FileUtil.escapeFileName(getArchiveFilePath(rootArchive.getArchiveFile())));
232 flagRootArchiveAsZipBomb(rootArchive, archiveFile, details, escapedFilePath);
238 }
catch (SevenZipException ex) {
239 logger.log(Level.WARNING,
"Error getting archive item size and cannot detect if zipbomb. ", ex);
255 private void flagRootArchiveAsZipBomb(Archive rootArchive, AbstractFile archiveFile, String details, String escapedFilePath) {
256 rootArchive.flagAsZipBomb();
257 logger.log(Level.INFO, details);
259 Collection<BlackboardAttribute> attributes = Arrays.asList(
260 new BlackboardAttribute(
261 TSK_SET_NAME, MODULE_NAME,
262 "Possible Zip Bomb"),
263 new BlackboardAttribute(
264 TSK_DESCRIPTION, MODULE_NAME,
265 Bundle.SevenZipExtractor_zipBombArtifactCreation_text(archiveFile.getName())),
266 new BlackboardAttribute(
267 TSK_COMMENT, MODULE_NAME,
270 if (!blackboard.artifactExists(archiveFile, TSK_INTERESTING_FILE_HIT, attributes)) {
271 BlackboardArtifact artifact = rootArchive.getArchiveFile().newArtifact(TSK_INTERESTING_FILE_HIT);
272 artifact.addAttributes(attributes);
279 blackboard.postArtifact(artifact, MODULE_NAME);
281 String msg = NbBundle.getMessage(SevenZipExtractor.class,
282 "EmbeddedFileExtractorIngestModule.ArchiveExtractor.isZipBombCheck.warnMsg", archiveFile.getName(), escapedFilePath);
284 services.postMessage(IngestMessage.createWarningMessage(MODULE_NAME, msg, details));
286 }
catch (Blackboard.BlackboardException ex) {
287 logger.log(Level.SEVERE,
"Unable to index blackboard artifact " + artifact.getArtifactID(), ex);
288 MessageNotifyUtil.Notify.error(
289 Bundle.SevenZipExtractor_indexError_message(), artifact.getDisplayName());
292 }
catch (TskCoreException ex) {
293 logger.log(Level.SEVERE,
"Error creating blackboard artifact for Zip Bomb Detection for file: " + escapedFilePath, ex);
305 private ArchiveFormat get7ZipOptions(AbstractFile archiveFile) {
307 String detectedFormat;
308 detectedFormat = archiveFile.getMIMEType();
310 if (detectedFormat == null) {
311 logger.log(Level.WARNING,
"Could not detect format for file: {0}", archiveFile);
314 String extension = archiveFile.getNameExtension();
315 if (
"rar".equals(extension))
324 }
else if (detectedFormat.contains(
"application/x-rar-compressed"))
345 private long getRootArchiveId(AbstractFile file)
throws TskCoreException {
346 long id = file.getId();
347 Content parentContent = file.getParent();
348 while (parentContent != null) {
349 id = parentContent.getId();
350 parentContent = parentContent.getParent();
369 private List<AbstractFile> getAlreadyExtractedFiles(AbstractFile archiveFile, String archiveFilePath)
throws TskCoreException, NoCurrentCaseException {
372 if (archiveFile.hasChildren() &&
new File(moduleDirAbsolute, EmbeddedFileExtractorIngestModule.getUniqueName(archiveFile)).exists()) {
373 return Case.getCurrentCaseThrows().getServices().getFileManager().findFilesByParentPath(getRootArchiveId(archiveFile), archiveFilePath);
375 return new ArrayList<>();
385 private String getArchiveFilePath(AbstractFile archiveFile) {
386 return archiveFile.getParentPath() + archiveFile.getName();
395 private void makeLocalDirectories(String uniqueArchiveFileName) {
396 final String localRootAbsPath = getLocalRootAbsPath(uniqueArchiveFileName);
397 final File localRoot =
new File(localRootAbsPath);
398 if (!localRoot.exists()) {
414 private String getPathInArchive(IInArchive archive,
int inArchiveItemIndex, AbstractFile archiveFile)
throws SevenZipException {
415 String pathInArchive = (String) archive.getProperty(inArchiveItemIndex, PropID.PATH);
417 if (pathInArchive == null || pathInArchive.isEmpty()) {
423 String archName = archiveFile.getName();
424 int dotI = archName.lastIndexOf(
".");
425 String useName = null;
427 String base = archName.substring(0, dotI);
428 String ext = archName.substring(dotI);
429 int colonIndex = ext.lastIndexOf(
":");
430 if (colonIndex != -1) {
433 ext = ext.substring(0, colonIndex);
440 useName = base +
".tar";
447 if (useName == null) {
448 pathInArchive =
"/" + archName +
"/" + Integer.toString(inArchiveItemIndex);
450 pathInArchive =
"/" + useName;
453 return pathInArchive;
456 private byte[] getPathBytesInArchive(IInArchive archive,
int inArchiveItemIndex, AbstractFile archiveFile)
throws SevenZipException {
457 return (byte[]) archive.getProperty(inArchiveItemIndex, PropID.PATH_BYTES);
464 private String getKeyAbstractFile(AbstractFile fileInDatabase) {
465 return fileInDatabase == null ? null : fileInDatabase.getParentPath() + fileInDatabase.getName();
472 private String getKeyFromUnpackedNode(UnpackedTree.UnpackedNode node, String archiveFilePath) {
473 return node == null ? null : archiveFilePath +
"/" + node.getFileName();
483 void unpack(AbstractFile archiveFile, ConcurrentHashMap<Long, Archive> depthMap) {
484 unpack(archiveFile, depthMap, null);
498 @Messages({
"SevenZipExtractor.indexError.message=Failed to index encryption detected artifact for keyword search.",
499 "# {0} - rootArchive",
500 "SevenZipExtractor.zipBombArtifactCreation.text=Zip Bomb Detected {0}"})
501 boolean unpack(AbstractFile archiveFile, ConcurrentHashMap<Long, Archive> depthMap, String password) {
502 boolean unpackSuccessful =
true;
503 boolean hasEncrypted =
false;
504 boolean fullEncryption =
true;
505 boolean progressStarted =
false;
506 final String archiveFilePath = getArchiveFilePath(archiveFile);
507 final String escapedArchiveFilePath = FileUtil.escapeFileName(archiveFilePath);
508 HashMap<String, ZipFileStatusWrapper> statusMap =
new HashMap<>();
509 List<AbstractFile> unpackedFiles = Collections.<AbstractFile>emptyList();
511 currentArchiveName = archiveFile.getName();
513 SevenZipContentReadStream stream = null;
514 progress = ProgressHandle.createHandle(Bundle.EmbeddedFileExtractorIngestModule_ArchiveExtractor_moduleName());
518 blackboard = Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboard();
519 }
catch (NoCurrentCaseException ex) {
520 logger.log(Level.INFO,
"Exception while getting open case.", ex);
521 unpackSuccessful =
false;
522 return unpackSuccessful;
525 List<AbstractFile> existingFiles = getAlreadyExtractedFiles(archiveFile, archiveFilePath);
526 for (AbstractFile file : existingFiles) {
527 statusMap.put(getKeyAbstractFile(file),
new ZipFileStatusWrapper(file, ZipFileStatus.EXISTS));
529 }
catch (TskCoreException e) {
530 logger.log(Level.INFO,
"Error checking if file already has been processed, skipping: {0}", escapedArchiveFilePath);
531 unpackSuccessful =
false;
532 return unpackSuccessful;
533 }
catch (NoCurrentCaseException ex) {
534 logger.log(Level.INFO,
"No open case was found while trying to unpack the archive file {0}", escapedArchiveFilePath);
535 unpackSuccessful =
false;
536 return unpackSuccessful;
538 parentAr = depthMap.get(archiveFile.getId());
539 if (parentAr == null) {
540 parentAr =
new Archive(0, archiveFile.getId(), archiveFile);
541 depthMap.put(archiveFile.getId(), parentAr);
543 Archive rootArchive = depthMap.get(parentAr.getRootArchiveId());
544 if (rootArchive.isFlaggedAsZipBomb()) {
546 unpackSuccessful =
false;
547 return unpackSuccessful;
548 }
else if (parentAr.getDepth() == MAX_DEPTH) {
549 String details = NbBundle.getMessage(SevenZipExtractor.class,
550 "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.warnDetails.zipBomb",
551 parentAr.getDepth(), FileUtil.escapeFileName(getArchiveFilePath(rootArchive.getArchiveFile())));
552 flagRootArchiveAsZipBomb(rootArchive, archiveFile, details, escapedArchiveFilePath);
553 unpackSuccessful =
false;
554 return unpackSuccessful;
557 IInArchive inArchive = null;
559 stream =
new SevenZipContentReadStream(
new ReadContentInputStream(archiveFile));
563 ArchiveFormat options = get7ZipOptions(archiveFile);
564 if (password == null) {
565 inArchive = SevenZip.openInArchive(options, stream);
567 inArchive = SevenZip.openInArchive(options, stream, password);
569 numItems = inArchive.getNumberOfItems();
570 progress.start(numItems);
571 progressStarted =
true;
574 final String uniqueArchiveFileName = FileUtil.escapeFileName(EmbeddedFileExtractorIngestModule.getUniqueName(archiveFile));
576 makeLocalDirectories(uniqueArchiveFileName);
577 }
catch (SecurityException e) {
578 logger.log(Level.SEVERE,
"Error setting up output path for archive root: {0}", getLocalRootAbsPath(uniqueArchiveFileName));
580 unpackSuccessful =
false;
581 return unpackSuccessful;
585 SevenZipExtractor.UnpackedTree unpackedTree =
new SevenZipExtractor.UnpackedTree(moduleDirRelative +
"/" + uniqueArchiveFileName, archiveFile);
589 freeDiskSpace = services.getFreeDiskSpace();
590 }
catch (NullPointerException ex) {
593 freeDiskSpace = IngestMonitor.DISK_FREE_SPACE_UNKNOWN;
596 Map<Integer, InArchiveItemDetails> archiveDetailsMap =
new HashMap<>();
597 for (
int inArchiveItemIndex = 0; inArchiveItemIndex < numItems; inArchiveItemIndex++) {
598 progress.progress(String.format(
"%s: Analyzing archive metadata and creating local files (%d of %d)", currentArchiveName, inArchiveItemIndex + 1, numItems), 0);
599 if (isZipBombArchiveItemCheck(archiveFile, inArchive, inArchiveItemIndex, depthMap, escapedArchiveFilePath)) {
600 unpackSuccessful =
false;
601 return unpackSuccessful;
604 String pathInArchive = getPathInArchive(inArchive, inArchiveItemIndex, archiveFile);
605 byte[] pathBytesInArchive = getPathBytesInArchive(inArchive, inArchiveItemIndex, archiveFile);
606 UnpackedTree.UnpackedNode unpackedNode = unpackedTree.addNode(pathInArchive, pathBytesInArchive);
608 final boolean isEncrypted = (Boolean) inArchive.getProperty(inArchiveItemIndex, PropID.ENCRYPTED);
610 if (isEncrypted && password == null) {
611 logger.log(Level.WARNING,
"Skipping encrypted file in archive: {0}", pathInArchive);
613 unpackSuccessful =
false;
616 fullEncryption =
false;
623 Long archiveItemSize = (Long) inArchive.getProperty(
624 inArchiveItemIndex, PropID.SIZE);
625 if (freeDiskSpace != IngestMonitor.DISK_FREE_SPACE_UNKNOWN && archiveItemSize != null && archiveItemSize > 0) {
626 String archiveItemPath = (String) inArchive.getProperty(
627 inArchiveItemIndex, PropID.PATH);
628 long newDiskSpace = freeDiskSpace - archiveItemSize;
629 if (newDiskSpace < MIN_FREE_DISK_SPACE) {
630 String msg = NbBundle.getMessage(SevenZipExtractor.class,
631 "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.notEnoughDiskSpace.msg",
632 escapedArchiveFilePath, archiveItemPath);
633 String details = NbBundle.getMessage(SevenZipExtractor.class,
634 "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.notEnoughDiskSpace.details");
635 services.postMessage(IngestMessage.createErrorMessage(MODULE_NAME, msg, details));
636 logger.log(Level.INFO,
"Skipping archive item due to insufficient disk space: {0}, {1}",
new String[]{escapedArchiveFilePath, archiveItemPath});
637 logger.log(Level.INFO,
"Available disk space: {0}",
new Object[]{freeDiskSpace});
638 unpackSuccessful =
false;
642 freeDiskSpace = newDiskSpace;
645 final String uniqueExtractedName = FileUtil.escapeFileName(uniqueArchiveFileName + File.separator + (inArchiveItemIndex / 1000) + File.separator + inArchiveItemIndex +
"_" +
new File(pathInArchive).getName());
646 final String localAbsPath = moduleDirAbsolute + File.separator + uniqueExtractedName;
647 final String localRelPath = moduleDirRelative + File.separator + uniqueExtractedName;
650 File localFile =
new java.io.File(localAbsPath);
652 if (!localFile.exists()) {
654 if ((Boolean) inArchive.getProperty(
655 inArchiveItemIndex, PropID.IS_FOLDER)) {
658 localFile.getParentFile().mkdirs();
660 localFile.createNewFile();
661 }
catch (IOException e) {
662 logger.log(Level.SEVERE,
"Error creating extracted file: "
663 + localFile.getAbsolutePath(), e);
666 }
catch (SecurityException e) {
667 logger.log(Level.SEVERE,
"Error setting up output path for unpacked file: {0}",
674 if (localFile.exists() ==
false) {
682 archiveDetailsMap.put(inArchiveItemIndex,
new InArchiveItemDetails(
683 unpackedNode, localAbsPath, localRelPath));
686 int[] extractionIndices = getExtractableFilesFromDetailsMap(archiveDetailsMap);
688 StandardIArchiveExtractCallback archiveCallBack
689 =
new StandardIArchiveExtractCallback(
690 inArchive, archiveFile, progress,
691 archiveDetailsMap, password, freeDiskSpace);
696 inArchive.extract(extractionIndices,
false, archiveCallBack);
698 unpackSuccessful &= archiveCallBack.wasSuccessful();
700 archiveDetailsMap = null;
705 unpackedTree.updateOrAddFileToCaseRec(statusMap, archiveFilePath);
706 unpackedFiles = unpackedTree.getAllFileObjects();
708 for (
int i = 0; i < unpackedFiles.size(); i++) {
709 progress.progress(String.format(
"%s: Searching for nested archives (%d of %d)", currentArchiveName, i + 1, unpackedFiles.size()));
710 AbstractFile unpackedFile = unpackedFiles.get(i);
711 if (unpackedFile == null) {
714 if (isSevenZipExtractionSupported(unpackedFile)) {
715 Archive child =
new Archive(parentAr.getDepth() + 1, parentAr.getRootArchiveId(), archiveFile);
716 parentAr.addChild(child);
717 depthMap.put(unpackedFile.getId(), child);
719 unpackedFile.close();
722 }
catch (TskCoreException | NoCurrentCaseException e) {
723 logger.log(Level.SEVERE,
"Error populating complete derived file hierarchy from the unpacked dir structure", e);
727 }
catch (SevenZipException ex) {
728 logger.log(Level.WARNING,
"Error unpacking file: " + archiveFile, ex);
732 if (archiveFile.isMetaFlagSet(TskData.TSK_FS_META_FLAG_ENUM.ALLOC)) {
733 String msg = NbBundle.getMessage(SevenZipExtractor.class,
734 "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.errUnpacking.msg",
736 String details = NbBundle.getMessage(SevenZipExtractor.class,
737 "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.errUnpacking.details",
738 escapedArchiveFilePath, ex.getMessage());
739 services.postMessage(IngestMessage.createErrorMessage(MODULE_NAME, msg, details));
742 if (inArchive != null) {
745 }
catch (SevenZipException e) {
746 logger.log(Level.SEVERE,
"Error closing archive: " + archiveFile, e);
750 if (stream != null) {
753 }
catch (IOException ex) {
754 logger.log(Level.SEVERE,
"Error closing stream after unpacking archive: " + archiveFile, ex);
759 if (progressStarted) {
766 String encryptionType = fullEncryption ? ENCRYPTION_FULL : ENCRYPTION_FILE_LEVEL;
768 BlackboardArtifact artifact = archiveFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED);
769 artifact.addAttribute(
new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT, MODULE_NAME, encryptionType));
777 blackboard.postArtifact(artifact, MODULE_NAME);
778 }
catch (Blackboard.BlackboardException ex) {
779 logger.log(Level.SEVERE,
"Unable to post blackboard artifact " + artifact.getArtifactID(), ex);
780 MessageNotifyUtil.Notify.error(
781 Bundle.SevenZipExtractor_indexError_message(), artifact.getDisplayName());
784 }
catch (TskCoreException ex) {
785 logger.log(Level.SEVERE,
"Error creating blackboard artifact for encryption detected for file: " + escapedArchiveFilePath, ex);
788 String msg = NbBundle.getMessage(SevenZipExtractor.class,
789 "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.encrFileDetected.msg");
790 String details = NbBundle.getMessage(SevenZipExtractor.class,
791 "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.encrFileDetected.details",
792 currentArchiveName, MODULE_NAME);
793 services.postMessage(IngestMessage.createWarningMessage(MODULE_NAME, msg, details));
797 if (!unpackedFiles.isEmpty()) {
799 services.fireModuleContentEvent(
new ModuleContentEvent(archiveFile));
800 if (context != null) {
801 context.addFilesToJob(unpackedFiles);
804 return unpackSuccessful;
807 private Charset detectFilenamesCharset(List<byte[]> byteDatas) {
808 Charset detectedCharset = null;
809 CharsetDetector charsetDetector =
new CharsetDetector();
812 for (byte[] byteData : byteDatas) {
814 byteSum += byteData.length;
816 if (byteSum >= 1000) {
820 byte[] allBytes =
new byte[byteSum];
822 for (
int i = 0; i < fileNum; i++) {
823 byte[] byteData = byteDatas.get(i);
824 System.arraycopy(byteData, 0, allBytes, start, byteData.length);
825 start += byteData.length;
827 charsetDetector.setText(allBytes);
828 CharsetMatch cm = charsetDetector.detect();
829 if (cm.getConfidence() >= 90 && Charset.isSupported(cm.getName())) {
830 detectedCharset = Charset.forName(cm.getName());
832 return detectedCharset;
839 private int[] getExtractableFilesFromDetailsMap(
840 Map<Integer, InArchiveItemDetails> archiveDetailsMap) {
842 Integer[] wrappedExtractionIndices = archiveDetailsMap.keySet()
843 .toArray(
new Integer[archiveDetailsMap.size()]);
845 return Arrays.stream(wrappedExtractionIndices)
846 .mapToInt(Integer::intValue)
857 private final static class UnpackStream implements ISequentialOutStream {
864 this.output =
new EncodedFileOutputStream(
new FileOutputStream(localAbsPath), TskData.EncodingType.XOR1);
866 this.bytesWritten = 0;
871 this.output =
new EncodedFileOutputStream(
new FileOutputStream(localAbsPath), TskData.EncodingType.XOR1);
873 this.bytesWritten = 0;
881 public int write(byte[] bytes)
throws SevenZipException {
884 this.bytesWritten += bytes.length;
885 }
catch (IOException ex) {
886 throw new SevenZipException(
887 NbBundle.getMessage(SevenZipExtractor.class,
888 "EmbeddedFileExtractorIngestModule.ArchiveExtractor.UnpackStream.write.exception.msg",
894 public void close() throws IOException {
895 try (EncodedFileOutputStream out = output) {
907 private final SevenZipExtractor.UnpackedTree.UnpackedNode
unpackedNode;
912 SevenZipExtractor.UnpackedTree.UnpackedNode
unpackedNode,
913 String localAbsPath, String localRelPath) {
937 implements IArchiveExtractCallback, ICryptoGetTextPassword {
954 private boolean unpackSuccessful =
true;
957 AbstractFile archiveFile, ProgressHandle progressHandle,
958 Map<Integer, InArchiveItemDetails> archiveDetailsMap,
959 String password,
long freeDiskSpace) {
982 public ISequentialOutStream
getStream(
int inArchiveItemIndex,
983 ExtractAskMode mode)
throws SevenZipException {
987 isFolder = (Boolean) inArchive
988 .getProperty(inArchiveItemIndex, PropID.IS_FOLDER);
989 if (isFolder || mode != ExtractAskMode.EXTRACT) {
993 final String localAbsPath = archiveDetailsMap.get(
994 inArchiveItemIndex).getLocalAbsPath();
1002 if (unpackStream != null) {
1007 }
catch (IOException ex) {
1008 logger.log(Level.WARNING, String.format(
"Error opening or setting new stream "
1009 +
"for archive file at %s", localAbsPath), ex.getMessage());
1026 final Date createTime = (Date) inArchive.getProperty(
1027 inArchiveItemIndex, PropID.CREATION_TIME);
1028 final Date accessTime = (Date) inArchive.getProperty(
1029 inArchiveItemIndex, PropID.LAST_ACCESS_TIME);
1030 final Date writeTime = (Date) inArchive.getProperty(
1031 inArchiveItemIndex, PropID.LAST_MODIFICATION_TIME);
1033 createTimeInSeconds = createTime == null ? 0L
1034 : createTime.getTime() / 1000;
1035 modTimeInSeconds = writeTime == null ? 0L
1036 : writeTime.getTime() / 1000;
1037 accessTimeInSeconds = accessTime == null ? 0L
1038 : accessTime.getTime() / 1000;
1040 progressHandle.progress(archiveFile.getName() +
": "
1041 + (String) inArchive.getProperty(inArchiveItemIndex, PropID.PATH),
1057 final SevenZipExtractor.UnpackedTree.UnpackedNode unpackedNode
1058 = archiveDetailsMap.get(inArchiveItemIndex).getUnpackedNode();
1059 final String localRelPath = archiveDetailsMap.get(
1060 inArchiveItemIndex).getLocalRelPath();
1062 unpackedNode.addDerivedInfo(0,
1063 !(Boolean) inArchive.getProperty(inArchiveItemIndex, PropID.IS_FOLDER),
1069 final String localAbsPath = archiveDetailsMap.get(
1070 inArchiveItemIndex).getLocalAbsPath();
1071 if (result != ExtractOperationResult.OK) {
1072 logger.log(Level.WARNING,
"Extraction of : {0} encountered error {1}",
1073 new Object[]{localAbsPath, result});
1074 unpackSuccessful =
false;
1078 unpackedNode.addDerivedInfo(unpackStream.
getSize(),
1079 !(Boolean) inArchive.getProperty(inArchiveItemIndex, PropID.IS_FOLDER),
1083 unpackStream.
close();
1084 }
catch (IOException e) {
1085 logger.log(Level.WARNING,
"Error closing unpack stream for file: {0}", localAbsPath);
1090 public void setTotal(
long value)
throws SevenZipException {
1134 UnpackedTree(String localPathRoot, AbstractFile archiveFile) {
1136 this.rootNode.setFile(archiveFile);
1137 this.rootNode.setFileName(archiveFile.getName());
1138 this.rootNode.setLocalRelPath(localPathRoot);
1150 UnpackedNode addNode(String filePath, byte[] filePathBytes) {
1151 String[] toks = filePath.split(
"[\\/\\\\]");
1152 List<String> tokens =
new ArrayList<>();
1153 for (
int i = 0; i < toks.length; ++i) {
1154 if (!toks[i].isEmpty()) {
1155 tokens.add(toks[i]);
1159 List<byte[]> byteTokens = null;
1160 if (filePathBytes == null) {
1161 return addNode(rootNode, tokens, null);
1163 byteTokens =
new ArrayList<>(tokens.size());
1165 for (
int i = 0; i < filePathBytes.length; i++) {
1166 if (filePathBytes[i] ==
'/') {
1168 byte[] arr =
new byte[len];
1169 System.arraycopy(filePathBytes, last, arr, 0, len);
1170 byteTokens.add(arr);
1174 int len = filePathBytes.length - last;
1176 byte[] arr =
new byte[len];
1177 System.arraycopy(filePathBytes, last, arr, 0, len);
1178 byteTokens.add(arr);
1181 if (tokens.size() != byteTokens.size()) {
1182 logger.log(Level.WARNING,
"Could not map path bytes to path string");
1183 return addNode(rootNode, tokens, null);
1187 return addNode(rootNode, tokens, byteTokens);
1200 List<String> tokenPath, List<byte[]> tokenPathBytes) {
1202 if (tokenPath.isEmpty()) {
1207 String childName = tokenPath.remove(0);
1208 byte[] childNameBytes = null;
1209 if (tokenPathBytes != null) {
1210 childNameBytes = tokenPathBytes.remove(0);
1214 if (child == null) {
1216 child.setFileNameBytes(childNameBytes);
1217 parent.addChild(child);
1221 return addNode(child, tokenPath, tokenPathBytes);
1230 List<AbstractFile> getRootFileObjects() {
1231 List<AbstractFile> ret =
new ArrayList<>();
1232 rootNode.getChildren().forEach((child) -> {
1233 ret.add(child.getFile());
1244 List<AbstractFile> getAllFileObjects() {
1245 List<AbstractFile> ret =
new ArrayList<>();
1246 rootNode.getChildren().forEach((child) -> {
1253 list.add(parent.getFile());
1254 parent.getChildren().forEach((child) -> {
1263 void updateOrAddFileToCaseRec(HashMap<String, ZipFileStatusWrapper> statusMap, String archiveFilePath)
throws TskCoreException,
NoCurrentCaseException {
1265 for (UnpackedNode child : rootNode.getChildren()) {
1266 updateOrAddFileToCaseRec(child, fileManager, statusMap, archiveFilePath);
1286 progress.progress(String.format(
"%s: Adding/updating files in case database (%d of %d)", currentArchiveName, ++nodesProcessed, numItems));
1288 String nameInDatabase = getKeyFromUnpackedNode(node, archiveFilePath);
1289 ZipFileStatusWrapper existingFile = nameInDatabase == null ? null : statusMap.get(nameInDatabase);
1290 if (existingFile == null) {
1291 df = fileManager.
addDerivedFile(node.getFileName(), node.getLocalRelPath(), node.getSize(),
1292 node.getCtime(), node.getCrtime(), node.getAtime(), node.getMtime(),
1293 node.isIsFile(), node.getParent().getFile(),
"", MODULE_NAME,
1294 "",
"", TskData.EncodingType.XOR1);
1297 String key = getKeyAbstractFile(existingFile.
getFile());
1300 statusMap.put(key, existingFile);
1304 String mimeType = existingFile.
getFile().getMIMEType().equalsIgnoreCase(
"application/octet-stream") ? null : existingFile.
getFile().getMIMEType();
1306 node.getCtime(), node.getCrtime(), node.getAtime(), node.getMtime(),
1307 node.isIsFile(), mimeType,
"", MODULE_NAME,
1308 "",
"", TskData.EncodingType.XOR1);
1312 df = (DerivedFile) existingFile.
getFile();
1316 }
catch (TskCoreException ex) {
1317 logger.log(Level.SEVERE,
"Error adding a derived file to db:" + node.getFileName(), ex);
1318 throw new TskCoreException(
1319 NbBundle.getMessage(SevenZipExtractor.class,
"EmbeddedFileExtractorIngestModule.ArchiveExtractor.UnpackedTree.exception.msg",
1320 node.getFileName()), ex);
1324 if (node.getChildren().size() > 0) {
1326 ArrayList<byte[]> byteDatas =
new ArrayList<>();
1328 byte[] childBytes = child.getFileNameBytes();
1329 if (childBytes != null) {
1330 byteDatas.add(childBytes);
1332 names += child.getFileName();
1334 Charset detectedCharset = detectFilenamesCharset(byteDatas);
1337 if (detectedCharset != null && detectedCharset.canEncode()) {
1339 byte[] childBytes = child.getFileNameBytes();
1340 if (childBytes != null) {
1341 String decodedName =
new String(childBytes, detectedCharset);
1342 child.setFileName(decodedName);
1350 updateOrAddFileToCaseRec(child, fileManager, statusMap, getKeyFromUnpackedNode(node, archiveFilePath));
1362 private final List<UnpackedNode>
children =
new ArrayList<>();
1363 private String localRelPath =
"";
1365 private long ctime, crtime, atime, mtime;
1377 this.localRelPath = parent.getLocalRelPath() + File.separator +
fileName;
1396 void setFileName(String fileName) {
1405 void addChild(UnpackedNode child) {
1406 children.add(child);
1415 List<UnpackedNode> getChildren() {
1424 UnpackedNode getParent() {
1428 void addDerivedInfo(
long size,
1430 long ctime,
long crtime,
long atime,
long mtime, String relLocalPath) {
1434 this.crtime = crtime;
1437 this.localRelPath = relLocalPath;
1440 void setFile(AbstractFile file) {
1451 UnpackedNode getChild(String childFileName) {
1452 UnpackedNode ret = null;
1453 for (UnpackedNode child : children) {
1454 if (child.getFileName().equals(childFileName)) {
1462 String getFileName() {
1466 AbstractFile getFile() {
1470 String getLocalRelPath() {
1480 void setLocalRelPath(String localRelativePath) {
1481 localRelPath = localRelativePath;
1488 boolean isIsFile() {
1492 void setFileNameBytes(byte[] fileNameBytes) {
1493 if (fileNameBytes != null) {
1494 this.fileNameBytes = Arrays.copyOf(fileNameBytes, fileNameBytes.length);
1498 byte[] getFileNameBytes() {
1499 if (fileNameBytes == null) {
1502 return Arrays.copyOf(fileNameBytes, fileNameBytes.length);
1511 static class Archive {
1514 private final int depth;
1515 private final List<Archive> children;
1516 private final long rootArchiveId;
1517 private boolean flaggedAsZipBomb =
false;
1518 private final AbstractFile archiveFile;
1532 Archive(
int depth,
long rootArchiveId, AbstractFile archiveFile) {
1533 this.children =
new ArrayList<>();
1535 this.rootArchiveId = rootArchiveId;
1536 this.archiveFile = archiveFile;
1545 void addChild(Archive child) {
1546 children.add(child);
1553 synchronized void flagAsZipBomb() {
1554 flaggedAsZipBomb =
true;
1562 synchronized boolean isFlaggedAsZipBomb() {
1563 return flaggedAsZipBomb;
1571 AbstractFile getArchiveFile() {
1580 long getRootArchiveId() {
1581 return rootArchiveId;
1589 long getObjectId() {
1590 return archiveFile.getId();
1622 abstractFile = file;
FileManager getFileManager()
synchronized DerivedFile addDerivedFile(String fileName, String localPath, long size, long ctime, long crtime, long atime, long mtime, boolean isFile, Content parentObj, String rederiveDetails, String toolName, String toolVersion, String otherDetails, TskData.EncodingType encodingType)
static Case getCurrentCaseThrows()
synchronized DerivedFile updateDerivedFile(DerivedFile derivedFile, String localPath, long size, long ctime, long crtime, long atime, long mtime, boolean isFile, String mimeType, String rederiveDetails, String toolName, String toolVersion, String otherDetails, TskData.EncodingType encodingType)