aalfaifi Feb 20, 2021 15:46 0 0

Exploring Windows Artifacts : LNK Files

blog

I decided to start a new blog post series that will focus on windows forensic artifacts. In these posts, I will try to go through all windows artifacts (at least until I get bored ๐Ÿ˜), understand its structure, and write a rust parser for them. You may ask, hey handsome guy, why reinvent the wheel? and I will say for two responses:

  • I am curious how windows artifacts work.
  • I like rust ๐Ÿฆ€ , and I want to write my parsers to learn rust more and to have parsers in rust.

And with that out of the way, let's start with the first artifact I looked into, which is LNK files.

What are LNK Files?

Windows uses LNK files to create a shortcut or link to a file or folder. LNK files could be created manually or automatically generated by windows. Windows generates many LNK files in different places to track the latest files opened by the user, The most known location for DFIR people is C:\Users\%username%\AppData\Roaming\Microsoft\Windows\Recent. This location contains many LNK files for the most recent files and folders opened by the user.

What information can we extract from LNK files ?

Here is the information we can extract from LNK files:

NameDescription
Target File FlagsThe flags of the target the LNK file is pointing to (ARCHIVE, DIRECTORY, etc)
Target File MAC timesThe MAC times (last modification, last access and creation time) for the target file
Target File SizeThe size in byte for the target file (0 if it is a folder)
Target Path MAC timesThe MAC times for all directories to the target. For example "C:\windows\temp\test.txt" will have three MAC times for each "windows" and "temp" directories and "test.txt" file.
MFT entryMFT entry for target file and for all directories to the target. For example "C:\windows\temp\test.txt" will have MFT entry for "windows", "temp" and "test.txt"
Volume InfoVolume Type (Fixed, Removable, etc) and Volume serial number (execute vol C: to get volume serial number for the C volume)
Target Full PathFull path to the target
UNC Pathfull UNC path if the target is on a share
HostnameHostname of the machine the LNK file was created on
File DroidTarget file droid (Object ID) and file birth droid

 

LNK File Structure

LNK file structure is documented very well, so I am not going to explain all of the data in each struct, However I will explain the data you can extract from each struct. If you want to learn everything about these structs, I will leave a couple of references at the end of the blog post. The following are the most essential structs that builds the LNK file:

  • ShellLinkHeader

  • LinkTargetIDList (Optional)

  • LinkInfo

  • VolumeID (Optional)

  • CommonNetworkRelativeLink (Optional)

  • StringData (Optional) (array)

  • ExtraData (Optional)

 

ShellLinkHeader

The ShellLinkHeader structure contains identification information, timestamps, and flags that specify the presence of optional structures. The following table explains some fields of this struct :

Field NameTypeDetails
Link CLSIDGUIDA GUID that identifies the LNK file. This should always be {00021401-0000-0000-C000-000000000046}
LinkFlagsStructThis struct contains flags that specify the presens of an optional struct, what type of data is present on this LNK file and more
FileAttributesStructContains the file attributes for the target file (the file referenced by the LNK file)
Creation TImeFILETIMECreation time for the target file
Access TimeFILETIMEAccess time for the target file
Write TimeFILETIMELast modification time for the target file
FileSizeu32File size in bytes

LinkTargetIDList

The LinkTargetIDList structure specifies the target of the link. The presence of this optional structure is specified by the HasLinkTargetIDList bit in LinkFlags field in ShellLinkHeader struct. The following table explains some fields of this struct:

Field NameTypeDetails
IDListSizeu16The size of the IDList struct
IDList[ShellItem]Array of shell items that contains information about the target file
TerminalIDu16This value is always \x00\x00

The following are some of the shell items found on LNK files:

  • RootShellItem : Contains CLSID which specifies the root of the target file. You can check all of the available CLSIDs by checking the following key on the registry HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\. This registry key contains a list of CLSIDs, So if you want to check the CLSID {20D04FE0-3AEA-1069-A2D8-08002B30309D} we can execute the following command:

reg query HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{20D04FE0-3AEA-1069-A2D8-08002B30309D} /ve


  • VolumeShellItem : Contains information about the volume which the target file reside in.

  • FileEntryShellIteam : Contains information about a file or directory, such as:

    1. File/Directory name
    2. File size in bytes
    3. File/Directory MAC times
    4. File/Directory MFT entry and sequence number

LinkInfo

The LinkInfo structure specifies information necessary to resolve a link target if it is not found in its original location. This includes information about the volume that the target was stored on, the mapped drive letter, and a Universal Naming Convention (UNC) form of the path if one existed when the link was created.

VolumeID

The VolumeID structure specifies information about the volume that a link target was on when the link was created. The following table contains the most important fields on this struct:

Field NameTypeDetails
DriveTypeu32 (enum)Specify the type of the drive (fixed, removable, etc)
DriveSerialNumberu32Specifies the drive serial number of the volume the link target is stored on
VolumeLabelStringThe volume label of the volume the link target is stored on

CommonNetworkRelativeLink

The CommonNetworkRelativeLink structure specifies information about the network location where a link target is stored, including the mapped drive letter and the UNC path prefix. The following table contains the most important fields on this struct:

Field NameTypeDetails
NetNameStringSpecify a share path
DeviceNameStringSpecify the file/folder name

The full path to the target file/folder is {Netname}\{DeviceName}

StringData

StringData refers to a set of structures that convey user interface and path identification information. The presence of these optional structures is controlled by LinkFlags in the ShellLinkHeader.

Field NameDetailsAvailable if
NAME_STRINGAn optional structure that specifies a description of the shortcut that is displayed to end users to identify the purpose of the shell linkHasName flag is set
RELATIVE_PATHAn optional structure that specifies the location of the link target relative to the file that contains the shell linkHasRelativePath flag is set
WORKING_DIRAn optional structure that specifies the file system path of the working directory to be used when activating the link targetHasWorkingDir flag is set
COMMAND_LINE_ARGUMENTSAn optional structure that stores the command-line arguments that are specified when activating the link targetHasArguments flag is set
ICON_LOCATIONAn optional structure that specifies the location of the icon to be used when displaying a shell link item in an icon viewHasIconLocation flag is set

ExtraData

ExtraData refers to a set of structures that convey additional information about a link target. These optional structures can be present in an extra data section that is appended to the basic Shell Link Binary File Format.

What did I learned from this ?

During the parsing, I learned a lot about the artifact and about parsing binary data. Here are some of my findings:

  • LNK file contain FIleReference (MFT entry/sequence number)
  • MS-DOS date and time timezone is not always UTC
  • MS-DOS date and time resolution is 2 seconds
  • LNK files get generated only if the target was opened from an interactive session. For example, if you execute notepad.exe test.txt a LNK file to test.txt will not be generated
  • A lot of rust stuff!
  • LNK struct is used in the JumpList artifact (next parser ๐Ÿค”)
  • ShellItems are annoying to parse๐Ÿ˜†

Some parsers only show the most relevant data and remove the "irrelevant" data, such as the FileReference. This is fine most of the time, but it is nice to have all the data and decide if you need it.

The parser

Now we get to the fun part! In the past couple of weeks, I worked on a rust parser to extract all of the data I can and output it in multiple formats. During the LNK parser development, I developed a rust library called winparsingtools which, as the name suggests, is a collection of structs and utilities to parse common windows binary formats. I will keep adding implementation for different structs as I explore new windows artifacts. You can check the winparsingtools library on my github or on crates.io.

Now let's get to the LNKParser. You can use the LNKParser library or the binary. Here is the help message for the binary LNKParser:

lnk_parser 0.1.0
AbdulRhman Alfaifi - @A__ALFAIFI
Windows LNK Files Parser

USAGE:
    lnk_parser.exe [FLAGS] [OPTIONS]

FLAGS:
    -h, --help          Prints help information
        --no-headers    Don't print headers when using CSV as the output format
        --normalize     Normalize the result to the most important fields
    -V, --version       Prints version information

OPTIONS:
    -p, --path <PATH>...                   Path(s) to LNK Metadata Files to be Parsed - accepts glob (Defaults to
                                           'RecetItems' for all users)
    -o, --output <output>                  The file path to write the output to [default: stdout]
        --output-format <output-format>    Output format. [default: csv]  [possible values: csv, jsonl, json]

And here is a sample output in JSON format:

  1. {
  2. "target_full_path": "C:\\Users\\u0041\\Desktop\\test\\test.txt",
  3. "lnk_file_metadata": {
  4. "full_path": "C:\\Users\\u0041\\Documents\\Projects\\LNKParser-rs\\samples\\WIN10\\1607_14393\\windows_generated.lnk",
  5. "mtime": "2021-02-08T12:52:20Z",
  6. "atime": "2021-02-13T19:14:07Z",
  7. "ctime": "2021-02-08T12:52:13Z"
  8. },
  9. "shell_link_header": {
  10. "file_attr": [
  11. "ARCHIVE"
  12. ],
  13. "mtime": "2021-02-08T12:41:58Z",
  14. "atime": "2021-02-08T12:41:03Z",
  15. "ctime": "2021-02-08T12:41:03Z",
  16. "file_size": 4
  17. },
  18. "link_target_id_list": {
  19. "id_list": [
  20. {
  21. "shell_item_data": {
  22. "FileEntry": {
  23. "is_file": false,
  24. "file_size": 0,
  25. "last_modified": "2021-02-08T12:46:24Z",
  26. "file_attr_flags": [
  27. "DIRECTORY"
  28. ],
  29. "name": "test",
  30. "extention_block": {
  31. "ctime": "2021-02-08T12:46:24Z",
  32. "atime": "2021-02-08T12:46:24Z",
  33. "file_ref": {
  34. "mft_entry": 91461,
  35. "sequence_number": 3
  36. },
  37. "primary_name": "test"
  38. }
  39. }
  40. }
  41. },
  42. {
  43. "shell_item_data": {
  44. "FileEntry": {
  45. "is_file": true,
  46. "file_size": 4,
  47. "last_modified": "2021-02-08T12:42:00Z",
  48. "file_attr_flags": [
  49. "ARCHIVE"
  50. ],
  51. "name": "test.txt",
  52. "extention_block": {
  53. "ctime": "2021-02-08T12:41:04Z",
  54. "atime": "2021-02-08T12:41:04Z",
  55. "file_ref": {
  56. "mft_entry": 90070,
  57. "sequence_number": 3
  58. },
  59. "primary_name": "test.txt"
  60. }
  61. }
  62. }
  63. }
  64. ]
  65. },
  66. "link_info": {
  67. "volume_id": {
  68. "drive_type": "DRIVE_FIXED",
  69. "serial_number": "E02E-8A93"
  70. },
  71. "local_base_path": "C:\\Users\\u0041\\Desktop\\test\\test.txt"
  72. },
  73. "relative_path": "..\\..\\..\\..\\..\\Desktop\\test\\test.txt",
  74. "working_dir": "C:\\Users\\u0041\\Desktop\\test",
  75. "extra_data": {
  76. "extra_data_blocks": [
  77. {
  78. "Tracker": {
  79. "machine_id": "win10",
  80. "file_droid": "BD4FAD74-6A0A-11EB-8ECF-5076AFA95947",
  81. "file_droid_birth": "BD4FAD74-6A0A-11EB-8ECF-5076AFA95947",
  82. "volume_droid": "00D2581C-4749-44BD-9381-9BDFADF8A9DE",
  83. "volume_droid_birth": "00D2581C-4749-44BD-9381-9BDFADF8A9DE"
  84. }
  85. }
  86. ]
  87. }
  88. }

You can also use the lib as follows:

  1. use std::fs::File;
  2. use lnk_parser::LNKParser;
  3. fn main(){
  4. // Open the LNK file
  5. let file = File::open("samples\\WIN10\\1607_14393\\windows_generated.lnk")
  6. .unwrap();
  7. // Pass the `File` instance to `from_reader` function.
  8. // `std::fs::File` implements `Read` & `Seek` traits.
  9. let lnk_file = LNKParser::from_reader(file);
  10. println!("{:?}", lnk_file);
  11. }

You can find my LNK parser in GitHub or crates.io

Reference

Comments

No Comments - Yet 😁

Write a comment

You need to login to write comments - you can login from HERE or register from HERE