Windows Shell Items (AKA "shellbags") is an undocumented set of structures used internally within Windows to identify paths in Windows Folder Hierarchy. It is widely used in Windows Shell (and most visible in File Explorer), both as in-memory and in-file structures. Some formats embed them, namely:
The format is mostly undocumented, and is known to vary between various Windows versions.
This page hosts a formal specification of Windows Shell Items using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.
All parsing code for Ruby generated by Kaitai Struct depends on the Ruby runtime library. You have to install it before you can parse data.
The Ruby runtime library can be installed from RubyGems:
gem install kaitai-struct
Parse a local file and get structure in memory:
data = WindowsShellItems.from_file("path/to/local/file.bin")
Or parse structure from a string of bytes:
bytes = "\x00\x01\x02..."
data = WindowsShellItems.new(Kaitai::Struct::Stream.new(bytes))
After that, one can get various attributes from the structure by invoking getter methods like:
data.items # => get items
# This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
require 'kaitai/struct/struct'
unless Gem::Version.new(Kaitai::Struct::VERSION) >= Gem::Version.new('0.9')
raise "Incompatible Kaitai Struct Ruby API: 0.9 or later is required, but you have #{Kaitai::Struct::VERSION}"
end
##
# Windows Shell Items (AKA "shellbags") is an undocumented set of
# structures used internally within Windows to identify paths in
# Windows Folder Hierarchy. It is widely used in Windows Shell (and
# most visible in File Explorer), both as in-memory and in-file
# structures. Some formats embed them, namely:
#
# * Windows Shell link files (.lnk) Windows registry
# * Windows registry "ShellBags" keys
#
# The format is mostly undocumented, and is known to vary between
# various Windows versions.
# @see https://github.com/libyal/libfwsi/blob/main/documentation/Windows%20Shell%20Item%20format.asciidoc Source
class WindowsShellItems < Kaitai::Struct::Struct
def initialize(_io, _parent = nil, _root = self)
super(_io, _parent, _root)
_read
end
def _read
@items = []
i = 0
begin
_ = ShellItem.new(@_io, self, @_root)
@items << _
i += 1
end until _.len_data == 0
self
end
class ShellItemData < Kaitai::Struct::Struct
def initialize(_io, _parent = nil, _root = self)
super(_io, _parent, _root)
_read
end
def _read
@code = @_io.read_u1
case code
when 31
@body1 = RootFolderBody.new(@_io, self, @_root)
end
case (code & 112)
when 32
@body2 = VolumeBody.new(@_io, self, @_root)
when 48
@body2 = FileEntryBody.new(@_io, self, @_root)
end
self
end
attr_reader :code
attr_reader :body1
attr_reader :body2
end
##
# @see https://winprotocoldoc.blob.core.windows.net/productionwindowsarchives/MS-SHLLINK/[MS-SHLLINK].pdf Section 2.2.2
class ShellItem < Kaitai::Struct::Struct
def initialize(_io, _parent = nil, _root = self)
super(_io, _parent, _root)
_read
end
def _read
@len_data = @_io.read_u2le
if len_data >= 2
@_raw_data = @_io.read_bytes((len_data - 2))
_io__raw_data = Kaitai::Struct::Stream.new(@_raw_data)
@data = ShellItemData.new(_io__raw_data, self, @_root)
end
self
end
attr_reader :len_data
attr_reader :data
attr_reader :_raw_data
end
##
# @see https://github.com/libyal/libfwsi/blob/main/documentation/Windows%20Shell%20Item%20format.asciidoc#32-root-folder-shell-item Source
class RootFolderBody < Kaitai::Struct::Struct
def initialize(_io, _parent = nil, _root = self)
super(_io, _parent, _root)
_read
end
def _read
@sort_index = @_io.read_u1
@shell_folder_id = @_io.read_bytes(16)
self
end
attr_reader :sort_index
attr_reader :shell_folder_id
end
##
# @see https://github.com/libyal/libfwsi/blob/main/documentation/Windows%20Shell%20Item%20format.asciidoc#33-volume-shell-item Source
class VolumeBody < Kaitai::Struct::Struct
def initialize(_io, _parent = nil, _root = self)
super(_io, _parent, _root)
_read
end
def _read
@flags = @_io.read_u1
self
end
attr_reader :flags
end
##
# @see https://github.com/libyal/libfwsi/blob/main/documentation/Windows%20Shell%20Item%20format.asciidoc#34-file-entry-shell-item Source
class FileEntryBody < Kaitai::Struct::Struct
def initialize(_io, _parent = nil, _root = self)
super(_io, _parent, _root)
_read
end
def _read
@_unnamed0 = @_io.read_u1
@file_size = @_io.read_u4le
@last_mod_time = @_io.read_u4le
@file_attrs = @_io.read_u2le
self
end
def is_dir
return @is_dir unless @is_dir.nil?
@is_dir = (_parent.code & 1) != 0
@is_dir
end
def is_file
return @is_file unless @is_file.nil?
@is_file = (_parent.code & 2) != 0
@is_file
end
attr_reader :_unnamed0
attr_reader :file_size
attr_reader :last_mod_time
attr_reader :file_attrs
end
##
# @see https://winprotocoldoc.blob.core.windows.net/productionwindowsarchives/MS-SHLLINK/[MS-SHLLINK].pdf Section 2.2.1
attr_reader :items
end