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

Copyright © 2012-2015 Basis Technology. Generated on: Mon Oct 19 2015
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.