diff --git a/.gitignore b/.gitignore index c6d4072f..c3275dc9 100644 --- a/.gitignore +++ b/.gitignore @@ -90,3 +90,6 @@ windows/*/packages/ .vscode/ tests/mapper + +# Sphinx build +/docs/_build/* diff --git a/BUILD.bazel b/BUILD.bazel index 21666869..1f19b7ee 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -80,6 +80,7 @@ yara_library( "time", "console", "string", + "lnk", ], modules_srcs = [ "libyara/modules/cuckoo/cuckoo.c", @@ -96,6 +97,8 @@ yara_library( "libyara/modules/time/time.c", "libyara/modules/console/console.c", "libyara/modules/string/string.c", + "libyara/modules/lnk/lnk.c", + "libyara/modules/lnk/lnk_utils.c", ], deps = [ "@jansson", diff --git a/Makefile.am b/Makefile.am index ef4fd47c..9dc2341f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -98,6 +98,9 @@ MODULES += libyara/modules/console/console.c MODULES += libyara/modules/string/string.c +MODULES += libyara/modules/lnk/lnk.c +MODULES += libyara/modules/lnk/lnk_utils.c + if CUCKOO_MODULE MODULES += libyara/modules/cuckoo/cuckoo.c endif @@ -192,6 +195,8 @@ noinst_HEADERS = \ libyara/include/yara/pe.h \ libyara/include/yara/pe_utils.h \ libyara/include/yara/re_lexer.h \ + libyara/include/yara/lnk.h \ + libyara/include/yara/lnk_utils.h \ libyara/modules/module_list dist_noinst_DATA = libyara/pb/yara.proto @@ -348,6 +353,9 @@ test_re_split_LDFLAGS = -static test_async_SOURCES = tests/test-async.c tests/util.c test_async_LDADD = libyara.la test_async_LDFLAGS = -static +test_lnk_SOURCES = tests/test-lnk.c tests/util.c +test_lnk_LDADD = libyara.la +test_lnk_LDFLAGS = -static TESTS = $(check_PROGRAMS) TESTS_ENVIRONMENT = TOP_SRCDIR=$(top_srcdir) TOP_BUILDDIR=$(top_builddir) @@ -366,7 +374,8 @@ check_PROGRAMS = \ test-stack \ test-re-split \ test-async \ - test-string + test-string \ + test-lnk EXTRA_PROGRAMS = tests/mapper CLEANFILES = tests/mapper$(EXEEXT) diff --git a/bazel/yara.bzl b/bazel/yara.bzl index ff4834b4..7a7ab3f4 100644 --- a/bazel/yara.bzl +++ b/bazel/yara.bzl @@ -171,6 +171,8 @@ def yara_library( "libyara/include/yara/lexer.h", "libyara/include/yara/libyara.h", "libyara/include/yara/limits.h", + "libyara/include/yara/lnk.h", + "libyara/include/yara/lnk_utils.h", "libyara/include/yara/macho.h", "libyara/include/yara/mem.h", "libyara/include/yara/modules.h", diff --git a/docs/modules.rst b/docs/modules.rst index a0f8bbf5..4bee503c 100644 --- a/docs/modules.rst +++ b/docs/modules.rst @@ -22,6 +22,6 @@ modules in the :ref:`writing-modules` section. Time Console String - + LNK diff --git a/docs/modules/lnk.rst b/docs/modules/lnk.rst new file mode 100644 index 00000000..0da3323c --- /dev/null +++ b/docs/modules/lnk.rst @@ -0,0 +1,741 @@ + +.. _lnk-module: + +########## +LNK module +########## + +The LNK module allows you to create more fine-grained rules for LNK files by +using attributes and features of the LNK file format. This module exposes most of +the fields present in an LNK file. Let's see some examples: + +.. code-block:: yara + + import "lnk" + + rule is_lnk + { + condition: + lnk.is_lnk + } + + rule machine_id_tracking + { + condition: + lnk.tracker_data.machine_id == "chris-xps" + } + + rule local_base_path + { + condition: + lnk.link_info.local_base_path == "C:\\test\\a.txt" + } + +The LNK documentation can be found on Microsoft's website: +https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-shllink/16cb4ca1-9339-4d0c-a68d-bf1d6cc0f943 + +Reference +--------- + +.. c:type:: is_lnk + + Return true if the file is an LNK. + + *Example: lnk.is_lnk* + +.. c:type:: creation_time + + An epoch integer that specifies the creation time (UTC) of the link target + + *Example: lnk.creation_time == 1221247637* + +.. c:type:: access_time + + An epoch integer that specifies the access time (UTC) of the link target + + *Example: lnk.access_time == 1221247637* + +.. c:type:: write_time + + An epoch integer that specifies the write time (UTC) of the link target + + *Example: lnk.write_time == 1221247637* + +.. c:type:: file_size + + An unsigned integer that specifies the size, in bytes, of the link target + + *Example: lnk.file_size > 100KB* + +.. c:type:: link_flags + + The LinkFlags value specifies information about the shell link and the presence of optional portions of the LNK file. Values can be checked by performing a bitwise AND operation with the following constants: + + .. c:member:: HAS_LINK_TARGET_ID_LIST + .. c:member:: HAS_LINK_INFO + .. c:member:: HAS_NAME + .. c:member:: HAS_RELATIVE_PATH + .. c:member:: HAS_WORKING_DIR + .. c:member:: HAS_ARGUMENTS + .. c:member:: HAS_ICON_LOCATION + .. c:member:: IS_UNICODE + .. c:member:: FORCE_NO_LINK_INFO + .. c:member:: HAS_EXP_STRING + .. c:member:: RUN_IN_SEPARATE_PROCESS + .. c:member:: UNUSED_1 + .. c:member:: HAS_DARWIN_ID + .. c:member:: RUN_AS_USER + .. c:member:: HAS_EXP_ICON + .. c:member:: NO_PIDL_ALIAS + .. c:member:: UNUSED_2 + .. c:member:: RUN_WITH_SHIM_LAYER + .. c:member:: FORCE_NO_LINK_TRACK + .. c:member:: ENABLE_TARGET_METADATA + .. c:member:: DISABLE_LINK_PATH_TRACKING + .. c:member:: DISABLE_KNOWN_FOLDER_TRACKING + .. c:member:: DISABLE_KNOWN_FOLDER_ALIAS + .. c:member:: ALLOW_LINK_TO_LINK + .. c:member:: UNALIAS_ON_SAVE + .. c:member:: PREFER_ENVIRONMENT_PATH + .. c:member:: KEEP_LOCAL_ID_LIST_FOR_UNC_TARGET + + *Example: lnk.link_flags & lnk.HAS_LINK_INFO* + +.. c:type:: file_attributes_flags + A file attributes flag that specifies information about the link target. Values can be checked by performing a bitwise AND operation with the following constants: + + .. c:member:: FILE_ATTRIBUTE_READONLY + .. c:member:: FILE_ATTRIBUTE_HIDDEN + .. c:member:: FILE_ATTRIBUTE_SYSTEM + .. c:member:: RESERVED_1 + .. c:member:: FILE_ATTRIBUTE_DIRECTORY + .. c:member:: FILE_ATTRIBUTE_ARCHIVE + .. c:member:: RESERVED_2 + .. c:member:: FILE_ATTRIBUTE_NORMAL + .. c:member:: FILE_ATTRIBUTE_TEMPORARY + .. c:member:: FILE_ATTRIBUTE_SPARSE_FILE + .. c:member:: FILE_ATTRIBUTE_REPARSE_POINT + .. c:member:: FILE_ATTRIBUTE_COMPRESSED + .. c:member:: FILE_ATTRIBUTE_OFFLINE + .. c:member:: FILE_ATTRIBUTE_NOT_CONTENT_INDEXED + .. c:member:: FILE_ATTRIBUTE_ENCRYPTED + + *Example: lnk.file_attributes_flags & lnk.FILE_ATTRIBUTE_READONLY* + +.. c:type:: icon_index + An integer that specifies the index of an icon within a given icon location. + +.. c:type:: show_command + An unsigned integer that specifies the expected window state of an application launched by the link. This value should be equal to one of the following: + + .. c:member:: FILE_ATTRIBUTE_READONLY + .. c:member:: FILE_ATTRIBUTE_HIDDEN + .. c:member:: FILE_ATTRIBUTE_SYSTEM + + *Example: lnk.show_command == lnk.SW_SHOWNORMAL* + +.. c:type:: has_hotkey + Boolean value to indicate whether a hotkey is present for the LNK file. + + *Example: lnk.has_hotkey* + +.. c:type:: hotkey_flags + Flags that detail the hotkey that's present (if applicable), and modifiers for how it should operate. + +.. c:type:: hotkey + A string representing the hotkey that is assigned to launch the LNK. + + *Example: lnk.hotkey == "F5"* + +.. c:type:: hotkey_modifier_flags + An unsigned integer that specifies bits that correspond to modifier keys on the keyboard. This value must be one or a combination of the following: + + .. c:member:: HOTKEYF_SHIFT + .. c:member:: HOTKEYF_CONTROL + .. c:member:: HOTKEYF_ALT + + *Example: lnk.hotkey_modifier_flags & lnk.HOTKEYF_SHIFT* + +.. c:type:: link_target_id_list + An optional structure that specifies the target of the link. + + .. c:member:: number_of_item_ids + The number of ItemIDs within the list. + + *Example: lnk.link_target_id_list.number_of_item_ids == 4* + + .. c:member:: item_id_list_size + The size of the ItemID list. + + *Example: lnk.link_target_id_list.item_id_list_size == 0xBD* + + .. c:type:: item_id_list + A zero-based array of ItemIDs structures. The data stored in a given ItemID is defined by the source that corresponds to the location in the target namespace of the preceding ItemIDs. This data uniquely identifies the items in that part of the namespace. Each ItemID has the following members: + + .. c:member:: data + The shell data source-defined data that specifies an item. + + *Example: lnk.link_target_id_list.item_id_list[0].data == "\\x1fP\\xe0O\\xd0 \\xea:i\\x10\\xa2\\xd8\\x08\\x00+00\\x9d"* + + .. c:member:: size + The size of the ItemID. + + *Example: lnk.link_target_id_list.item_id_list[0].size == 0x12* + +.. c:type:: link_info + The LinkInfo structure provides information necessary to resolve a link target if it is not found in its original location. + + .. c:member:: size + An unsigned integer that specifies the size, in bytes, of the LinkInfo structure. All offsets specified in this structure MUST be less than this value, and all strings contained in this structure MUST fit within the extent defined by this size. + + *Example: lnk.link_info.size == 0x3C* + + .. c:member:: header_size + An unsigned integer that specifies the size, in bytes, of the LinkInfo header section. Note that if the value is 0x1C, then offsets to some optional fields (local_base_path_offset_unicode and common_path_suffix_offset_unicode) will not be set. + + *Example: lnk.link_info.header_size == 0x1C* + + .. c:member:: flags + Flags that specify whether the VolumeID, LocalBasePath, LocalBasePathUnicode, and CommonNetworkRelativeLink fields are present in this structure. Values can be checked by performing a bitwise AND operation with the following constants: + + .. c:member:: VOLUME_ID_AND_LOCAL_BASE_PATH + .. c:member:: COMMON_NETWORK_RELATIVE_LINK_AND_PATH_SUFFIX + + *Example: lnk.link_info.flags & lnk.VOLUME_ID_AND_LOCAL_BASE_PATH* + + .. c:member:: volume_id_offset + An unsigned integer that specifies the location of the VolumeID field. + + *Example: lnk.link_info.volume_id_offset == 0x1C* + + .. c:member:: local_base_path_offset + An unsigned integer that specifies the location of the LocalBasePath field. + + *Example: lnk.link_info.local_base_path_offset == 0x2D* + + .. c:member:: common_network_relative_link_offset + An unsigned integer that specifies the location of the CommonNetworkRelativeLink field. + + .. c:member:: common_path_suffix_offset + An unsigned integer that specifies the location of the CommonPathSuffix field. + + .. c:member:: local_base_path_offset_unicode + An optional unsigned integer that specifies the location of the LocalBasePathUnicode field. + + .. c:member:: common_path_suffix_offset_unicode + An optional unsigned integer that specifies the location of the CommonPathSuffixUnicode field. + + .. c:type:: has_volume_id + Boolean flag which is set if a VolumeID structure is present. + + *Example: lnk.link_info.has_volume_id* + + .. c:type:: volume_id + An optional VolumeID structure (section 2.3.1) that specifies information about the volume that the link target was on when the link was created. + + .. c:member:: size + The size of the structure. + + .. c:member:: drive_type + An unsigned integer that specifies the type of drive the link target is stored on. It must be equal to one of the following: + + .. c:member:: DRIVE_UNKNOWN + .. c:member:: DRIVE_NO_ROOT_DIR + .. c:member:: DRIVE_REMOVABLE + .. c:member:: DRIVE_FIXED + .. c:member:: DRIVE_REMOTE + .. c:member:: DRIVE_CDROM + .. c:member:: DRIVE_RAMDISK + + *Example: lnk.link_info.volume_id.drive_type & lnk.DRIVE_FIXED* + + .. c:member:: drive_serial_number + An unsigned integer that specifies the drive serial number of the volume the link target is stored on. + + *Example: lnk.link_info.volume_id.drive_serial_number == 0x307A8A81* + + .. c:member:: volume_label_offset + An unsigned integer that specifies the location of a string that contains the volume label of the drive that the link target is stored on. + + .. c:member:: volume_label_offset_unicode + An optional unsigned integer that specifies the location of a string that contains the volume label of the drive that the link target is stored on. + + .. c:member:: data + A buffer of data that contains the volume label of the drive as a string defined by the system default code page or Unicode characters, as specified by preceding fields. + + *Example: lnk.link_info.volume_id.data == "\\x00"* + + .. c:member:: local_base_path + An optional, NULL–terminated string, defined by the system default code page, which is used to construct the full path to the link item or link target by appending the string in the CommonPathSuffix field. + + *Example: lnk.link_info.local_base_path == "C:\\test\\a.txt"* + + .. c:type:: common_network_relative_link + 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. + + .. c:member:: size + The size of the structure. + + .. c:member:: flags + Flags that specify the contents of the DeviceNameOffset and NetProviderType fields. Values can be checked by performing a bitwise AND operation with the following constants: + + .. c:member:: VALID_DEVICE + .. c:member:: VALID_NET_TYPE + + *Example: lnk.common_network_relative_link.flags & lnk.VALID_DEVICE* + + .. c:member:: net_name_offset + An unsigned integer that specifies the location of the NetName field. + + .. c:member:: device_name_offset + An unsigned integer that specifies the location of the DeviceName field. + + .. c:member:: network_provider_type + An unsigned integer that specifies the type of network provider. If present, it must be one of the following: + + .. c:member:: WNNC_NET_AVID + .. c:member:: WNNC_NET_DOCUSPACE + .. c:member:: WNNC_NET_MANGOSOFT + .. c:member:: WNNC_NET_SERNET + .. c:member:: WNNC_NET_RIVERFRONT1 + .. c:member:: WNNC_NET_RIVERFRONT2 + .. c:member:: WNNC_NET_DECORB + .. c:member:: WNNC_NET_PROTSTOR + .. c:member:: WNNC_NET_FJ_REDIR + .. c:member:: WNNC_NET_DISTINCT + .. c:member:: WNNC_NET_TWINS + .. c:member:: WNNC_NET_RDR2SAMPLE + .. c:member:: WNNC_NET_CSC + .. c:member:: WNNC_NET_3IN1 + .. c:member:: WNNC_NET_EXTENDNET + .. c:member:: WNNC_NET_STAC + .. c:member:: WNNC_NET_FOXBAT + .. c:member:: WNNC_NET_YAHOO + .. c:member:: WNNC_NET_EXIFS + .. c:member:: WNNC_NET_DAV + .. c:member:: WNNC_NET_KNOWARE + .. c:member:: WNNC_NET_OBJECT_DIRE + .. c:member:: WNNC_NET_MASFAX + .. c:member:: WNNC_NET_HOB_NFS + .. c:member:: WNNC_NET_SHIVA + .. c:member:: WNNC_NET_IBMAL + .. c:member:: WNNC_NET_LOCK + .. c:member:: WNNC_NET_TERMSRV + .. c:member:: WNNC_NET_SRT + .. c:member:: WNNC_NET_QUINCY + .. c:member:: WNNC_NET_OPENAFS + .. c:member:: WNNC_NET_AVID1 + .. c:member:: WNNC_NET_DFS + .. c:member:: WNNC_NET_KWNP + .. c:member:: WNNC_NET_ZENWORKS + .. c:member:: WNNC_NET_DRIVEONWEB + .. c:member:: WNNC_NET_VMWARE + .. c:member:: WNNC_NET_RSFX + .. c:member:: WNNC_NET_MFILES + .. c:member:: WNNC_NET_MS_NFS + .. c:member:: WNNC_NET_GOOGLE + + *Example: lnk.common_network_relative_link.network_provider_type == lnk.WNNC_NET_GOOGLE* + + .. c:member:: net_name_offset_unicode + An unsigned integer that specifies the location of the NetNameUnicode field. + + .. c:member:: device_name_offset_unicode + An unsigned integer that specifies the location of the DeviceNameUnicode field. + + .. c:member:: net_name + A NULL–terminated string, as defined by the system default code page, which specifies a server share path. + + *Example: lnk.link_info.common_network_relative_link.net_name == "\\\\server\\share"* + + .. c:member:: device_name + A NULL–terminated string, as defined by the system default code page, which specifies a device. + + *Example: lnk.link_info.common_network_relative_link.device_name == "Z:"* + + .. c:member:: net_name_unicode + An optional, NULL–terminated, Unicode string that is the Unicode version of the NetName string. + + .. c:member:: device_name_unicode + An optional, NULL–terminated, Unicode string that is the Unicode version of the DeviceName string. + + .. c:member:: common_path_suffix + A NULL–terminated string, defined by the system default code page, which is used to construct the full path to the link item or link target by being appended to the string in the LocalBasePath field. + + *Example: lnk.link_info.common_path_suffix == "\\x00"* + + .. c:member:: local_base_path_unicode + An optional, NULL–terminated, Unicode string that is used to construct the full path to the link item or link target by appending the string in the CommonPathSuffixUnicode field. + + .. c:member:: common_path_suffix_unicode + An optional, NULL–terminated, Unicode string that is used to construct the full path to the link item or link target by being appended to the string in the LocalBasePathUnicode field. + +.. c:type:: name_string +An optional string that specifies a description of the shortcut that is displayed to end users to identify the purpose of the shell link. + +*Example: lnk.name_string == "P\\x00e\\x00a\\x00Z\\x00i\\x00p\\x00 \\x00a\\x00r\\x00c\\x00h\\x00i\\x00v\\x00e\\x00r\\x00,\\x00 \\x00a\\x00d\\x00d\\x00 \\x00t\\x00o\\x00 \\x00e\\x00n\\x00c\\x00r\\x00y\\x00p\\x00t\\x00e\\x00d\\x00 \\x00.\\x00p\\x00e\\x00a\\x00 \\x00a\\x00r\\x00c\\x00h\\x00i\\x00v\\x00e\\x00"* + +.. c:type:: relative_path +An optional string that specifies the location of the link target relative to the file that contains the shell link. + +*Example: lnk.relative_path == ".\\x00\\\\\\x00a\\x00.\\x00t\\x00x\\x00t\\x00"* + +.. c:type:: working_dir +An optional string that specifies the file system path of the working directory to be used when activating the link target. + +*Example: lnk.working_dir == "C\\x00:\\x00\\\\\\x00t\\x00e\\x00s\\x00t\\x00"* + +.. c:type:: command_line_arguments +An optional string that stores the command-line arguments that are specified when activating the link target + +*Example: lnk.command_line_arguments == "-\\x00a\\x00d\\x00d\\x002\\x00c\\x00r\\x00y\\x00p\\x00t\\x00"* + +.. c:type:: icon_location +An optional string that specifies the location of the icon to be used when displaying a shell link item in an icon view. + +*Example: lnk.icon_location == "C\\x00:\\x00\\\\x00P\\x00r\\x00o\\x00g\\x00r\\x00a\\x00m\\x00 \\x00F\\x00i\\x00l\\x00e\\x00s\\x00\\\\x00P\\x00e\\x00a\\x00Z\\x00i\\x00p\\x00\\\\x00r\\x00e\\x00s\\x00\\\\x00i\\x00c\\x00o\\x00n\\x00s\\x00\\\\x00p\\x00e\\x00a\\x00z\\x00i\\x00p\\x00_\\x00n\\x00e\\x00w\\x00.\\x00i\\x00c\\x00l\\x00"* + +.. c:type:: has_console_data +Boolean flag which is set if a ConsoleDataBlock structure is present. + +*Example: lnk.has_console_data* + +.. c:type:: console_data +The ConsoleDataBlock structure specifies the display settings to use when a link target specifies an application that is run in a console window. + + .. c:member:: block_size + The block size of this structure, which will be equal to lnk.CONSOLE_DATA_BLOCK_SIZE. + + .. c:member:: block_signature + The signature of the block, which will be equal to lnk.CONSOLE_DATA_BLOCK_SIGNATURE. + + .. c:member:: fill_attributes + An unsigned integer that specifies the fill attributes that control the foreground and background text colors in the console window. The following bit definitions can be combined to specify 16 different values each for the foreground and background color: + + .. c:member:: FOREGROUND_BLUE + .. c:member:: FOREGROUND_GREEN + .. c:member:: FOREGROUND_RED + .. c:member:: FOREGROUND_INTENSITY + .. c:member:: BACKGROUND_BLUE + .. c:member:: BACKGROUND_GREEN + .. c:member:: BACKGROUND_RED + .. c:member:: BACKGROUND_INTENSITY + + *Example: lnk.console_data.fill_attributes & lnk.FOREGROUND_BLUE* + + .. c:member:: popup_fill_attributes + An unsigned integer that specifies the fill attributes that control the foreground and background text color in the console window popup. The values are the same as for the FillAttributes field. + + .. c:member:: screen_buffer_size_x + A signed integer that specifies the horizontal size (X axis), in characters, of the console window buffer. + + *Example: lnk.console_data.screen_buffer_size_x == 120* + + .. c:member:: screen_buffer_size_y + A signed integer that specifies the vertical size (Y axis), in characters, of the console window buffer. + + *Example: lnk.console_data.screen_buffer_size_y == 3000* + + .. c:member:: window_size_x + A signed integer that specifies the horizontal size (X axis), in characters, of the console window. + + *Example: lnk.console_data.window_size_x == 120* + + .. c:member:: window_size_y + A signed integer that specifies the vertical size (Y axis), in characters, of the console window. + + *Example: lnk.console_data.window_size_y == 50* + + .. c:member:: window_origin_x + A signed integer that specifies the horizontal coordinate (X axis), in pixels, of the console window origin. + + *Example: lnk.console_data.window_origin_x == 0* + + .. c:member:: window_origin_y + A signed integer that specifies the vertical coordinate (Y axis), in pixels, of the console window origin. + + *Example: lnk.console_data.window_origin_y == 0* + + .. c:member:: font_size + An unsigned integer that specifies the size, in pixels, of the font used in the console window. The two most significant bytes contain the font height and the two least significant bytes contain the font width. For vector fonts, the width is set to zero. + + .. c:member:: font_family + An unsigned integer that specifies the family of the font used in the console window. This value must be comprised of a font family and a font pitch. The values for the font family are as follows: + + .. c:member:: FF_DONTCARE + .. c:member:: FF_ROMAN + .. c:member:: FF_SWISS + .. c:member:: FF_MODERN + .. c:member:: FF_SCRIPT + .. c:member:: FF_DECORATIVE + + A bitwise OR of one or more of the following font-pitch bits is added to the font family from the previous values: + + .. c:member:: TMPF_NONE + .. c:member:: TMPF_FIXED_PITCH + .. c:member:: TMPF_VECTOR + .. c:member:: TMPF_TRUETYPE + .. c:member:: TMPF_DEVICE + + .. c:member:: font_weight + An unsigned integer that specifies the stroke weight of the font used in the console window. + + .. c:member:: face_name + A 32-character Unicode string that specifies the face name of the font used in the console window. + + .. c:member:: cursor_size + An unsigned integer that specifies the size of the cursor, in pixels, used in the console window. + + .. c:member:: full_screen + An unsigned integer that specifies whether to open the console window in full-screen mode. + + .. c:member:: quick_edit + An unsigned integer that specifies whether to open the console window in QuickEdit mode. + + .. c:member:: insert_mode + An unsigned integer that specifies insert mode in the console window. + + .. c:member:: auto_position + An unsigned integer that specifies auto-position mode of the console window. + + .. c:member:: history_buffer_size + An unsigned integer that specifies the size, in characters, of the buffer that is used to store a history of user input into the console window + + .. c:member:: number_of_history_buffers + An unsigned integer that specifies the number of history buffers to use. + + .. c:member:: history_no_dup + An unsigned integer that specifies whether to remove duplicates in the history buffer. + + .. c:member:: color_table + A table of 16 32-bit, unsigned integers specifying the RGB colors that are used for text in the console window. The values of the fill attribute fields FillAttributes and PopupFillAttributes are used as indexes into this table to specify the final foreground and background color for a character. + +.. c:type:: has_console_fe_data +Boolean flag which is set if a ConsoleFEDataBlock structure is present. + +*Example: lnk.has_console_fe_data* + +.. c:type:: console_fe_data +The ConsoleFEDataBlock structure specifies the code page to use for displaying text when a link target specifies an application that is run in a console window. + + .. c:member:: block_size + The block size of this structure, which will be equal to lnk.CONSOLE_FE_DATA_BLOCK_SIZE. + + .. c:member:: block_signature + The signature of the block, which will be equal to lnk.CONSOLE_FE_DATA_BLOCK_SIGNATURE. + + .. c:member:: code_page + An unsigned integer that specifies a code page language code identifier. + +.. c:type:: has_darwin_data +Boolean flag which is set if a DarwinDataBlock structure is present. + +*Example: lnk.has_darwin_data* + +.. c:type:: darwin_data +The DarwinDataBlock structure specifies an application identifier that can be used instead of a link target IDList to install an application when a shell link is activated. + + .. c:member:: block_size + The block size of this structure, which will be equal to lnk.DARWIN_DATA_BLOCK_SIZE. + + .. c:member:: block_signature + The signature of the block, which will be equal to lnk.DARWIN_DATA_BLOCK_SIGNATURE. + + .. c:member:: darwin_data_ansi + A NULL–terminated string, defined by the system default code page, which specifies an application identifier. + + .. c:member:: darwin_data_unicode + An optional, NULL–terminated, Unicode string that specifies an application identifier. + +.. c:type:: has_environment_variable_data +Boolean flag which is set if a EnvironmentVariableDataBlock structure is present. + +*Example: lnk.has_environment_variable_data* + +.. c:type:: environment_variable_data +The EnvironmentVariableDataBlock structure specifies a path to environment variable information when the link target refers to a location that has a corresponding environment variable. + + .. c:member:: block_size + The block size of this structure, which will be equal to lnk.ENVIRONMENT_VARIABLE_DATA_BLOCK_SIZE. + + .. c:member:: block_signature + The signature of the block, which will be equal to lnk.ENVIRONMENT_VARIABLE_DATA_BLOCK_SIGNATURE. + + .. c:member:: target_ansi + A NULL-terminated string, defined by the system default code page, which specifies a path to environment variable information + + *Example: lnk.environment_variable_data.target_ansi == "%SystemRoot%\\sysWOW64\\WindowsPowerShell\\v1.0\\powershell.exe"* + + .. c:member:: target_unicode + An optional, NULL-terminated, Unicode string that specifies a path to environment variable information. + +.. c:type:: has_icon_environment_data +Boolean flag which is set if a IconEnvironmentDataBlock structure is present. + +*Example: lnk.has_icon_environment_data* + +.. c:type:: icon_environment_data +The IconEnvironmentDataBlock structure specifies the path to an icon. The path is encoded using environment variables, which makes it possible to find the icon across machines where the locations vary but are expressed using environment variables. + + .. c:member:: block_size + The block size of this structure, which will be equal to lnk.ICON_ENVIRONMENT_DATA_BLOCK_SIZE. + + .. c:member:: block_signature + The signature of the block, which will be equal to lnk.ICON_ENVIRONMENT_DATA_BLOCK_SIGNATURE. + + .. c:member:: target_ansi + A NULL-terminated string, defined by the system default code page, which specifies a path that is constructed with environment variables + + *Example: lnk.icon_environment_data.target_ansi == "%ProgramFiles%\\PeaZip\\res\\icons\\peazip_new.icl"* + + .. c:member:: target_unicode + An optional, NULL-terminated, Unicode string that specifies a path that is constructed with environment variables. + +.. c:type:: has_known_folder_data +Boolean flag which is set if a KnownFolderDataBlock structure is present. + +*Example: lnk.has_known_folder_data* + +.. c:type:: known_folder_data +The KnownFolderDataBlock structure specifies the location of a known folder. This data can be used when a link target is a known folder to keep track of the folder so that the link target IDList can be translated when the link is loaded. + + .. c:member:: block_size + The block size of this structure, which will be equal to lnk.KNOWN_FOLDER_DATA_BLOCK_SIZE. + + .. c:member:: block_signature + The signature of the block, which will be equal to lnk.KNOWN_FOLDER_DATA_BLOCK_SIGNATURE. + + .. c:member:: offset + An unsigned integer that specifies the location of the ItemID of the first child segment of the IDList specified by KnownFolderID. This value is the offset, in bytes, into the link target IDList. + + *Example: lnk.known_folder_data.offset == 177* + + .. c:member:: known_folder_id + A value in GUID packet representation that specifies the folder GUID ID. + + *Example: lnk.known_folder_data.known_folder_id[15] == 142* + +.. c:type:: has_property_store_data +Boolean flag which is set if a PropertyStoreDataBlock structure is present. + +*Example: lnk.has_property_store_data* + +.. c:type:: property_store_data +A PropertyStoreDataBlock structure specifies a set of properties that can be used by applications to store extra data in the shell link. (TODO: implement the rest of this structure) + + .. c:member:: block_size + The block size of this structure, which will be greater than or equal to lnk.PROPERTY_STORE_DATA_BLOCK_MIN_SIZE. + + .. c:member:: block_signature + The signature of the block, which will be equal to lnk.PROPERTY_STORE_DATA_BLOCK_SIGNATURE. + +.. c:type:: has_shim_data +Boolean flag which is set if a ShimDataBlock structure is present. + +*Example: lnk.has_shim_data* + +.. c:type:: shim_data +The ShimDataBlock structure specifies the name of a shim that can be applied when activating a link target. + + .. c:member:: block_size + The block size of this structure, which will be greater than or equal to lnk.SHIM_DATA_BLOCK_MIN_SIZE. + + .. c:member:: block_signature + The signature of the block, which will be equal to lnk.SHIM_DATA_BLOCK_SIGNATURE. + + .. c:member:: layer_name + A Unicode string that specifies the name of a shim layer to apply to a link target when it is being activated. + +.. c:type:: has_special_folder_data +Boolean flag which is set if a SpecialFolderDataBlock structure is present. + +*Example: lnk.has_special_folder_data* + +.. c:type:: special_folder_data +The SpecialFolderDataBlock structure specifies the location of a special folder. This data can be used when a link target is a special folder to keep track of the folder, so that the link target IDList can be translated when the link is loaded. + + .. c:member:: block_size + The block size of this structure, which will be equal to lnk.SPECIAL_FOLDER_DATA_BLOCK_SIZE. + + .. c:member:: block_signature + The signature of the block, which will be equal to lnk.SPECIAL_FOLDER_DATA_BLOCK_SIGNATURE. + + .. c:member:: offset + An unsigned integer that specifies the location of the ItemID of the first child segment of the IDList specified by SpecialFolderID. This value is the offset, in bytes, into the link target IDList. + + .. c:member:: special_folder_id + An unsigned integer that specifies the folder integer ID. + +.. c:type:: has_tracker_data +Boolean flag which is set if a TrackerDataBlock structure is present. + +*Example: lnk.has_tracker_data* + +.. c:type:: tracker_data +The TrackerDataBlock structure specifies data that can be used to resolve a link target if it is not found in its original location when the link is resolved. This data is passed to the Link Tracking service to find the link target. + + .. c:member:: block_size + The block size of this structure, which will be equal to lnk.TRACKER_DATA_BLOCK_SIZE. + + .. c:member:: block_signature + The signature of the block, which will be equal to lnk.TRACKER_DATA_BLOCK_SIGNATURE. + + .. c:member:: machine_id + A NULL–terminated character string, as defined by the system default code page, which specifies the NetBIOS name of the machine where the link target was last known to reside. + + *Example: lnk.tracker_data.machine_id == "chris-xps"* + + .. c:member:: droid_volume_identifier + A parsed Droid volume identifier GUID. + + *Example: lnk.tracker_data.droid_volume_identifier == "\\x40\\x78\\xC7\\x94\\x47\\xFA\\xC7\\x46\\xB3\\x56\\x5C\\x2D\\xC6\\xB6\\xD1\\x15"* + + .. c:member:: droid_file_identifier + A parsed Droid file identifier GUID. + + .. c:member:: droid_birth_volume_identifier + A parsed DroidBirth volume identifier GUID. + + .. c:member:: droid_birth_file_identifier + A parsed DroidBirth file identifier GUID. + +.. c:type:: has_vista_and_above_id_list_data +Boolean flag which is set if a VistaAndAboveIDListDataBlock structure is present. + +*Example: lnk.has_vista_and_above_id_list_data* + +.. c:type:: vista_and_above_id_list_data +The VistaAndAboveIDListDataBlock structure specifies an alternate IDList that can be used instead of the LinkTargetIDList structure on platforms that support it. + + .. c:member:: block_size + The block size of this structure, which will be greater than or equal to lnk.VISTA_AND_ABOVE_ID_LIST_DATA_BLOCK_MIN_SIZE. + + .. c:member:: block_signature + The signature of the block, which will be equal to lnk.VISTA_AND_ABOVE_ID_LIST_DATA_BLOCK_SIGNATURE. + + .. c:member:: number_of_item_ids + The number of ItemID entries in the list. + + .. c:type:: item_id_list + A zero-based array of ItemIDs structures. The data stored in a given ItemID is defined by the source that corresponds to the location in the target namespace of the preceding ItemIDs. This data uniquely identifies the items in that part of the namespace. Each ItemID has the following members: + + .. c:member:: data + The shell data source-defined data that specifies an item. + + .. c:member:: size + The size of the ItemID. + +.. c:type:: has_overlay +A boolean value that is true if the LNK has extra data appended to it. + +*Example: lnk.has_overlay* + +.. c:type:: overlay_offset +An unsigned integer representing the offset into the LNK file of where the overlay starts (only set if the has_overlay flag is true). + +*Example: lnk.overlay_offset == 0x1CB* + +.. c:type:: is_malformed +A boolean value that is true if the LNK failed to be parsed due to it having malformed data. + +*Example: lnk.is_malformed* \ No newline at end of file diff --git a/libyara/include/yara/lnk.h b/libyara/include/yara/lnk.h new file mode 100644 index 00000000..c7fb5a3c --- /dev/null +++ b/libyara/include/yara/lnk.h @@ -0,0 +1,294 @@ +#include + +#ifndef _LNK_H +#define _LNK_H + +#define TICKS_PER_SECOND 10000000 +#define EPOCH_DIFFERENCE 11644473600LL + +#pragma pack(push) +#pragma pack(1) + +typedef struct _shell_link_header_t +{ + uint32_t header_size; + uint32_t clsid[4]; + uint32_t link_flags; + uint32_t file_attributes_flags; + uint64_t creation_time; + uint64_t access_time; + uint64_t write_time; + uint32_t file_size; + uint32_t icon_index; + uint32_t show_command; + uint16_t hotkey_flags; + uint16_t reserved1; + uint32_t reserved2; + uint32_t reserved3; +} shell_link_header_t; + +typedef struct _link_info_fixed_header_t +{ + uint32_t link_info_size; + uint32_t link_info_header_size; + uint32_t link_info_flags; + uint32_t volume_id_offset; + uint32_t local_base_path_offset; + uint32_t common_network_relative_link_offset; + uint32_t common_path_suffix_offset; +} link_info_fixed_header_t; + +typedef struct _volume_id_t +{ + uint32_t volume_id_size; + uint32_t drive_type; + uint32_t drive_serial_number; + uint32_t volume_label_offset; +} volume_id_t; + +typedef struct _common_network_relative_link_t +{ + uint32_t common_network_relative_link_size; + uint32_t common_network_relative_link_flags; + uint32_t net_name_offset; + uint32_t device_name_offset; + uint32_t network_provider_type; +} common_network_relative_link_t; + +typedef struct _console_data_block_t +{ + uint16_t fill_attributes; + uint16_t popup_fill_attributes; + int16_t screen_buffer_size_x; + int16_t screen_buffer_size_y; + int16_t window_size_x; + int16_t window_size_y; + int16_t window_origin_x; + int16_t window_origin_y; + uint32_t unused_1; + uint32_t unused_2; + uint32_t font_size; + uint32_t font_family; + uint32_t font_weight; + uint16_t face_name[32]; + uint32_t cursor_size; + uint32_t full_screen; + uint32_t quick_edit; + uint32_t insert_mode; + uint32_t auto_position; + uint32_t history_buffer_size; + uint32_t number_of_history_buffers; + uint32_t history_no_dup; + uint32_t color_table[16]; +} console_data_block_t; + +typedef struct _console_fe_data_block_t +{ + uint32_t code_page; +} console_fe_data_block_t; + +typedef struct _darwin_data_block_t +{ + char darwin_data_ansi[260]; + uint16_t darwin_data_unicode[260]; +} darwin_data_block_t; + +typedef struct _environment_variable_data_block_t +{ + char target_ansi[260]; + uint16_t target_unicode[260]; +} environment_variable_data_block_t; + +typedef struct _icon_environment_data_block_t +{ + char target_ansi[260]; + uint16_t target_unicode[260]; +} icon_environment_data_block_t; + +typedef struct _known_folder_data_block_t +{ + uint8_t known_folder_id[16]; + uint32_t offset; +} known_folder_data_block_t; + +typedef struct _special_folder_data_block_t +{ + uint32_t special_folder_id; + uint32_t offset; +} special_folder_data_block_t; + +typedef struct _tracker_data_block_t +{ + uint32_t length; + uint32_t version; + char machine_id[16]; + uint8_t droid_volume_identifier[16]; + uint8_t droid_file_identifier[16]; + uint8_t droid_birth_volume_identifier[16]; + uint8_t droid_birth_file_identifier[16]; +} tracker_data_block_t; + +#pragma pack(pop) + +#define HEADER_SIZE 0x0000004C + +#define LINK_CLSID_0 0x00021401 +#define LINK_CLSID_1 0x00000000 +#define LINK_CLSID_2 0x000000C0 +#define LINK_CLSID_3 0x46000000 + +#define HAS_LINK_TARGET_ID_LIST 0x00000001 +#define HAS_LINK_INFO 0x00000002 +#define HAS_NAME 0x00000004 +#define HAS_RELATIVE_PATH 0x00000008 +#define HAS_WORKING_DIR 0x00000010 +#define HAS_ARGUMENTS 0x00000020 +#define HAS_ICON_LOCATION 0x00000040 +#define IS_UNICODE 0x00000080 +#define FORCE_NO_LINK_INFO 0x00000100 +#define HAS_EXP_STRING 0x00000200 +#define RUN_IN_SEPARATE_PROCESS 0x00000400 +#define UNUSED_1 0x00000800 +#define HAS_DARWIN_ID 0x00001000 +#define RUN_AS_USER 0x00002000 +#define HAS_EXP_ICON 0x00004000 +#define NO_PIDL_ALIAS 0x00008000 +#define UNUSED_2 0x00010000 +#define RUN_WITH_SHIM_LAYER 0x00020000 +#define FORCE_NO_LINK_TRACK 0x00040000 +#define ENABLE_TARGET_METADATA 0x00080000 +#define DISABLE_LINK_PATH_TRACKING 0x00100000 +#define DISABLE_KNOWN_FOLDER_TRACKING 0x00200000 +#define DISABLE_KNOWN_FOLDER_ALIAS 0x00400000 +#define ALLOW_LINK_TO_LINK 0x00800000 +#define UNALIAS_ON_SAVE 0x01000000 +#define PREFER_ENVIRONMENT_PATH 0x02000000 +#define KEEP_LOCAL_ID_LIST_FOR_UNC_TARGET 0x04000000 + +#define FILE_ATTRIBUTE_READONLY 0x00000001 +#define FILE_ATTRIBUTE_HIDDEN 0x00000002 +#define FILE_ATTRIBUTE_SYSTEM 0x00000004 +#define RESERVED_1 0x00000008 +#define FILE_ATTRIBUTE_DIRECTORY 0x00000010 +#define FILE_ATTRIBUTE_ARCHIVE 0x00000020 +#define RESERVED_2 0x00000040 +#define FILE_ATTRIBUTE_NORMAL 0x00000080 +#define FILE_ATTRIBUTE_TEMPORARY 0x00000100 +#define FILE_ATTRIBUTE_SPARSE_FILE 0x00000200 +#define FILE_ATTRIBUTE_REPARSE_POINT 0x00000400 +#define FILE_ATTRIBUTE_COMPRESSED 0x00000800 +#define FILE_ATTRIBUTE_OFFLINE 0x00001000 +#define FILE_ATTRIBUTE_NOT_CONTENT_INDEXED 0x00002000 +#define FILE_ATTRIBUTE_ENCRYPTED 0x00004000 + +#define SW_SHOWNORMAL 0x00000001 +#define SW_SHOWMAXIMIZED 0x00000003 +#define SW_SHOWMINNOACTIVE 0x00000007 + +#define HOTKEYF_SHIFT 0x01 +#define HOTKEYF_CONTROL 0x02 +#define HOTKEYF_ALT 0x04 + +#define VOLUME_ID_AND_LOCAL_BASE_PATH 0x01 +#define COMMON_NETWORK_RELATIVE_LINK_AND_PATH_SUFFIX 0x02 + +#define DRIVE_UNKNOWN 0x00 +#define DRIVE_NO_ROOT_DIR 0x01 +#define DRIVE_REMOVABLE 0x02 +#define DRIVE_FIXED 0x03 +#define DRIVE_REMOTE 0x04 +#define DRIVE_CDROM 0x05 +#define DRIVE_RAMDISK 0x06 + +#define VALID_DEVICE 0x01 +#define VALID_NET_TYPE 0x02 + +#define WNNC_NET_AVID 0x001A0000 +#define WNNC_NET_DOCUSPACE 0x001B0000 +#define WNNC_NET_MANGOSOFT 0x001C0000 +#define WNNC_NET_SERNET 0x001D0000 +#define WNNC_NET_RIVERFRONT1 0X001E0000 +#define WNNC_NET_RIVERFRONT2 0x001F0000 +#define WNNC_NET_DECORB 0x00200000 +#define WNNC_NET_PROTSTOR 0x00210000 +#define WNNC_NET_FJ_REDIR 0x00220000 +#define WNNC_NET_DISTINCT 0x00230000 +#define WNNC_NET_TWINS 0x00240000 +#define WNNC_NET_RDR2SAMPLE 0x00250000 +#define WNNC_NET_CSC 0x00260000 +#define WNNC_NET_3IN1 0x00270000 +#define WNNC_NET_EXTENDNET 0x00290000 +#define WNNC_NET_STAC 0x002A0000 +#define WNNC_NET_FOXBAT 0x002B0000 +#define WNNC_NET_YAHOO 0x002C0000 +#define WNNC_NET_EXIFS 0x002D0000 +#define WNNC_NET_DAV 0x002E0000 +#define WNNC_NET_KNOWARE 0x002F0000 +#define WNNC_NET_OBJECT_DIRE 0x00300000 +#define WNNC_NET_MASFAX 0x00310000 +#define WNNC_NET_HOB_NFS 0x00320000 +#define WNNC_NET_SHIVA 0x00330000 +#define WNNC_NET_IBMAL 0x00340000 +#define WNNC_NET_LOCK 0x00350000 +#define WNNC_NET_TERMSRV 0x00360000 +#define WNNC_NET_SRT 0x00370000 +#define WNNC_NET_QUINCY 0x00380000 +#define WNNC_NET_OPENAFS 0x00390000 +#define WNNC_NET_AVID1 0X003A0000 +#define WNNC_NET_DFS 0x003B0000 +#define WNNC_NET_KWNP 0x003C0000 +#define WNNC_NET_ZENWORKS 0x003D0000 +#define WNNC_NET_DRIVEONWEB 0x003E0000 +#define WNNC_NET_VMWARE 0x003F0000 +#define WNNC_NET_RSFX 0x00400000 +#define WNNC_NET_MFILES 0x00410000 +#define WNNC_NET_MS_NFS 0x00420000 +#define WNNC_NET_GOOGLE 0x00430000 + +#define FOREGROUND_BLUE 0x0001 +#define FOREGROUND_GREEN 0x0002 +#define FOREGROUND_RED 0x0004 +#define FOREGROUND_INTENSITY 0x0008 +#define BACKGROUND_BLUE 0x0010 +#define BACKGROUND_GREEN 0x0020 +#define BACKGROUND_RED 0x0040 +#define BACKGROUND_INTENSITY 0x0080 + +#define FF_DONTCARE 0x0000 +#define FF_ROMAN 0x0010 +#define FF_SWISS 0x0020 +#define FF_MODERN 0x0030 +#define FF_SCRIPT 0x0040 +#define FF_DECORATIVE 0x0050 + +#define TMPF_NONE 0x0000 +#define TMPF_FIXED_PITCH 0x0001 +#define TMPF_VECTOR 0x0002 +#define TMPF_TRUETYPE 0x0004 +#define TMPF_DEVICE 0x0008 + +#define CONSOLE_DATA_BLOCK_SIZE 0x000000CC +#define CONSOLE_FE_DATA_BLOCK_SIZE 0x0000000C +#define DARWIN_DATA_BLOCK_SIZE 0x00000314 +#define ENVIRONMENT_VARIABLE_DATA_BLOCK_SIZE 0x00000314 +#define ICON_ENVIRONMENT_DATA_BLOCK_SIZE 0x00000314 +#define KNOWN_FOLDER_DATA_BLOCK_SIZE 0x0000001C +#define PROPERTY_STORE_DATA_BLOCK_MIN_SIZE 0x0000000C +#define SHIM_DATA_BLOCK_MIN_SIZE 0x00000088 +#define SPECIAL_FOLDER_DATA_BLOCK_SIZE 0x00000010 +#define TRACKER_DATA_BLOCK_SIZE 0x00000060 +#define VISTA_AND_ABOVE_ID_LIST_DATA_BLOCK_MIN_SIZE 0x0000000A + +#define CONSOLE_DATA_BLOCK_SIGNATURE 0xA0000002 +#define CONSOLE_FE_DATA_BLOCK_SIGNATURE 0xA0000004 +#define DARWIN_DATA_BLOCK_SIGNATURE 0xA0000006 +#define ENVIRONMENT_VARIABLE_DATA_BLOCK_SIGNATURE 0xA0000001 +#define ICON_ENVIRONMENT_DATA_BLOCK_SIGNATURE 0xA0000007 +#define KNOWN_FOLDER_DATA_BLOCK_SIGNATURE 0xA000000B +#define PROPERTY_STORE_DATA_BLOCK_SIGNATURE 0xA0000009 +#define SHIM_DATA_BLOCK_SIGNATURE 0xA0000008 +#define SPECIAL_FOLDER_DATA_BLOCK_SIGNATURE 0xA0000005 +#define TRACKER_DATA_BLOCK_SIGNATURE 0xA0000003 +#define VISTA_AND_ABOVE_ID_LIST_DATA_BLOCK_SIGNATURE 0xA000000C + +#endif \ No newline at end of file diff --git a/libyara/include/yara/lnk_utils.h b/libyara/include/yara/lnk_utils.h new file mode 100644 index 00000000..4643ae9c --- /dev/null +++ b/libyara/include/yara/lnk_utils.h @@ -0,0 +1,9 @@ +#ifndef YR_LNK_UTILS_H +#define YR_LNK_UTILS_H + +#include + +uint64_t convertWindowsTimeToUnixTime(uint64_t input); +char* get_hotkey_char(uint8_t key); + +#endif \ No newline at end of file diff --git a/libyara/modules/lnk/lnk.c b/libyara/modules/lnk/lnk.c new file mode 100644 index 00000000..2cf5d334 --- /dev/null +++ b/libyara/modules/lnk/lnk.c @@ -0,0 +1,2391 @@ +#include +#include +#include +#include +#include +#include + +#define MODULE_NAME lnk + +begin_declarations + declare_integer("HAS_LINK_TARGET_ID_LIST"); + declare_integer("HAS_LINK_INFO"); + declare_integer("HAS_NAME"); + declare_integer("HAS_RELATIVE_PATH"); + declare_integer("HAS_WORKING_DIR"); + declare_integer("HAS_ARGUMENTS"); + declare_integer("HAS_ICON_LOCATION"); + declare_integer("IS_UNICODE"); + declare_integer("FORCE_NO_LINK_INFO"); + declare_integer("HAS_EXP_STRING"); + declare_integer("RUN_IN_SEPARATE_PROCESS"); + declare_integer("UNUSED_1"); + declare_integer("HAS_DARWIN_ID"); + declare_integer("RUN_AS_USER"); + declare_integer("HAS_EXP_ICON"); + declare_integer("NO_PIDL_ALIAS"); + declare_integer("UNUSED_2"); + declare_integer("RUN_WITH_SHIM_LAYER"); + declare_integer("FORCE_NO_LINK_TRACK"); + declare_integer("ENABLE_TARGET_METADATA"); + declare_integer("DISABLE_LINK_PATH_TRACKING"); + declare_integer("DISABLE_KNOWN_FOLDER_TRACKING"); + declare_integer("DISABLE_KNOWN_FOLDER_ALIAS"); + declare_integer("ALLOW_LINK_TO_LINK"); + declare_integer("UNALIAS_ON_SAVE"); + declare_integer("PREFER_ENVIRONMENT_PATH"); + declare_integer("KEEP_LOCAL_ID_LIST_FOR_UNC_TARGET"); + + declare_integer("FILE_ATTRIBUTE_READONLY"); + declare_integer("FILE_ATTRIBUTE_HIDDEN"); + declare_integer("FILE_ATTRIBUTE_SYSTEM"); + declare_integer("RESERVED_1"); + declare_integer("FILE_ATTRIBUTE_DIRECTORY"); + declare_integer("FILE_ATTRIBUTE_ARCHIVE"); + declare_integer("RESERVED_2"); + declare_integer("FILE_ATTRIBUTE_NORMAL"); + declare_integer("FILE_ATTRIBUTE_TEMPORARY"); + declare_integer("FILE_ATTRIBUTE_SPARSE_FILE"); + declare_integer("FILE_ATTRIBUTE_REPARSE_POINT"); + declare_integer("FILE_ATTRIBUTE_COMPRESSED"); + declare_integer("FILE_ATTRIBUTE_OFFLINE"); + declare_integer("FILE_ATTRIBUTE_NOT_CONTENT_INDEXED"); + declare_integer("FILE_ATTRIBUTE_ENCRYPTED"); + + declare_integer("SW_SHOWNORMAL"); + declare_integer("SW_SHOWMAXIMIZED"); + declare_integer("SW_SHOWMINNOACTIVE"); + + declare_integer("HOTKEYF_SHIFT"); + declare_integer("HOTKEYF_CONTROL"); + declare_integer("HOTKEYF_ALT"); + + declare_integer("VOLUME_ID_AND_LOCAL_BASE_PATH"); + declare_integer("COMMON_NETWORK_RELATIVE_LINK_AND_PATH_SUFFIX"); + + declare_integer("DRIVE_UNKNOWN"); + declare_integer("DRIVE_NO_ROOT_DIR"); + declare_integer("DRIVE_REMOVABLE"); + declare_integer("DRIVE_FIXED"); + declare_integer("DRIVE_REMOTE"); + declare_integer("DRIVE_CDROM"); + declare_integer("DRIVE_RAMDISK"); + + declare_integer("VALID_DEVICE"); + declare_integer("VALID_NET_TYPE"); + + declare_integer("WNNC_NET_AVID"); + declare_integer("WNNC_NET_DOCUSPACE"); + declare_integer("WNNC_NET_MANGOSOFT"); + declare_integer("WNNC_NET_SERNET"); + declare_integer("WNNC_NET_RIVERFRONT1"); + declare_integer("WNNC_NET_RIVERFRONT2"); + declare_integer("WNNC_NET_DECORB"); + declare_integer("WNNC_NET_PROTSTOR"); + declare_integer("WNNC_NET_FJ_REDIR"); + declare_integer("WNNC_NET_DISTINCT"); + declare_integer("WNNC_NET_TWINS"); + declare_integer("WNNC_NET_RDR2SAMPLE"); + declare_integer("WNNC_NET_CSC"); + declare_integer("WNNC_NET_3IN1"); + declare_integer("WNNC_NET_EXTENDNET"); + declare_integer("WNNC_NET_STAC"); + declare_integer("WNNC_NET_FOXBAT"); + declare_integer("WNNC_NET_YAHOO"); + declare_integer("WNNC_NET_EXIFS"); + declare_integer("WNNC_NET_DAV"); + declare_integer("WNNC_NET_KNOWARE"); + declare_integer("WNNC_NET_OBJECT_DIRE"); + declare_integer("WNNC_NET_MASFAX"); + declare_integer("WNNC_NET_HOB_NFS"); + declare_integer("WNNC_NET_SHIVA"); + declare_integer("WNNC_NET_IBMAL"); + declare_integer("WNNC_NET_LOCK"); + declare_integer("WNNC_NET_TERMSRV"); + declare_integer("WNNC_NET_SRT"); + declare_integer("WNNC_NET_QUINCY"); + declare_integer("WNNC_NET_OPENAFS"); + declare_integer("WNNC_NET_AVID1"); + declare_integer("WNNC_NET_DFS"); + declare_integer("WNNC_NET_KWNP"); + declare_integer("WNNC_NET_ZENWORKS"); + declare_integer("WNNC_NET_DRIVEONWEB"); + declare_integer("WNNC_NET_VMWARE"); + declare_integer("WNNC_NET_RSFX"); + declare_integer("WNNC_NET_MFILES"); + declare_integer("WNNC_NET_MS_NFS"); + declare_integer("WNNC_NET_GOOGLE"); + + declare_integer("FOREGROUND_BLUE"); + declare_integer("FOREGROUND_GREEN"); + declare_integer("FOREGROUND_RED"); + declare_integer("FOREGROUND_INTENSITY"); + declare_integer("BACKGROUND_BLUE"); + declare_integer("BACKGROUND_GREEN"); + declare_integer("BACKGROUND_RED"); + declare_integer("BACKGROUND_INTENSITY"); + + declare_integer("FF_DONTCARE"); + declare_integer("FF_ROMAN"); + declare_integer("FF_SWISS"); + declare_integer("FF_MODERN"); + declare_integer("FF_SCRIPT"); + declare_integer("FF_DECORATIVE"); + + declare_integer("TMPF_NONE"); + declare_integer("TMPF_FIXED_PITCH"); + declare_integer("TMPF_VECTOR"); + declare_integer("TMPF_TRUETYPE"); + declare_integer("TMPF_DEVICE"); + + declare_integer("is_lnk"); + declare_integer("creation_time"); + declare_integer("access_time"); + declare_integer("write_time"); + declare_integer("file_size"); + declare_integer("link_flags"); + declare_integer("file_attributes_flags"); + declare_integer("icon_index"); + declare_integer("show_command"); + declare_integer("hotkey_flags"); + + declare_string("hotkey"); + declare_integer("hotkey_modifier_flags"); + declare_integer("has_hotkey"); + + begin_struct("link_target_id_list"); + begin_struct_array("item_id_list"); + declare_integer("size"); + declare_string("data"); + end_struct_array("item_id_list"); + + declare_integer("number_of_item_ids"); + declare_integer("item_id_list_size"); + end_struct("link_target_id_list"); + + begin_struct("link_info"); + declare_integer("size"); + declare_integer("header_size"); + declare_integer("flags"); + declare_integer("volume_id_offset"); + declare_integer("local_base_path_offset"); + declare_integer("common_network_relative_link_offset"); + declare_integer("common_path_suffix_offset"); + declare_integer("local_base_path_offset_unicode"); + declare_integer("common_path_suffix_offset_unicode"); + + declare_integer("has_volume_id"); + + begin_struct("volume_id"); + declare_integer("size"); + declare_integer("drive_type"); + declare_integer("drive_serial_number"); + declare_integer("volume_label_offset"); + declare_integer("volume_label_offset_unicode"); + declare_string("data"); + end_struct("volume_id"); + + declare_string("local_base_path"); + + declare_integer("has_common_network_relative_link"); + + begin_struct("common_network_relative_link"); + declare_integer("size"); + declare_integer("flags"); + declare_integer("net_name_offset"); + declare_integer("device_name_offset"); + declare_integer("network_provider_type"); + declare_integer("net_name_offset_unicode"); + declare_integer("device_name_offset_unicode"); + declare_string("net_name"); + declare_string("device_name"); + declare_string("net_name_unicode"); + declare_string("device_name_unicode"); + end_struct("common_network_relative_link"); + + declare_string("common_path_suffix"); + declare_string("local_base_path_unicode"); + declare_string("common_path_suffix_unicode"); + end_struct("link_info"); + + declare_string("name_string"); + declare_string("relative_path"); + declare_string("working_dir"); + declare_string("command_line_arguments"); + declare_string("icon_location"); + + declare_integer("has_console_data"); + + begin_struct("console_data"); + declare_integer("block_size"); + declare_integer("block_signature"); + declare_integer("fill_attributes"); + declare_integer("popup_fill_attributes"); + declare_integer("screen_buffer_size_x"); + declare_integer("screen_buffer_size_y"); + declare_integer("window_size_x"); + declare_integer("window_size_y"); + declare_integer("window_origin_x"); + declare_integer("window_origin_y"); + declare_integer("font_size"); + declare_integer("font_family"); + declare_integer("font_weight"); + declare_string("face_name"); + declare_integer("cursor_size"); + declare_integer("full_screen"); + declare_integer("quick_edit"); + declare_integer("insert_mode"); + declare_integer("auto_position"); + declare_integer("history_buffer_size"); + declare_integer("number_of_history_buffers"); + declare_integer("history_no_dup"); + declare_integer_array("color_table"); + end_struct("console_data"); + + declare_integer("has_console_fe_data"); + + begin_struct("console_fe_data"); + declare_integer("block_size"); + declare_integer("block_signature"); + declare_integer("code_page"); + end_struct("console_fe_data"); + + declare_integer("has_darwin_data"); + + begin_struct("darwin_data"); + declare_integer("block_size"); + declare_integer("block_signature"); + declare_string("darwin_data_ansi"); + declare_string("darwin_data_unicode"); + end_struct("darwin_data"); + + declare_integer("has_environment_variable_data"); + + begin_struct("environment_variable_data"); + declare_integer("block_size"); + declare_integer("block_signature"); + declare_string("target_ansi"); + declare_string("target_unicode"); + end_struct("environment_variable_data"); + + declare_integer("has_icon_environment_data"); + + begin_struct("icon_environment_data"); + declare_integer("block_size"); + declare_integer("block_signature"); + declare_string("target_ansi"); + declare_string("target_unicode"); + end_struct("icon_environment_data"); + + declare_integer("has_known_folder_data"); + + begin_struct("known_folder_data"); + declare_integer("block_size"); + declare_integer("block_signature"); + declare_integer("offset"); + declare_integer_array("known_folder_id"); + end_struct("known_folder_data"); + + declare_integer("has_property_store_data"); + + begin_struct("property_store_data"); + declare_integer("block_size"); + declare_integer("block_signature"); + end_struct("property_store_data"); + + declare_integer("has_shim_data"); + + begin_struct("shim_data"); + declare_integer("block_size"); + declare_integer("block_signature"); + declare_string("layer_name"); + end_struct("shim_data"); + + declare_integer("has_special_folder_data"); + + begin_struct("special_folder_data"); + declare_integer("block_size"); + declare_integer("block_signature"); + declare_integer("special_folder_id"); + declare_integer("offset"); + end_struct("special_folder_data"); + + declare_integer("has_tracker_data"); + + begin_struct("tracker_data"); + declare_integer("block_size"); + declare_integer("block_signature"); + declare_string("machine_id"); + declare_string("droid_volume_identifier"); + declare_string("droid_file_identifier"); + declare_string("droid_birth_volume_identifier"); + declare_string("droid_birth_file_identifier"); + end_struct("tracker_data"); + + declare_integer("has_vista_and_above_id_list_data"); + + begin_struct("vista_and_above_id_list_data"); + declare_integer("block_size"); + declare_integer("block_signature"); + declare_integer("number_of_item_ids"); + + begin_struct_array("item_id_list"); + declare_integer("size"); + declare_string("data"); + end_struct_array("item_id_list"); + end_struct("vista_and_above_id_list_data"); + + declare_integer("has_overlay"); + declare_integer("overlay_offset"); + + declare_integer("is_malformed"); +end_declarations + +uint32_t parse_id_list( + const uint8_t* id_list_ptr, + YR_OBJECT* module_object, + uint32_t block_data_size_remaining, + bool extra_data) +{ + uint16_t item_id_size; + uint32_t num_item_ids = 0; + const uint8_t* item_id_data_ptr; + + // Get the first ItemIDSize + if (block_data_size_remaining < sizeof(item_id_size)) + { + return 0; + } + memcpy(&item_id_size, id_list_ptr, sizeof(item_id_size)); + block_data_size_remaining -= sizeof(item_id_size); + + while (item_id_size != 0) + { + // Subtract 2 to not include it + if (extra_data) + { + yr_set_integer( + item_id_size - 2, + module_object, + "vista_and_above_id_list_data.item_id_list[%i].size", + num_item_ids); + } + else + { + yr_set_integer( + item_id_size - 2, + module_object, + "link_target_id_list.item_id_list[%i].size", + num_item_ids); + } + + // Get pointer to the ItemID Data + item_id_data_ptr = id_list_ptr + sizeof(item_id_size); + + if (block_data_size_remaining < item_id_size - sizeof(item_id_size)) + { + return 0; + } + + if (extra_data) + { + yr_set_sized_string( + (const char*) item_id_data_ptr, + item_id_size - sizeof(item_id_size), + module_object, + "vista_and_above_id_list_data.item_id_list[%i].data", + num_item_ids); + } + else + { + yr_set_sized_string( + (const char*) item_id_data_ptr, + item_id_size - sizeof(item_id_size), + module_object, + "link_target_id_list.item_id_list[%i].data", + num_item_ids); + } + block_data_size_remaining -= item_id_size - sizeof(item_id_size); + + num_item_ids += 1; + id_list_ptr += item_id_size; + + // Get the next ItemIDSize (or 0x0000 if we've reached TerminalID) + if (block_data_size_remaining < sizeof(item_id_size)) + { + return 0; + } + memcpy(&item_id_size, id_list_ptr, sizeof(item_id_size)); + block_data_size_remaining -= sizeof(item_id_size); + } + + if (extra_data) + { + yr_set_integer( + num_item_ids, + module_object, + "vista_and_above_id_list_data.number_of_item_ids"); + } + else + { + yr_set_integer( + num_item_ids, module_object, "link_target_id_list.number_of_item_ids"); + } + + return 1; +} + +uint32_t parse_link_target_id_list( + const uint8_t* link_target_id_list_ptr, + YR_OBJECT* module_object, + uint32_t block_data_size_remaining) +{ + uint16_t id_list_size; + + // First, get the IDListSize + if (block_data_size_remaining < sizeof(id_list_size)) + { + return 0; + } + memcpy(&id_list_size, link_target_id_list_ptr, sizeof(id_list_size)); + block_data_size_remaining -= sizeof(id_list_size); + + yr_set_integer( + id_list_size, module_object, "link_target_id_list.item_id_list_size"); + + // Get pointer to start of IDList + link_target_id_list_ptr += sizeof(id_list_size); + + if (!parse_id_list( + link_target_id_list_ptr, + module_object, + block_data_size_remaining, + false)) + { + return 0; + } + + // Return the size of the whole section to compute where the next one starts + return id_list_size + 2; +} + +uint32_t parse_volume_id( + const uint8_t* volume_id_ptr, + YR_OBJECT* module_object, + uint32_t block_data_size_remaining) +{ + volume_id_t volume_id; + uint32_t size_of_data; + uint32_t volume_label_offset_unicode; + char* volume_id_data=NULL; + uint32_t total_data_read = 0; + + if (block_data_size_remaining < sizeof(volume_id_t)) + { + return 0; + } + + memcpy(&volume_id, (volume_id_t*) volume_id_ptr, sizeof(volume_id_t)); + + yr_set_integer( + volume_id.volume_id_size, module_object, "link_info.volume_id.size"); + yr_set_integer( + volume_id.drive_type, module_object, "link_info.volume_id.drive_type"); + yr_set_integer( + volume_id.drive_serial_number, + module_object, + "link_info.volume_id.drive_serial_number"); + yr_set_integer( + volume_id.volume_label_offset, + module_object, + "link_info.volume_id.volume_label_offset"); + + // To work out the size of the data, we need to subtract the size of + // the whole structure from the VolumeIDSize. However, this structure + // size is variable based on if the unicode offset is present. + size_of_data = volume_id.volume_id_size - volume_id.volume_label_offset; + + volume_id_ptr += sizeof(volume_id_t); + block_data_size_remaining -= sizeof(volume_id_t); + total_data_read += sizeof(volume_id_t); + + if (volume_id.volume_label_offset == 0x14) + { + if (block_data_size_remaining < sizeof(volume_label_offset_unicode)) + { + return 0; + } + + memcpy( + &volume_label_offset_unicode, + volume_id_ptr, + sizeof(volume_label_offset_unicode)); + yr_set_integer( + volume_label_offset_unicode, + module_object, + "link_info.volume_id.volume_label_offset_unicode"); + volume_id_ptr += sizeof(volume_label_offset_unicode); + block_data_size_remaining -= sizeof(volume_label_offset_unicode); + total_data_read += sizeof(volume_label_offset_unicode); + + // Compensate for extra entry in the structure + // Todo: Extra checks if this size makes sense? + size_of_data = volume_id.volume_id_size - volume_label_offset_unicode; + } + + if (block_data_size_remaining < size_of_data) + { + return 0; + } + + if (size_of_data > 256) { + return 0; + } + + volume_id_data = yr_malloc(size_of_data); + + memcpy(volume_id_data, volume_id_ptr, size_of_data); + yr_set_sized_string( + volume_id_data, size_of_data, module_object, "link_info.volume_id.data"); + + volume_id_ptr += size_of_data; + block_data_size_remaining -= size_of_data; + total_data_read += size_of_data; + + if (volume_id_data) { + yr_free(volume_id_data); + } + + return total_data_read; +} + +uint32_t parse_common_network_relative_link( + const uint8_t* common_network_relative_link_ptr, + YR_OBJECT* module_object, + uint32_t block_data_size_remaining) +{ + common_network_relative_link_t common_network_relative_link; + uint32_t net_name_offset_unicode = 0; + uint32_t device_name_offset_unicode = 0; + char* net_name=NULL; + char* device_name=NULL; + wchar_t* net_name_unicode=NULL; + wchar_t* device_name_unicode=NULL; + uint32_t net_name_len; + uint32_t device_name_len; + uint32_t net_name_unicode_len; + uint32_t device_name_unicode_len; + + if (block_data_size_remaining < sizeof(common_network_relative_link_t)) + { + return 0; + } + + memcpy( + &common_network_relative_link, + (common_network_relative_link_t*) common_network_relative_link_ptr, + sizeof(common_network_relative_link_t)); + + yr_set_integer( + common_network_relative_link.common_network_relative_link_size, + module_object, + "link_info.common_network_relative_link.size"); + yr_set_integer( + common_network_relative_link.common_network_relative_link_flags, + module_object, + "link_info.common_network_relative_link.flags"); + yr_set_integer( + common_network_relative_link.net_name_offset, + module_object, + "link_info.common_network_relative_link.net_name_offset"); + yr_set_integer( + common_network_relative_link.device_name_offset, + module_object, + "link_info.common_network_relative_link.device_name_offset"); + yr_set_integer( + common_network_relative_link.network_provider_type, + module_object, + "link_info.common_network_relative_link.network_provider_type"); + + common_network_relative_link_ptr += sizeof(common_network_relative_link_t); + block_data_size_remaining -= sizeof(common_network_relative_link_t); + + if (common_network_relative_link.net_name_offset > 0x14) + { + if (block_data_size_remaining < sizeof(net_name_offset_unicode)) + { + return 0; + } + + memcpy( + &net_name_offset_unicode, + common_network_relative_link_ptr, + sizeof(net_name_offset_unicode)); + yr_set_integer( + net_name_offset_unicode, + module_object, + "link_info.common_network_relative_link.net_name_offset_unicode"); + common_network_relative_link_ptr += sizeof(net_name_offset_unicode); + block_data_size_remaining -= sizeof(net_name_offset_unicode); + + if (block_data_size_remaining < sizeof(device_name_offset_unicode)) + { + return 0; + } + + memcpy( + &device_name_offset_unicode, + common_network_relative_link_ptr, + sizeof(device_name_offset_unicode)); + yr_set_integer( + device_name_offset_unicode, + module_object, + "link_info.common_network_relative_link.device_name_offset_unicode"); + common_network_relative_link_ptr += sizeof(device_name_offset_unicode); + block_data_size_remaining -= sizeof(device_name_offset_unicode); + + // Parse unicode strings + net_name_unicode_len = wcslen( + (const wchar_t*) common_network_relative_link_ptr); + + if (block_data_size_remaining < net_name_unicode_len * 2) + { + return 0; + } + + if (net_name_unicode_len > 260) { + return 0; + } + + net_name_unicode = yr_malloc(net_name_unicode_len * 2); + + memcpy( + net_name_unicode, + common_network_relative_link_ptr, + net_name_unicode_len * 2); + + yr_set_sized_string( + (char*) net_name_unicode, + net_name_unicode_len, + module_object, + "link_info.common_network_relative_link.net_name_unicode"); + + // Add 1 to deal with null terminator + common_network_relative_link_ptr += (net_name_unicode_len * 2) + 1; + block_data_size_remaining -= (net_name_unicode_len * 2) + 1; + + device_name_unicode_len = wcslen( + (const wchar_t*) common_network_relative_link_ptr); + + if (block_data_size_remaining < device_name_unicode_len * 2) + { + return 0; + } + + if (device_name_unicode_len > 260) { + return 0; + } + + device_name_unicode = yr_malloc(device_name_unicode_len * 2); + + memcpy( + device_name_unicode, + common_network_relative_link_ptr, + device_name_unicode_len * 2); + + yr_set_sized_string( + (char*) device_name_unicode, + device_name_unicode_len, + module_object, + "link_info.common_network_relative_link.device_name_unicode"); + + // Add 1 to deal with null terminator + common_network_relative_link_ptr += (device_name_unicode_len * 2) + 1; + block_data_size_remaining -= (device_name_unicode_len * 2) + 1; + } + + // Otherwise parse ASCII strings + else + { + net_name_len = strlen((const char*) common_network_relative_link_ptr); + + if (block_data_size_remaining < net_name_len) + { + return 0; + } + + if (net_name_len > 260) { + return 0; + } + + net_name = yr_malloc(net_name_len); + + memcpy(net_name, common_network_relative_link_ptr, net_name_len); + + yr_set_sized_string( + net_name, + net_name_len, + module_object, + "link_info.common_network_relative_link.net_name"); + + // Add 1 to deal with null terminator + common_network_relative_link_ptr += net_name_len + 1; + block_data_size_remaining -= net_name_len + 1; + + device_name_len = strlen((const char*) common_network_relative_link_ptr); + + if (block_data_size_remaining < device_name_len) + { + return 0; + } + + if (device_name_len > 260) { + return 0; + } + + device_name = yr_malloc(device_name_len); + + memcpy(device_name, common_network_relative_link_ptr, device_name_len); + + yr_set_sized_string( + device_name, + device_name_len, + module_object, + "link_info.common_network_relative_link.device_name"); + + // Add 1 to deal with null terminator + common_network_relative_link_ptr += device_name_len + 1; + block_data_size_remaining -= device_name_len + 1; + } + + if (net_name) { + yr_free(net_name); + } + + if (device_name) { + yr_free(device_name); + } + + if (net_name_unicode) { + yr_free(net_name_unicode); + } + + if (device_name_unicode) { + yr_free(device_name_unicode); + } + + return common_network_relative_link.common_network_relative_link_size; +} + +uint32_t parse_link_info( + const uint8_t* link_info_ptr, + YR_OBJECT* module_object, + uint32_t block_data_size_remaining) +{ + link_info_fixed_header_t* link_info_fixed_header; + uint32_t local_base_path_offset_unicode = 0; + uint32_t common_path_suffix_offset_unicode = 0; + char* local_base_path=NULL; + char* common_path_suffix=NULL; + wchar_t* local_base_path_unicode=NULL; + wchar_t* common_path_suffix_unicode=NULL; + uint32_t local_base_path_len; + uint32_t common_path_suffix_len; + uint32_t local_base_path_unicode_len; + uint32_t common_path_suffix_unicode_len; + uint32_t volume_id_size; + uint32_t common_network_relative_link_size; + + if (block_data_size_remaining < sizeof(link_info_fixed_header_t)) + { + return 0; + } + link_info_fixed_header = (link_info_fixed_header_t*) link_info_ptr; + + yr_set_integer( + link_info_fixed_header->link_info_size, module_object, "link_info.size"); + yr_set_integer( + link_info_fixed_header->link_info_header_size, + module_object, + "link_info.header_size"); + yr_set_integer( + link_info_fixed_header->link_info_flags, + module_object, + "link_info.flags"); + yr_set_integer( + link_info_fixed_header->volume_id_offset, + module_object, + "link_info.volume_id_offset"); + yr_set_integer( + link_info_fixed_header->local_base_path_offset, + module_object, + "link_info.local_base_path_offset"); + yr_set_integer( + link_info_fixed_header->common_network_relative_link_offset, + module_object, + "link_info.common_network_relative_link_offset"); + yr_set_integer( + link_info_fixed_header->common_path_suffix_offset, + module_object, + "link_info.common_path_suffix_offset"); + + link_info_ptr += sizeof(link_info_fixed_header_t); + block_data_size_remaining -= sizeof(link_info_fixed_header_t); + + // if VOLUME_ID_AND_LOCAL_BASE_PATH flag: + // VolumeID and LocalBasePath present + // VolumeIDOffset and LocalBasePathOffset specify offsets + // if LinkInfoHeaderSize > 0x24: + // LocalBasePathUnicode present (specified by offset value) + // else + // VolumeID, LocalBasePath, and LocalBasePathUnicode fields are not present + // VolumeIDOffset and LocalBasePathOffset are 0 + // if LinkInfoHeaderSize > 0x24: + // LocalBasePathOffsetUnicode is 0 + + if (link_info_fixed_header->link_info_flags & VOLUME_ID_AND_LOCAL_BASE_PATH) + { + if (link_info_fixed_header->link_info_header_size >= 0x24) + { + if (block_data_size_remaining < sizeof(local_base_path_offset_unicode)) + { + return 0; + } + + memcpy( + &local_base_path_offset_unicode, + link_info_ptr, + sizeof(local_base_path_offset_unicode)); + yr_set_integer( + local_base_path_offset_unicode, + module_object, + "link_info.local_base_path_offset_unicode"); + link_info_ptr += sizeof(local_base_path_offset_unicode); + block_data_size_remaining -= sizeof(local_base_path_offset_unicode); + } + + if (link_info_fixed_header->volume_id_offset) + { + yr_set_integer(1, module_object, "link_info.has_volume_id"); + + volume_id_size = parse_volume_id( + link_info_ptr, module_object, block_data_size_remaining); + + if (volume_id_size == 0) + { + return 0; + } + + if (block_data_size_remaining < volume_id_size) + { + return 0; + } + + link_info_ptr += volume_id_size; + block_data_size_remaining -= volume_id_size; + } + + else + { + yr_set_integer(0, module_object, "link_info.has_volume_id"); + } + + // Handle LocalBasePath + if (link_info_fixed_header->local_base_path_offset) + { + local_base_path_len = strlen((const char*) link_info_ptr); + + if (local_base_path_len > 256) { + return 0; + } + + if (block_data_size_remaining < local_base_path_len) + { + return 0; + } + + local_base_path = (char*)yr_malloc(local_base_path_len); + + memcpy(local_base_path, link_info_ptr, local_base_path_len); + yr_set_sized_string( + local_base_path, + local_base_path_len, + module_object, + "link_info.local_base_path"); + + // Add 1 to deal with null terminator + link_info_ptr += local_base_path_len + 1; + block_data_size_remaining -= local_base_path_len + 1; + } + } + + if (link_info_fixed_header->link_info_header_size >= 0x24) + { + if (block_data_size_remaining < sizeof(common_path_suffix_offset_unicode)) + { + return 0; + } + + memcpy( + &common_path_suffix_offset_unicode, + link_info_ptr, + sizeof(common_path_suffix_offset_unicode)); + yr_set_integer( + common_path_suffix_offset_unicode, + module_object, + "link_info.common_path_suffix_offset_unicode"); + link_info_ptr += sizeof(common_path_suffix_offset_unicode); + block_data_size_remaining -= sizeof(common_path_suffix_offset_unicode); + } + + if (link_info_fixed_header->link_info_flags & + COMMON_NETWORK_RELATIVE_LINK_AND_PATH_SUFFIX) + { + if (link_info_fixed_header->common_network_relative_link_offset) + { + common_network_relative_link_size = parse_common_network_relative_link( + link_info_ptr, module_object, block_data_size_remaining); + + if (common_network_relative_link_size == 0) + { + return 0; + } + + if (block_data_size_remaining < common_network_relative_link_size) + { + return 0; + } + + link_info_ptr += common_network_relative_link_size; + block_data_size_remaining -= common_network_relative_link_size; + } + } + + // Handle CommonPathSuffix + if (link_info_fixed_header->common_path_suffix_offset) + { + if (block_data_size_remaining < 1) + { + return 0; + } + + // Have to deal with this possibly being an empty string + if (memcmp(link_info_ptr, "\x00", 1) == 0) + { + yr_set_sized_string( + "\x00", 1, module_object, "link_info.common_path_suffix"); + link_info_ptr += 1; + block_data_size_remaining -= 1; + } + + else + { + common_path_suffix_len = strlen((const char*) link_info_ptr); + + if (block_data_size_remaining < common_path_suffix_len) + { + return 0; + } + + if (common_path_suffix_len > 256) { + return 0; + } + + common_path_suffix = yr_malloc(common_path_suffix_len); + + memcpy(common_path_suffix, link_info_ptr, common_path_suffix_len); + + yr_set_sized_string( + common_path_suffix, + common_path_suffix_len, + module_object, + "link_info.common_path_suffix"); + + // Add 1 to deal with null terminator + link_info_ptr += common_path_suffix_len + 1; + block_data_size_remaining -= common_path_suffix_len + 1; + } + } + + // TODO: These unicode functions will need some careful testing + // Need some samples to test against + if (local_base_path_offset_unicode) + { + local_base_path_unicode_len = wcslen((const wchar_t*) link_info_ptr); + + if (block_data_size_remaining < local_base_path_unicode_len * 2) + { + return 0; + } + + if (local_base_path_unicode_len > 256) { + return 0; + } + + local_base_path_unicode = yr_malloc(local_base_path_unicode_len * 2); + + memcpy( + local_base_path_unicode, + link_info_ptr, + local_base_path_unicode_len * 2); + + yr_set_sized_string( + (char*) local_base_path_unicode, + local_base_path_unicode_len, + module_object, + "link_info.local_base_path_unicode"); + + // Add 1 to deal with null terminator + link_info_ptr += (local_base_path_unicode_len * 2) + 1; + block_data_size_remaining -= (local_base_path_unicode_len * 2) + 1; + } + + if (common_path_suffix_offset_unicode) + { + if (block_data_size_remaining < 1) + { + return 0; + } + + // Have to deal with this possibly being an empty string + if (memcmp(link_info_ptr, "\x00", 1) == 0) + { + yr_set_sized_string( + "\x00", 1, module_object, "link_info.common_path_suffix_unicode"); + link_info_ptr += 1; + block_data_size_remaining -= 1; + } + + else + { + common_path_suffix_unicode_len = wcslen((const wchar_t*) link_info_ptr); + + if (block_data_size_remaining < common_path_suffix_unicode_len * 2) + { + return 0; + } + + if (common_path_suffix_unicode_len > 256) { + return 0; + } + + common_path_suffix_unicode = yr_malloc(common_path_suffix_unicode_len * 2); + + memcpy( + common_path_suffix_unicode, + link_info_ptr, + common_path_suffix_unicode_len * 2); + + yr_set_sized_string( + (char*) common_path_suffix_unicode, + common_path_suffix_unicode_len, + module_object, + "link_info.common_path_suffix_unicode"); + + // Add 1 to deal with null terminator + link_info_ptr += (common_path_suffix_unicode_len * 2) + 1; + block_data_size_remaining -= (common_path_suffix_unicode_len * 2) + 1; + } + } + + if (local_base_path){ + yr_free(local_base_path); + } + + if (common_path_suffix){ + yr_free(common_path_suffix); + } + + if (local_base_path_unicode) { + yr_free(local_base_path_unicode); + } + + if (common_path_suffix_unicode) { + yr_free(common_path_suffix_unicode); + } + + return (int) link_info_fixed_header->link_info_size; +} + +uint32_t parse_string_data( + const uint8_t* string_data_ptr, + YR_OBJECT* module_object, + uint32_t block_data_size_remaining, + const char* name, + bool is_unicode) +{ + uint16_t count_characters; + uint32_t string_size; + + // CountCharacters only returns the number of characters in the string, but + // not information on whether the string is unicode vs. another type of + // string. The IS_UNICODE flag will tell us if the StringData values are + // unicode, and if it is not set, we'll assume it is ascii (although it can be + // whatever is the default codepage from where the LNK is generated) + + if (block_data_size_remaining < sizeof(count_characters)) + { + return 0; + } + + memcpy(&count_characters, string_data_ptr, sizeof(count_characters)); + string_data_ptr += sizeof(count_characters); + block_data_size_remaining -= sizeof(count_characters); + + if (is_unicode) + { + if (block_data_size_remaining < count_characters * 2) + { + return 0; + } + + string_size = count_characters * 2; + } + + else + { + string_size = count_characters; + } + + // Do these extra comparisons due to "format not a string literal and no + // format arguments" error on compilation + if (strcmp(name, "name_string") == 0) + { + yr_set_sized_string( + (char*) string_data_ptr, string_size, module_object, "name_string"); + } + + else if (strcmp(name, "relative_path") == 0) + { + yr_set_sized_string( + (char*) string_data_ptr, string_size, module_object, "relative_path"); + } + + else if (strcmp(name, "working_dir") == 0) + { + yr_set_sized_string( + (char*) string_data_ptr, string_size, module_object, "working_dir"); + } + + else if (strcmp(name, "command_line_arguments") == 0) + { + yr_set_sized_string( + (char*) string_data_ptr, + string_size, + module_object, + "command_line_arguments"); + } + + else if (strcmp(name, "icon_location") == 0) + { + yr_set_sized_string( + (char*) string_data_ptr, string_size, module_object, "icon_location"); + } + + else + { + return 0; + } + + return string_size + sizeof(count_characters); +} + +uint32_t parse_console_data_block( + const uint8_t* extra_block_ptr, + YR_OBJECT* module_object, + uint32_t block_data_size_remaining, + uint32_t extra_data_block_size, + uint32_t extra_data_block_signature) +{ + console_data_block_t console_data_block; + int i; + + if (block_data_size_remaining < sizeof(console_data_block_t)) + { + return 0; + } + + memcpy( + &console_data_block, + (console_data_block_t*) extra_block_ptr, + sizeof(console_data_block_t)); + + yr_set_integer(extra_data_block_size, module_object, "console_data.block_size"); + yr_set_integer( + extra_data_block_signature, + module_object, + "console_data.block_signature"); + yr_set_integer( + console_data_block.fill_attributes, + module_object, + "console_data.fill_attributes"); + yr_set_integer( + console_data_block.popup_fill_attributes, + module_object, + "console_data.popup_fill_attributes"); + yr_set_integer( + console_data_block.screen_buffer_size_x, + module_object, + "console_data.screen_buffer_size_x"); + yr_set_integer( + console_data_block.screen_buffer_size_y, + module_object, + "console_data.screen_buffer_size_y"); + yr_set_integer( + console_data_block.window_size_x, + module_object, + "console_data.window_size_x"); + yr_set_integer( + console_data_block.window_size_y, + module_object, + "console_data.window_size_y"); + yr_set_integer( + console_data_block.window_origin_x, + module_object, + "console_data.window_origin_x"); + yr_set_integer( + console_data_block.window_origin_y, + module_object, + "console_data.window_origin_y"); + yr_set_integer( + console_data_block.font_size, module_object, "console_data.font_size"); + yr_set_integer( + console_data_block.font_family, + module_object, + "console_data.font_family"); + yr_set_integer( + console_data_block.font_weight, + module_object, + "console_data.font_weight"); + yr_set_sized_string( + (char*) console_data_block.face_name, + wcslen((wchar_t*) console_data_block.face_name), + module_object, + "console_data.face_name"); + yr_set_integer( + console_data_block.cursor_size, + module_object, + "console_data.cursor_size"); + yr_set_integer( + console_data_block.full_screen, + module_object, + "console_data.full_screen"); + yr_set_integer( + console_data_block.quick_edit, module_object, "console_data.quick_edit"); + yr_set_integer( + console_data_block.insert_mode, + module_object, + "console_data.insert_mode"); + yr_set_integer( + console_data_block.auto_position, + module_object, + "console_data.auto_position"); + yr_set_integer( + console_data_block.history_buffer_size, + module_object, + "console_data.history_buffer_size"); + yr_set_integer( + console_data_block.number_of_history_buffers, + module_object, + "console_data.number_of_history_buffers"); + yr_set_integer( + console_data_block.history_no_dup, + module_object, + "console_data.history_no_dup"); + + for (i = 0; i < 16; i++) + { + yr_set_integer( + console_data_block.color_table[i], + module_object, + "console_data.color_table[%i]", + i); + } + + return 1; +} + +uint32_t parse_console_fe_data_block( + const uint8_t* extra_block_ptr, + YR_OBJECT* module_object, + uint32_t block_data_size_remaining, + uint32_t extra_data_block_size, + uint32_t extra_data_block_signature) +{ + console_fe_data_block_t console_fe_data; + + if (block_data_size_remaining < sizeof(console_fe_data_block_t)) + { + return 0; + } + + memcpy( + &console_fe_data, + (console_fe_data_block_t*) extra_block_ptr, + sizeof(console_fe_data_block_t)); + + yr_set_integer( + extra_data_block_size, module_object, "console_fe_data.block_size"); + yr_set_integer( + extra_data_block_signature, + module_object, + "console_fe_data.block_signature"); + yr_set_integer( + console_fe_data.code_page, module_object, "console_fe_data.code_page"); + + return 1; +} + +uint32_t parse_darwin_data_block( + const uint8_t* extra_block_ptr, + YR_OBJECT* module_object, + uint32_t block_data_size_remaining, + uint32_t extra_data_block_size, + uint32_t extra_data_block_signature) +{ + darwin_data_block_t darwin_data; + + if (block_data_size_remaining < sizeof(darwin_data_block_t)) + { + return 0; + } + + memcpy( + &darwin_data, + (darwin_data_block_t*) extra_block_ptr, + sizeof(darwin_data_block_t)); + + yr_set_integer(extra_data_block_size, module_object, "darwin_data.block_size"); + yr_set_integer( + extra_data_block_signature, module_object, "darwin_data.block_signature"); + yr_set_string( + darwin_data.darwin_data_ansi, + module_object, + "darwin_data.darwin_data_ansi"); + yr_set_sized_string( + (char*) darwin_data.darwin_data_unicode, + wcslen((wchar_t*) darwin_data.darwin_data_unicode) * 2, + module_object, + "darwin_data.darwin_data_unicode"); + + return 1; +} + +uint32_t parse_environment_variable_data_block( + const uint8_t* extra_block_ptr, + YR_OBJECT* module_object, + uint32_t block_data_size_remaining, + uint32_t extra_data_block_size, + uint32_t extra_data_block_signature) +{ + environment_variable_data_block_t environment_variable_data; + + if (block_data_size_remaining < sizeof(environment_variable_data_block_t)) + { + return 0; + } + + memcpy( + &environment_variable_data, + (environment_variable_data_block_t*) extra_block_ptr, + sizeof(environment_variable_data_block_t)); + + yr_set_integer( + extra_data_block_size, + module_object, + "environment_variable_data.block_size"); + yr_set_integer( + extra_data_block_signature, + module_object, + "environment_variable_data.block_signature"); + yr_set_string( + environment_variable_data.target_ansi, + module_object, + "environment_variable_data.target_ansi"); + yr_set_sized_string( + (char*) environment_variable_data.target_unicode, + wcslen((wchar_t*) environment_variable_data.target_unicode) * 2, + module_object, + "environment_variable_data.target_unicode"); + + return 1; +} + +uint32_t parse_icon_environment_data_block( + const uint8_t* extra_block_ptr, + YR_OBJECT* module_object, + uint32_t block_data_size_remaining, + uint32_t extra_data_block_size, + uint32_t extra_data_block_signature) +{ + icon_environment_data_block_t icon_environment_data; + + if (block_data_size_remaining < sizeof(icon_environment_data_block_t)) + { + return 0; + } + + memcpy( + &icon_environment_data, + (icon_environment_data_block_t*) extra_block_ptr, + sizeof(icon_environment_data_block_t)); + + yr_set_integer( + extra_data_block_size, module_object, "icon_environment_data.block_size"); + yr_set_integer( + extra_data_block_signature, + module_object, + "icon_environment_data.block_signature"); + yr_set_string( + icon_environment_data.target_ansi, + module_object, + "icon_environment_data.target_ansi"); + yr_set_sized_string( + (char*) icon_environment_data.target_unicode, + wcslen((wchar_t*) icon_environment_data.target_unicode) * 2, + module_object, + "icon_environment_data.target_unicode"); + + return 1; +} + +uint32_t parse_known_folder_data_block( + const uint8_t* extra_block_ptr, + YR_OBJECT* module_object, + uint32_t block_data_size_remaining, + uint32_t extra_data_block_size, + uint32_t extra_data_block_signature) +{ + known_folder_data_block_t known_folder_data; + int i; + + if (block_data_size_remaining < sizeof(known_folder_data_block_t)) + { + return 0; + } + + memcpy( + &known_folder_data, + (known_folder_data_block_t*) extra_block_ptr, + sizeof(known_folder_data_block_t)); + + yr_set_integer( + extra_data_block_size, module_object, "known_folder_data.block_size"); + yr_set_integer( + extra_data_block_signature, + module_object, + "known_folder_data.block_signature"); + yr_set_integer( + known_folder_data.offset, module_object, "known_folder_data.offset"); + + for (i = 0; i < 16; i++) + { + yr_set_integer( + known_folder_data.known_folder_id[i], + module_object, + "known_folder_data.known_folder_id[%i]", + i); + } + + return 1; +} + +uint32_t parse_property_store_data_block( + const uint8_t* extra_block_ptr, + YR_OBJECT* module_object, + uint32_t block_data_size_remaining, + uint32_t extra_data_block_size, + uint32_t extra_data_block_signature) +{ + yr_set_integer( + extra_data_block_size, module_object, "property_store_data.block_size"); + yr_set_integer( + extra_data_block_signature, + module_object, + "property_store_data.block_signature"); + + // TODO: implement parsing the rest of the structure + // https://winprotocoldoc.blob.core.windows.net/productionwindowsarchives/MS-PROPSTORE/%5bMS-PROPSTORE%5d.pdf + + return 1; +} + +uint32_t parse_shim_data_block( + const uint8_t* extra_block_ptr, + YR_OBJECT* module_object, + uint32_t block_data_size_remaining, + uint32_t extra_data_block_size, + uint32_t extra_data_block_signature) +{ + wchar_t* layer_name; + + if (block_data_size_remaining < extra_data_block_size - + sizeof(extra_data_block_size) - + sizeof(extra_data_block_signature)) + { + return 0; + } + + layer_name = (wchar_t*) extra_block_ptr; + + yr_set_integer(extra_data_block_size, module_object, "shim_data.block_size"); + yr_set_integer( + extra_data_block_signature, module_object, "shim_data.block_signature"); + yr_set_sized_string( + (char*) layer_name, + extra_data_block_size - sizeof(extra_data_block_size) - + sizeof(extra_data_block_signature), + module_object, + "shim_data.layer_name"); + + return 1; +} + +uint32_t parse_special_folder_data_block( + const uint8_t* extra_block_ptr, + YR_OBJECT* module_object, + uint32_t block_data_size_remaining, + uint32_t extra_data_block_size, + uint32_t extra_data_block_signature) +{ + special_folder_data_block_t special_folder_data; + + if (block_data_size_remaining < sizeof(special_folder_data_block_t)) + { + return 0; + } + + memcpy( + &special_folder_data, + (special_folder_data_block_t*) extra_block_ptr, + sizeof(special_folder_data_block_t)); + + yr_set_integer( + extra_data_block_size, module_object, "special_folder_data.block_size"); + yr_set_integer( + extra_data_block_signature, + module_object, + "special_folder_data.block_signature"); + yr_set_integer( + special_folder_data.special_folder_id, + module_object, + "special_folder_data.special_folder_id"); + yr_set_integer( + special_folder_data.offset, module_object, "special_folder_data.offset"); + + return 1; +} + +uint32_t parse_tracker_data_block( + const uint8_t* extra_block_ptr, + YR_OBJECT* module_object, + uint32_t block_data_size_remaining, + uint32_t extra_data_block_size, + uint32_t extra_data_block_signature) +{ + tracker_data_block_t tracker_data_block; + + if (block_data_size_remaining < sizeof(tracker_data_block_t)) + { + return 0; + } + + memcpy( + &tracker_data_block, + (tracker_data_block_t*) extra_block_ptr, + sizeof(tracker_data_block_t)); + + yr_set_integer(extra_data_block_size, module_object, "tracker_data.block_size"); + yr_set_integer( + extra_data_block_signature, + module_object, + "tracker_data.block_signature"); + yr_set_string( + tracker_data_block.machine_id, module_object, "tracker_data.machine_id"); + yr_set_sized_string( + (char*) tracker_data_block.droid_volume_identifier, + sizeof(tracker_data_block.droid_volume_identifier), + module_object, + "tracker_data.droid_volume_identifier"); + yr_set_sized_string( + (char*) tracker_data_block.droid_file_identifier, + sizeof(tracker_data_block.droid_file_identifier), + module_object, + "tracker_data.droid_file_identifier"); + yr_set_sized_string( + (char*) tracker_data_block.droid_birth_volume_identifier, + sizeof(tracker_data_block.droid_birth_volume_identifier), + module_object, + "tracker_data.droid_birth_volume_identifier"); + yr_set_sized_string( + (char*) tracker_data_block.droid_birth_file_identifier, + sizeof(tracker_data_block.droid_birth_file_identifier), + module_object, + "tracker_data.droid_birth_file_identifier"); + + return 1; +} + +uint32_t parse_vista_and_above_id_list_data_block( + const uint8_t* extra_block_ptr, + YR_OBJECT* module_object, + uint32_t block_data_size_remaining, + uint32_t extra_data_block_size, + uint32_t extra_data_block_signature) +{ + if (block_data_size_remaining < extra_data_block_size - + sizeof(extra_data_block_size) - + sizeof(extra_data_block_signature)) + { + return 0; + } + + yr_set_integer( + extra_data_block_size, + module_object, + "vista_and_above_id_list_data.block_size"); + yr_set_integer( + extra_data_block_signature, + module_object, + "vista_and_above_id_list_data.block_signature"); + if (!parse_id_list( + extra_block_ptr, module_object, block_data_size_remaining, true)) + { + return 0; + } + + return 1; +} + +uint32_t parse_extra_block( + const uint8_t* extra_block_ptr, + YR_OBJECT* module_object, + uint32_t block_data_size_remaining, + uint32_t extra_data_block_size, + uint32_t extra_data_block_signature) +{ + + switch (extra_data_block_signature) + { + case CONSOLE_DATA_BLOCK_SIGNATURE: + + yr_set_integer(1, module_object, "has_console_data"); + + if (extra_data_block_size == CONSOLE_DATA_BLOCK_SIZE && + parse_console_data_block( + extra_block_ptr, + module_object, + block_data_size_remaining, + extra_data_block_size, + extra_data_block_signature)) + { + return 1; + } + break; + + case CONSOLE_FE_DATA_BLOCK_SIGNATURE: + + yr_set_integer(1, module_object, "has_console_fe_data"); + + if (extra_data_block_size == CONSOLE_FE_DATA_BLOCK_SIZE && + parse_console_fe_data_block( + extra_block_ptr, + module_object, + block_data_size_remaining, + extra_data_block_size, + extra_data_block_signature)) + { + return 1; + } + break; + + case DARWIN_DATA_BLOCK_SIGNATURE: + + yr_set_integer(1, module_object, "has_darwin_data"); + + if (extra_data_block_size == DARWIN_DATA_BLOCK_SIZE && + parse_darwin_data_block( + extra_block_ptr, + module_object, + block_data_size_remaining, + extra_data_block_size, + extra_data_block_signature)) + { + return 1; + } + break; + + case ENVIRONMENT_VARIABLE_DATA_BLOCK_SIGNATURE: + + yr_set_integer(1, module_object, "has_environment_variable_data"); + + if (extra_data_block_size == ENVIRONMENT_VARIABLE_DATA_BLOCK_SIZE && + parse_environment_variable_data_block( + extra_block_ptr, + module_object, + block_data_size_remaining, + extra_data_block_size, + extra_data_block_signature)) + { + return 1; + } + break; + + case ICON_ENVIRONMENT_DATA_BLOCK_SIGNATURE: + + yr_set_integer(1, module_object, "has_icon_environment_data"); + + if (extra_data_block_size == ICON_ENVIRONMENT_DATA_BLOCK_SIZE && + parse_icon_environment_data_block( + extra_block_ptr, + module_object, + block_data_size_remaining, + extra_data_block_size, + extra_data_block_signature)) + { + return 1; + } + break; + + case KNOWN_FOLDER_DATA_BLOCK_SIGNATURE: + + yr_set_integer(1, module_object, "has_known_folder_data"); + + if (extra_data_block_size == KNOWN_FOLDER_DATA_BLOCK_SIZE && + parse_known_folder_data_block( + extra_block_ptr, + module_object, + block_data_size_remaining, + extra_data_block_size, + extra_data_block_signature)) + { + return 1; + } + break; + + case PROPERTY_STORE_DATA_BLOCK_SIGNATURE: + + yr_set_integer(1, module_object, "has_property_store_data"); + + if (extra_data_block_size >= PROPERTY_STORE_DATA_BLOCK_MIN_SIZE && + parse_property_store_data_block( + extra_block_ptr, + module_object, + block_data_size_remaining, + extra_data_block_size, + extra_data_block_signature)) + { + return 1; + } + break; + + case SHIM_DATA_BLOCK_SIGNATURE: + + yr_set_integer(1, module_object, "has_shim_data"); + + if (extra_data_block_size >= SHIM_DATA_BLOCK_MIN_SIZE && + parse_shim_data_block( + extra_block_ptr, + module_object, + block_data_size_remaining, + extra_data_block_size, + extra_data_block_signature)) + { + return 1; + } + break; + + case SPECIAL_FOLDER_DATA_BLOCK_SIGNATURE: + + yr_set_integer(1, module_object, "has_special_folder_data"); + + if (extra_data_block_size == SPECIAL_FOLDER_DATA_BLOCK_SIZE && + parse_special_folder_data_block( + extra_block_ptr, + module_object, + block_data_size_remaining, + extra_data_block_size, + extra_data_block_signature)) + { + return 1; + } + break; + + case TRACKER_DATA_BLOCK_SIGNATURE: + + yr_set_integer(1, module_object, "has_tracker_data"); + + if (extra_data_block_size == TRACKER_DATA_BLOCK_SIZE && + parse_tracker_data_block( + extra_block_ptr, + module_object, + block_data_size_remaining, + extra_data_block_size, + extra_data_block_signature)) + { + return 1; + } + break; + + case VISTA_AND_ABOVE_ID_LIST_DATA_BLOCK_SIGNATURE: + + yr_set_integer(1, module_object, "has_vista_and_above_id_list_data"); + + if (extra_data_block_size >= VISTA_AND_ABOVE_ID_LIST_DATA_BLOCK_MIN_SIZE && + parse_vista_and_above_id_list_data_block( + extra_block_ptr, + module_object, + block_data_size_remaining, + extra_data_block_size, + extra_data_block_signature)) + { + return 1; + } + break; + + default: + return 0; + } + + return 0; +} + +int module_initialize(YR_MODULE* module) +{ + return ERROR_SUCCESS; +} + +int module_finalize(YR_MODULE* module) +{ + return ERROR_SUCCESS; +} + +int module_load( + YR_SCAN_CONTEXT* context, + YR_OBJECT* module_object, + void* module_data, + size_t module_data_size) +{ + YR_MEMORY_BLOCK* block; + shell_link_header_t* lnk_header; + yr_set_integer(0, module_object, "is_lnk"); + + yr_set_integer(HAS_LINK_TARGET_ID_LIST, module_object, "HAS_LINK_TARGET_ID_LIST"); + yr_set_integer(HAS_LINK_INFO, module_object, "HAS_LINK_INFO"); + yr_set_integer(HAS_NAME, module_object, "HAS_NAME"); + yr_set_integer(HAS_RELATIVE_PATH, module_object, "HAS_RELATIVE_PATH"); + yr_set_integer(HAS_WORKING_DIR, module_object, "HAS_WORKING_DIR"); + yr_set_integer(HAS_ARGUMENTS, module_object, "HAS_ARGUMENTS"); + yr_set_integer(HAS_ICON_LOCATION, module_object, "HAS_ICON_LOCATION"); + yr_set_integer(IS_UNICODE, module_object, "IS_UNICODE"); + yr_set_integer(FORCE_NO_LINK_INFO, module_object, "FORCE_NO_LINK_INFO"); + yr_set_integer(HAS_EXP_STRING, module_object, "HAS_EXP_STRING"); + yr_set_integer(RUN_IN_SEPARATE_PROCESS, module_object, "RUN_IN_SEPARATE_PROCESS"); + yr_set_integer(UNUSED_1, module_object, "UNUSED_1"); + yr_set_integer(HAS_DARWIN_ID, module_object, "HAS_DARWIN_ID"); + yr_set_integer(RUN_AS_USER, module_object, "RUN_AS_USER"); + yr_set_integer(HAS_EXP_ICON, module_object, "HAS_EXP_ICON"); + yr_set_integer(NO_PIDL_ALIAS, module_object, "NO_PIDL_ALIAS"); + yr_set_integer(UNUSED_2, module_object, "UNUSED_2"); + yr_set_integer(RUN_WITH_SHIM_LAYER, module_object, "RUN_WITH_SHIM_LAYER"); + yr_set_integer(FORCE_NO_LINK_TRACK, module_object, "FORCE_NO_LINK_TRACK"); + yr_set_integer(ENABLE_TARGET_METADATA, module_object, "ENABLE_TARGET_METADATA"); + yr_set_integer( + DISABLE_LINK_PATH_TRACKING, module_object, "DISABLE_LINK_PATH_TRACKING"); + yr_set_integer( + DISABLE_KNOWN_FOLDER_TRACKING, module_object, "DISABLE_KNOWN_FOLDER_TRACKING"); + yr_set_integer( + DISABLE_KNOWN_FOLDER_ALIAS, module_object, "DISABLE_KNOWN_FOLDER_ALIAS"); + yr_set_integer(ALLOW_LINK_TO_LINK, module_object, "ALLOW_LINK_TO_LINK"); + yr_set_integer(UNALIAS_ON_SAVE, module_object, "UNALIAS_ON_SAVE"); + yr_set_integer(PREFER_ENVIRONMENT_PATH, module_object, "PREFER_ENVIRONMENT_PATH"); + yr_set_integer( + KEEP_LOCAL_ID_LIST_FOR_UNC_TARGET, + module_object, + "KEEP_LOCAL_ID_LIST_FOR_UNC_TARGET"); + + yr_set_integer( + FILE_ATTRIBUTE_READONLY, module_object, "FILE_ATTRIBUTE_READONLY"); + yr_set_integer(FILE_ATTRIBUTE_HIDDEN, module_object, "FILE_ATTRIBUTE_HIDDEN"); + yr_set_integer(FILE_ATTRIBUTE_SYSTEM, module_object, "FILE_ATTRIBUTE_SYSTEM"); + yr_set_integer(RESERVED_1, module_object, "RESERVED_1"); + yr_set_integer( + FILE_ATTRIBUTE_DIRECTORY, module_object, "FILE_ATTRIBUTE_DIRECTORY"); + yr_set_integer(FILE_ATTRIBUTE_ARCHIVE, module_object, "FILE_ATTRIBUTE_ARCHIVE"); + yr_set_integer(RESERVED_2, module_object, "RESERVED_2"); + yr_set_integer(FILE_ATTRIBUTE_NORMAL, module_object, "FILE_ATTRIBUTE_NORMAL"); + yr_set_integer( + FILE_ATTRIBUTE_TEMPORARY, module_object, "FILE_ATTRIBUTE_TEMPORARY"); + yr_set_integer( + FILE_ATTRIBUTE_SPARSE_FILE, module_object, "FILE_ATTRIBUTE_SPARSE_FILE"); + yr_set_integer( + FILE_ATTRIBUTE_REPARSE_POINT, + module_object, + "FILE_ATTRIBUTE_REPARSE_POINT"); + yr_set_integer( + FILE_ATTRIBUTE_COMPRESSED, module_object, "FILE_ATTRIBUTE_COMPRESSED"); + yr_set_integer(FILE_ATTRIBUTE_OFFLINE, module_object, "FILE_ATTRIBUTE_OFFLINE"); + yr_set_integer( + FILE_ATTRIBUTE_NOT_CONTENT_INDEXED, + module_object, + "FILE_ATTRIBUTE_NOT_CONTENT_INDEXED"); + yr_set_integer( + FILE_ATTRIBUTE_ENCRYPTED, module_object, "FILE_ATTRIBUTE_ENCRYPTED"); + + yr_set_integer(SW_SHOWNORMAL, module_object, "SW_SHOWNORMAL"); + yr_set_integer(SW_SHOWMAXIMIZED, module_object, "SW_SHOWMAXIMIZED"); + yr_set_integer(SW_SHOWMINNOACTIVE, module_object, "SW_SHOWMINNOACTIVE"); + + yr_set_integer(HOTKEYF_SHIFT, module_object, "HOTKEYF_SHIFT"); + yr_set_integer(HOTKEYF_CONTROL, module_object, "HOTKEYF_CONTROL"); + yr_set_integer(HOTKEYF_ALT, module_object, "HOTKEYF_ALT"); + + yr_set_integer( + VOLUME_ID_AND_LOCAL_BASE_PATH, module_object, "VOLUME_ID_AND_LOCAL_BASE_PATH"); + yr_set_integer( + COMMON_NETWORK_RELATIVE_LINK_AND_PATH_SUFFIX, + module_object, + "COMMON_NETWORK_RELATIVE_LINK_AND_PATH_SUFFIX"); + + yr_set_integer(DRIVE_UNKNOWN, module_object, "DRIVE_UNKNOWN"); + yr_set_integer(DRIVE_NO_ROOT_DIR, module_object, "DRIVE_NO_ROOT_DIR"); + yr_set_integer(DRIVE_REMOVABLE, module_object, "DRIVE_REMOVABLE"); + yr_set_integer(DRIVE_FIXED, module_object, "DRIVE_FIXED"); + yr_set_integer(DRIVE_REMOTE, module_object, "DRIVE_REMOTE"); + yr_set_integer(DRIVE_CDROM, module_object, "DRIVE_CDROM"); + yr_set_integer(DRIVE_RAMDISK, module_object, "DRIVE_RAMDISK"); + + yr_set_integer(VALID_DEVICE, module_object, "VALID_DEVICE"); + yr_set_integer(VALID_NET_TYPE, module_object, "VALID_NET_TYPE"); + + yr_set_integer(WNNC_NET_AVID, module_object, "WNNC_NET_AVID"); + yr_set_integer(WNNC_NET_DOCUSPACE, module_object, "WNNC_NET_DOCUSPACE"); + yr_set_integer(WNNC_NET_MANGOSOFT, module_object, "WNNC_NET_MANGOSOFT"); + yr_set_integer(WNNC_NET_SERNET, module_object, "WNNC_NET_SERNET"); + yr_set_integer(WNNC_NET_RIVERFRONT1, module_object, "WNNC_NET_RIVERFRONT1"); + yr_set_integer(WNNC_NET_RIVERFRONT2, module_object, "WNNC_NET_RIVERFRONT2"); + yr_set_integer(WNNC_NET_DECORB, module_object, "WNNC_NET_DECORB"); + yr_set_integer(WNNC_NET_PROTSTOR, module_object, "WNNC_NET_PROTSTOR"); + yr_set_integer(WNNC_NET_FJ_REDIR, module_object, "WNNC_NET_FJ_REDIR"); + yr_set_integer(WNNC_NET_DISTINCT, module_object, "WNNC_NET_DISTINCT"); + yr_set_integer(WNNC_NET_TWINS, module_object, "WNNC_NET_TWINS"); + yr_set_integer(WNNC_NET_RDR2SAMPLE, module_object, "WNNC_NET_RDR2SAMPLE"); + yr_set_integer(WNNC_NET_CSC, module_object, "WNNC_NET_CSC"); + yr_set_integer(WNNC_NET_3IN1, module_object, "WNNC_NET_3IN1"); + yr_set_integer(WNNC_NET_EXTENDNET, module_object, "WNNC_NET_EXTENDNET"); + yr_set_integer(WNNC_NET_STAC, module_object, "WNNC_NET_STAC"); + yr_set_integer(WNNC_NET_FOXBAT, module_object, "WNNC_NET_FOXBAT"); + yr_set_integer(WNNC_NET_YAHOO, module_object, "WNNC_NET_YAHOO"); + yr_set_integer(WNNC_NET_EXIFS, module_object, "WNNC_NET_EXIFS"); + yr_set_integer(WNNC_NET_DAV, module_object, "WNNC_NET_DAV"); + yr_set_integer(WNNC_NET_KNOWARE, module_object, "WNNC_NET_KNOWARE"); + yr_set_integer(WNNC_NET_OBJECT_DIRE, module_object, "WNNC_NET_OBJECT_DIRE"); + yr_set_integer(WNNC_NET_MASFAX, module_object, "WNNC_NET_MASFAX"); + yr_set_integer(WNNC_NET_HOB_NFS, module_object, "WNNC_NET_HOB_NFS"); + yr_set_integer(WNNC_NET_SHIVA, module_object, "WNNC_NET_SHIVA"); + yr_set_integer(WNNC_NET_IBMAL, module_object, "WNNC_NET_IBMAL"); + yr_set_integer(WNNC_NET_LOCK, module_object, "WNNC_NET_LOCK"); + yr_set_integer(WNNC_NET_TERMSRV, module_object, "WNNC_NET_TERMSRV"); + yr_set_integer(WNNC_NET_SRT, module_object, "WNNC_NET_SRT"); + yr_set_integer(WNNC_NET_QUINCY, module_object, "WNNC_NET_QUINCY"); + yr_set_integer(WNNC_NET_OPENAFS, module_object, "WNNC_NET_OPENAFS"); + yr_set_integer(WNNC_NET_AVID1, module_object, "WNNC_NET_AVID1"); + yr_set_integer(WNNC_NET_DFS, module_object, "WNNC_NET_DFS"); + yr_set_integer(WNNC_NET_KWNP, module_object, "WNNC_NET_KWNP"); + yr_set_integer(WNNC_NET_ZENWORKS, module_object, "WNNC_NET_ZENWORKS"); + yr_set_integer(WNNC_NET_DRIVEONWEB, module_object, "WNNC_NET_DRIVEONWEB"); + yr_set_integer(WNNC_NET_VMWARE, module_object, "WNNC_NET_VMWARE"); + yr_set_integer(WNNC_NET_RSFX, module_object, "WNNC_NET_RSFX"); + yr_set_integer(WNNC_NET_MFILES, module_object, "WNNC_NET_MFILES"); + yr_set_integer(WNNC_NET_MS_NFS, module_object, "WNNC_NET_MS_NFS"); + yr_set_integer(WNNC_NET_GOOGLE, module_object, "WNNC_NET_GOOGLE"); + + yr_set_integer(FOREGROUND_BLUE, module_object, "FOREGROUND_BLUE"); + yr_set_integer(FOREGROUND_GREEN, module_object, "FOREGROUND_GREEN"); + yr_set_integer(FOREGROUND_RED, module_object, "FOREGROUND_RED"); + yr_set_integer(FOREGROUND_INTENSITY, module_object, "FOREGROUND_INTENSITY"); + yr_set_integer(BACKGROUND_BLUE, module_object, "BACKGROUND_BLUE"); + yr_set_integer(BACKGROUND_GREEN, module_object, "BACKGROUND_GREEN"); + yr_set_integer(BACKGROUND_RED, module_object, "BACKGROUND_RED"); + yr_set_integer(BACKGROUND_INTENSITY, module_object, "BACKGROUND_INTENSITY"); + + yr_set_integer(FF_DONTCARE, module_object, "FF_DONTCARE"); + yr_set_integer(FF_ROMAN, module_object, "FF_ROMAN"); + yr_set_integer(FF_SWISS, module_object, "FF_SWISS"); + yr_set_integer(FF_MODERN, module_object, "FF_MODERN"); + yr_set_integer(FF_SCRIPT, module_object, "FF_SCRIPT"); + yr_set_integer(FF_DECORATIVE, module_object, "FF_DECORATIVE"); + + yr_set_integer(TMPF_NONE, module_object, "TMPF_NONE"); + yr_set_integer(TMPF_FIXED_PITCH, module_object, "TMPF_FIXED_PITCH"); + yr_set_integer(TMPF_VECTOR, module_object, "TMPF_VECTOR"); + yr_set_integer(TMPF_TRUETYPE, module_object, "TMPF_TRUETYPE"); + yr_set_integer(TMPF_DEVICE, module_object, "TMPF_DEVICE"); + + const uint8_t* block_data; + uint32_t block_data_size_remaining; + char* hotkey_str; + const uint8_t* current_location; + uint32_t id_list_size; + uint32_t link_info_size; + uint32_t string_data_size; + uint32_t extra_data_block_size; + uint32_t extra_data_block_signature; + + block = first_memory_block(context); + block_data = block->fetch_data(block); + + // Keep track the amount of space in the current block we have left + // to prevent any issues when dereferencing pointers + block_data_size_remaining = (int) block->size; + + // Don't try to parse a file unless it is the minimum size an LNK can be + // based on fixed length headers it has (described in shell_link_header_t) + if (block_data != NULL && + block_data_size_remaining >= sizeof(shell_link_header_t)) + { + // Validate LNK header + lnk_header = (shell_link_header_t*) block_data; + if (lnk_header->header_size == HEADER_SIZE && + lnk_header->clsid[0] == LINK_CLSID_0 && + lnk_header->clsid[1] == LINK_CLSID_1 && + lnk_header->clsid[2] == LINK_CLSID_2 && + lnk_header->clsid[3] == LINK_CLSID_3) + { + yr_set_integer(1, module_object, "is_lnk"); + + // Convert timestamps from Windows TIMESTAMP to Unix timestamp + yr_set_integer( + convertWindowsTimeToUnixTime(lnk_header->creation_time), + module_object, + "creation_time"); + + yr_set_integer( + convertWindowsTimeToUnixTime(lnk_header->access_time), + module_object, + "access_time"); + + yr_set_integer( + convertWindowsTimeToUnixTime(lnk_header->write_time), + module_object, + "write_time"); + + yr_set_integer(lnk_header->file_size, module_object, "file_size"); + yr_set_integer(lnk_header->link_flags, module_object, "link_flags"); + yr_set_integer( + lnk_header->file_attributes_flags, + module_object, + "file_attributes_flags"); + yr_set_integer(lnk_header->icon_index, module_object, "icon_index"); + yr_set_integer(lnk_header->show_command, module_object, "show_command"); + yr_set_integer(lnk_header->hotkey_flags, module_object, "hotkey_flags"); + + // Hotkey handling code + if (lnk_header->hotkey_flags & 0xFF) + { + hotkey_str = get_hotkey_char(lnk_header->hotkey_flags & 0xFF); + + if (hotkey_str) + { + yr_set_string(hotkey_str, module_object, "hotkey"); + } + + yr_set_integer(1, module_object, "has_hotkey"); + } + + else + { + yr_set_integer(0, module_object, "has_hotkey"); + } + + yr_set_integer( + (lnk_header->hotkey_flags >> 8), + module_object, + "hotkey_modifier_flags"); + + // Set pointer of current location to be after the LNK fixed header + current_location = block_data + sizeof(shell_link_header_t); + block_data_size_remaining -= sizeof(shell_link_header_t); + + // Optional parsing of LinkTargetIDList + if (lnk_header->link_flags & HAS_LINK_TARGET_ID_LIST) + { + id_list_size = parse_link_target_id_list( + current_location, module_object, block_data_size_remaining); + + if (id_list_size == 0) + { + yr_set_integer(1, module_object, "is_malformed"); + return ERROR_SUCCESS; + } + + if (block_data_size_remaining < id_list_size) + { + yr_set_integer(1, module_object, "is_malformed"); + return ERROR_SUCCESS; + } + + current_location += id_list_size; + block_data_size_remaining -= id_list_size; + } + + if (lnk_header->link_flags & HAS_LINK_INFO) + { + link_info_size = parse_link_info( + current_location, module_object, block_data_size_remaining); + + if (link_info_size == 0) + { + yr_set_integer(1, module_object, "is_malformed"); + return ERROR_SUCCESS; + } + + if (block_data_size_remaining < link_info_size) + { + yr_set_integer(1, module_object, "is_malformed"); + return ERROR_SUCCESS; + } + + current_location += link_info_size; + block_data_size_remaining -= link_info_size; + } + + // NAME_STRING + if (lnk_header->link_flags & HAS_NAME) + { + string_data_size = parse_string_data( + current_location, + module_object, + block_data_size_remaining, + "name_string", + lnk_header->link_flags & IS_UNICODE); + + if (string_data_size == 0) + { + yr_set_integer(1, module_object, "is_malformed"); + return ERROR_SUCCESS; + } + + if (block_data_size_remaining < string_data_size) + { + yr_set_integer(1, module_object, "is_malformed"); + return ERROR_SUCCESS; + } + + current_location += string_data_size; + block_data_size_remaining -= string_data_size; + } + + // RELATIVE_PATH + if (lnk_header->link_flags & HAS_RELATIVE_PATH) + { + string_data_size = parse_string_data( + current_location, + module_object, + block_data_size_remaining, + "relative_path", + lnk_header->link_flags & IS_UNICODE); + + if (string_data_size == 0) + { + yr_set_integer(1, module_object, "is_malformed"); + return ERROR_SUCCESS; + } + + if (block_data_size_remaining < string_data_size) + { + yr_set_integer(1, module_object, "is_malformed"); + return ERROR_SUCCESS; + } + + current_location += string_data_size; + block_data_size_remaining -= string_data_size; + } + + // WORKING_DIR + if (lnk_header->link_flags & HAS_WORKING_DIR) + { + string_data_size = parse_string_data( + current_location, + module_object, + block_data_size_remaining, + "working_dir", + lnk_header->link_flags & IS_UNICODE); + + if (string_data_size == 0) + { + yr_set_integer(1, module_object, "is_malformed"); + return ERROR_SUCCESS; + } + + if (block_data_size_remaining < string_data_size) + { + yr_set_integer(1, module_object, "is_malformed"); + return ERROR_SUCCESS; + } + + current_location += string_data_size; + block_data_size_remaining -= string_data_size; + } + + // COMMAND_LINK_ARGUMENTS + if (lnk_header->link_flags & HAS_ARGUMENTS) + { + string_data_size = parse_string_data( + current_location, + module_object, + block_data_size_remaining, + "command_line_arguments", + lnk_header->link_flags & IS_UNICODE); + + if (string_data_size == 0) + { + yr_set_integer(1, module_object, "is_malformed"); + return ERROR_SUCCESS; + } + + if (block_data_size_remaining < string_data_size) + { + yr_set_integer(1, module_object, "is_malformed"); + return ERROR_SUCCESS; + } + + current_location += string_data_size; + block_data_size_remaining -= string_data_size; + } + + // ICON_LOCATION + if (lnk_header->link_flags & HAS_ICON_LOCATION) + { + string_data_size = parse_string_data( + current_location, + module_object, + block_data_size_remaining, + "icon_location", + lnk_header->link_flags & IS_UNICODE); + + if (string_data_size == 0) + { + yr_set_integer(1, module_object, "is_malformed"); + return ERROR_SUCCESS; + } + + if (block_data_size_remaining < string_data_size) + { + yr_set_integer(1, module_object, "is_malformed"); + return ERROR_SUCCESS; + } + + current_location += string_data_size; + block_data_size_remaining -= string_data_size; + } + + yr_set_integer(0, module_object, "has_console_data"); + yr_set_integer(0, module_object, "has_console_fe_data"); + yr_set_integer(0, module_object, "has_darwin_data"); + yr_set_integer(0, module_object, "has_environment_variable_data"); + yr_set_integer(0, module_object, "has_icon_environment_data"); + yr_set_integer(0, module_object, "has_known_folder_data"); + yr_set_integer(0, module_object, "has_property_store_data"); + yr_set_integer(0, module_object, "has_shim_data"); + yr_set_integer(0, module_object, "has_special_folder_data"); + yr_set_integer(0, module_object, "has_tracker_data"); + yr_set_integer(0, module_object, "has_vista_and_above_id_list_data"); + + // Parse ExtraData + if (block_data_size_remaining > 0) + { + if (block_data_size_remaining < sizeof(extra_data_block_size)) + { + yr_set_integer(1, module_object, "is_malformed"); + return ERROR_SUCCESS; + } + + memcpy( + &extra_data_block_size, + current_location, + sizeof(extra_data_block_size)); + current_location += sizeof(extra_data_block_size); + + // The TerminalBlock must be less than 0x04, so iterate until we find it + // (or run out of space) + while (extra_data_block_size >= 0x04) + { + // Only do this in the loop so we don't overshoot the end of the block + block_data_size_remaining -= sizeof(extra_data_block_size); + + if (block_data_size_remaining < sizeof(extra_data_block_signature)) + { + yr_set_integer(1, module_object, "is_malformed"); + return ERROR_SUCCESS; + } + + memcpy( + &extra_data_block_signature, + current_location, + sizeof(extra_data_block_signature)); + current_location += sizeof(extra_data_block_signature); + block_data_size_remaining -= sizeof(extra_data_block_signature); + + if (!parse_extra_block( + current_location, + module_object, + block_data_size_remaining, + extra_data_block_size, + extra_data_block_signature)) + { + break; + } + + // Don't add/take away the block size + signature, as those have + // already been dealt with + current_location += extra_data_block_size - + sizeof(extra_data_block_size) - + sizeof(extra_data_block_signature); + block_data_size_remaining -= extra_data_block_size - + sizeof(extra_data_block_size) - + sizeof(extra_data_block_signature); + + if (block_data_size_remaining < sizeof(extra_data_block_size)) + { + yr_set_integer(1, module_object, "is_malformed"); + return ERROR_SUCCESS; + } + + memcpy( + &extra_data_block_size, + current_location, + sizeof(extra_data_block_size)); + current_location += sizeof(extra_data_block_size); + } + + // Finally, take away size of the TerminalBlock + block_data_size_remaining -= 4; + } + + if (block_data_size_remaining > 0) + { + yr_set_integer(1, module_object, "has_overlay"); + yr_set_integer( + block->size - block_data_size_remaining, + module_object, + "overlay_offset"); + } + + else + { + yr_set_integer(0, module_object, "has_overlay"); + } + + // If all parsing successful, say that the LNK isn't malformed + yr_set_integer(0, module_object, "is_malformed"); + } + } + + return ERROR_SUCCESS; +} + +int module_unload(YR_OBJECT* module_object) +{ + return ERROR_SUCCESS; +} diff --git a/libyara/modules/lnk/lnk_utils.c b/libyara/modules/lnk/lnk_utils.c new file mode 100644 index 00000000..4253f16c --- /dev/null +++ b/libyara/modules/lnk/lnk_utils.c @@ -0,0 +1,277 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +uint64_t convertWindowsTimeToUnixTime(uint64_t input) +{ + // https://stackoverflow.com/questions/6161776/convert-windows-filetime-to-second-in-unix-linux + long long int temp; + temp = input / TICKS_PER_SECOND; // convert from 100ns intervals to seconds; + temp = temp - EPOCH_DIFFERENCE; // subtract number of seconds between epochs + return temp; +} + +char* get_hotkey_char(uint8_t key) +{ + char key_str[64]; + key_str[0] = '\0'; + + switch (key) + { + case 0x30: + sprintf(key_str, "0"); + break; + + case 0x31: + sprintf(key_str, "1"); + break; + + case 0x32: + sprintf(key_str, "2"); + break; + + case 0x33: + sprintf(key_str, "3"); + break; + + case 0x34: + sprintf(key_str, "4"); + break; + + case 0x35: + sprintf(key_str, "5"); + break; + + case 0x36: + sprintf(key_str, "6"); + break; + + case 0x37: + sprintf(key_str, "7"); + break; + + case 0x38: + sprintf(key_str, "8"); + break; + + case 0x39: + sprintf(key_str, "9"); + break; + + case 0x41: + sprintf(key_str, "A"); + break; + + case 0x42: + sprintf(key_str, "B"); + break; + + case 0x43: + sprintf(key_str, "C"); + break; + + case 0x44: + sprintf(key_str, "D"); + break; + + case 0x45: + sprintf(key_str, "E"); + break; + + case 0x46: + sprintf(key_str, "F"); + break; + + case 0x47: + sprintf(key_str, "G"); + break; + + case 0x48: + sprintf(key_str, "H"); + break; + + case 0x49: + sprintf(key_str, "I"); + break; + + case 0x4A: + sprintf(key_str, "J"); + break; + + case 0x4B: + sprintf(key_str, "K"); + break; + + case 0x4C: + sprintf(key_str, "L"); + break; + + case 0x4D: + sprintf(key_str, "M"); + break; + + case 0x4E: + sprintf(key_str, "N"); + break; + + case 0x4F: + sprintf(key_str, "O"); + break; + + case 0x50: + sprintf(key_str, "P"); + break; + + case 0x51: + sprintf(key_str, "Q"); + break; + + case 0x52: + sprintf(key_str, "R"); + break; + + case 0x53: + sprintf(key_str, "S"); + break; + + case 0x54: + sprintf(key_str, "T"); + break; + + case 0x55: + sprintf(key_str, "U"); + break; + + case 0x56: + sprintf(key_str, "V"); + break; + + case 0x57: + sprintf(key_str, "W"); + break; + + case 0x58: + sprintf(key_str, "X"); + break; + + case 0x59: + sprintf(key_str, "Y"); + break; + + case 0x5A: + sprintf(key_str, "Z"); + break; + + case 0x70: + sprintf(key_str, "F1"); + break; + + case 0x71: + sprintf(key_str, "F2"); + break; + + case 0x72: + sprintf(key_str, "F3"); + break; + + case 0x73: + sprintf(key_str, "F4"); + break; + + case 0x74: + sprintf(key_str, "F5"); + break; + + case 0x75: + sprintf(key_str, "F6"); + break; + + case 0x76: + sprintf(key_str, "F7"); + break; + + case 0x77: + sprintf(key_str, "F8"); + break; + + case 0x78: + sprintf(key_str, "F9"); + break; + + case 0x79: + sprintf(key_str, "F10"); + break; + + case 0x7A: + sprintf(key_str, "F11"); + break; + + case 0x7B: + sprintf(key_str, "F12"); + break; + + case 0x7C: + sprintf(key_str, "F13"); + break; + + case 0x7D: + sprintf(key_str, "F14"); + break; + + case 0x7E: + sprintf(key_str, "F15"); + break; + + case 0x7F: + sprintf(key_str, "F16"); + break; + + case 0x80: + sprintf(key_str, "F17"); + break; + + case 0x81: + sprintf(key_str, "F18"); + break; + + case 0x82: + sprintf(key_str, "F19"); + break; + + case 0x83: + sprintf(key_str, "F20"); + break; + + case 0x84: + sprintf(key_str, "F21"); + break; + + case 0x85: + sprintf(key_str, "F22"); + break; + + case 0x86: + sprintf(key_str, "F23"); + break; + + case 0x87: + sprintf(key_str, "F24"); + break; + + case 0x90: + sprintf(key_str, "NUM LOCK"); + break; + + case 0x91: + sprintf(key_str, "SCROLL LOCK"); + break; + } + + return yr_strdup(key_str); +} \ No newline at end of file diff --git a/libyara/modules/module_list b/libyara/modules/module_list index 35e848ae..d881f874 100644 --- a/libyara/modules/module_list +++ b/libyara/modules/module_list @@ -5,6 +5,7 @@ MODULE(math) MODULE(time) MODULE(console) MODULE(string) +MODULE(lnk) #ifdef DOTNET_MODULE MODULE(dotnet) @@ -32,4 +33,4 @@ MODULE(dex) #ifdef PB_TESTS_MODULE MODULE(pb_tests) -#endif +#endif \ No newline at end of file diff --git a/tests/BUILD.bazel b/tests/BUILD.bazel index 46c26f2a..fad5d5f3 100644 --- a/tests/BUILD.bazel +++ b/tests/BUILD.bazel @@ -278,3 +278,22 @@ cc_test( "@//:libyara", ], ) + +cc_test( + name = "test_lnk", + srcs = ["test-lnk.c"], + copts = COPTS, + data = [ + "data/lnk-standard", + "data/lnk-extradata-1", + "data/lnk-extradata-2", + "data/lnk-malformed", + "data/lnk-network", + "data/lnk-overlay", + ], + linkstatic = True, + deps = [ + ":util", + "@//:libyara", + ], +) \ No newline at end of file diff --git a/tests/data/lnk-extradata-1 b/tests/data/lnk-extradata-1 new file mode 100644 index 00000000..137b750f Binary files /dev/null and b/tests/data/lnk-extradata-1 differ diff --git a/tests/data/lnk-extradata-2 b/tests/data/lnk-extradata-2 new file mode 100644 index 00000000..489e46b3 Binary files /dev/null and b/tests/data/lnk-extradata-2 differ diff --git a/tests/data/lnk-malformed b/tests/data/lnk-malformed new file mode 100644 index 00000000..35b48f1a Binary files /dev/null and b/tests/data/lnk-malformed differ diff --git a/tests/data/lnk-network b/tests/data/lnk-network new file mode 100644 index 00000000..10fa6476 Binary files /dev/null and b/tests/data/lnk-network differ diff --git a/tests/data/lnk-overlay b/tests/data/lnk-overlay new file mode 100644 index 00000000..49258c60 Binary files /dev/null and b/tests/data/lnk-overlay differ diff --git a/tests/data/lnk-standard b/tests/data/lnk-standard new file mode 100644 index 00000000..1b710966 Binary files /dev/null and b/tests/data/lnk-standard differ diff --git a/tests/test-lnk.c b/tests/test-lnk.c new file mode 100644 index 00000000..cb6034d7 --- /dev/null +++ b/tests/test-lnk.c @@ -0,0 +1,505 @@ +#include +#include +#include + +#include "util.h" + +int main(int argc, char** argv) +{ + int result = 0; + + YR_DEBUG_INITIALIZE(); + YR_DEBUG_FPRINTF(1, stderr, "+ %s() { // in %s\n", __FUNCTION__, argv[0]); + + init_top_srcdir(); + + yr_initialize(); + + assert_true_rule_file( + "import \"lnk\" \ + rule is_lnk { \ + condition: \ + lnk.is_lnk \ + }", + "tests/data/lnk-standard"); + + assert_true_rule_file( + "import \"lnk\" \ + rule creation_time { \ + condition: \ + lnk.creation_time == 1221251237 \ + }", + "tests/data/lnk-standard"); + + assert_true_rule_file( + "import \"lnk\" \ + rule access_time { \ + condition: \ + lnk.access_time == 1221251237 \ + }", + "tests/data/lnk-standard"); + + assert_true_rule_file( + "import \"lnk\" \ + rule write_time { \ + condition: \ + lnk.write_time == 1221251237 \ + }", + "tests/data/lnk-standard"); + + assert_true_rule_file( + "import \"lnk\" \ + rule has_link_target_id_list { \ + condition: \ + lnk.link_flags & lnk.HAS_LINK_TARGET_ID_LIST \ + }", + "tests/data/lnk-standard"); + + assert_true_rule_file( + "import \"lnk\" \ + rule has_link_info { \ + condition: \ + lnk.link_flags & lnk.HAS_LINK_INFO \ + }", + "tests/data/lnk-standard"); + + assert_false_rule_file( + "import \"lnk\" \ + rule has_name { \ + condition: \ + lnk.link_flags & lnk.HAS_NAME \ + }", + "tests/data/lnk-standard"); + + assert_true_rule_file( + "import \"lnk\" \ + rule file_attribute_archive { \ + condition: \ + lnk.file_attributes_flags & lnk.FILE_ATTRIBUTE_ARCHIVE \ + }", + "tests/data/lnk-standard"); + + assert_false_rule_file( + "import \"lnk\" \ + rule file_attribute_readonly { \ + condition: \ + lnk.file_attributes_flags & lnk.FILE_ATTRIBUTE_READONLY \ + }", + "tests/data/lnk-standard"); + + assert_true_rule_file( + "import \"lnk\" \ + rule show_command { \ + condition: \ + lnk.show_command == lnk.SW_SHOWNORMAL \ + }", + "tests/data/lnk-standard"); + + assert_true_rule_file( + "import \"lnk\" \ + rule no_hotkey { \ + condition: \ + not lnk.has_hotkey and not defined lnk.hotkey \ + }", + "tests/data/lnk-standard"); + + assert_true_rule_file( + "import \"lnk\" \ + rule item_id_list_size { \ + condition: \ + lnk.link_target_id_list.item_id_list_size == 0xBD \ + }", + "tests/data/lnk-standard"); + + assert_true_rule_file( + "import \"lnk\" \ + rule number_of_item_ids { \ + condition: \ + lnk.link_target_id_list.number_of_item_ids == 4 \ + }", + "tests/data/lnk-standard"); + + assert_true_rule_file( + "import \"lnk\" \ + rule item_id_first_element { \ + condition: \ + lnk.link_target_id_list.item_id_list[0].size == 0x12 and lnk.link_target_id_list.item_id_list[0].data == \"\\x1fP\\xe0O\\xd0 \\xea:i\\x10\\xa2\\xd8\\x08\\x00+00\\x9d\" \ + }", + "tests/data/lnk-standard"); + + assert_true_rule_file( + "import \"lnk\" \ + rule item_id_first_element { \ + condition: \ + lnk.link_target_id_list.item_id_list[3].size == 0x46 and lnk.link_target_id_list.item_id_list[3].data == \"2\\x00\\x00\\x00\\x00\\x00,9i\\xa3 \\x00a.txt\\x004\\x00\\x07\\x00\\x04\\x00\\xef\\xbe,9i\\xa3,9i\\xa3&\\x00\\x00\\x00-n\\x00\\x00\\x00\\x00\\x96\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00a\\x00.\\x00t\\x00x\\x00t\\x00\\x00\\x00\\x14\\x00\" \ + }", + "tests/data/lnk-standard"); + + assert_true_rule_file( + "import \"lnk\" \ + rule link_info_size { \ + condition: \ + lnk.link_info.size == 0x3C \ + }", + "tests/data/lnk-standard"); + + assert_true_rule_file( + "import \"lnk\" \ + rule link_info_header_size { \ + condition: \ + lnk.link_info.header_size == 0x1C \ + }", + "tests/data/lnk-standard"); + + assert_true_rule_file( + "import \"lnk\" \ + rule link_info_flags { \ + condition: \ + lnk.link_info.flags & lnk.VOLUME_ID_AND_LOCAL_BASE_PATH \ + }", + "tests/data/lnk-standard"); + + assert_true_rule_file( + "import \"lnk\" \ + rule volume_id_offset { \ + condition: \ + lnk.link_info.volume_id_offset == 0x1C \ + }", + "tests/data/lnk-standard"); + + assert_true_rule_file( + "import \"lnk\" \ + rule has_volume_id { \ + condition: \ + lnk.link_info.has_volume_id \ + }", + "tests/data/lnk-standard"); + + assert_true_rule_file( + "import \"lnk\" \ + rule local_base_path_offset { \ + condition: \ + lnk.link_info.local_base_path_offset == 0x2D \ + }", + "tests/data/lnk-standard"); + + assert_true_rule_file( + "import \"lnk\" \ + rule common_network_relative_link_offset { \ + condition: \ + lnk.link_info.common_network_relative_link_offset == 0 \ + }", + "tests/data/lnk-standard"); + + assert_true_rule_file( + "import \"lnk\" \ + rule common_path_suffix_offset { \ + condition: \ + lnk.link_info.common_path_suffix_offset == 0x3B \ + }", + "tests/data/lnk-standard"); + + assert_true_rule_file( + "import \"lnk\" \ + rule volume_id_size { \ + condition: \ + lnk.link_info.volume_id.size == 17 \ + }", + "tests/data/lnk-standard"); + + assert_true_rule_file( + "import \"lnk\" \ + rule drive_type { \ + condition: \ + lnk.link_info.volume_id.drive_type & lnk.DRIVE_FIXED \ + }", + "tests/data/lnk-standard"); + + assert_true_rule_file( + "import \"lnk\" \ + rule drive_serial_number { \ + condition: \ + lnk.link_info.volume_id.drive_serial_number == 0x307A8A81 \ + }", + "tests/data/lnk-standard"); + + assert_true_rule_file( + "import \"lnk\" \ + rule volume_label_offset { \ + condition: \ + lnk.link_info.volume_id.volume_label_offset == 0x10 \ + }", + "tests/data/lnk-standard"); + + assert_true_rule_file( + "import \"lnk\" \ + rule volume_id_data { \ + condition: \ + lnk.link_info.volume_id.data == \"\\x00\" \ + }", + "tests/data/lnk-standard"); + + assert_true_rule_file( + "import \"lnk\" \ + rule local_base_path { \ + condition: \ + lnk.link_info.local_base_path == \"C:\\\\test\\\\a.txt\" \ + }", + "tests/data/lnk-standard"); + + assert_true_rule_file( + "import \"lnk\" \ + rule common_path_suffix { \ + condition: \ + lnk.link_info.common_path_suffix == \"\\x00\" \ + }", + "tests/data/lnk-standard"); + + assert_true_rule_file( + "import \"lnk\" \ + rule relative_path { \ + condition: \ + lnk.relative_path == \".\\x00\\\\\\x00a\\x00.\\x00t\\x00x\\x00t\\x00\" \ + }", + "tests/data/lnk-standard"); + + assert_true_rule_file( + "import \"lnk\" \ + rule working_dir { \ + condition: \ + lnk.working_dir == \"C\\x00:\\x00\\\\\\x00t\\x00e\\x00s\\x00t\\x00\" \ + }", + "tests/data/lnk-standard"); + + assert_true_rule_file( + "import \"lnk\" \ + rule has_tracker_data { \ + condition: \ + lnk.has_tracker_data \ + }", + "tests/data/lnk-standard"); + + assert_true_rule_file( + "import \"lnk\" \ + rule tracker_data_block_size { \ + condition: \ + lnk.tracker_data.block_size == 0x60 \ + }", + "tests/data/lnk-standard"); + + assert_true_rule_file( + "import \"lnk\" \ + rule tracker_data_block_signature { \ + condition: \ + lnk.tracker_data.block_signature == 0xA0000003 \ + }", + "tests/data/lnk-standard"); + + assert_true_rule_file( + "import \"lnk\" \ + rule machine_id { \ + condition: \ + lnk.tracker_data.machine_id == \"chris-xps\" \ + }", + "tests/data/lnk-standard"); + + assert_true_rule_file( + "import \"lnk\" \ + rule droid_volume_identifier { \ + condition: \ + lnk.tracker_data.droid_volume_identifier == \"\\x40\\x78\\xC7\\x94\\x47\\xFA\\xC7\\x46\\xB3\\x56\\x5C\\x2D\\xC6\\xB6\\xD1\\x15\" \ + }", + "tests/data/lnk-standard"); + + assert_true_rule_file( + "import \"lnk\" \ + rule droid_file_identifier { \ + condition: \ + lnk.tracker_data.droid_file_identifier == \"\\xEC\\x46\\xCD\\x7B\\x22\\x7F\\xDD\\x11\\x94\\x99\\x00\\x13\\x72\\x16\\x87\\x4A\" \ + }", + "tests/data/lnk-standard"); + + assert_true_rule_file( + "import \"lnk\" \ + rule droid_birth_volume_identifier { \ + condition: \ + lnk.tracker_data.droid_birth_volume_identifier == \"\\x40\\x78\\xC7\\x94\\x47\\xFA\\xC7\\x46\\xB3\\x56\\x5C\\x2D\\xC6\\xB6\\xD1\\x15\" \ + }", + "tests/data/lnk-standard"); + + assert_true_rule_file( + "import \"lnk\" \ + rule droid_birth_file_identifier { \ + condition: \ + lnk.tracker_data.droid_birth_file_identifier == \"\\xEC\\x46\\xCD\\x7B\\x22\\x7F\\xDD\\x11\\x94\\x99\\x00\\x13\\x72\\x16\\x87\\x4A\" \ + }", + "tests/data/lnk-standard"); + + assert_true_rule_file( + "import \"lnk\" \ + rule common_network_relative_link_flags { \ + condition: \ + lnk.link_info.common_network_relative_link.flags & lnk.VALID_NET_TYPE \ + }", + "tests/data/lnk-network"); + + assert_true_rule_file( + "import \"lnk\" \ + rule net_name { \ + condition: \ + lnk.link_info.common_network_relative_link.net_name == \"\\\\\\\\localhost\\\\c$\\\\Users\\\\yarac\\\\Documents\\\\testing\" \ + }", + "tests/data/lnk-network"); + + assert_true_rule_file( + "import \"lnk\" \ + rule device_name { \ + condition: \ + lnk.link_info.common_network_relative_link.device_name == \"Z:\" \ + }", + "tests/data/lnk-network"); + + // VolumeID has been set from 0x11 to an impossibly large value 0xFFFFFFFF + assert_true_rule_file( + "import \"lnk\" \ + rule malformed_lnk { \ + condition: \ + lnk.is_malformed \ + }", + "tests/data/lnk-malformed"); + + assert_true_rule_file( + "import \"lnk\" \ + rule overlay_lnk { \ + condition: \ + lnk.has_overlay and lnk.overlay_offset == 0x1CB \ + }", + "tests/data/lnk-overlay"); + + assert_true_rule_file( + "import \"lnk\" \ + rule has_console_data { \ + condition: \ + lnk.has_console_data \ + }", + "tests/data/lnk-extradata-1"); + + assert_true_rule_file( + "import \"lnk\" \ + rule number_of_history_buffers { \ + condition: \ + lnk.console_data.number_of_history_buffers == 4 \ + }", + "tests/data/lnk-extradata-1"); + + assert_true_rule_file( + "import \"lnk\" \ + rule color_table { \ + condition: \ + lnk.console_data.color_table[15] == 0x00ffffff \ + }", + "tests/data/lnk-extradata-1"); + + assert_true_rule_file( + "import \"lnk\" \ + rule screen_buffer_size { \ + condition: \ + lnk.console_data.screen_buffer_size_x == 120 and lnk.console_data.screen_buffer_size_y == 3000 \ + }", + "tests/data/lnk-extradata-1"); + + assert_true_rule_file( + "import \"lnk\" \ + rule window_size { \ + condition: \ + lnk.console_data.window_size_x == 120 and lnk.console_data.window_size_y == 50 \ + }", + "tests/data/lnk-extradata-1"); + + assert_true_rule_file( + "import \"lnk\" \ + rule window_origin { \ + condition: \ + lnk.console_data.window_origin_x == 0 and lnk.console_data.window_origin_y == 0 \ + }", + "tests/data/lnk-extradata-1"); + + assert_true_rule_file( + "import \"lnk\" \ + rule has_environment_variable_data { \ + condition: \ + lnk.has_environment_variable_data \ + }", + "tests/data/lnk-extradata-1"); + + assert_true_rule_file( + "import \"lnk\" \ + rule environment_variable_data_ansi { \ + condition: \ + lnk.environment_variable_data.target_ansi == \"%SystemRoot%\\\\sysWOW64\\\\WindowsPowerShell\\\\v1.0\\\\powershell.exe\" \ + }", + "tests/data/lnk-extradata-1"); + + assert_true_rule_file( + "import \"lnk\" \ + rule has_special_folder_data { \ + condition: \ + lnk.has_special_folder_data \ + }", + "tests/data/lnk-extradata-1"); + + assert_true_rule_file( + "import \"lnk\" \ + rule special_folder_data { \ + condition: \ + lnk.special_folder_data.special_folder_id == 36 and lnk.special_folder_data.offset == 105 \ + }", + "tests/data/lnk-extradata-1"); + + assert_true_rule_file( + "import \"lnk\" \ + rule icon_environment_data_target_ansi { \ + condition: \ + lnk.icon_environment_data.target_ansi == \"%ProgramFiles%\\\\PeaZip\\\\res\\\\icons\\\\peazip_new.icl\" \ + }", + "tests/data/lnk-extradata-2"); + + assert_true_rule_file( + "import \"lnk\" \ + rule has_icon_environment_data { \ + condition: \ + lnk.has_icon_environment_data \ + }", + "tests/data/lnk-extradata-2"); + + assert_true_rule_file( + "import \"lnk\" \ + rule has_known_folder_data { \ + condition: \ + lnk.has_known_folder_data \ + }", + "tests/data/lnk-extradata-2"); + + assert_true_rule_file( + "import \"lnk\" \ + rule known_folder_offset { \ + condition: \ + lnk.known_folder_data.offset == 177 \ + }", + "tests/data/lnk-extradata-2"); + + assert_true_rule_file( + "import \"lnk\" \ + rule known_folder_id { \ + condition: \ + lnk.known_folder_data.known_folder_id[15] == 142 \ + }", + "tests/data/lnk-extradata-2"); + + yr_finalize(); + + YR_DEBUG_FPRINTF( + 1, stderr, "} = %d // %s() in %s\n", result, __FUNCTION__, argv[0]); + + return result; +} diff --git a/windows/vs2015/libyara/libyara.vcxproj b/windows/vs2015/libyara/libyara.vcxproj index cb78879a..85042c9a 100644 --- a/windows/vs2015/libyara/libyara.vcxproj +++ b/windows/vs2015/libyara/libyara.vcxproj @@ -244,6 +244,8 @@ + + diff --git a/windows/vs2017/libyara/libyara.vcxproj b/windows/vs2017/libyara/libyara.vcxproj index 5511c70e..dce54ccc 100644 --- a/windows/vs2017/libyara/libyara.vcxproj +++ b/windows/vs2017/libyara/libyara.vcxproj @@ -243,6 +243,8 @@ + +