Autopsy  4.18.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
ExtractRegistry.java
Go to the documentation of this file.
1 /*
2  *
3  * Autopsy Forensic Browser
4  *
5  * Copyright 2012-2021 Basis Technology Corp.
6  *
7  * Copyright 2012 42six Solutions.
8  * Contact: aebadirad <at> 42six <dot> com
9  * Project Contact/Architect: carrier <at> sleuthkit <dot> org
10  *
11  * Licensed under the Apache License, Version 2.0 (the "License");
12  * you may not use this file except in compliance with the License.
13  * You may obtain a copy of the License at
14  *
15  * http://www.apache.org/licenses/LICENSE-2.0
16  *
17  * Unless required by applicable law or agreed to in writing, software
18  * distributed under the License is distributed on an "AS IS" BASIS,
19  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20  * See the License for the specific language governing permissions and
21  * limitations under the License.
22  */
23 package org.sleuthkit.autopsy.recentactivity;
24 
25 import java.io.BufferedReader;
26 import java.io.File;
27 import java.io.FileInputStream;
28 import java.io.FileNotFoundException;
29 import java.io.FileReader;
30 import java.io.FileWriter;
31 import java.io.IOException;
32 import java.io.InputStreamReader;
33 import java.io.StringReader;
34 import java.nio.charset.StandardCharsets;
35 import java.text.ParseException;
36 import java.text.SimpleDateFormat;
37 import java.util.logging.Level;
38 import javax.xml.parsers.DocumentBuilder;
39 import javax.xml.parsers.DocumentBuilderFactory;
40 import javax.xml.parsers.ParserConfigurationException;
41 import org.apache.commons.io.FilenameUtils;
42 import org.openide.modules.InstalledFileLocator;
43 import org.openide.util.NbBundle;
51 import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
52 import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
53 import org.w3c.dom.Document;
54 import org.w3c.dom.Element;
55 import org.w3c.dom.Node;
56 import org.w3c.dom.NodeList;
57 import org.xml.sax.InputSource;
58 import org.xml.sax.SAXException;
59 import java.nio.file.Path;
60 import java.util.AbstractMap;
61 import java.util.ArrayList;
62 import java.util.List;
63 import java.util.Collection;
64 import java.util.Date;
65 import java.util.HashMap;
66 import java.util.Map;
67 import java.util.Scanner;
68 import java.util.Set;
69 import java.util.HashSet;
70 import static java.util.Locale.US;
71 import java.util.Optional;
72 import static java.util.TimeZone.getTimeZone;
73 import java.util.stream.Collectors;
74 import org.openide.util.Lookup;
79 import org.sleuthkit.datamodel.AbstractFile;
80 import org.sleuthkit.datamodel.Account;
81 import org.sleuthkit.datamodel.Blackboard.BlackboardException;
82 import org.sleuthkit.datamodel.BlackboardArtifact;
83 import org.sleuthkit.datamodel.BlackboardAttribute;
84 import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT;
85 import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME;
86 import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED;
87 import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED;
88 import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_MODIFIED;
89 import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DEVICE_ID;
90 import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME;
91 import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH;
92 import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_HOME_DIR;
93 import org.sleuthkit.datamodel.Content;
94 import org.sleuthkit.datamodel.DataSource;
95 import org.sleuthkit.datamodel.Host;
96 import org.sleuthkit.datamodel.HostManager;
97 import org.sleuthkit.datamodel.OsAccount;
98 import org.sleuthkit.datamodel.OsAccount.OsAccountAttribute;
99 import org.sleuthkit.datamodel.OsAccountInstance;
100 import org.sleuthkit.datamodel.OsAccountManager;
101 import org.sleuthkit.datamodel.OsAccountManager.NotUserSIDException;
102 import org.sleuthkit.datamodel.OsAccountManager.OsAccountUpdateResult;
103 import org.sleuthkit.datamodel.OsAccountRealm;
104 import org.sleuthkit.datamodel.ReadContentInputStream.ReadContentInputStreamException;
105 import org.sleuthkit.datamodel.Report;
106 import org.sleuthkit.datamodel.TskCoreException;
107 import org.sleuthkit.datamodel.TskDataException;
108 
115 @NbBundle.Messages({
116  "RegRipperNotFound=Autopsy RegRipper executable not found.",
117  "RegRipperFullNotFound=Full version RegRipper executable not found.",
118  "Progress_Message_Analyze_Registry=Analyzing Registry Files",
119  "Shellbag_Artifact_Display_Name=Shell Bags",
120  "Shellbag_Key_Attribute_Display_Name=Key",
121  "Shellbag_Last_Write_Attribute_Display_Name=Last Write",
122  "Recently_Used_Artifacts_Office_Trustrecords=Stored in TrustRecords because Office security exception was granted",
123  "Recently_Used_Artifacts_ArcHistory=Recently opened by 7Zip",
124  "Recently_Used_Artifacts_Applets=Recently opened according to Applets registry key",
125  "Recently_Used_Artifacts_Mmc=Recently opened according to Windows Management Console MRU",
126  "Recently_Used_Artifacts_Winrar=Recently opened according to WinRAR MRU",
127  "Recently_Used_Artifacts_Officedocs=Recently opened according to Office MRU",
128  "Recently_Used_Artifacts_Adobe=Recently opened according to Adobe MRU",
129  "Recently_Used_Artifacts_Mediaplayer=Recently opened according to Media Player MRU",
130  "Registry_System_Bam=Recently Executed according to Background Activity Moderator (BAM)"
131 })
132 class ExtractRegistry extends Extract {
133 
134  private static final String USERNAME_KEY = "Username"; //NON-NLS
135  private static final String SID_KEY = "SID"; //NON-NLS
136  private static final String RID_KEY = "RID"; //NON-NLS
137  private static final String ACCOUNT_CREATED_KEY = "Account Created"; //NON-NLS
138  private static final String LAST_LOGIN_KEY = "Last Login Date"; //NON-NLS
139  private static final String LOGIN_COUNT_KEY = "Login Count"; //NON-NLS
140  private static final String FULL_NAME_KEY = "Full Name"; //NON-NLS
141  private static final String USER_COMMENT_KEY = "User Comment"; //NON-NLS
142  private static final String ACCOUNT_TYPE_KEY = "Account Type"; //NON-NLS
143  private static final String NAME_KEY = "Name"; //NON-NLS
144  private static final String PWD_RESET_KEY = "Pwd Rest Date"; //NON-NLS
145  private static final String PWD_FAILE_KEY = "Pwd Fail Date"; //NON-NLS
146  private static final String INTERNET_NAME_KEY = "InternetName"; //NON-NLS
147  private static final String PWD_DOES_NOT_EXPIRE_KEY = "Password does not expire"; //NON-NLS
148  private static final String ACCOUNT_DISABLED_KEY = "Account Disabled"; //NON-NLS
149  private static final String PWD_NOT_REQUIRED_KEY = "Password not required"; //NON-NLS
150  private static final String NORMAL_ACCOUNT_KEY = "Normal user account"; //NON-NLS
151  private static final String HOME_DIRECTORY_REQUIRED_KEY = "Home directory required";
152  private static final String TEMPORARY_DUPLICATE_ACCOUNT = "Temporary duplicate account";
153  private static final String MNS_LOGON_ACCOUNT_KEY = "MNS logon user account";
154  private static final String INTERDOMAIN_TRUST_ACCOUNT_KEY = "Interdomain trust account";
155  private static final String WORKSTATION_TRUST_ACCOUNT = "Workstation trust account";
156  private static final String SERVER_TRUST_ACCOUNT = "Server trust account";
157  private static final String ACCOUNT_AUTO_LOCKED = "Account auto locked";
158  private static final String PASSWORD_HINT = "Password Hint";
159 
160  private static final String[] PASSWORD_SETTINGS_FLAGS = {PWD_DOES_NOT_EXPIRE_KEY, PWD_NOT_REQUIRED_KEY};
161  private static final String[] ACCOUNT_SETTINGS_FLAGS = {ACCOUNT_AUTO_LOCKED, HOME_DIRECTORY_REQUIRED_KEY, ACCOUNT_DISABLED_KEY};
162  private static final String[] ACCOUNT_TYPE_FLAGS = {NORMAL_ACCOUNT_KEY, SERVER_TRUST_ACCOUNT, WORKSTATION_TRUST_ACCOUNT, INTERDOMAIN_TRUST_ACCOUNT_KEY, MNS_LOGON_ACCOUNT_KEY, TEMPORARY_DUPLICATE_ACCOUNT};
163 
164  final private static UsbDeviceIdMapper USB_MAPPER = new UsbDeviceIdMapper();
165  final private static String RIP_EXE = "rip.exe";
166  final private static String RIP_PL = "rip.pl";
167  final private static String RIP_PL_INCLUDE_FLAG = "-I";
168  final private static int MS_IN_SEC = 1000;
169  final private static String NEVER_DATE = "Never";
170  final private static String SECTION_DIVIDER = "-------------------------";
171  final private static Logger logger = Logger.getLogger(ExtractRegistry.class.getName());
172  private final List<String> rrCmd = new ArrayList<>();
173  private final List<String> rrFullCmd = new ArrayList<>();
174  private final Path rrHome; // Path to the Autopsy version of RegRipper
175  private final Path rrFullHome; // Path to the full version of RegRipper
176  private Content dataSource;
177  private IngestJobContext context;
178  private Map<String, String> userNameMap;
179 
180  private String compName = "";
181  private String domainName = "";
182 
183  private static final String SHELLBAG_ARTIFACT_NAME = "RA_SHELL_BAG"; //NON-NLS
184  private static final String SHELLBAG_ATTRIBUTE_LAST_WRITE = "RA_SHELL_BAG_LAST_WRITE"; //NON-NLS
185  private static final String SHELLBAG_ATTRIBUTE_KEY = "RA_SHELL_BAG_KEY"; //NON-NLS
186 
187  private static final SimpleDateFormat REG_RIPPER_TIME_FORMAT = new SimpleDateFormat("EEE MMM dd HH:mm:ss yyyy 'Z'", US);
188 
189  private BlackboardArtifact.Type shellBagArtifactType = null;
190  private BlackboardAttribute.Type shellBagKeyAttributeType = null;
191  private BlackboardAttribute.Type shellBagLastWriteAttributeType = null;
192 
193  static {
194  REG_RIPPER_TIME_FORMAT.setTimeZone(getTimeZone("GMT"));
195  }
196 
197  ExtractRegistry() throws IngestModuleException {
198  super(NbBundle.getMessage(ExtractIE.class, "ExtractRegistry.moduleName.text"));
199 
200  final File rrRoot = InstalledFileLocator.getDefault().locate("rr", ExtractRegistry.class.getPackage().getName(), false); //NON-NLS
201  if (rrRoot == null) {
202  throw new IngestModuleException(Bundle.RegRipperNotFound());
203  }
204 
205  final File rrFullRoot = InstalledFileLocator.getDefault().locate("rr-full", ExtractRegistry.class.getPackage().getName(), false); //NON-NLS
206  if (rrFullRoot == null) {
207  throw new IngestModuleException(Bundle.RegRipperFullNotFound());
208  }
209 
210  String executableToRun = RIP_EXE;
211  if (!PlatformUtil.isWindowsOS()) {
212  executableToRun = RIP_PL;
213  }
214  rrHome = rrRoot.toPath();
215  String rrPath = rrHome.resolve(executableToRun).toString();
216  rrFullHome = rrFullRoot.toPath();
217 
218  if (!(new File(rrPath).exists())) {
219  throw new IngestModuleException(Bundle.RegRipperNotFound());
220  }
221  String rrFullPath = rrFullHome.resolve(executableToRun).toString();
222  if (!(new File(rrFullPath).exists())) {
223  throw new IngestModuleException(Bundle.RegRipperFullNotFound());
224  }
225  if (PlatformUtil.isWindowsOS()) {
226  rrCmd.add(rrPath);
227  rrFullCmd.add(rrFullPath);
228  } else {
229  String perl;
230  File usrBin = new File("/usr/bin/perl");
231  File usrLocalBin = new File("/usr/local/bin/perl");
232  if (usrBin.canExecute() && usrBin.exists() && !usrBin.isDirectory()) {
233  perl = "/usr/bin/perl";
234  } else if (usrLocalBin.canExecute() && usrLocalBin.exists() && !usrLocalBin.isDirectory()) {
235  perl = "/usr/local/bin/perl";
236  } else {
237  throw new IngestModuleException("perl not found in your system");
238  }
239  rrCmd.add(perl);
240  rrCmd.add(RIP_PL_INCLUDE_FLAG);
241  rrCmd.add(rrHome.toString());
242  rrCmd.add(rrPath);
243  rrFullCmd.add(perl);
244  rrFullCmd.add(RIP_PL_INCLUDE_FLAG);
245  rrFullCmd.add(rrFullHome.toString());
246  rrFullCmd.add(rrFullPath);
247  }
248  }
249 
253  private List<AbstractFile> findRegistryFiles() {
254  List<AbstractFile> allRegistryFiles = new ArrayList<>();
255  org.sleuthkit.autopsy.casemodule.services.FileManager fileManager = currentCase.getServices().getFileManager();
256 
257  // find the sam hives', process this first so we can map the user id's and sids for later use
258  try {
259  allRegistryFiles.addAll(fileManager.findFiles(dataSource, "sam", "/system32/config")); //NON-NLS
260  } catch (TskCoreException ex) {
261  String msg = NbBundle.getMessage(this.getClass(),
262  "ExtractRegistry.findRegFiles.errMsg.errReadingFile", "sam");
263  logger.log(Level.WARNING, msg, ex);
264  this.addErrorMessage(this.getName() + ": " + msg);
265  }
266 
267  // find the user-specific ntuser-dat files
268  try {
269  allRegistryFiles.addAll(fileManager.findFiles(dataSource, "ntuser.dat")); //NON-NLS
270  } catch (TskCoreException ex) {
271  logger.log(Level.WARNING, "Error fetching 'ntuser.dat' file."); //NON-NLS
272  }
273 
274  // find the user-specific ntuser-dat files
275  try {
276  allRegistryFiles.addAll(fileManager.findFiles(dataSource, "usrclass.dat")); //NON-NLS
277  } catch (TskCoreException ex) {
278  logger.log(Level.WARNING, String.format("Error finding 'usrclass.dat' files."), ex); //NON-NLS
279  }
280 
281  // find the system hives'
282  String[] regFileNames = new String[]{"system", "software", "security"}; //NON-NLS
283  for (String regFileName : regFileNames) {
284  try {
285  allRegistryFiles.addAll(fileManager.findFiles(dataSource, regFileName, "/system32/config")); //NON-NLS
286  } catch (TskCoreException ex) {
287  String msg = NbBundle.getMessage(this.getClass(),
288  "ExtractRegistry.findRegFiles.errMsg.errReadingFile", regFileName);
289  logger.log(Level.WARNING, msg, ex);
290  this.addErrorMessage(this.getName() + ": " + msg);
291  }
292  }
293  return allRegistryFiles;
294  }
295 
301  private void analyzeRegistryFiles(long ingestJobId) {
302  List<AbstractFile> allRegistryFiles = findRegistryFiles();
303 
304  // open the log file
305  FileWriter logFile = null;
306  try {
307  logFile = new FileWriter(RAImageIngestModule.getRAOutputPath(currentCase, "reg", ingestJobId) + File.separator + "regripper-info.txt"); //NON-NLS
308  } catch (IOException ex) {
309  logger.log(Level.SEVERE, null, ex);
310  }
311 
312  for (AbstractFile regFile : allRegistryFiles) {
313  if (context.dataSourceIngestIsCancelled()) {
314  return;
315  }
316 
317  String regFileName = regFile.getName();
318  long regFileId = regFile.getId();
319  String regFileNameLocal = RAImageIngestModule.getRATempPath(currentCase, "reg", ingestJobId) + File.separator + regFileName;
320  String outputPathBase = RAImageIngestModule.getRAOutputPath(currentCase, "reg", ingestJobId) + File.separator + regFileName + "-regripper-" + Long.toString(regFileId); //NON-NLS
321  File regFileNameLocalFile = new File(regFileNameLocal);
322  try {
323  ContentUtils.writeToFile(regFile, regFileNameLocalFile, context::dataSourceIngestIsCancelled);
324  } catch (ReadContentInputStreamException ex) {
325  logger.log(Level.WARNING, String.format("Error reading registry file '%s' (id=%d).",
326  regFile.getName(), regFileId), ex); //NON-NLS
327  this.addErrorMessage(
328  NbBundle.getMessage(this.getClass(), "ExtractRegistry.analyzeRegFiles.errMsg.errWritingTemp",
329  this.getName(), regFileName));
330  continue;
331  } catch (IOException ex) {
332  logger.log(Level.SEVERE, String.format("Error writing temp registry file '%s' for registry file '%s' (id=%d).",
333  regFileNameLocal, regFile.getName(), regFileId), ex); //NON-NLS
334  this.addErrorMessage(
335  NbBundle.getMessage(this.getClass(), "ExtractRegistry.analyzeRegFiles.errMsg.errWritingTemp",
336  this.getName(), regFileName));
337  continue;
338  }
339 
340  if (context.dataSourceIngestIsCancelled()) {
341  break;
342  }
343 
344  try {
345  if (logFile != null) {
346  logFile.write(Long.toString(regFileId) + "\t" + regFile.getUniquePath() + "\n");
347  }
348  } catch (TskCoreException | IOException ex) {
349  logger.log(Level.SEVERE, null, ex);
350  }
351 
352  logger.log(Level.INFO, "{0}- Now getting registry information from {1}", new Object[]{getName(), regFileNameLocal}); //NON-NLS
353  RegOutputFiles regOutputFiles = ripRegistryFile(regFileNameLocal, outputPathBase);
354  if (context.dataSourceIngestIsCancelled()) {
355  break;
356  }
357 
358  // parse the autopsy-specific output
359  if (regOutputFiles.autopsyPlugins.isEmpty() == false && parseAutopsyPluginOutput(regOutputFiles.autopsyPlugins, regFile) == false) {
360  this.addErrorMessage(
361  NbBundle.getMessage(this.getClass(), "ExtractRegistry.analyzeRegFiles.failedParsingResults",
362  this.getName(), regFileName));
363  }
364 
365  if (context.dataSourceIngestIsCancelled()) {
366  return;
367  }
368 
369  // create a report for the full output
370  if (!regOutputFiles.fullPlugins.isEmpty()) {
371  //parse the full regripper output from SAM hive files
372  if (regFileNameLocal.toLowerCase().contains("sam") && parseSamPluginOutput(regOutputFiles.fullPlugins, regFile, ingestJobId) == false) {
373  this.addErrorMessage(
374  NbBundle.getMessage(this.getClass(), "ExtractRegistry.analyzeRegFiles.failedParsingResults",
375  this.getName(), regFileName));
376  } else if (regFileNameLocal.toLowerCase().contains("ntuser") || regFileNameLocal.toLowerCase().contains("usrclass")) {
377  try {
378  List<ShellBag> shellbags = ShellBagParser.parseShellbagOutput(regOutputFiles.fullPlugins);
379  createShellBagArtifacts(regFile, shellbags);
380  createRecentlyUsedArtifacts(regOutputFiles.fullPlugins, regFile);
381  } catch (IOException | TskCoreException ex) {
382  logger.log(Level.WARNING, String.format("Unable to get shell bags from file %s", regOutputFiles.fullPlugins), ex);
383  }
384  } else if (regFileNameLocal.toLowerCase().contains("system") && parseSystemPluginOutput(regOutputFiles.fullPlugins, regFile) == false) {
385  this.addErrorMessage(
386  NbBundle.getMessage(this.getClass(), "ExtractRegistry.analyzeRegFiles.failedParsingResults",
387  this.getName(), regFileName));
388  }
389 
390  if (context.dataSourceIngestIsCancelled()) {
391  return;
392  }
393 
394  try {
395  Report report = currentCase.addReport(regOutputFiles.fullPlugins,
396  NbBundle.getMessage(this.getClass(), "ExtractRegistry.parentModuleName.noSpace"),
397  "RegRipper " + regFile.getUniquePath(), regFile); //NON-NLS
398 
399  // Index the report content so that it will be available for keyword search.
400  KeywordSearchService searchService = Lookup.getDefault().lookup(KeywordSearchService.class);
401  if (null == searchService) {
402  logger.log(Level.WARNING, "Keyword search service not found. Report will not be indexed");
403  } else {
404  searchService.index(report);
405  report.close();
406  }
407  } catch (TskCoreException e) {
408  this.addErrorMessage("Error adding regripper output as Autopsy report: " + e.getLocalizedMessage()); //NON-NLS
409  }
410  }
411  // delete the hive
412  regFileNameLocalFile.delete();
413  }
414 
415  try {
416  if (logFile != null) {
417  logFile.close();
418  }
419  } catch (IOException ex) {
420  logger.log(Level.SEVERE, null, ex);
421  }
422  }
423 
431  private RegOutputFiles ripRegistryFile(String regFilePath, String outFilePathBase) {
432  String autopsyType = ""; // Type argument for rr for autopsy-specific modules
433  String fullType; // Type argument for rr for full set of modules
434 
435  RegOutputFiles regOutputFiles = new RegOutputFiles();
436 
437  if (regFilePath.toLowerCase().contains("system")) { //NON-NLS
438  autopsyType = "autopsysystem"; //NON-NLS
439  fullType = "system"; //NON-NLS
440  } else if (regFilePath.toLowerCase().contains("software")) { //NON-NLS
441  autopsyType = "autopsysoftware"; //NON-NLS
442  fullType = "software"; //NON-NLS
443  } else if (regFilePath.toLowerCase().contains("ntuser")) { //NON-NLS
444  autopsyType = "autopsyntuser"; //NON-NLS
445  fullType = "ntuser"; //NON-NLS
446  } else if (regFilePath.toLowerCase().contains("sam")) { //NON-NLS
447  //fullType sam output files are parsed for user information
448  fullType = "sam"; //NON-NLS
449  } else if (regFilePath.toLowerCase().contains("security")) { //NON-NLS
450  fullType = "security"; //NON-NLS
451  } else if (regFilePath.toLowerCase().contains("usrclass")) { //NON-NLS
452  fullType = "usrclass"; //NON-NLS
453  } else {
454  return regOutputFiles;
455  }
456 
457  // run the autopsy-specific set of modules
458  if (!autopsyType.isEmpty()) {
459  regOutputFiles.autopsyPlugins = outFilePathBase + "-autopsy.txt"; //NON-NLS
460  String errFilePath = outFilePathBase + "-autopsy.err.txt"; //NON-NLS
461  logger.log(Level.INFO, "Writing RegRipper results to: {0}", regOutputFiles.autopsyPlugins); //NON-NLS
462  executeRegRipper(rrCmd, rrHome, regFilePath, autopsyType, regOutputFiles.autopsyPlugins, errFilePath);
463  }
464  if (context.dataSourceIngestIsCancelled()) {
465  return regOutputFiles;
466  }
467 
468  // run the full set of rr modules
469  if (!fullType.isEmpty()) {
470  regOutputFiles.fullPlugins = outFilePathBase + "-full.txt"; //NON-NLS
471  String errFilePath = outFilePathBase + "-full.err.txt"; //NON-NLS
472  logger.log(Level.INFO, "Writing Full RegRipper results to: {0}", regOutputFiles.fullPlugins); //NON-NLS
473  executeRegRipper(rrFullCmd, rrFullHome, regFilePath, fullType, regOutputFiles.fullPlugins, errFilePath);
474  try {
475  scanErrorLogs(errFilePath);
476  } catch (IOException ex) {
477  logger.log(Level.SEVERE, String.format("Unable to run RegRipper on %s", regFilePath), ex); //NON-NLS
478  this.addErrorMessage(NbBundle.getMessage(this.getClass(), "ExtractRegistry.execRegRip.errMsg.failedAnalyzeRegFile", this.getName(), regFilePath));
479  }
480  }
481  return regOutputFiles;
482  }
483 
484  private void scanErrorLogs(String errFilePath) throws IOException {
485  File regfile = new File(errFilePath);
486  try (BufferedReader reader = new BufferedReader(new FileReader(regfile))) {
487  String line = reader.readLine();
488  while (line != null) {
489  line = line.trim();
490  if (line.toLowerCase().contains("error") || line.toLowerCase().contains("@inc")) {
491  logger.log(Level.WARNING, "Regripper file {0} contains errors from run", errFilePath); //NON-NLS
492 
493  }
494  line = reader.readLine();
495  }
496  }
497  }
498 
499  private void executeRegRipper(List<String> regRipperPath, Path regRipperHomeDir, String hiveFilePath, String hiveFileType, String outputFile, String errFile) {
500  try {
501  List<String> commandLine = new ArrayList<>();
502  for (String cmd : regRipperPath) {
503  commandLine.add(cmd);
504  }
505  commandLine.add("-r"); //NON-NLS
506  commandLine.add(hiveFilePath);
507  commandLine.add("-f"); //NON-NLS
508  commandLine.add(hiveFileType);
509 
510  ProcessBuilder processBuilder = new ProcessBuilder(commandLine);
511  processBuilder.directory(regRipperHomeDir.toFile()); // RegRipper 2.8 has to be run from its own directory
512  processBuilder.redirectOutput(new File(outputFile));
513  processBuilder.redirectError(new File(errFile));
514  ExecUtil.execute(processBuilder, new DataSourceIngestModuleProcessTerminator(context, true));
515  } catch (IOException ex) {
516  logger.log(Level.SEVERE, String.format("Error running RegRipper on %s", hiveFilePath), ex); //NON-NLS
517  this.addErrorMessage(NbBundle.getMessage(this.getClass(), "ExtractRegistry.execRegRip.errMsg.failedAnalyzeRegFile", this.getName(), hiveFilePath));
518  }
519  }
520 
521  // @@@ VERIFY that we are doing the right thing when we parse multiple NTUSER.DAT
530  private boolean parseAutopsyPluginOutput(String regFilePath, AbstractFile regFile) {
531  FileInputStream fstream = null;
532  List<BlackboardArtifact> newArtifacts = new ArrayList<>();
533  try {
534  // Read the file in and create a Document and elements
535  File regfile = new File(regFilePath);
536  fstream = new FileInputStream(regfile);
537  String regString = new Scanner(fstream, "UTF-8").useDelimiter("\\Z").next(); //NON-NLS
538  String startdoc = "<?xml version=\"1.0\"?><document>"; //NON-NLS
539  String result = regString.replaceAll("----------------------------------------", "");
540  result = result.replaceAll("\\n", ""); //NON-NLS
541  result = result.replaceAll("\\r", ""); //NON-NLS
542  result = result.replaceAll("'", "&apos;"); //NON-NLS
543  result = result.replaceAll("&", "&amp;"); //NON-NLS
544  result = result.replace('\0', ' '); // NON-NLS
545  String enddoc = "</document>"; //NON-NLS
546  String stringdoc = startdoc + result + enddoc;
547  DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
548  Document doc = builder.parse(new InputSource(new StringReader(stringdoc)));
549 
550  // cycle through the elements in the doc
551  Element oroot = doc.getDocumentElement();
552  NodeList children = oroot.getChildNodes();
553  int len = children.getLength();
554  for (int i = 0; i < len; i++) {
555 
556  if (context.dataSourceIngestIsCancelled()) {
557  return false;
558  }
559 
560  Element tempnode = (Element) children.item(i);
561 
562  String dataType = tempnode.getNodeName();
563  NodeList timenodes = tempnode.getElementsByTagName("mtime"); //NON-NLS
564  Long mtime = null;
565  if (timenodes.getLength() > 0) {
566  Element timenode = (Element) timenodes.item(0);
567  String etime = timenode.getTextContent();
568  //sometimes etime will be an empty string and therefore can not be parsed into a date
569  if (etime != null && !etime.isEmpty()) {
570  try {
571  mtime = new SimpleDateFormat("EEE MMM d HH:mm:ss yyyy", US).parse(etime).getTime();
572  String Tempdate = mtime.toString();
573  mtime = Long.valueOf(Tempdate) / MS_IN_SEC;
574  } catch (ParseException ex) {
575  logger.log(Level.WARNING, "Failed to parse epoch time when parsing the registry.", ex); //NON-NLS
576  }
577  }
578  }
579 
580  NodeList artroots = tempnode.getElementsByTagName("artifacts"); //NON-NLS
581  if (artroots.getLength() == 0) {
582  // If there isn't an artifact node, skip this entry
583  continue;
584  }
585 
586  Element artroot = (Element) artroots.item(0);
587  NodeList myartlist = artroot.getChildNodes();
588  String parentModuleName = RecentActivityExtracterModuleFactory.getModuleName();
589 
590  // If all artifact nodes should really go under one Blackboard artifact, need to process it differently
591  switch (dataType) {
592  case "WinVersion": //NON-NLS
593  String version = "";
594  String systemRoot = "";
595  String productId = "";
596  String regOwner = "";
597  String regOrg = "";
598  Long installtime = null;
599  for (int j = 0; j < myartlist.getLength(); j++) {
600  Node artchild = myartlist.item(j);
601  // If it has attributes, then it is an Element (based off API)
602  if (artchild.hasAttributes()) {
603  Element artnode = (Element) artchild;
604 
605  String value = artnode.getTextContent();
606  if (value != null) {
607  value = value.trim();
608  }
609  String name = artnode.getAttribute("name"); //NON-NLS
610  if (name == null) {
611  continue;
612  }
613  switch (name) {
614  case "ProductName": // NON-NLS
615  version = value;
616  break;
617  case "CSDVersion": // NON-NLS
618  // This is dependant on the fact that ProductName shows up first in the module output
619  version = version + " " + value;
620  break;
621  case "SystemRoot": //NON-NLS
622  systemRoot = value;
623  break;
624  case "ProductId": //NON-NLS
625  productId = value;
626  break;
627  case "RegisteredOwner": //NON-NLS
628  regOwner = value;
629  break;
630  case "RegisteredOrganization": //NON-NLS
631  regOrg = value;
632  break;
633  case "InstallDate": //NON-NLS
634  if (value != null && !value.isEmpty()) {
635  try {
636  installtime = new SimpleDateFormat("EEE MMM d HH:mm:ss yyyyZ", US).parse(value+"+0000").getTime();
637  String Tempdate = installtime.toString();
638  installtime = Long.valueOf(Tempdate) / MS_IN_SEC;
639  } catch (ParseException e) {
640  logger.log(Level.WARNING, "RegRipper::Conversion on DateTime -> ", e); //NON-NLS
641  }
642  }
643  break;
644  default:
645  break;
646  }
647  }
648  }
649  try {
650  Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
651  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME, parentModuleName, version));
652  if (installtime != null) {
653  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME, parentModuleName, installtime));
654  }
655  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH, parentModuleName, systemRoot));
656  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PRODUCT_ID, parentModuleName, productId));
657  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_OWNER, parentModuleName, regOwner));
658  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_ORGANIZATION, parentModuleName, regOrg));
659 
660  // Check if there is already an OS_INFO artifact for this file, and add to that if possible.
661  ArrayList<BlackboardArtifact> results = tskCase.getBlackboardArtifacts(ARTIFACT_TYPE.TSK_OS_INFO, regFile.getId());
662  if (results.isEmpty()) {
663  newArtifacts.add(createArtifactWithAttributes(ARTIFACT_TYPE.TSK_OS_INFO, regFile, bbattributes));
664  } else {
665  results.get(0).addAttributes(bbattributes);
666  }
667 
668  } catch (TskCoreException ex) {
669  logger.log(Level.SEVERE, String.format("Error adding installed program artifact to blackboard for file %d.", regFile.getId()), ex); //NON-NLS
670  }
671  break;
672  case "Profiler": // NON-NLS
673  String os = "";
674  String procArch = "";
675  String tempDir = "";
676  for (int j = 0; j < myartlist.getLength(); j++) {
677  Node artchild = myartlist.item(j);
678  // If it has attributes, then it is an Element (based off API)
679  if (artchild.hasAttributes()) {
680  Element artnode = (Element) artchild;
681 
682  String value = artnode.getTextContent().trim();
683  String name = artnode.getAttribute("name"); //NON-NLS
684  switch (name) {
685  case "OS": // NON-NLS
686  os = value;
687  break;
688  case "PROCESSOR_ARCHITECTURE": // NON-NLS
689  procArch = value;
690  break;
691  case "PROCESSOR_IDENTIFIER": //NON-NLS
692  break;
693  case "TEMP": //NON-NLS
694  tempDir = value;
695  break;
696  default:
697  break;
698  }
699  }
700  }
701  try {
702  Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
703  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_VERSION, parentModuleName, os));
704  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROCESSOR_ARCHITECTURE, parentModuleName, procArch));
705  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_TEMP_DIR, parentModuleName, tempDir));
706 
707  // Check if there is already an OS_INFO artifact for this file and add to that if possible
708  ArrayList<BlackboardArtifact> results = tskCase.getBlackboardArtifacts(ARTIFACT_TYPE.TSK_OS_INFO, regFile.getId());
709  if (results.isEmpty()) {
710  newArtifacts.add(createArtifactWithAttributes(ARTIFACT_TYPE.TSK_OS_INFO, regFile, bbattributes));
711  } else {
712  results.get(0).addAttributes(bbattributes);
713  }
714  } catch (TskCoreException ex) {
715  logger.log(Level.SEVERE, String.format("Error adding installed os_info to blackboard for file %d.", regFile.getId()), ex); //NON-NLS
716  }
717  break;
718  case "CompName": // NON-NLS
719  for (int j = 0; j < myartlist.getLength(); j++) {
720  Node artchild = myartlist.item(j);
721  // If it has attributes, then it is an Element (based off API)
722  if (artchild.hasAttributes()) {
723  Element artnode = (Element) artchild;
724 
725  String value = artnode.getTextContent().trim();
726  String name = artnode.getAttribute("name"); //NON-NLS
727 
728  if (name.equals("ComputerName")) { // NON-NLS
729  compName = value;
730  } else if (name.equals("Domain")) { // NON-NLS
731  domainName = value;
732  }
733  }
734  }
735  try {
736  Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
737  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME, parentModuleName, compName));
738  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN, parentModuleName, domainName));
739 
740  // Check if there is already an OS_INFO artifact for this file and add to that if possible
741  ArrayList<BlackboardArtifact> results = tskCase.getBlackboardArtifacts(ARTIFACT_TYPE.TSK_OS_INFO, regFile.getId());
742  if (results.isEmpty()) {
743  newArtifacts.add(createArtifactWithAttributes(ARTIFACT_TYPE.TSK_OS_INFO, regFile, bbattributes));
744  } else {
745  results.get(0).addAttributes(bbattributes);
746  }
747  for (Map.Entry<String, String> userMap : getUserNameMap().entrySet()) {
748  String sid = "";
749  try{
750  sid = userMap.getKey();
751  String userName = userMap.getValue();
752  createOrUpdateOsAccount(regFile, sid, userName, null);
753  } catch(TskCoreException | TskDataException | NotUserSIDException ex) {
754  logger.log(Level.WARNING, String.format("Failed to update Domain for existing OsAccount: %s, sid: %s", regFile.getId(), sid), ex);
755  }
756  }
757  } catch (TskCoreException ex) {
758  logger.log(Level.SEVERE, String.format("Error adding os_info artifact to blackboard for file %d.", regFile.getId()), ex); //NON-NLS
759  }
760  break;
761  default:
762  for (int j = 0; j < myartlist.getLength(); j++) {
763  Node artchild = myartlist.item(j);
764  // If it has attributes, then it is an Element (based off API)
765  if (artchild.hasAttributes()) {
766  Element artnode = (Element) artchild;
767 
768  String value = artnode.getTextContent().trim();
769  Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
770 
771  switch (dataType) {
772  case "recentdocs": //NON-NLS
773  // BlackboardArtifact bbart = tskCase.getContentById(orgId).newArtifact(ARTIFACT_TYPE.TSK_RECENT_OBJECT);
774  // bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_LAST_ACCESSED.getTypeID(), "RecentActivity", dataType, mtime));
775  // bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME.getTypeID(), "RecentActivity", dataType, mtimeItem));
776  // bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_VALUE.getTypeID(), "RecentActivity", dataType, value));
777  // bbart.addAttributes(bbattributes);
778  // @@@ BC: Why are we ignoring this...
779  break;
780  case "usb": //NON-NLS
781  try {
782  Long usbMtime = Long.parseLong(artnode.getAttribute("mtime")); //NON-NLS
783  usbMtime = Long.valueOf(usbMtime.toString());
784  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME, parentModuleName, usbMtime));
785  String dev = artnode.getAttribute("dev"); //NON-NLS
786  String make = "";
787  String model = dev;
788  if (dev.toLowerCase().contains("vid")) { //NON-NLS
789  USBInfo info = USB_MAPPER.parseAndLookup(dev);
790  if (info.getVendor() != null) {
791  make = info.getVendor();
792  }
793  if (info.getProduct() != null) {
794  model = info.getProduct();
795  }
796  }
797  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DEVICE_MAKE, parentModuleName, make));
798  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DEVICE_MODEL, parentModuleName, model));
799  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DEVICE_ID, parentModuleName, value));
800  newArtifacts.add(createArtifactWithAttributes(ARTIFACT_TYPE.TSK_DEVICE_ATTACHED, regFile, bbattributes));
801  } catch (TskCoreException ex) {
802  logger.log(Level.SEVERE, String.format("Error adding device_attached artifact to blackboard for file %d.", regFile.getId()), ex); //NON-NLS
803  }
804  break;
805  case "uninstall": //NON-NLS
806  Long itemMtime = null;
807  try {
808  String mTimeAttr = artnode.getAttribute("mtime");
809  if (mTimeAttr != null && !mTimeAttr.isEmpty()) {
810  itemMtime = new SimpleDateFormat("EEE MMM d HH:mm:ss yyyy", US).parse(mTimeAttr).getTime(); //NON-NLS
811  itemMtime /= MS_IN_SEC;
812  }
813  } catch (ParseException ex) {
814  logger.log(Level.SEVERE, "Failed to parse epoch time for installed program artifact.", ex); //NON-NLS
815  }
816 
817  try {
818  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME, parentModuleName, value));
819  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME, parentModuleName, itemMtime));
820  BlackboardArtifact bbart = regFile.newDataArtifact(new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_INSTALLED_PROG), bbattributes);
821  newArtifacts.add(bbart);
822  } catch (TskCoreException ex) {
823  logger.log(Level.SEVERE, "Error adding installed program artifact to blackboard.", ex); //NON-NLS
824  }
825  break;
826  case "office": //NON-NLS
827  String officeName = artnode.getAttribute("name"); //NON-NLS
828 
829  try {
830  // @@@ BC: Consider removing this after some more testing. It looks like an Mtime associated with the root key and not the individual item
831  if (mtime != null) {
832  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED, parentModuleName, mtime));
833  }
834  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME, parentModuleName, officeName));
835  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_VALUE, parentModuleName, value));
836  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME, parentModuleName, artnode.getNodeName()));
837  BlackboardArtifact bbart = regFile.newDataArtifact(new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_RECENT_OBJECT), bbattributes);
838 
839  newArtifacts.add(bbart);
840  } catch (TskCoreException ex) {
841  logger.log(Level.SEVERE, "Error adding recent object artifact to blackboard.", ex); //NON-NLS
842  }
843  break;
844 
845  case "ProcessorArchitecture": //NON-NLS
846  // Architecture is now included under Profiler
847  //try {
848  // String processorArchitecture = value;
849  // if (processorArchitecture.equals("AMD64"))
850  // processorArchitecture = "x86-64";
851 
852  // BlackboardArtifact bbart = regFile.newArtifact(ARTIFACT_TYPE.TSK_OS_INFO);
853  // bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROCESSOR_ARCHITECTURE.getTypeID(), parentModuleName, processorArchitecture));
854  // bbart.addAttributes(bbattributes);
855  //} catch (TskCoreException ex) {
856  // logger.log(Level.SEVERE, "Error adding os info artifact to blackboard."); //NON-NLS
857  //}
858  break;
859 
860  case "ProfileList": //NON-NLS
861  String homeDir = value;
862  String sid = artnode.getAttribute("sid"); //NON-NLS
863  String username = artnode.getAttribute("username"); //NON-NLS
864 
865  try{
866  createOrUpdateOsAccount(regFile, sid, username, homeDir);
867  } catch(TskCoreException | TskDataException | NotUserSIDException ex) {
868  logger.log(Level.SEVERE, String.format("Failed to create OsAccount for file: %s, sid: %s", regFile.getId(), sid), ex);
869  }
870  break;
871 
872  case "NtuserNetwork": // NON-NLS
873  try {
874  String localPath = artnode.getAttribute("localPath"); //NON-NLS
875  String remoteName = value;
876 
877  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_LOCAL_PATH,
878  parentModuleName, localPath));
879  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_REMOTE_PATH,
880  parentModuleName, remoteName));
881  BlackboardArtifact bbart = regFile.newDataArtifact(new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_REMOTE_DRIVE), bbattributes);
882  newArtifacts.add(bbart);
883  } catch (TskCoreException ex) {
884  logger.log(Level.SEVERE, "Error adding network artifact to blackboard.", ex); //NON-NLS
885  }
886  break;
887  case "SSID": // NON-NLS
888  String adapter = artnode.getAttribute("adapter"); //NON-NLS
889  try {
890  Long lastWriteTime = Long.parseLong(artnode.getAttribute("writeTime")); //NON-NLS
891  lastWriteTime = Long.valueOf(lastWriteTime.toString());
892  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SSID, parentModuleName, value));
893  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME, parentModuleName, lastWriteTime));
894  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DEVICE_ID, parentModuleName, adapter));
895  BlackboardArtifact bbart = regFile.newDataArtifact(new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_WIFI_NETWORK), bbattributes);
896  newArtifacts.add(bbart);
897  } catch (TskCoreException ex) {
898  logger.log(Level.SEVERE, "Error adding SSID artifact to blackboard.", ex); //NON-NLS
899  }
900  break;
901  case "shellfolders": // NON-NLS
902  // The User Shell Folders subkey stores the paths to Windows Explorer folders for the current user of the computer
903  // (https://technet.microsoft.com/en-us/library/Cc962613.aspx).
904  // No useful information. Skip.
905  break;
906 
907  default:
908  logger.log(Level.SEVERE, "Unrecognized node name: {0}", dataType); //NON-NLS
909  break;
910  }
911  }
912  }
913  break;
914  }
915  } // for
916  return true;
917  } catch (FileNotFoundException ex) {
918  logger.log(Level.WARNING, String.format("Error finding the registry file: %s", regFilePath), ex); //NON-NLS
919  } catch (SAXException ex) {
920  logger.log(Level.WARNING, String.format("Error parsing the registry XML: %s", regFilePath), ex); //NON-NLS
921  } catch (IOException ex) {
922  logger.log(Level.WARNING, String.format("Error building the document parser: %s", regFilePath), ex); //NON-NLS
923  } catch (ParserConfigurationException ex) {
924  logger.log(Level.WARNING, String.format("Error configuring the registry parser: %s", regFilePath), ex); //NON-NLS
925  } finally {
926  try {
927  if (fstream != null) {
928  fstream.close();
929  }
930  } catch (IOException ex) {
931  }
932 
933  if (!context.dataSourceIngestIsCancelled()) {
934  postArtifacts(newArtifacts);
935  }
936  }
937  return false;
938  }
939 
940  private boolean parseSystemPluginOutput(String regfilePath, AbstractFile regAbstractFile) {
941  File regfile = new File(regfilePath);
942  try (BufferedReader reader = new BufferedReader(new FileReader(regfile))) {
943  String line = reader.readLine();
944  while (line != null) {
945  line = line.trim();
946 
947  if (line.toLowerCase().matches("^bam v.*")) {
948  parseBamKey(regAbstractFile, reader, Bundle.Registry_System_Bam());
949  } else if (line.toLowerCase().matches("^bthport v..*")) {
950  parseBlueToothDevices(regAbstractFile, reader);
951  }
952  line = reader.readLine();
953  }
954  return true;
955  } catch (FileNotFoundException ex) {
956  logger.log(Level.WARNING, "Error finding the registry file.", ex); //NON-NLS
957  } catch (IOException ex) {
958  logger.log(Level.WARNING, "Error reading the system hive: {0}", ex); //NON-NLS
959  }
960 
961  return false;
962 
963  }
964 
977  private void parseBlueToothDevices(AbstractFile regFile, BufferedReader reader) throws FileNotFoundException, IOException {
978  List<BlackboardArtifact> bbartifacts = new ArrayList<>();
979  String line = reader.readLine();
980  while ((line != null) && (!line.contains(SECTION_DIVIDER))) {
981  line = reader.readLine();
982 
983  if (line != null) {
984  line = line.trim();
985  }
986 
987  if ((line != null) && (line.toLowerCase().contains("device unique id"))) {
988  // Columns are seperated by colons :
989  // Data : Values
990  // Record is 4 lines in length (Device Unique Id, Name, Last Seen, LastConnected
991  while (line != null && !line.contains(SECTION_DIVIDER) && !line.isEmpty() && !line.toLowerCase().contains("radio support not found")) {
992  Collection<BlackboardAttribute> attributes = new ArrayList<>();
993  addBlueToothAttribute(line, attributes, TSK_DEVICE_ID);
994  line = reader.readLine();
995  // Name may not exist, check for it to make sure.
996  if ((line != null) && (line.toLowerCase().contains("name"))) {
997  addBlueToothAttribute(line, attributes, TSK_NAME);
998  line = reader.readLine();
999  }
1000  addBlueToothAttribute(line, attributes, TSK_DATETIME);
1001  line = reader.readLine();
1002  addBlueToothAttribute(line, attributes, TSK_DATETIME_ACCESSED);
1003 
1004  try {
1005  bbartifacts.add(createArtifactWithAttributes(ARTIFACT_TYPE.TSK_BLUETOOTH_PAIRING, regFile, attributes));
1006  } catch (TskCoreException ex) {
1007  logger.log(Level.SEVERE, String.format("Failed to create bluetooth_pairing artifact for file %d", regFile.getId()), ex);
1008  }
1009  // Read blank line between records then next read line is start of next block
1010  reader.readLine();
1011  line = reader.readLine();
1012  }
1013 
1014  if (line != null) {
1015  line = line.trim();
1016  }
1017  }
1018  }
1019 
1020  if (!bbartifacts.isEmpty() && !context.dataSourceIngestIsCancelled()) {
1021  postArtifacts(bbartifacts);
1022  }
1023  }
1024 
1025  private void addBlueToothAttribute(String line, Collection<BlackboardAttribute> attributes, ATTRIBUTE_TYPE attributeType) {
1026  if (line == null) {
1027  return;
1028  }
1029 
1030  String tokens[] = line.split(": ");
1031  if (tokens.length > 1 && !tokens[1].isEmpty()) {
1032  String tokenString = tokens[1];
1033  if (attributeType.getDisplayName().toLowerCase().contains("date")) {
1034  String dateString = tokenString.toLowerCase().replace(" z", "");
1035  // date format for plugin Tue Jun 23 10:27:54 2020 Z
1036  SimpleDateFormat dateFormat = new SimpleDateFormat("EEE MMM d HH:mm:ss yyyy", US);
1037  Long dateLong = Long.valueOf(0);
1038  try {
1039  Date newDate = dateFormat.parse(dateString);
1040  dateLong = newDate.getTime() / 1000;
1041  } catch (ParseException ex) {
1042  // catching error and displaying date that could not be parsed
1043  // we set the timestamp to 0 and continue on processing
1044  logger.log(Level.WARNING, String.format("Failed to parse date/time %s for Bluetooth Last Seen attribute.", dateString), ex); //NON-NLS
1045  }
1046  attributes.add(new BlackboardAttribute(attributeType, getName(), dateLong));
1047  } else {
1048  attributes.add(new BlackboardAttribute(attributeType, getName(), tokenString));
1049  }
1050  }
1051  }
1052 
1063  private boolean parseSamPluginOutput(String regFilePath, AbstractFile regAbstractFile, long ingestJobId) {
1064 
1065  File regfile = new File(regFilePath);
1066  List<BlackboardArtifact> newArtifacts = new ArrayList<>();
1067  try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(regfile), StandardCharsets.UTF_8))) {
1068  // Read the file in and create a Document and elements
1069  String userInfoSection = "User Information";
1070  String previousLine = null;
1071  String line = bufferedReader.readLine();
1072  Set<Map<String, String>> userSet = new HashSet<>();
1073  Map<String, List<String>> groupMap = null;
1074  while (line != null) {
1075  if (line.contains(SECTION_DIVIDER) && previousLine != null && previousLine.contains(userInfoSection)) {
1076  readUsers(bufferedReader, userSet);
1077  }
1078 
1079  if (line.contains(SECTION_DIVIDER) && previousLine != null && previousLine.contains("Group Membership Information")) {
1080  groupMap = readGroups(bufferedReader);
1081  }
1082 
1083  previousLine = line;
1084  line = bufferedReader.readLine();
1085  }
1086  Map<String, Map<String, String>> userInfoMap = new HashMap<>();
1087  //load all the user info which was read into a map
1088  for (Map<String, String> userInfo : userSet) {
1089  userInfoMap.put(userInfo.get(SID_KEY), userInfo);
1090  }
1091 
1092  // New OsAccount Code
1093  OsAccountManager accountMgr = tskCase.getOsAccountManager();
1094  HostManager hostMrg = tskCase.getHostManager();
1095  Host host = hostMrg.getHostByDataSource((DataSource)dataSource);
1096 
1097  List<OsAccount> existingAccounts = accountMgr.getOsAccounts(host);
1098  for(OsAccount osAccount: existingAccounts) {
1099  Optional<String> optional = osAccount.getAddr();
1100  if(!optional.isPresent()) {
1101  continue;
1102  }
1103 
1104  String sid = optional.get();
1105  Map<String, String> userInfo = userInfoMap.remove(sid);
1106  if(userInfo != null) {
1107  updateOsAccount(osAccount, userInfo, groupMap.get(sid), regAbstractFile);
1108  }
1109  }
1110 
1111  //add remaining userinfos as accounts;
1112  for (Map<String, String> userInfo : userInfoMap.values()) {
1113  OsAccount osAccount = accountMgr.newWindowsOsAccount(userInfo.get(SID_KEY), null, domainName, host, domainName != null && !domainName.isEmpty() ? OsAccountRealm.RealmScope.DOMAIN : OsAccountRealm.RealmScope.UNKNOWN);
1114  accountMgr.newOsAccountInstance(osAccount, (DataSource)dataSource, OsAccountInstance.OsAccountInstanceType.LAUNCHED);
1115  updateOsAccount(osAccount, userInfo, groupMap.get(userInfo.get(SID_KEY)), regAbstractFile);
1116  }
1117  return true;
1118  } catch (FileNotFoundException ex) {
1119  logger.log(Level.WARNING, "Error finding the registry file.", ex); //NON-NLS
1120  } catch (IOException ex) {
1121  logger.log(Level.WARNING, "Error building the document parser: {0}", ex); //NON-NLS
1122  } catch (TskDataException | TskCoreException ex) {
1123  logger.log(Level.WARNING, "Error updating TSK_OS_ACCOUNT artifacts to include newly parsed data.", ex); //NON-NLS
1124  } catch (OsAccountManager.NotUserSIDException ex) {
1125  logger.log(Level.WARNING, "Error creating OS Account, input SID is not a user SID.", ex); //NON-NLS
1126  }
1127  finally {
1128  if (!context.dataSourceIngestIsCancelled()) {
1129  postArtifacts(newArtifacts);
1130  }
1131  }
1132  return false;
1133  }
1134 
1146  private void readUsers(BufferedReader bufferedReader, Set<Map<String, String>> users) throws IOException {
1147  String line = bufferedReader.readLine();
1148  //read until end of file or next section divider
1149  String userName = "";
1150  String user_rid = "";
1151  while (line != null && !line.contains(SECTION_DIVIDER)) {
1152  //when a user name field exists read the name and id number
1153  if (line.contains(USERNAME_KEY)) {
1154  String regx = USERNAME_KEY + "\\s*?:";
1155  String userNameAndIdString = line.replaceAll(regx, "");
1156  userName = userNameAndIdString.substring(0, userNameAndIdString.lastIndexOf('[')).trim();
1157  user_rid = userNameAndIdString.substring(userNameAndIdString.lastIndexOf('['), userNameAndIdString.lastIndexOf(']'));
1158  } else if (line.contains(SID_KEY) && !userName.isEmpty()) {
1159  Map.Entry<String, String> entry = getSAMKeyValue(line);
1160 
1161  HashMap<String, String> userInfo = new HashMap<>();
1162  userInfo.put(USERNAME_KEY, userName);
1163  userInfo.put(RID_KEY, user_rid);
1164  userInfo.put(entry.getKey(), entry.getValue());
1165 
1166  //continue reading this users information until end of file or a blank line between users
1167  line = bufferedReader.readLine();
1168  while (line != null && !line.isEmpty()) {
1169  entry = getSAMKeyValue(line);
1170  if (entry != null) {
1171  userInfo.put(entry.getKey(), entry.getValue());
1172  }
1173  line = bufferedReader.readLine();
1174  }
1175  users.add(userInfo);
1176 
1177  userName = "";
1178  }
1179  line = bufferedReader.readLine();
1180  }
1181  }
1182 
1192  private void createRecentlyUsedArtifacts(String regFileName, AbstractFile regFile) throws FileNotFoundException, IOException {
1193  File regfile = new File(regFileName);
1194  try (BufferedReader reader = new BufferedReader(new FileReader(regfile))) {
1195  String line = reader.readLine();
1196  while (line != null) {
1197  line = line.trim();
1198 
1199  if (line.matches("^adoberdr v.*")) {
1200  parseAdobeMRUList(regFile, reader, Bundle.Recently_Used_Artifacts_Adobe());
1201  } else if (line.matches("^mpmru v.*")) {
1202  parseMediaPlayerMRUList(regFile, reader, Bundle.Recently_Used_Artifacts_Mediaplayer());
1203  } else if (line.matches("^trustrecords v.*")) {
1204  parseOfficeTrustRecords(regFile, reader, Bundle.Recently_Used_Artifacts_Office_Trustrecords());
1205  } else if (line.matches("^ArcHistory:")) {
1206  parse7ZipMRU(regFile, reader, Bundle.Recently_Used_Artifacts_ArcHistory());
1207  } else if (line.matches("^applets v.*")) {
1208  parseGenericMRUList(regFile, reader, Bundle.Recently_Used_Artifacts_Applets());
1209  } else if (line.matches("^mmc v.*")) {
1210  parseGenericMRUList(regFile, reader, Bundle.Recently_Used_Artifacts_Mmc());
1211  } else if (line.matches("^winrar v.*")) {
1212  parseWinRARMRUList(regFile, reader, Bundle.Recently_Used_Artifacts_Winrar());
1213  } else if (line.matches("^officedocs2010 v.*")) {
1214  parseOfficeDocs2010MRUList(regFile, reader, Bundle.Recently_Used_Artifacts_Officedocs());
1215  }
1216  line = reader.readLine();
1217  }
1218  }
1219  }
1220 
1232  private void parseBamKey(AbstractFile regFile, BufferedReader reader, String comment) throws FileNotFoundException, IOException {
1233  List<BlackboardArtifact> bbartifacts = new ArrayList<>();
1234  String line = reader.readLine();
1235  // Read thru first bam output to get to second bam output which is the same but delimited
1236  while (!line.contains(SECTION_DIVIDER)) {
1237  line = reader.readLine();
1238  line = line.trim();
1239  }
1240  line = reader.readLine();
1241  line = line.trim();
1242  while (!line.contains(SECTION_DIVIDER)) {
1243  // Split the line into it parts based on delimiter of "|"
1244  // 1570493613|BAM|||\Device\HarddiskVolume3\Program Files\TechSmith\Snagit 2018\Snagit32.exe (S-1-5-21-3042408413-2583535980-1301764466-1001)
1245  String tokens[] = line.split("\\|");
1246  Long progRunDateTime = Long.valueOf(tokens[0]);
1247  // Split on " (S-" as this signifies a User SID, if S- not used then may have issues becuase of (x86) in path is valid.
1248  // We can add the S- back to the string that we split on since S- is a valid beginning of a User SID
1249  String fileNameSid[] = tokens[4].split("\\s+\\(S-");
1250  String userSid = "S-" + fileNameSid[1].substring(0, fileNameSid[1].length() - 1);
1251  String userName = getUserNameMap().get(userSid);
1252  if (userName == null) {
1253  userName = userSid;
1254  }
1255  String fileName = fileNameSid[0];
1256  if (fileName.startsWith("\\Device\\HarddiskVolume")) {
1257  // Start at point past the 2nd slash
1258  int fileNameStart = fileName.indexOf('\\', 16);
1259  fileName = fileName.substring(fileNameStart, fileName.length());
1260 
1261  }
1262  Collection<BlackboardAttribute> attributes = new ArrayList<>();
1263  attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME, getName(), fileName));
1264  attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_NAME, getName(), userName));
1265  attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME, getName(), progRunDateTime));
1266  attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COMMENT, getName(), comment));
1267 
1268  try {
1269  BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_PROG_RUN, regFile, attributes);
1270  bbartifacts.add(bba);
1271  bba = createAssociatedArtifact(FilenameUtils.normalize(fileName, true), bba);
1272  if (bba != null) {
1273  bbartifacts.add(bba);
1274  }
1275  } catch (TskCoreException ex) {
1276  logger.log(Level.SEVERE, String.format("Failed to create TSK_PROG_RUN artifact for file %d", regFile.getId()), ex);
1277  }
1278  line = reader.readLine();
1279  }
1280  if (!bbartifacts.isEmpty() && !context.dataSourceIngestIsCancelled()) {
1281  postArtifacts(bbartifacts);
1282  }
1283  }
1284 
1296  private void parseAdobeMRUList(AbstractFile regFile, BufferedReader reader, String comment) throws FileNotFoundException, IOException {
1297  List<BlackboardArtifact> bbartifacts = new ArrayList<>();
1298  String line = reader.readLine();
1299  SimpleDateFormat adobePluginDateFormat = new SimpleDateFormat("yyyyMMddHHmmssZ", US);
1300  Long adobeUsedTime = Long.valueOf(0);
1301  while (!line.contains(SECTION_DIVIDER)) {
1302  line = reader.readLine();
1303  line = line.trim();
1304  if (line.matches("^Key name,file name,sDate,uFileSize,uPageCount")) {
1305  line = reader.readLine();
1306  // Columns are
1307  // Key name, file name, sDate, uFileSize, uPageCount
1308  while (!line.contains(SECTION_DIVIDER)) {
1309  // Split csv line, handles double quotes around individual file names
1310  // since file names can contain commas
1311  String tokens[] = line.split(",(?=([^\"]*\"[^\"]*\")*[^\"]*$)");
1312  String fileName = tokens[1].substring(0, tokens[1].length() - 1);
1313  fileName = fileName.replace("\"", "");
1314  if (fileName.charAt(0) == '/') {
1315  fileName = fileName.substring(1, fileName.length() - 1);
1316  fileName = fileName.replaceFirst("/", ":/");
1317  }
1318  // Check to see if more then 2 tokens, Date may not be populated, will default to 0
1319  if (tokens.length > 2) {
1320  // Time in the format of 20200131104456-05'00'
1321  try {
1322  String fileUsedTime = tokens[2].replaceAll("'", "");
1323  Date usedDate = adobePluginDateFormat.parse(fileUsedTime);
1324  adobeUsedTime = usedDate.getTime() / 1000;
1325  } catch (ParseException ex) {
1326  // catching error and displaying date that could not be parsed
1327  // we set the timestamp to 0 and continue on processing
1328  logger.log(Level.WARNING, String.format("Failed to parse date/time %s for adobe file artifact.", tokens[2]), ex); //NON-NLS
1329  }
1330  }
1331  Collection<BlackboardAttribute> attributes = new ArrayList<>();
1332  attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName));
1333  attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED, getName(), adobeUsedTime));
1334  attributes.add(new BlackboardAttribute(TSK_COMMENT, getName(), comment));
1335  try{
1336  BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes);
1337  if (bba != null) {
1338  bbartifacts.add(bba);
1339  fileName = fileName.replace("\0", "");
1340  bba = createAssociatedArtifact(FilenameUtils.normalize(fileName, true), bba);
1341  if (bba != null) {
1342  bbartifacts.add(bba);
1343  }
1344  }
1345  } catch(TskCoreException ex) {
1346  logger.log(Level.SEVERE, String.format("Failed to create TSK_RECENT_OBJECT artifact for file %d", regFile.getId()), ex);
1347  }
1348  line = reader.readLine();
1349  }
1350  line = line.trim();
1351  }
1352  }
1353  if (!bbartifacts.isEmpty() && !context.dataSourceIngestIsCancelled()) {
1354  postArtifacts(bbartifacts);
1355  }
1356  }
1357 
1370  private void parseMediaPlayerMRUList(AbstractFile regFile, BufferedReader reader, String comment) throws FileNotFoundException, IOException {
1371  List<BlackboardArtifact> bbartifacts = new ArrayList<>();
1372  String line = reader.readLine();
1373  while (!line.contains(SECTION_DIVIDER)) {
1374  line = reader.readLine();
1375  line = line.trim();
1376  if (line.contains("LastWrite")) {
1377  line = reader.readLine();
1378  // Columns are
1379  // FileX -> <Media file>
1380  while (!line.contains(SECTION_DIVIDER) && !line.contains("RecentFileList has no values.")) {
1381  // Split line on "> " which is the record delimiter between position and file
1382  String tokens[] = line.split("> ");
1383  String fileName = tokens[1];
1384  Collection<BlackboardAttribute> attributes = new ArrayList<>();
1385  attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName));
1386  attributes.add(new BlackboardAttribute(TSK_COMMENT, getName(), comment));
1387  try{
1388  BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes);
1389  if (bba != null) {
1390  bbartifacts.add(bba);
1391  bba = createAssociatedArtifact(fileName, bba);
1392  if (bba != null) {
1393  bbartifacts.add(bba);
1394  bba = createAssociatedArtifact(FilenameUtils.normalize(fileName, true), bba);
1395  if (bba != null) {
1396  bbartifacts.add(bba);
1397  }
1398  }
1399  }
1400  } catch(TskCoreException ex) {
1401  logger.log(Level.SEVERE, String.format("Failed to create TSK_RECENT_OBJECT artifact for file %d", regFile.getId()), ex);
1402  }
1403  line = reader.readLine();
1404  }
1405  line = line.trim();
1406  }
1407  }
1408  if (!bbartifacts.isEmpty()&& !context.dataSourceIngestIsCancelled()) {
1409  postArtifacts(bbartifacts);
1410  }
1411  }
1412 
1425  private void parseGenericMRUList(AbstractFile regFile, BufferedReader reader, String comment) throws FileNotFoundException, IOException {
1426  List<BlackboardArtifact> bbartifacts = new ArrayList<>();
1427  String line = reader.readLine();
1428  while (!line.contains(SECTION_DIVIDER)) {
1429  line = reader.readLine();
1430  line = line.trim();
1431  if (line.contains("LastWrite")) {
1432  line = reader.readLine();
1433  // Columns are
1434  // FileX -> <file>
1435  while (!line.contains(SECTION_DIVIDER) && !line.isEmpty() && !line.contains("Applets")
1436  && !line.contains(("Recent File List"))) {
1437  // Split line on "> " which is the record delimiter between position and file
1438  String tokens[] = line.split("> ");
1439  if (tokens.length > 1) {
1440  String fileName = tokens[1];
1441  Collection<BlackboardAttribute> attributes = new ArrayList<>();
1442  attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName));
1443  attributes.add(new BlackboardAttribute(TSK_COMMENT, getName(), comment));
1444  try{
1445  BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes);
1446  if (bba != null) {
1447  bbartifacts.add(bba);
1448  bba = createAssociatedArtifact(FilenameUtils.normalize(fileName, true), bba);
1449  if (bba != null) {
1450  bbartifacts.add(bba);
1451  }
1452  }
1453  } catch(TskCoreException ex) {
1454  logger.log(Level.SEVERE, String.format("Failed to create TSK_RECENT_OBJECT artifact for file %d", regFile.getId()), ex);
1455  }
1456  }
1457  line = reader.readLine();
1458  }
1459  line = line.trim();
1460  }
1461  }
1462  if (!bbartifacts.isEmpty() && !context.dataSourceIngestIsCancelled()) {
1463  postArtifacts(bbartifacts);
1464  }
1465  }
1466 
1479  private void parseWinRARMRUList(AbstractFile regFile, BufferedReader reader, String comment) throws FileNotFoundException, IOException {
1480  List<BlackboardArtifact> bbartifacts = new ArrayList<>();
1481  String line = reader.readLine();
1482  while (!line.contains(SECTION_DIVIDER)) {
1483  line = reader.readLine();
1484  line = line.trim();
1485  if (line.contains("LastWrite")) {
1486  line = reader.readLine();
1487  // Columns are
1488  // FileX -> <Media file>
1489  if (!line.isEmpty()) {
1490  while (!line.contains(SECTION_DIVIDER)) {
1491  // Split line on "> " which is the record delimiter between position and file
1492  String tokens[] = line.split("> ");
1493  String fileName = tokens[1];
1494  Collection<BlackboardAttribute> attributes = new ArrayList<>();
1495  attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName));
1496  attributes.add(new BlackboardAttribute(TSK_COMMENT, getName(), comment));
1497  try{
1498  BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes);
1499  bbartifacts.add(bba);
1500  bba = createAssociatedArtifact(FilenameUtils.normalize(fileName, true), bba);
1501  if (bba != null) {
1502  bbartifacts.add(bba);
1503  }
1504  } catch(TskCoreException ex) {
1505  logger.log(Level.SEVERE, String.format("Failed to create TSK_RECENT_OBJECT artifact for file %d", regFile.getId()), ex);
1506  }
1507  line = reader.readLine();
1508  }
1509  }
1510  line = line.trim();
1511  }
1512  }
1513  if (!bbartifacts.isEmpty() && !context.dataSourceIngestIsCancelled()) {
1514  postArtifacts(bbartifacts);
1515  }
1516  }
1517 
1530  private void parse7ZipMRU(AbstractFile regFile, BufferedReader reader, String comment) throws FileNotFoundException, IOException {
1531  List<BlackboardArtifact> bbartifacts = new ArrayList<>();
1532  String line = reader.readLine();
1533  line = line.trim();
1534  if (!line.contains("PathHistory:")) {
1535  while (!line.contains("PathHistory:") && !line.isEmpty()) {
1536  // Columns are
1537  // <fileName>
1538  String fileName = line;
1539  Collection<BlackboardAttribute> attributes = new ArrayList<>();
1540  attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName));
1541  attributes.add(new BlackboardAttribute(TSK_COMMENT, getName(), comment));
1542  try{
1543  BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes);
1544  bbartifacts.add(bba);
1545  bba = createAssociatedArtifact(FilenameUtils.normalize(fileName, true), bba);
1546  if (bba != null) {
1547  bbartifacts.add(bba);
1548  }
1549 
1550  } catch(TskCoreException ex) {
1551  logger.log(Level.SEVERE, String.format("Failed to create TSK_RECENT_OBJECT artifact for file %d", regFile.getId()), ex);
1552  }
1553  line = reader.readLine();
1554  line = line.trim();
1555  }
1556  }
1557  if (!bbartifacts.isEmpty() && !context.dataSourceIngestIsCancelled()) {
1558  postArtifacts(bbartifacts);
1559  }
1560  }
1561 
1574  private void parseOfficeDocs2010MRUList(AbstractFile regFile, BufferedReader reader, String comment) throws FileNotFoundException, IOException {
1575  List<BlackboardArtifact> bbartifacts = new ArrayList<>();
1576  String line = reader.readLine();
1577  line = line.trim();
1578  // Reading to the SECTION DIVIDER to get next section of records to process. Dates appear to have
1579  // multiple spaces in them that makes it harder to parse so next section will be easier to parse
1580  while (!line.contains(SECTION_DIVIDER)) {
1581  line = reader.readLine();
1582  }
1583  line = reader.readLine();
1584  while (!line.contains(SECTION_DIVIDER)) {
1585  // record has the following format
1586  // 1294283922|REG|||OfficeDocs2010 - F:\Windows_time_Rules_xp.doc
1587  String tokens[] = line.split("\\|");
1588  Long docDate = Long.valueOf(tokens[0]);
1589  String fileNameTokens[] = tokens[4].split(" - ");
1590  String fileName = fileNameTokens[1];
1591  Collection<BlackboardAttribute> attributes = new ArrayList<>();
1592  attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName));
1593  attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED, getName(), docDate));
1594  attributes.add(new BlackboardAttribute(TSK_COMMENT, getName(), comment));
1595  try{
1596  BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes);
1597  bbartifacts.add(bba);
1598  bba = createAssociatedArtifact(FilenameUtils.normalize(fileName, true), bba);
1599  if (bba != null) {
1600  bbartifacts.add(bba);
1601  }
1602  } catch(TskCoreException ex) {
1603  logger.log(Level.SEVERE, String.format("Failed to create TSK_RECENT_OBJECT artifact for file %d", regFile.getId()), ex);
1604  }
1605  line = reader.readLine();
1606  line = line.trim();
1607  }
1608  if (!bbartifacts.isEmpty() && !context.dataSourceIngestIsCancelled()) {
1609  postArtifacts(bbartifacts);
1610  }
1611  }
1612 
1625  private void parseOfficeTrustRecords(AbstractFile regFile, BufferedReader reader, String comment) throws FileNotFoundException, IOException {
1626  String userProfile = regFile.getParentPath();
1627  userProfile = userProfile.substring(0, userProfile.length() - 1);
1628  List<BlackboardArtifact> bbartifacts = new ArrayList<>();
1629  SimpleDateFormat pluginDateFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss yyyy", US);
1630  Long usedTime = Long.valueOf(0);
1631  String line = reader.readLine();
1632  while (!line.contains(SECTION_DIVIDER)) {
1633  line = reader.readLine();
1634  line = line.trim();
1635  usedTime = Long.valueOf(0);
1636  if (!line.contains("**") && !line.contains("----------") && !line.contains("LastWrite")
1637  && !line.contains(SECTION_DIVIDER) && !line.isEmpty() && !line.contains("TrustRecords")
1638  && !line.contains("VBAWarnings =")) {
1639  // Columns are
1640  // Date : <File Name>/<Website>
1641  // Split line on " : " which is the record delimiter between position and file
1642  String fileName = null;
1643  String tokens[] = line.split(" : ");
1644  fileName = tokens[1];
1645  fileName = fileName.replace("%USERPROFILE%", userProfile);
1646  // Time in the format of Wed May 31 14:33:03 2017 Z
1647  try {
1648  String fileUsedTime = tokens[0].replaceAll(" Z", "");
1649  Date usedDate = pluginDateFormat.parse(fileUsedTime);
1650  usedTime = usedDate.getTime() / 1000;
1651  } catch (ParseException ex) {
1652  // catching error and displaying date that could not be parsed
1653  // we set the timestamp to 0 and continue on processing
1654  logger.log(Level.WARNING, String.format("Failed to parse date/time %s for TrustRecords artifact.", tokens[0]), ex); //NON-NLS
1655  }
1656  Collection<BlackboardAttribute> attributes = new ArrayList<>();
1657  attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName));
1658  attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED, getName(), usedTime));
1659  attributes.add(new BlackboardAttribute(TSK_COMMENT, getName(), comment));
1660  try{
1661  BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes);
1662  bbartifacts.add(bba);
1663  bba = createAssociatedArtifact(FilenameUtils.normalize(fileName, true), bba);
1664  if (bba != null) {
1665  bbartifacts.add(bba);
1666  }
1667  } catch(TskCoreException ex) {
1668  logger.log(Level.SEVERE, String.format("Failed to create TSK_RECENT_OBJECT artifact for file %d", regFile.getId()), ex);
1669  }
1670  line = line.trim();
1671  }
1672  }
1673  if (!bbartifacts.isEmpty() && !context.dataSourceIngestIsCancelled()) {
1674  postArtifacts(bbartifacts);
1675  }
1676  }
1677 
1688  private BlackboardArtifact createAssociatedArtifact(String filePathName, BlackboardArtifact bba) {
1689  String fileName = FilenameUtils.getName(filePathName);
1690  String filePath = FilenameUtils.getPath(filePathName);
1691  List<AbstractFile> sourceFiles;
1692  try {
1693  sourceFiles = currentCase.getSleuthkitCase().getFileManager().findFilesExactNameExactPath(dataSource, fileName, filePath);
1694  if (!sourceFiles.isEmpty()) {
1695  return createAssociatedArtifact(sourceFiles.get(0), bba);
1696  }
1697  } catch (TskCoreException ex) {
1698  // only catching the error and displaying the message as the file may not exist on the
1699  // system anymore
1700  logger.log(Level.WARNING, String.format("Error finding actual file %s. file may not exist", filePathName)); //NON-NLS
1701  }
1702 
1703  return null;
1704  }
1705 
1715  private Map<String, String> makeUserNameMap(Content dataSource) throws TskCoreException {
1716  Map<String, String> map = new HashMap<>();
1717 
1718  for(OsAccount account: tskCase.getOsAccountManager().getOsAccounts(((DataSource)dataSource).getHost())) {
1719  Optional<String> userName = account.getLoginName();
1720  map.put(account.getName(), userName.isPresent() ? userName.get() : "");
1721  }
1722 
1723  return map;
1724  }
1725 
1731  private Map<String, String> getUserNameMap() {
1732  if(userNameMap == null) {
1733  // Get a mapping of user sids to user names and save globally so it can be used for other areas
1734  // of the registry, ie: BAM key
1735  try {
1736  userNameMap = makeUserNameMap(dataSource);
1737  } catch (TskCoreException ex) {
1738  logger.log(Level.WARNING, "Unable to create OS Account user name map", ex);
1739  // This is not the end of the world we will just continue without
1740  // user names
1741  userNameMap = new HashMap<>();
1742  }
1743  }
1744 
1745  return userNameMap;
1746  }
1747 
1758  private BlackboardAttribute getAttributeForArtifact(BlackboardArtifact artifact, BlackboardAttribute.ATTRIBUTE_TYPE type) throws TskCoreException {
1759  return artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.fromID(type.getTypeID())));
1760  }
1761 
1770  void createShellBagArtifacts(AbstractFile regFile, List<ShellBag> shellbags) throws TskCoreException {
1771  List<BlackboardArtifact> artifacts = new ArrayList<>();
1772  try {
1773  for (ShellBag bag : shellbags) {
1774  Collection<BlackboardAttribute> attributes = new ArrayList<>();
1775  attributes.add(new BlackboardAttribute(TSK_PATH, getName(), bag.getResource()));
1776  attributes.add(new BlackboardAttribute(getKeyAttribute(), getName(), bag.getKey()));
1777 
1778  long time;
1779  time = bag.getLastWrite();
1780  if (time != 0) {
1781  attributes.add(new BlackboardAttribute(getLastWriteAttribute(), getName(), time));
1782  }
1783 
1784  time = bag.getModified();
1785  if (time != 0) {
1786  attributes.add(new BlackboardAttribute(TSK_DATETIME_MODIFIED, getName(), time));
1787  }
1788 
1789  time = bag.getCreated();
1790  if (time != 0) {
1791  attributes.add(new BlackboardAttribute(TSK_DATETIME_CREATED, getName(), time));
1792  }
1793 
1794  time = bag.getAccessed();
1795  if (time != 0) {
1796  attributes.add(new BlackboardAttribute(TSK_DATETIME_ACCESSED, getName(), time));
1797  }
1798 
1799  artifacts.add(createArtifactWithAttributes(getShellBagArtifact(), regFile, attributes));
1800  }
1801  } finally {
1802  if(!context.dataSourceIngestIsCancelled()) {
1803  postArtifacts(artifacts);
1804  }
1805  }
1806  }
1807 
1816  private BlackboardArtifact.Type getShellBagArtifact() throws TskCoreException {
1817  if (shellBagArtifactType == null) {
1818  try {
1819  shellBagArtifactType = tskCase.getBlackboard().getOrAddArtifactType(SHELLBAG_ARTIFACT_NAME, Bundle.Shellbag_Artifact_Display_Name());
1820  } catch (BlackboardException ex) {
1821  throw new TskCoreException(String.format("Failed to get shell bag artifact type", SHELLBAG_ARTIFACT_NAME), ex);
1822  }
1823  }
1824 
1825  return shellBagArtifactType;
1826  }
1827 
1836  private BlackboardAttribute.Type getLastWriteAttribute() throws TskCoreException {
1837  if (shellBagLastWriteAttributeType == null) {
1838  try {
1839  shellBagLastWriteAttributeType = tskCase.getBlackboard().getOrAddAttributeType(SHELLBAG_ATTRIBUTE_LAST_WRITE,
1840  BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME,
1841  Bundle.Shellbag_Last_Write_Attribute_Display_Name());
1842  } catch (BlackboardException ex) {
1843  // Attribute already exists get it from the case
1844  throw new TskCoreException(String.format("Failed to get custom attribute %s", SHELLBAG_ATTRIBUTE_LAST_WRITE), ex);
1845  }
1846  }
1847  return shellBagLastWriteAttributeType;
1848  }
1849 
1858  private BlackboardAttribute.Type getKeyAttribute() throws TskCoreException {
1859  if (shellBagKeyAttributeType == null) {
1860  try {
1861  shellBagKeyAttributeType = tskCase.getBlackboard().getOrAddAttributeType(SHELLBAG_ATTRIBUTE_KEY,
1862  BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.STRING,
1863  Bundle.Shellbag_Key_Attribute_Display_Name());
1864  } catch (BlackboardException ex) {
1865  throw new TskCoreException(String.format("Failed to get key attribute %s", SHELLBAG_ATTRIBUTE_KEY), ex);
1866  }
1867  }
1868  return shellBagKeyAttributeType;
1869  }
1870 
1880  Map<String, List<String>> readGroups(BufferedReader bufferedReader) throws IOException {
1881  Map<String, List<String>> groupMap = new HashMap<>();
1882 
1883  String line = bufferedReader.readLine();
1884 
1885  int userCount = 0;
1886  String groupName = null;
1887 
1888  while (line != null && !line.contains(SECTION_DIVIDER)) {
1889 
1890  if (line.contains("Group Name")) {
1891  String value = line.replaceAll("Group Name\\s*?:", "").trim();
1892  groupName = (value.replaceAll("\\[\\d*?\\]", "")).trim();
1893  int startIndex = value.indexOf(" [") + 1;
1894  int endIndex = value.indexOf(']');
1895 
1896  if (startIndex != -1 && endIndex != -1) {
1897  String countStr = value.substring(startIndex + 1, endIndex);
1898  userCount = Integer.parseInt(countStr);
1899  }
1900  } else if (line.matches("Users\\s*?:")) {
1901  for (int i = 0; i < userCount; i++) {
1902  line = bufferedReader.readLine();
1903  if (line != null) {
1904  String sid = line.trim();
1905  List<String> groupList = groupMap.get(sid);
1906  if (groupList == null) {
1907  groupList = new ArrayList<>();
1908  groupMap.put(sid, groupList);
1909  }
1910  groupList.add(groupName);
1911  }
1912  }
1913  groupName = null;
1914  }
1915  line = bufferedReader.readLine();
1916  }
1917  return groupMap;
1918  }
1919 
1928  private Map.Entry<String, String> getSAMKeyValue(String line) {
1929  int index = line.indexOf(':');
1930  Map.Entry<String, String> returnValue = null;
1931  String key = null;
1932  String value = null;
1933 
1934  if (index != -1) {
1935  key = line.substring(0, index).trim();
1936  if (index + 1 < line.length()) {
1937  value = line.substring(index + 1).trim();
1938  } else {
1939  value = "";
1940  }
1941 
1942  } else if (line.contains("-->")) {
1943  key = line.replace("-->", "").trim();
1944  value = "true";
1945  }
1946 
1947  if (key != null) {
1948  returnValue = new AbstractMap.SimpleEntry<>(key, value);
1949  }
1950 
1951  return returnValue;
1952  }
1953 
1954  @Override
1955  public void process(Content dataSource, IngestJobContext context, DataSourceIngestModuleProgress progressBar) {
1956  this.dataSource = dataSource;
1957  this.context = context;
1958 
1959  progressBar.progress(Bundle.Progress_Message_Analyze_Registry());
1960  analyzeRegistryFiles(context.getJobId());
1961 
1962  }
1963 
1967  private class RegOutputFiles {
1968 
1969  public String autopsyPlugins = "";
1970  public String fullPlugins = "";
1971  }
1972 
1985  private void createOrUpdateOsAccount(AbstractFile file, String sid, String userName, String homeDir) throws TskCoreException, TskDataException, NotUserSIDException {
1986  OsAccountManager accountMgr = tskCase.getOsAccountManager();
1987  HostManager hostMrg = tskCase.getHostManager();
1988  Host host = hostMrg.getHostByDataSource((DataSource)dataSource);
1989 
1990  Optional<OsAccount> optional = accountMgr.getWindowsOsAccount(sid, null, null, host);
1991  OsAccount osAccount;
1992  if (!optional.isPresent()) {
1993  osAccount = accountMgr.newWindowsOsAccount(sid, userName != null && userName.isEmpty() ? null : userName, domainName, host, domainName != null && !domainName.isEmpty()? OsAccountRealm.RealmScope.DOMAIN : OsAccountRealm.RealmScope.UNKNOWN);
1994  accountMgr.newOsAccountInstance(osAccount, (DataSource)dataSource, OsAccountInstance.OsAccountInstanceType.LAUNCHED);
1995  } else {
1996  osAccount = optional.get();
1997  if (userName != null && !userName.isEmpty()) {
1998  OsAccountUpdateResult updateResult= accountMgr.updateCoreWindowsOsAccountAttributes(osAccount, null, userName, null, host);
1999  osAccount = updateResult.getUpdatedAccount().orElse(osAccount);
2000  }
2001  }
2002 
2003  if (homeDir != null && !homeDir.isEmpty()) {
2004  List<OsAccountAttribute> attributes = new ArrayList<>();
2005  String dir = homeDir.replaceFirst("^(%\\w*%)", "");
2006  dir = dir.replace("\\", "/");
2007  attributes.add(createOsAccountAttribute(TSK_HOME_DIR, dir, osAccount, host, file));
2008  accountMgr.addExtendedOsAccountAttributes(osAccount, attributes);
2009  }
2010 
2011  }
2012 
2019  private void addEmailAccount(AbstractFile regFile, String emailAddress) {
2020  try {
2021  currentCase.getSleuthkitCase()
2022  .getCommunicationsManager()
2023  .createAccountFileInstance(Account.Type.EMAIL,
2024  emailAddress, getRAModuleName(), regFile);
2025  } catch (TskCoreException ex) {
2026  logger.log(Level.SEVERE,
2027  String.format("Error adding email account with value "
2028  + "%s, to the case database for file %s [objId=%d]",
2029  emailAddress, regFile.getName(), regFile.getId()), ex);
2030  }
2031  }
2032 
2041  private Long parseRegRipTime(String value) {
2042  try {
2043  return REG_RIPPER_TIME_FORMAT.parse(value).getTime() / MS_IN_SEC;
2044  } catch (ParseException ex) {
2045  logger.log(Level.SEVERE, String.format("Failed to parse reg rip time: %s", value));
2046  }
2047  return null;
2048  }
2049 
2061  private void updateOsAccount(OsAccount osAccount, Map<String, String> userInfo, List<String> groupList, AbstractFile regFile) throws TskDataException, TskCoreException, NotUserSIDException {
2062  Host host = ((DataSource)dataSource).getHost();
2063 
2064  SimpleDateFormat regRipperTimeFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss yyyy 'Z'", US);
2065  regRipperTimeFormat.setTimeZone(getTimeZone("GMT"));
2066 
2067  List<OsAccountAttribute> attributes = new ArrayList<>();
2068 
2069  Long creationTime = null;
2070 
2071  String value = userInfo.get(ACCOUNT_CREATED_KEY);
2072  if (value != null && !value.isEmpty() && !value.equals(NEVER_DATE)) {
2073  creationTime = parseRegRipTime(value);
2074  }
2075 
2076  value = userInfo.get(LAST_LOGIN_KEY);
2077  if (value != null && !value.isEmpty() && !value.equals(NEVER_DATE)) {
2078  Long time = parseRegRipTime(value);
2079  if (time != null) {
2080  attributes.add(createOsAccountAttribute(TSK_DATETIME_ACCESSED,
2081  parseRegRipTime(value),
2082  osAccount, host, regFile));
2083  }
2084  }
2085 
2086  String loginName = null;
2087  value = userInfo.get(USERNAME_KEY);
2088  if (value != null && !value.isEmpty()) {
2089  loginName = value;
2090  }
2091 
2092  value = userInfo.get(LOGIN_COUNT_KEY);
2093  if (value != null && !value.isEmpty()) {
2094  attributes.add(createOsAccountAttribute(ATTRIBUTE_TYPE.TSK_COUNT,
2095  Integer.parseInt(value),
2096  osAccount, host, regFile));
2097  }
2098 
2099  // From regripper the possible values for this key are
2100  // "Default Admin User", "Custom Limited Acct"
2101  // and "Default Guest Acct"
2102  value = userInfo.get(ACCOUNT_TYPE_KEY);
2103  if (value != null && !value.isEmpty() && value.toLowerCase().contains("admin")) {
2104  attributes.add(createOsAccountAttribute(ATTRIBUTE_TYPE.TSK_IS_ADMIN,
2105  1, osAccount, host, regFile));
2106  }
2107 
2108  value = userInfo.get(USER_COMMENT_KEY);
2109  if (value != null && !value.isEmpty()) {
2110  attributes.add(createOsAccountAttribute(ATTRIBUTE_TYPE.TSK_DESCRIPTION,
2111  value, osAccount, host, regFile));
2112  }
2113 
2114  value = userInfo.get(INTERNET_NAME_KEY);
2115  if (value != null && !value.isEmpty()) {
2116  addEmailAccount(regFile, value);
2117 
2118  attributes.add(createOsAccountAttribute(ATTRIBUTE_TYPE.TSK_EMAIL,
2119  value, osAccount, host, regFile));
2120  }
2121 
2122  // FULL_NAME_KEY and NAME_KEY appear to be the same value.
2123  String fullName = null;
2124  value = userInfo.get(FULL_NAME_KEY);
2125  if (value != null && !value.isEmpty()) {
2126  fullName = value;
2127  } else {
2128  value = userInfo.get(NAME_KEY);
2129  if (value != null && !value.isEmpty()) {
2130  fullName = value;
2131  }
2132  }
2133 
2134  value = userInfo.get(PWD_RESET_KEY);
2135  if (value != null && !value.isEmpty() && !value.equals(NEVER_DATE)) {
2136  Long time = parseRegRipTime(value);
2137  if (time != null) {
2138  attributes.add(createOsAccountAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_PASSWORD_RESET,
2139  time, osAccount, host, regFile));
2140  }
2141  }
2142 
2143  value = userInfo.get(PASSWORD_HINT);
2144  if (value != null && !value.isEmpty()) {
2145  attributes.add(createOsAccountAttribute(ATTRIBUTE_TYPE.TSK_PASSWORD_HINT,
2146  value, osAccount, host, regFile));
2147  }
2148 
2149  value = userInfo.get(PWD_FAILE_KEY);
2150  if (value != null && !value.isEmpty() && !value.equals(NEVER_DATE)) {
2151  Long time = parseRegRipTime(value);
2152  if (time != null) {
2153  attributes.add(createOsAccountAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_PASSWORD_FAIL,
2154  time, osAccount, host, regFile));
2155  }
2156  }
2157 
2158  String settingString = getSettingsFromMap(PASSWORD_SETTINGS_FLAGS, userInfo);
2159  if (!settingString.isEmpty()) {
2160  attributes.add(createOsAccountAttribute(ATTRIBUTE_TYPE.TSK_PASSWORD_SETTINGS,
2161  settingString, osAccount, host, regFile));
2162  }
2163 
2164  settingString = getSettingsFromMap(ACCOUNT_SETTINGS_FLAGS, userInfo);
2165  if (!settingString.isEmpty()) {
2166  attributes.add(createOsAccountAttribute(ATTRIBUTE_TYPE.TSK_ACCOUNT_SETTINGS,
2167  settingString, osAccount, host, regFile));
2168  }
2169 
2170  settingString = getSettingsFromMap(ACCOUNT_TYPE_FLAGS, userInfo);
2171  if (!settingString.isEmpty()) {
2172  attributes.add(createOsAccountAttribute(ATTRIBUTE_TYPE.TSK_FLAG,
2173  settingString, osAccount, host, regFile));
2174  }
2175 
2176  if (groupList != null && groupList.isEmpty()) {
2177  String groups = groupList.stream()
2178  .map(String::valueOf)
2179  .collect(Collectors.joining(", "));
2180 
2181  attributes.add(createOsAccountAttribute(ATTRIBUTE_TYPE.TSK_GROUPS,
2182  groups, osAccount, host, regFile));
2183  }
2184 
2185  // add the attributes to account.
2186  OsAccountManager accountMgr = tskCase.getOsAccountManager();
2187  accountMgr.addExtendedOsAccountAttributes(osAccount, attributes);
2188 
2189  // update the loginname
2190  accountMgr.updateCoreWindowsOsAccountAttributes(osAccount, null, loginName, null, host);
2191 
2192  // update other standard attributes - fullname, creationdate
2193  accountMgr.updateStandardOsAccountAttributes(osAccount, fullName, null, null, creationTime);
2194 
2195 
2196  }
2197 
2206  private String getSettingsFromMap(String[] keys, Map<String, String> map) {
2207  List<String> settingsList = new ArrayList<>();
2208  for (String setting : keys) {
2209  if (map.containsKey(setting)) {
2210  settingsList.add(setting);
2211  }
2212  }
2213 
2214  if (!settingsList.isEmpty()) {
2215  return settingsList.stream()
2216  .map(String::valueOf)
2217  .collect(Collectors.joining(", "));
2218  }
2219 
2220  return "";
2221  }
2222 
2234  private OsAccountAttribute createOsAccountAttribute(BlackboardAttribute.ATTRIBUTE_TYPE type, String value, OsAccount osAccount, Host host, AbstractFile file) {
2235  return osAccount.new OsAccountAttribute(new BlackboardAttribute.Type(type), value, osAccount, host, file);
2236  }
2237 
2249  private OsAccountAttribute createOsAccountAttribute(BlackboardAttribute.ATTRIBUTE_TYPE type, Long value, OsAccount osAccount, Host host, AbstractFile file) {
2250  return osAccount.new OsAccountAttribute(new BlackboardAttribute.Type(type), value, osAccount, host, file);
2251  }
2252 
2264  private OsAccountAttribute createOsAccountAttribute(BlackboardAttribute.ATTRIBUTE_TYPE type, Integer value, OsAccount osAccount, Host host, AbstractFile file) {
2265  return osAccount.new OsAccountAttribute(new BlackboardAttribute.Type(type), value, osAccount, host, file);
2266  }
2267 }

Copyright © 2012-2021 Basis Technology. Generated on: Thu Jul 8 2021
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.