23 package org.sleuthkit.autopsy.recentactivity;
26 import java.io.FileNotFoundException;
27 import java.io.IOException;
28 import java.nio.ByteBuffer;
29 import java.nio.ByteOrder;
30 import java.nio.file.Files;
31 import java.nio.file.Path;
32 import java.nio.file.Paths;
33 import java.util.ArrayList;
34 import java.util.Arrays;
35 import java.util.HashMap;
36 import java.util.List;
38 import java.util.logging.Level;
39 import org.joda.time.Instant;
40 import org.openide.util.NbBundle.Messages;
49 import static org.
sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_OS_ACCOUNT;
51 import static org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_DELETED;
52 import static org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH;
53 import static org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_USER_ID;
54 import static org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_USER_NAME;
69 final class ExtractRecycleBin
extends Extract {
71 private static final Logger logger = Logger.getLogger(ExtractRecycleBin.class.getName());
73 private static final String RECYCLE_BIN_ARTIFACT_NAME =
"TSK_RECYCLE_BIN";
75 private static final int V1_FILE_NAME_OFFSET = 24;
76 private static final int V2_FILE_NAME_OFFSET = 28;
79 "ExtractRecycleBin_module_name=Recycle Bin"
82 this.moduleName = Bundle.ExtractRecycleBin_module_name();
86 void process(Content dataSource, IngestJobContext context, DataSourceIngestModuleProgress progressBar) {
90 createRecycleBinArtifactType();
91 }
catch (TskCoreException ex) {
92 logger.log(Level.WARNING, String.format(
"%s may not have been created.", RECYCLE_BIN_ARTIFACT_NAME), ex);
95 BlackboardArtifact.Type recycleBinArtifactType;
98 recycleBinArtifactType = tskCase.getArtifactType(RECYCLE_BIN_ARTIFACT_NAME);
99 }
catch (TskCoreException ex) {
100 logger.log(Level.WARNING, String.format(
"Unable to retrive custom artifact type %s", RECYCLE_BIN_ARTIFACT_NAME), ex);
106 Map<String, String> userNameMap;
108 userNameMap = makeUserNameMap(dataSource);
109 }
catch (TskCoreException ex) {
110 logger.log(Level.WARNING,
"Unable to create OS Account user name map", ex);
113 userNameMap =
new HashMap<>();
116 FileManager fileManager = Case.getCurrentCase().getServices().getFileManager();
119 Map<String, List<AbstractFile>> rFileMap;
121 rFileMap = makeRFileMap(dataSource);
122 }
catch (TskCoreException ex) {
123 logger.log(Level.WARNING, String.format(
"Unable to create $R file map for dataSource: %s", dataSource.getName()), ex);
128 List<AbstractFile> iFiles;
130 iFiles = fileManager.findFiles(dataSource,
"$I%");
131 }
catch (TskCoreException ex) {
132 logger.log(Level.WARNING,
"Unable to find recycle bin I files.", ex);
136 String tempRARecycleBinPath = RAImageIngestModule.getRATempPath(Case.getCurrentCase(),
"recyclebin");
139 for (AbstractFile iFile : iFiles) {
141 if (context.dataSourceIngestIsCancelled()) {
145 processIFile(context, recycleBinArtifactType, iFile, userNameMap, rFileMap, tempRARecycleBinPath);
148 (
new File(tempRARecycleBinPath)).
delete();
160 private void processIFile(IngestJobContext context, BlackboardArtifact.Type recycleBinArtifactType, AbstractFile iFile, Map<String, String> userNameMap, Map<String, List<AbstractFile>> rFileMap, String tempRARecycleBinPath) {
161 String tempFilePath = tempRARecycleBinPath + File.separator + Instant.now().getMillis() + iFile.getName();
164 ContentUtils.writeToFile(iFile,
new File(tempFilePath));
165 }
catch (IOException ex) {
166 logger.log(Level.WARNING, String.format(
"Unable to write %s to temp directory. File name: %s", iFile.getName(), tempFilePath), ex);
173 RecycledFileMetaData metaData;
175 metaData = parseIFile(tempFilePath);
176 }
catch (IOException ex) {
177 logger.log(Level.WARNING, String.format(
"Unable to parse iFile %s", iFile.getName()), ex);
183 String userID = getUserIDFromPath(iFile.getParentPath());
184 String userName =
"";
185 if (!userID.isEmpty()) {
186 userName = userNameMap.get(userID);
194 String rFileName = iFile.getName().replace(
"$I",
"$R");
195 List<AbstractFile> rFiles = rFileMap.get(rFileName);
196 if (rFiles == null) {
199 SleuthkitCase skCase = Case.getCurrentCase().getSleuthkitCase();
200 for (AbstractFile rFile : rFiles) {
201 if (context.dataSourceIngestIsCancelled()) {
205 if (iFile.getParentPath().equals(rFile.getParentPath())
206 && iFile.getMetaFlagsAsString().equals(rFile.getMetaFlagsAsString())) {
208 postArtifact(createArtifact(rFile, recycleBinArtifactType, metaData.getFullWindowsPath(), userName, metaData.getDeletedTimeStamp()));
213 if (rFile instanceof FsContent) {
217 AbstractFile directory = getOrMakeFolder(Case.getCurrentCase().getSleuthkitCase(), (FsContent) rFile, metaData.getFullWindowsPath());
218 popuplateDeletedDirectory(Case.getCurrentCase().getSleuthkitCase(), directory, rFile.getChildren(), metaData.getFullWindowsPath(), metaData.getDeletedTimeStamp());
221 AbstractFile folder = getOrMakeFolder(Case.getCurrentCase().getSleuthkitCase(), (FsContent) rFile.getParent(), Paths.get(metaData.getFullWindowsPath()).getParent().toString());
222 addFileSystemFile(skCase, (FsContent)rFile, folder, Paths.get(metaData.getFullWindowsPath()).getFileName().toString(), metaData.getDeletedTimeStamp());
225 }
catch (TskCoreException ex) {
226 logger.log(Level.WARNING, String.format(
"Unable to add attributes to artifact %s", rFile.getName()), ex);
231 (
new File(tempFilePath)).
delete();
249 private void popuplateDeletedDirectory(SleuthkitCase skCase, AbstractFile parentFolder, List<Content> recycledChildren, String parentPath,
long deletedTimeStamp)
throws TskCoreException {
250 if (recycledChildren == null) {
254 for (Content child : recycledChildren) {
255 if (child instanceof FsContent) {
256 FsContent fsContent = (FsContent) child;
257 if (fsContent.isFile()) {
258 addFileSystemFile(skCase, fsContent, parentFolder, fsContent.getName(), deletedTimeStamp);
259 }
else if (fsContent.isDir()) {
260 String newPath = parentPath +
"\\" + fsContent.getName();
261 AbstractFile childFolder = getOrMakeFolder(skCase, fsContent, parentPath);
262 popuplateDeletedDirectory(skCase, childFolder, fsContent.getChildren(), newPath, deletedTimeStamp);
298 private RecycledFileMetaData parseIFile(String iFilePath)
throws FileNotFoundException, IOException {
299 byte[] allBytes = Files.readAllBytes(Paths.get(iFilePath));
301 ByteBuffer byteBuffer = ByteBuffer.wrap(allBytes);
302 byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
304 long version = byteBuffer.getLong();
305 long fileSize = byteBuffer.getLong();
306 long timestamp = byteBuffer.getLong();
309 timestamp = Util.filetimeToMillis(timestamp) / 1000;
314 stringBytes = Arrays.copyOfRange(allBytes, V1_FILE_NAME_OFFSET, allBytes.length);
316 int fileNameLength = byteBuffer.getInt() * 2;
317 stringBytes = Arrays.copyOfRange(allBytes, V2_FILE_NAME_OFFSET, V2_FILE_NAME_OFFSET + fileNameLength);
320 String fileName =
new String(stringBytes,
"UTF-16LE");
322 return new RecycledFileMetaData(fileSize, timestamp, fileName);
334 private Map<String, String> makeUserNameMap(Content dataSource)
throws TskCoreException {
335 Map<String, String> userNameMap =
new HashMap<>();
337 List<BlackboardArtifact> accounts = blackboard.getArtifacts(TSK_OS_ACCOUNT.getTypeID(), dataSource.getId());
339 for (BlackboardArtifact account : accounts) {
340 BlackboardAttribute nameAttribute = getAttributeForArtifact(account, TSK_USER_NAME);
341 BlackboardAttribute idAttribute = getAttributeForArtifact(account, TSK_USER_ID);
343 String userName = nameAttribute != null ? nameAttribute.getDisplayString() :
"";
344 String userID = idAttribute != null ? idAttribute.getDisplayString() :
"";
346 if (!userID.isEmpty()) {
347 userNameMap.put(userID, userName);
364 private Map<String, List<AbstractFile>> makeRFileMap(Content dataSource)
throws TskCoreException {
365 FileManager fileManager = Case.getCurrentCase().getServices().getFileManager();
366 List<AbstractFile> rFiles = fileManager.findFiles(dataSource,
"$R%");
367 Map<String, List<AbstractFile>> fileMap =
new HashMap<>();
369 for (AbstractFile rFile : rFiles) {
370 String fileName = rFile.getName();
371 List<AbstractFile> fileList = fileMap.get(fileName);
373 if (fileList == null) {
374 fileList =
new ArrayList<>();
375 fileMap.put(fileName, fileList);
392 private String getUserIDFromPath(String iFileParentPath) {
393 int index = iFileParentPath.indexOf(
'-') - 1;
395 return (iFileParentPath.substring(index)).replace(
"/",
"");
411 private BlackboardAttribute getAttributeForArtifact(BlackboardArtifact artifact, BlackboardAttribute.ATTRIBUTE_TYPE type) throws TskCoreException {
412 return artifact.getAttribute(
new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.fromID(type.getTypeID())));
420 private void createRecycleBinArtifactType() throws TskCoreException {
422 tskCase.addBlackboardArtifactType(RECYCLE_BIN_ARTIFACT_NAME,
"Recycle Bin");
423 }
catch (TskDataException ex) {
424 logger.log(Level.INFO, String.format(
"%s may have already been defined for this case", RECYCLE_BIN_ARTIFACT_NAME));
442 private BlackboardArtifact createArtifact(AbstractFile rFile, BlackboardArtifact.Type type, String fileName, String userName,
long dateTime)
throws TskCoreException {
443 BlackboardArtifact bba = rFile.newArtifact(type.getTypeID());
444 bba.addAttribute(
new BlackboardAttribute(TSK_PATH, getName(), fileName));
445 bba.addAttribute(
new BlackboardAttribute(TSK_DATETIME_DELETED, getName(), dateTime));
446 bba.addAttribute(
new BlackboardAttribute(TSK_USER_NAME, getName(), userName == null || userName.isEmpty() ?
"" : userName));
462 private AbstractFile getOrMakeFolder(SleuthkitCase skCase, FsContent dataSource, String path)
throws TskCoreException {
464 String parentPath = getParentPath(path);
465 String folderName = getFileName(path);
467 List<AbstractFile> files = null;
468 if (parentPath != null) {
469 if (!parentPath.equals(
"/")) {
470 parentPath = parentPath +
"/";
473 files = skCase.findAllFilesWhere(String.format(
"fs_obj_id=%s AND parent_path='%s' AND name='%s'",
474 dataSource.getFileSystemId(), SleuthkitCase.escapeSingleQuotes(parentPath), folderName != null ? SleuthkitCase.escapeSingleQuotes(folderName) :
""));
476 files = skCase.findAllFilesWhere(String.format(
"fs_obj_id=%s AND parent_path='/' AND name=''", dataSource.getFileSystemId()));
479 if (files == null || files.isEmpty()) {
480 AbstractFile parent = getOrMakeFolder(skCase, dataSource, parentPath);
481 return skCase.addVirtualDirectory(parent.getId(), folderName);
499 private void addFileSystemFile(SleuthkitCase skCase, FsContent recycleBinFile, Content parentDir, String fileName,
long deletedTime)
throws TskCoreException {
500 skCase.addFileSystemFile(
501 recycleBinFile.getDataSourceObjectId(),
502 recycleBinFile.getFileSystemId(),
504 recycleBinFile.getMetaAddr(),
505 (int) recycleBinFile.getMetaSeq(),
506 recycleBinFile.getAttrType(),
507 recycleBinFile.getAttributeId(),
508 TskData.TSK_FS_NAME_FLAG_ENUM.UNALLOC,
509 (short) (TskData.TSK_FS_META_FLAG_ENUM.UNALLOC.getValue() | TskData.TSK_FS_META_FLAG_ENUM.USED.getValue()),
510 recycleBinFile.getSize(),
511 recycleBinFile.getCtime(), recycleBinFile.getCrtime(), recycleBinFile.getAtime(), deletedTime,
523 String normalizeFilePath(String pathString) {
524 if (pathString == null || pathString.isEmpty()) {
528 Path path = Paths.get(pathString);
529 int nameCount = path.getNameCount();
531 String rootless =
"/" + path.subpath(0, nameCount);
532 return rootless.replace(
"\\",
"/");
547 String getFileName(String filePath) {
548 Path fileNamePath = Paths.get(filePath).getFileName();
549 if (fileNamePath != null) {
550 return fileNamePath.toString();
562 String getParentPath(String path) {
563 Path parentPath = Paths.get(path).getParent();
564 if (parentPath != null) {
565 return normalizeFilePath(parentPath.toString());
573 final class RecycledFileMetaData {
575 private final long fileSize;
576 private final long deletedTimeStamp;
577 private final String fileName;
586 RecycledFileMetaData(Long fileSize,
long deletedTimeStamp, String fileName) {
587 this.fileSize = fileSize;
588 this.deletedTimeStamp = deletedTimeStamp;
589 this.fileName = fileName;
606 long getDeletedTimeStamp() {
607 return deletedTimeStamp;
616 String getFullWindowsPath() {
617 return fileName.trim();