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:
Name | Description |
---|---|
Target File Flags | The flags of the target the LNK file is pointing to (ARCHIVE, DIRECTORY, etc) |
Target File MAC times | The MAC times (last modification, last access and creation time) for the target file |
Target File Size | The size in byte for the target file (0 if it is a folder) |
Target Path MAC times | The 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 entry | MFT 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 Info | Volume Type (Fixed, Removable, etc) and Volume serial number (execute vol C: to get volume serial number for the C volume) |
Target Full Path | Full path to the target |
UNC Path | full UNC path if the target is on a share |
Hostname | Hostname of the machine the LNK file was created on |
File Droid | Target 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 Name | Type | Details |
---|---|---|
Link CLSID | GUID | A GUID that identifies the LNK file. This should always be {00021401-0000-0000-C000-000000000046} |
LinkFlags | Struct | This struct contains flags that specify the presens of an optional struct, what type of data is present on this LNK file and more |
FileAttributes | Struct | Contains the file attributes for the target file (the file referenced by the LNK file) |
Creation TIme | FILETIME | Creation time for the target file |
Access Time | FILETIME | Access time for the target file |
Write Time | FILETIME | Last modification time for the target file |
FileSize | u32 | File 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 Name | Type | Details |
---|---|---|
IDListSize | u16 | The size of the IDList struct |
IDList | [ShellItem] | Array of shell items that contains information about the target file |
TerminalID | u16 | This 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:
- File/Directory name
- File size in bytes
- File/Directory MAC times
- 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 Name | Type | Details |
---|---|---|
DriveType | u32 (enum) | Specify the type of the drive (fixed, removable, etc) |
DriveSerialNumber | u32 | Specifies the drive serial number of the volume the link target is stored on |
VolumeLabel | String | The 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 Name | Type | Details |
---|---|---|
NetName | String | Specify a share path |
DeviceName | String | Specify 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 Name | Details | Available if |
---|---|---|
NAME_STRING | An optional structure that specifies a description of the shortcut that is displayed to end users to identify the purpose of the shell link | HasName flag is set |
RELATIVE_PATH | An optional structure that specifies the location of the link target relative to the file that contains the shell link | HasRelativePath flag is set |
WORKING_DIR | An optional structure that specifies the file system path of the working directory to be used when activating the link target | HasWorkingDir flag is set |
COMMAND_LINE_ARGUMENTS | An optional structure that stores the command-line arguments that are specified when activating the link target | HasArguments flag is set |
ICON_LOCATION | An optional structure that specifies the location of the icon to be used when displaying a shell link item in an icon view | HasIconLocation 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:
{
"target_full_path": "C:\\Users\\u0041\\Desktop\\test\\test.txt",
"lnk_file_metadata": {
"full_path": "C:\\Users\\u0041\\Documents\\Projects\\LNKParser-rs\\samples\\WIN10\\1607_14393\\windows_generated.lnk",
"mtime": "2021-02-08T12:52:20Z",
"atime": "2021-02-13T19:14:07Z",
"ctime": "2021-02-08T12:52:13Z"
},
"shell_link_header": {
"file_attr": [
"ARCHIVE"
],
"mtime": "2021-02-08T12:41:58Z",
"atime": "2021-02-08T12:41:03Z",
"ctime": "2021-02-08T12:41:03Z",
"file_size": 4
},
"link_target_id_list": {
"id_list": [
{
"shell_item_data": {
"FileEntry": {
"is_file": false,
"file_size": 0,
"last_modified": "2021-02-08T12:46:24Z",
"file_attr_flags": [
"DIRECTORY"
],
"name": "test",
"extention_block": {
"ctime": "2021-02-08T12:46:24Z",
"atime": "2021-02-08T12:46:24Z",
"file_ref": {
"mft_entry": 91461,
"sequence_number": 3
},
"primary_name": "test"
}
}
}
},
{
"shell_item_data": {
"FileEntry": {
"is_file": true,
"file_size": 4,
"last_modified": "2021-02-08T12:42:00Z",
"file_attr_flags": [
"ARCHIVE"
],
"name": "test.txt",
"extention_block": {
"ctime": "2021-02-08T12:41:04Z",
"atime": "2021-02-08T12:41:04Z",
"file_ref": {
"mft_entry": 90070,
"sequence_number": 3
},
"primary_name": "test.txt"
}
}
}
}
]
},
"link_info": {
"volume_id": {
"drive_type": "DRIVE_FIXED",
"serial_number": "E02E-8A93"
},
"local_base_path": "C:\\Users\\u0041\\Desktop\\test\\test.txt"
},
"relative_path": "..\\..\\..\\..\\..\\Desktop\\test\\test.txt",
"working_dir": "C:\\Users\\u0041\\Desktop\\test",
"extra_data": {
"extra_data_blocks": [
{
"Tracker": {
"machine_id": "win10",
"file_droid": "BD4FAD74-6A0A-11EB-8ECF-5076AFA95947",
"file_droid_birth": "BD4FAD74-6A0A-11EB-8ECF-5076AFA95947",
"volume_droid": "00D2581C-4749-44BD-9381-9BDFADF8A9DE",
"volume_droid_birth": "00D2581C-4749-44BD-9381-9BDFADF8A9DE"
}
}
]
}
}
You can also use the lib as follows:
use std::fs::File;
use lnk_parser::LNKParser;
fn main(){
// Open the LNK file
let file = File::open("samples\\WIN10\\1607_14393\\windows_generated.lnk")
.unwrap();
// Pass the `File` instance to `from_reader` function.
// `std::fs::File` implements `Read` & `Seek` traits.
let lnk_file = LNKParser::from_reader(file);
println!("{:?}", lnk_file);
}
You can find my LNK parser in GitHub or crates.io
Reference
- https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-shllink/16cb4ca1-9339-4d0c-a68d-bf1d6cc0f943
- https://github.com/libyal/libfwsi/blob/main/documentation/Windows%20Shell%20Item%20format.asciidoc
- https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-dosdatetimetofiletime?redirectedfrom=MSDN
- https://www.youtube.com/watch?v=bWxbfARqBPY
- https://github.com/EricZimmerman/Lnk/tree/master/Lnk/ShellItems