Autopsy  4.20.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
JLnkParser.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2011-2016 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.autopsy.coreutils;
20 
21 import java.io.*;
22 import java.nio.ByteBuffer;
23 import java.nio.ByteOrder;
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.List;
27 import java.util.logging.Level;
31 
38 public class JLnkParser {
39 
40  private byte[] content;
41  private static final Logger logger = Logger.getLogger(JLnkParser.class.getName());
42 
43  public JLnkParser(InputStream is, int length) {
44  content = new byte[length];
45  try {
46  is.read(content);
47  } catch (IOException ex) {
48  Logger.getLogger(JLnkParser.class.getName()).log(Level.WARNING, "Error reading input stream", ex); //NON-NLS
49  }
50  }
51 
52  public JLNK parse() throws JLnkParserException {
53  try {
54  ByteBuffer bb = ByteBuffer.wrap(content);
55  bb.order(ByteOrder.LITTLE_ENDIAN);
56  int header = bb.getInt();
57  ByteBuffer linkClassIdentifier = bb.get(new byte[16]);
58  int linkFlags = bb.getInt();
59  int fileAttributes = bb.getInt();
60  long crtime = bb.getLong();
61  long atime = bb.getLong();
62  long mtime = bb.getLong();
63  int fileSize = bb.getInt();
64  int iconIndex = bb.getInt();
65  int showCommand = bb.getInt();
66  short hotkey = bb.getShort();
67  bb.get(new byte[10]); // reserved (???)
68  List<String> linkTargetIdList = new ArrayList<>();
69  if ((linkFlags & LnkEnums.LinkFlags.HasLinkTargetIDList.getFlag())
70  == LnkEnums.LinkFlags.HasLinkTargetIDList.getFlag()) {
71  int idListSize = bb.getShort();
72  int bytesRead = 0;
73  List<byte[]> linkTargetIdListBytes = new ArrayList<>();
74  while (true) {
75  short itemIdSize = bb.getShort();
76  if (itemIdSize == 0) {
77  bytesRead += 2; // two null bytes to terminate id list
78  break;
79  }
80  byte[] theArray = new byte[itemIdSize - 2];
81  bb.get(theArray); // an idlist data object
82  linkTargetIdListBytes.add(theArray);
83  bytesRead += itemIdSize;
84  }
85  linkTargetIdList = parseLinkTargetIdList(linkTargetIdListBytes);
86  }
87  boolean hasUnicodeLocalBaseAndCommonSuffixOffset = false;
88  String localBasePath = null;
89  String commonPathSuffix = null;
90  String localBasePathUnicode = null;
91  String commonPathSuffixUnicode = null;
92  int driveSerialNumber = -1;
93  DriveType driveType = null;
94  String volumeLabel = null;
95  int commonNetworkRelativeLinkFlags = -1;
96  NetworkProviderType networkProviderType = null;
97  boolean unicodeNetAndDeviceName = false;
98  String netName = null;
99  String netNameUnicode = null;
100  String deviceName = null;
101  String deviceNameUnicode = null;
102 
103  if ((linkFlags & LnkEnums.LinkFlags.HasLinkInfo.getFlag())
104  == LnkEnums.LinkFlags.HasLinkInfo.getFlag()) {
105  int startOfLinkInfo = bb.position();
106  int linkInfoSize = bb.getInt();
107  int linkInfoHeaderSize = bb.getInt();
108  hasUnicodeLocalBaseAndCommonSuffixOffset = linkInfoHeaderSize >= 0x24;
109  int linkInfoFlags = bb.getInt();
110  int volumeIdOffset = bb.getInt();
111  int localBasePathOffset = bb.getInt();
112  int commonNetworkRelativeLinkOffset = bb.getInt();
113  int commonPathSuffixOffset = bb.getInt();
114  int localBasePathOffsetUnicode = 0;
115  int commonPathSuffixOffsetUnicode = 0;
116  if (hasUnicodeLocalBaseAndCommonSuffixOffset) {
117  localBasePathOffsetUnicode = bb.getInt();
118  commonPathSuffixOffsetUnicode = bb.getInt();
119  }
120  if ((linkInfoFlags & LnkEnums.LinkInfoFlags.VolumeIDAndLocalBasePath.getFlag())
121  == LnkEnums.LinkInfoFlags.VolumeIDAndLocalBasePath.getFlag()) {
122  bb.position(startOfLinkInfo + volumeIdOffset);
123  int volumeIdSize = bb.getInt();
124  driveType = DriveType.valueOf(bb.getInt());
125  driveSerialNumber = bb.getInt();
126  int volumeLabelOffset = bb.getInt();
127  if (volumeLabelOffset != 0x14) {
128  volumeLabel = parseString(startOfLinkInfo + volumeIdOffset + volumeLabelOffset, false, volumeIdSize - 0x10);
129  } else {
130  int volumeLabelOffsetUnicode = bb.getInt();
131  volumeLabel = parseString(startOfLinkInfo + volumeIdOffset + volumeLabelOffsetUnicode, false, volumeIdSize - 0x14);
132  }
133  localBasePath = parseLocalBasePath(startOfLinkInfo + localBasePathOffset, false);
134  }
135  if ((linkInfoFlags & LnkEnums.LinkInfoFlags.CommonNetworkRelativeLinkAndPathSuffix.getFlag())
136  == LnkEnums.LinkInfoFlags.CommonNetworkRelativeLinkAndPathSuffix.getFlag()) {
137  bb.position(startOfLinkInfo + commonNetworkRelativeLinkOffset);
138  int commonNetworkRelativeLinkSize = bb.getInt();
139  commonNetworkRelativeLinkFlags = bb.getInt();
140  int netNameOffset = bb.getInt();
141  unicodeNetAndDeviceName = netNameOffset > 0x14;
142  int deviceNameOffset = bb.getInt();
143  int netType = bb.getInt();
144  int netNameOffsetUnicode = 0;
145  int deviceNameOffsetUnicode = 0;
146  if (unicodeNetAndDeviceName) {
147  netNameOffsetUnicode = bb.getInt();
148  deviceNameOffsetUnicode = bb.getInt();
149  }
150  netName = parseNetName(startOfLinkInfo + commonNetworkRelativeLinkOffset + netNameOffset, false);
151  if (unicodeNetAndDeviceName) {
152  netNameUnicode = parseNetName(startOfLinkInfo + commonNetworkRelativeLinkOffset + netNameOffsetUnicode, true);
153  }
154  if ((commonNetworkRelativeLinkFlags & LnkEnums.CommonNetworkRelativeLinkFlags.ValidNetType.getFlag())
155  == LnkEnums.CommonNetworkRelativeLinkFlags.ValidNetType.getFlag()) {
156  networkProviderType = LnkEnums.NetworkProviderType.valueOf(netType);
157  }
158  if ((commonNetworkRelativeLinkFlags & LnkEnums.CommonNetworkRelativeLinkFlags.ValidDevice.getFlag())
159  == LnkEnums.CommonNetworkRelativeLinkFlags.ValidDevice.getFlag()) {
160  deviceName = parseDeviceName(startOfLinkInfo + commonNetworkRelativeLinkOffset + deviceNameOffset, false);
161  if (unicodeNetAndDeviceName) {
162  deviceNameUnicode = parseDeviceName(startOfLinkInfo + commonNetworkRelativeLinkOffset + deviceNameOffsetUnicode, true);
163  }
164  }
165  }
166  commonPathSuffix = parseCommonPathSuffix(startOfLinkInfo + commonPathSuffixOffset, false);
167  if (((linkInfoFlags & LnkEnums.LinkInfoFlags.VolumeIDAndLocalBasePath.getFlag())
168  == LnkEnums.LinkInfoFlags.VolumeIDAndLocalBasePath.getFlag())
169  && hasUnicodeLocalBaseAndCommonSuffixOffset) {
170  localBasePathUnicode = parseLocalBasePath(startOfLinkInfo + localBasePathOffsetUnicode, true);
171  commonPathSuffixUnicode = parseCommonPathSuffix(startOfLinkInfo + commonPathSuffixOffsetUnicode, true);
172  }
173 
174  bb.position(startOfLinkInfo + linkInfoSize);
175  }
176  String name = null;
177  if ((linkFlags & LnkEnums.LinkFlags.HasName.getFlag())
178  == LnkEnums.LinkFlags.HasName.getFlag()) {
179  name = readStringData(bb);
180  }
181  String relativePath = null;
182  if ((linkFlags & LnkEnums.LinkFlags.HasRelativePath.getFlag())
183  == LnkEnums.LinkFlags.HasRelativePath.getFlag()) {
184  relativePath = readStringData(bb);
185  }
186  String workingDir = null;
187  if ((linkFlags & LnkEnums.LinkFlags.HasWorkingDir.getFlag())
188  == LnkEnums.LinkFlags.HasWorkingDir.getFlag()) {
189  workingDir = readStringData(bb);
190  }
191  String arguments = null;
192  if ((linkFlags & LnkEnums.LinkFlags.HasArguments.getFlag())
193  == LnkEnums.LinkFlags.HasArguments.getFlag()) {
194  arguments = readStringData(bb);
195  }
196  String iconLocation = null;
197  if ((linkFlags & LnkEnums.LinkFlags.HasIconLocation.getFlag())
198  == LnkEnums.LinkFlags.HasIconLocation.getFlag()) {
199  iconLocation = readStringData(bb);
200  }
201 
202  return new JLNK(header, linkClassIdentifier.array(), linkFlags, fileAttributes,
203  crtime, atime, mtime, fileSize, iconIndex, showCommand, hotkey,
204  linkTargetIdList,
205  hasUnicodeLocalBaseAndCommonSuffixOffset, localBasePath,
206  commonPathSuffix, localBasePathUnicode, commonPathSuffixUnicode,
207  name, relativePath, workingDir, arguments, iconLocation, driveSerialNumber,
208  driveType, volumeLabel, commonNetworkRelativeLinkFlags,
209  networkProviderType, unicodeNetAndDeviceName, netName, netNameUnicode,
210  deviceName, deviceNameUnicode);
211  } catch (Exception e) {
212  throw new JLnkParserException(e);
213  }
214  }
215 
216  private String readStringData(ByteBuffer bb) {
217  short countCharacters = bb.getShort();
218  if (countCharacters == 0) {
219  return null;
220  }
221  byte[] theString = new byte[countCharacters * 2];
222  bb.get(theString);
223  try {
224  return new String(theString, "UTF-16LE");
225  } catch (UnsupportedEncodingException ex) {
226  logger.info("Shouldn't happen"); //NON-NLS
227  return null;
228  }
229  }
230 
231  private String parseString(int offset, boolean unicode, int maxlen) {
232  ByteBuffer bb = ByteBuffer.wrap(content);
233  bb.order(ByteOrder.LITTLE_ENDIAN);
234  bb.position(offset);
235  StringBuilder sb = new StringBuilder(bb.limit());
236  int i = 0;
237  while (bb.remaining() > 0 && (i < maxlen || maxlen == -1)) // safer
238  {
239  char c;
240  if (unicode) {
241  c = bb.getChar();
242  } else {
243  c = (char) bb.get();
244  }
245  if (c == '\0') {
246  break;
247  }
248  sb.append(c);
249  i++;
250  }
251  return sb.toString();
252  }
253 
254  private String parseCommonPathSuffix(int offset, boolean unicode) {
255  return parseString(offset, unicode, -1);
256 
257  }
258 
259  private String parseLocalBasePath(int offset, boolean unicode) {
260  return parseString(offset, unicode, -1);
261 
262  }
263 
264  private String parseNetName(int offset, boolean unicode) {
265  return parseString(offset, unicode, -1);
266  }
267 
268  private String parseDeviceName(int offset, boolean unicode) {
269  return parseString(offset, unicode, -1);
270  }
271 
272  private List<String> parseLinkTargetIdList(List<byte[]> idList) {
273  List<String> ret = new ArrayList<>();
274  if (!idList.isEmpty()) {
275  CommonCLSIDS clsid = CommonCLSIDS.valueOf(Arrays.copyOfRange(idList.remove(0), 2, 18));
276  switch (clsid) {
277  case CDrivesFolder:
278  ret.add(new String(Arrays.copyOfRange(idList.remove(0), 1, 17)).split("\0")[0]);
279  ret.addAll(parsePathElements(idList));
280  break;
281  case CMyDocsFolder:
282  ret.addAll(parsePathElements(idList));
283  break;
284  case IEFrameDLL:
285  break;
286  case Unknown:
287  break;
288  default:
289  break;
290  }
291  }
292  return ret;
293  }
294 
295  private List<String> parsePathElements(List<byte[]> idList) {
296  List<String> ret = new ArrayList<>();
297  for (byte[] pathElement : idList) {
298  ByteBuffer bb = ByteBuffer.wrap(pathElement);
299  bb.order(ByteOrder.LITTLE_ENDIAN);
300  int offset = bb.getShort(bb.limit() - 2) - 2;
301  if (pathElement[offset + 0x02] < 0x03
302  || pathElement[offset + 0x10] >= pathElement[offset]
303  || pathElement[offset + 0x10] < 0x14) {
304  ret.add(get0xC(bb));
305  continue;
306  }
307  if (pathElement[offset + 0x10] != 0) {
308  ret.add(getStringAt(bb, offset + pathElement[offset + 0x10], true));
309  continue;
310  } else {
311  if (pathElement[offset + 0x12] >= pathElement[offset]
312  || pathElement[offset + 0x12] < 0x14) {
313  ret.add(get0xC(bb));
314  continue;
315  } else {
316  ret.add(getStringAt(bb, offset + pathElement[offset + 0x12], false));
317  continue;
318  }
319  }
320  }
321  return ret;
322  }
323 
324  private String get0xC(ByteBuffer bb) {
325  return getStringAt(bb, 0xC, false);
326  }
327 
328  private String getStringAt(ByteBuffer bb, int offset, boolean unicode) {
329  byte[] nameArr = Arrays.copyOfRange(bb.array(), offset, bb.limit());
330  if (unicode) {
331  try {
332  return new String(nameArr, "UTF-16LE").split("\0")[0];
333  } catch (UnsupportedEncodingException ex) {
334  Logger.getLogger(JLnkParser.class.getName()).log(Level.SEVERE, null, ex);
335  }
336  }
337  return new String(nameArr).split("\0")[0];
338  }
339 }
String getStringAt(ByteBuffer bb, int offset, boolean unicode)
List< String > parsePathElements(List< byte[]> idList)
List< String > parseLinkTargetIdList(List< byte[]> idList)
String parseCommonPathSuffix(int offset, boolean unicode)
String parseNetName(int offset, boolean unicode)
JLnkParser(InputStream is, int length)
Definition: JLnkParser.java:43
String parseString(int offset, boolean unicode, int maxlen)
String parseDeviceName(int offset, boolean unicode)
String parseLocalBasePath(int offset, boolean unicode)
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
static CommonCLSIDS valueOf(byte[] type)
Definition: LnkEnums.java:51

Copyright © 2012-2022 Basis Technology. Generated on: Tue Aug 1 2023
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.