From 3efcc5ff0bb9844f700d8d90395d1da017ce39a0 Mon Sep 17 00:00:00 2001 From: "patrick.pdb" Date: Fri, 23 Aug 2024 11:53:28 -0400 Subject: [PATCH] '#2307 Adds some info as Metadata to each LNK file parsed, and makes the reference to any correspondent found item in case. The search is first based on metaAddress (mft idx) and latter, if not successfull, on relative path, after removing any volume letter info. The creationDate info is compared to confirm the match. --- .../iped/parsers/lnk/LNKShortcutParser.java | 144 +++++++++++++++++- 1 file changed, 143 insertions(+), 1 deletion(-) diff --git a/iped-parsers/iped-parsers-impl/src/main/java/iped/parsers/lnk/LNKShortcutParser.java b/iped-parsers/iped-parsers-impl/src/main/java/iped/parsers/lnk/LNKShortcutParser.java index e61bddc99d..10951c4abf 100644 --- a/iped-parsers/iped-parsers-impl/src/main/java/iped/parsers/lnk/LNKShortcutParser.java +++ b/iped-parsers/iped-parsers-impl/src/main/java/iped/parsers/lnk/LNKShortcutParser.java @@ -23,6 +23,7 @@ import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Collections; +import java.util.Date; import java.util.List; import java.util.Set; import java.util.TimeZone; @@ -38,7 +39,11 @@ import org.xml.sax.ContentHandler; import org.xml.sax.SAXException; +import iped.data.IItemReader; import iped.parsers.util.Messages; +import iped.properties.BasicProps; +import iped.properties.ExtraProperties; +import iped.search.IItemSearcher; /** * Parser para arquivos de atalho (LNK) do Windows Referencias utilizadas sobre @@ -56,6 +61,15 @@ public class LNKShortcutParser extends AbstractParser { private static final Set SUPPORTED_TYPES = Collections.singleton(MediaType.application("x-lnk")); //$NON-NLS-1$ public static final String LNK_MIME_TYPE = "application/x-lnk"; //$NON-NLS-1$ + public static final String LNK_METADATA_PREFIX = "lnk"; + public static final String LNK_METADATA_LOCALPATHINFO = "localPathInfo"; + public static final String LNK_METADATA_LOCALPATH = "localPath"; + public static final String LNK_METADATA_COMMONPATH = "commonPath"; + public static final String LNK_METADATA_NETWORKSHARE = "networkShare"; + public static final String LNK_METADATA_VOLUMELABEL = "volumeLabel"; + public static final String LNK_METADATA_FILEEXISTS = "fileExists"; + public static final String LNK_METADATA_FILEMODIFIED = "modifiedAfterOpen"; + @Override public Set getSupportedTypes(ParseContext arg0) { return SUPPORTED_TYPES; @@ -95,9 +109,45 @@ public void parse(InputStream stream, ContentHandler handler, Metadata metadata, showHeader(lnkObj, df, xhtml); // HasLinkInfo - if (lnkObj.hasLinkLocation()) + if (lnkObj.hasLinkLocation()) { showLinkLocation(lnkObj, df, xhtml); + // According to + // https://github.com/libyal/liblnk/blob/main/documentation/Windows%20Shortcut%20File%20(LNK)%20format.asciidoc#4-location-information + // the real local path is the concatenation of netshare, commonPath and + // localPath + String fullLocalPath = ""; + LNKLinkLocation lnkLoc = lnkObj.getLinkLocation(); + + metadata.add(LNK_METADATA_PREFIX + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER + LNK_METADATA_LOCALPATHINFO, lnkLoc.getLocalPath()); + metadata.add(LNK_METADATA_PREFIX + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER + LNK_METADATA_COMMONPATH, lnkLoc.getCommonPath()); + metadata.add(LNK_METADATA_PREFIX + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER + LNK_METADATA_NETWORKSHARE, lnkLoc.getNetShare()); + metadata.add(LNK_METADATA_PREFIX + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER + LNK_METADATA_VOLUMELABEL, lnkLoc.getVolumeLabel()); + + if (lnkLoc.getNetShare() != null) { + fullLocalPath = fullLocalPath + lnkLoc.getNetShare(); + if (!fullLocalPath.endsWith("\\")) { + fullLocalPath += "\\"; + } + } + if (lnkLoc.getCommonPath() != null) { + fullLocalPath = fullLocalPath + lnkLoc.getCommonPath(); + if (!fullLocalPath.equals("") && !fullLocalPath.endsWith("\\")) { + fullLocalPath += "\\"; + } + } + fullLocalPath = fullLocalPath + lnkLoc.getLocalPath(); + metadata.add(LNK_METADATA_PREFIX + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER + LNK_METADATA_LOCALPATH, fullLocalPath); + + try { + makeReference(metadata, context, lnkObj, fullLocalPath, df); + } catch (Exception e) { + // unpredictable error when making reference + e.printStackTrace(); + } + + } + // HasName HasRelativePath HasWorkingDir HasArguments HasIconLocation if (lnkObj.hasName() || lnkObj.hasRelativePath() || lnkObj.hasWorkingDir() || lnkObj.hasArguments() || lnkObj.hasIconLocation()) @@ -118,6 +168,98 @@ public void parse(InputStream stream, ContentHandler handler, Metadata metadata, xhtml.endDocument(); } + // + // Makes the reference to a found target if: + // 1) the metaAddress (mft idx in NTFS) and creation date is the same. + // - Only metaAddress can reference different file in other filesystem, as the + // number can in different FS may coincide. So creationDate is used to confirm. + // 2) the path is similar and creation date is the same + // - CreationDate is considerably reliable value, assuming no dates + // manipulation, as it is not probable 2 files created at same time. The path is + // used to confirm. + // + // If creation date is different, it still can mean that the file was moved to + // a different partition or drive (different file system). + // So here comes the question: should it still be considered the same file if + // only size and name is the same? Meanwhile, in this first version, it isn't. + private void makeReference(Metadata metadata, ParseContext context, LNKShortcut lnkObj, String fullLocalPath, DateFormat df) { + if (fullLocalPath.startsWith("file://")) { + fullLocalPath.substring(7); + } + LNKLinkLocation lnkLoc = lnkObj.getLinkLocation(); + if (lnkLoc.getNetShare() == null) { + // tries to link to local file only if net info not defined + IItemSearcher searcher = context.get(IItemSearcher.class); + if (searcher != null) { + + List items = null; + boolean mftIdxFound = false; + + if (lnkObj.hasTargetIDList() && lnkObj.getShellTargetIDList().size() > 0) { + // search based on MFT entry index, if existent + LNKShellItem lastTarget = lnkObj.getShellTargetIDList().get(lnkObj.getShellTargetIDList().size() - 1); + if (lastTarget.hasFileEntry()) { + LNKShellItemFileEntry fEntry = lastTarget.getFileEntry(); + items = searcher.search(BasicProps.META_ADDRESS + ":" + fEntry.getIndMft()); + if (items.size() <= 0) { + items = null; + } else { + mftIdxFound = true; + } + } + } + + if (items == null || makeReference(metadata, context, lnkObj, items, df) == null) {// if no reference could be done based on metaAddress + // searches based on path + String relLocalPath = fullLocalPath.replace("\\", "\\\\"); + int i = relLocalPath.indexOf(":");// search for drive letter separator + if (i > 0) { + relLocalPath = relLocalPath.substring(i + 1);// gets path starting from drive path separator + } + items = searcher.search(BasicProps.PATH + ":\"" + relLocalPath + "\""); + + makeReference(metadata, context, lnkObj, items, df); + } + } + } + } + + private IItemReader makeReference(Metadata metadata, ParseContext context, LNKShortcut lnkObj, List items, DateFormat df) { + for (IItemReader iReader : items) { + // creation date will confirm that the item is from the correct volume/path + Date created = iReader.getCreationDate(); + if (created != null) { + if (df.format(created).equals(lnkObj.getCreateDate(df))) { + metadata.add(LNK_METADATA_PREFIX + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER + LNK_METADATA_FILEEXISTS, "true"); + metadata.add(ExtraProperties.LINKED_ITEMS, BasicProps.ID + ":" + iReader.getId()); + + // if item with same path exists, mark it. + boolean sizeMatches = false; + if (iReader.getLength() == lnkObj.getFileSize()) { + sizeMatches = true; + } + + Date modifiedDate = iReader.getModDate(); + if (modifiedDate != null) { + if (!df.format(modifiedDate).equals(lnkObj.getModifiedDate(df))) { + // if item moddate is different than the registered in LNK file, informs that it + // was modified after seen by this link + metadata.add(LNK_METADATA_PREFIX + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER + LNK_METADATA_FILEMODIFIED, "true"); + } + } + if (!sizeMatches) { + // if item size is different than the registered in LNK file, informs that it + // was modified after seen by this link + metadata.add(LNK_METADATA_PREFIX + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER + LNK_METADATA_FILEMODIFIED, "true"); + } + return iReader; + } + } + } + return null; + } + + private void showHeader(LNKShortcut lnkObj, DateFormat df, XHTMLContentHandler xhtml) throws Exception { xhtml.startElement("table", "class", "t"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ addRowHeader(xhtml, Messages.getString("LNKShortcutParser.FileHeader")); //$NON-NLS-1$