搜索全站

输入至少 2 个字符,查找相关内容。

↑↓ 选择 Enter 打开Esc 关闭

LNK 快捷方式与 Jump List 跳转列表

.lnk 是 Windows 快捷方式文件,取证价值却远高于它的文件体积。一个通常只有 1–2 KB 的二进制文件,往往同时记录了目标文件的三个时间戳、目标所在卷的序列号、目标绝对路径、源机器的 NetBIOS 名,甚至源机器网卡的 MAC 地址。

最后更新 2026-10-09版本 v2.0维护 DigiForensics查看历史 1

一、概述

.lnk 是 Windows 快捷方式文件,取证价值却远高于它的文件体积。一个通常只有 1–2 KB 的二进制文件,往往同时记录了目标文件的三个时间戳、目标所在卷的序列号、目标绝对路径、源机器的 NetBIOS 名,甚至源机器网卡的 MAC 地址。

这意味着一个反直觉的事实:当目标文件本身已被删除、卷已被重新格式化、U盘已经拔走之后,快捷方式本身还独立存活,并且它携带的是「快捷方式被创建那一刻」的快照。它不只是导航,它是一份自包含的时间与来源记录。

Windows 还会在用户完全无感知的情况下批量生成 LNK:打开文档时在 %APPDATA%\Microsoft\Windows\Recent\ 写入一个 .lnk;任务栏右击菜单在 AutomaticDestinations\ 维护 Jump List;应用通过 ICustomDestinationList 写入 CustomDestinations\,其中甚至包含浏览器搜索词、VS Code 的最近工程等。

因此在数据泄露、钓鱼溯源、横向移动这几类案件里,LNK 与 Jump List 常常是唯一能同时回答「谁打开过哪个文件」和「这个文件是在哪台机器上生成、插过哪个 U 盘」的工件。这里讲的是 LNK 与 Jump List 两种工件。用户行为类注册表键见用户行为痕迹,浏览器数据库见浏览器取证。

它处于分析环节的「归属分析」路径上,位置比执行类工件更靠后——它回答的不是「执行了什么」,而是「谁做的、从哪来的」:

  • 上游依赖 用户身份确认。LNK 与 Jump List 都在用户配置文件下,AutomaticDestinations 属于当前用户,归属错了整套结论就错了,账户与 SID 判定见注册表取证。
  • 它自己 要分清三类落点。Recent\*.lnk(单文件快捷方式,携带卷序列号与 MachineID,保留较久)、Recent\AutomaticDestinations\(OLE 复合文档,每个应用一个)、Recent\CustomDestinations\(LNK 序列,格式自定义)。两类都要提取——Jump List 里的 LNK 会被条目上限淘汰,Recent 目录的单文件 LNK 保留更久,只查一类会漏掉另一半记录。
  • 下游支撑 两处。DriveSerialNumber 是与可疑介质匹配的钥匙,横向移动溯源靠它;MFU 排序反映用户对文件的实际使用频次,比单纯的访问时间更有指向性。
  • 它与执行类工件互补:Prefetch 说「程序启动过」,LNK 说「用户打开了哪个文档」——两者缺一,行为链条就不完整。

.lnk 的结构按区段划分:

区段 关键字段 取证用途
固定头 HeaderSize、LinkFlags、FileAttributes、三个目标时间戳、文件大小、图标索引 时间戳与结构存在性开关
LinkInfo LinkInfoSize、LinkInfoHeaderSize、LinkInfoFlags 见下
└ VolumeID VolumeIDSize、DriveType、DriveSerialNumber、卷标签偏移 核心:标识创建快捷方式时目标所在的卷
└ 路径 LocalBasePath(置位 0)/ NetName + CommonPathSuffix(置位 1),两者可同时置位 目标绝对路径
TrackerDataBlock MachineID 与 droid GUID 源机器标识,归属判定的关键
终端块 TerminalBlock(值小于 0x00000004,通常 0x00000000) 界定 ExtraData 结束位置

Jump List 侧:

落点 格式 关键点
AutomaticDestinations OLE 复合文档(CFB) DestList 流含 MRU/MFU 排序、固定状态位、访问时间、路径、条目编号;Win7/8 与 Win10 的首字段语义与条目长度不同(后者 130 字节)
CustomDestinations 带分类结构的 LNK 序列 以 0xBABFFBAB 结束,不是复合文档
文件名 16 位十六进制 AppID,如 1b4dd67f29cb1962.automaticDestinations-ms 文件名就是应用身份,可能是显式定义,也可能由可执行文件路径经 CRC-64 派生

DriveType 的标准值区分介质:0x02 可移动、0x03 固定磁盘、0x04 网络。0x02 或 0x04 出现在内部机的工作站 LNK 中,往往是横向移动的入口。

据此能回答的是:某个用户打开过哪些文档及其访问时间、目标文件创建时的三个时间戳、目标所在卷的序列号与介质类型、快捷方式在哪台机器上被创建(MachineID / droid GUID)、以及各应用最近访问过什么(Jump List)。

回答不了的是这些,外加两个必须先纠正的误用:

  • 「用 Win7 布局硬解 Win10 文件」会得到看似合理实则错误的结果。 Win7/8 与 Win10 的 DestList 首字段语义与条目长度不同(后者 130 字节),用错布局会导致路径字段整体错位,读出乱码却以为文件损坏。
  • 「AppID 未知」不是文件无效。 它只是缺少可读名称。报告里写「AppID 1b4dd67f29cb1962,未匹配到已知应用」是合格表述,写成「Chrome 记录」而不给出依据则不合格。
  • 文件当前是否还存在。 LNK 记录的是创建时的快照,目标文件早已删除是常态。
  • 打开的动作者。 MachineID 标识的是机器不是人,要落到具体用户仍需结合用户目录归属。
  • 打开的意图。 打开一个文件与修改它完全不同,LNK 只能证明「打开过」。
  • MFU 排序的精确语义。 它反映相对使用频次,不是精确计数,也不等于访问时间顺序。

读到这里你应该已经理解:NTFS 元数据与 FILETIME 时间基准,LNK 里的三个时间戳需要正确换算,见时间戳换算;OLE 复合文档(CFB)的结构,AutomaticDestinations 用的就是它,解析前需要先理解流与存储的关系;从十六进制手工校验文件头与 droid 字段,这是解析器出错时的兜底手段;注册表 hive 的用户归属判定,它决定了取哪个用户目录下的 Jump List;以及只读挂载后的镜像检索,Recent 与 AppData 下的目录需要按用户逐个遍历。

二、核心原理

所有 .lnk 以 76 字节固定头开始,其后是由标志位控制的可选结构。固定头本身就是一块高价值证据:

偏移 长度 字段 取证意义
0x00 4 HeaderSize 恒为 0x0000004C,可作为格式校验
0x04 16 LinkCLSID 恒为 00021401-0000-0000-C000-000000000046
0x14 4 LinkFlags 决定后续哪些结构存在
0x18 4 FileAttributes 目标属性,可识别目录/压缩/加密/离线等
0x1C 8 CreationTime 目标文件的创建时间快照
0x24 8 AccessTime 目标文件的最后访问时间快照
0x2C 8 WriteTime 目标文件的最后修改时间快照
0x34 4 FileSize 目标文件当时的大小
0x38 4 IconIndex / ShowCommand 隐藏窗口(0x07)等异常信号
0x3C 2 HotKey 通常无价值
0x3E–0x4B — Reserved1/2/3 必须为 0,非零是人为构造的信号

三个 FILETIME 是创建快捷方式那一刻从目标文件读到的 MAC 时间,不是快捷方式自身的时间。

快捷方式自身的时间要看它的 NTFS $STANDARD_INFORMATION。

这两组时间放在同一份报告里时,差值本身就是结论:若 $SI 的修改时间远晚于头里的目标修改时间,说明文件在这期间被改过。

2.2 LinkFlags:结构的存在性开关

LinkFlags 是低 27 位的位标志,从最低位(bit A)开始:

位 值 名称 存在则紧跟固定头的结构
0 0x00000001 HasLinkTargetIDList LinkTargetIDList(Shell Item 列表)
1 0x00000002 HasLinkInfo LinkInfo(卷序列号 + 路径)
2 0x00000004 HasName NAME_STRING
3 0x00000008 HasRelativePath RELATIVE_PATH
4 0x00000010 HasWorkingDir WORKING_DIR
5 0x00000020 HasArguments COMMAND_LINE_ARGUMENTS
6 0x00000040 HasIconLocation ICON_LOCATION
7 0x00000080 IsUnicode 字符串为 UTF-16LE,否则为系统 ANSI 代码页

StringData 各串的顺序是强制的:NAME_STRING → RELATIVE_PATH → WORKING_DIR → COMMAND_LINE_ARGUMENTS → ICON_LOCATION,每个串前置一个 16 位「字符数」长度前缀。跳过任何一个被置位的标志,后续所有串都会错位。

从第 8 位起是行为控制位,取证上最值得注意的是 ForceNoLinkInfo(bit 8,0x0100,忽略 LinkInfo)、HasDarwinID(bit 12,MSI 安装程序快捷方式)、ForceNoLinkTrack(bit 18,忽略 Tracker 块)与 EnableTargetMetadata(bit 19,写入属性存储)。

正常由资源管理器生成的 LNK 不会置 ForceNoLinkInfo;恶意 LNK 构建工具常置位它并抹掉卷序列号来隐藏 U 盘痕迹。

2.3 LinkInfo:卷序列号与路径

LinkInfo 开头是 LinkInfoSize(整个结构长度)、LinkInfoHeaderSize、LinkInfoFlags,以及一个可选的 4 字节 VolumeIDOffset/LocalBasePathOffset(LinkInfoHeaderSize 为 0x00000024 时存在)。LinkInfoFlags 两位定义如下:

位 值 含义
0 0x00000001 VolumeIDAndLocalBasePath
1 0x00000002 CommonNetworkRelativeLinkAndLinkInfo

置位 0 时内含 VolumeID(VolumeIDSize、DriveType、DriveSerialNumber、卷标签偏移)与 LocalBasePath / LocalBasePathUnicode;置位 1 时改为 NetName / NetNameUnicode(UNC 前缀)加 CommonPathSuffix(UNC 尾部)。两者可同时置位。

DriveSerialNumber 是 LNK 取证的核心:它标识创建快捷方式时目标所在的卷。

同一个序列号出现在某台主机上成千上万个 LNK 里,说明这些 LNK 全部指向同一个源卷——而那个卷很可能就是已拔走的 U 盘或已送修的移动硬盘。

只要拿到可疑介质,读取它的卷序列号即可完成匹配。

DriveType 用标准值区分介质:0x02 可移动、0x03 固定磁盘、0x04 网络。0x02 或 0x04 出现在一台内部机的工作站 LNK 中,往往是横向移动的入口。

2.4 TrackerDataBlock:MachineID 与 droid GUID

ExtraData 区是一串 BlockSize + BlockSignature + 载荷 的重复结构,最后跟一个 4 字节 TerminalBlock(值小于 0x00000004,通常为 0x00000000)。常见签名:

签名 块名 取证用途
0xA0000001 EnvironmentVariableDataBlock %WINDIR%、%TEMP% 等环境变量形式的路径
0xA0000002 ConsoleDataBlock 目标为控制台程序时的窗口设置
0xA0000003 TrackerDataBlock MachineID、droid GUID,归属证据的核心
0xA0000004 ConsoleFEDataBlock 控制台代码页
0xA0000005 SpecialFolderDataBlock 特殊文件夹 GUID
0xA0000006 DarwinDataBlock Windows Installer 的安装描述符
0xA0000007 IconEnvironmentDataBlock 图标路径中的环境变量
0xA0000008 ShimDataBlock 兼容性 shim 名称
0xA0000009 PropertyStoreDataBlock 键值化属性集合,含 SID 等
0xA000000B KnownFolderDataBlock 已知文件夹 GUID

TrackerDataBlock 标准块长 0x60、内部 Length 为 0x58,载荷含 16 字节 MachineID(源机器 NetBIOS 名)、Droid 与 DroidBirth 各 32 字节。droid v1 GUID 的后 6 字节可形成源机器 MAC,但虚拟机 NAT 端口、虚拟网卡、MAC 随机化都会影响归属结论,不能仅凭 MAC 相同就断言同一台物理机。

2.5 Recent 目录与 Jump List 的关系

%APPDATA%\Microsoft\Windows\Recent\ 下有两类内容:

位置 内容 特点
Recent\*.lnk 单文件快捷方式 打开文档时生成,携带卷序列号与 MachineID,保留较久
Recent\AutomaticDestinations\ 复合文档(OLE CFB) 每个应用一个文件,记录该应用最近访问的文档
Recent\CustomDestinations\ LNK 序列 由应用自行写入,格式自定义

两类都要提取。

Jump List 里的 LNK 会被条目上限淘汰,Recent 目录的单文件 LNK 保留更久,只查后者会漏掉应用级访问记录,反之亦然。

2.6 Jump List 文件结构

AutomaticDestinations 是 OLE 复合文档,其中的 DestList 流含 MRU/MFU 排序、固定状态位、访问时间、路径与条目编号。CustomDestinations 不是复合文档,而是带分类结构的 LNK 序列,以 0xBABFFBAB 结束。

Windows 7/8 与 Windows 10 的 DestList 首字段语义与条目长度不同(后者为 130 字节)。用 Win7 布局硬解 Win10 文件会导致路径字段整体错位,读出乱码却以为文件损坏。

DestList 还含 8 字节 MRU / MFU 排序值——MFU(最常用)排序能反映用户对该文件的实际使用频次,比单纯的访问时间更有指向性。

2.7 AppID:文件名就是应用身份

Jump List 文件名是 16 位十六进制的 AppID,如 1b4dd67f29cb1962.automaticDestinations-ms。

它可能是应用显式定义,也可能由可执行文件路径经 CRC-64 派生。

知道哪个 AppID 属于哪个应用,是「知道哪个应用记录了哪些文件」的前提。

AppID 未知不等于文件无效,只是缺少可读名称;JLECmd 内置常见 AppID 表,也可用 --appIds 提供自定义映射。

报告里写「AppID 1b4dd67f29cb1962,未匹配到已知应用」是合格表述,写成「Chrome 记录」而不给出依据则不合格。

三、操作步骤

3.1 从镜像中定位并提取

# 1) 确认分区与系统卷
mmls /work/DigiForensics.dd
fsstat -o 2048 /work/DigiForensics.dd | head -8

# 2) 列出用户配置(fls -p 列序:mode(1) inode(2) name(3) size(4) ...)
fls -o 2048 -r -p /work/DigiForensics.dd \
  | grep -Ei '/Users/[^/]+/(NTUSER\.DAT|Recent)' | head -20

# 3) 批量提取 .lnk(用 inode 命名避免重名覆盖)
mkdir -p /evidence/users/jsmith/Recent
fls -o 2048 -r -p /work/DigiForensics.dd \
  | grep -Ei '/Users/jsmith/AppData/Roaming/Microsoft/Windows/Recent/.*\.lnk$' \
  | grep -E '^r/r' | awk '{print $2}' \
  | while read -r ino; do
      icat -o 2048 /work/DigiForensics.dd "$ino" \
        > "/evidence/users/jsmith/Recent/$(printf '%s.lnk' "${ino%%:*}")"
    done

inode 在第 2 列(形如 294-294-1:),第 3 列才是带空格的完整路径。取错列会把文件名当 inode 传给 icat。CustomDestinations 的文件没有 .lnk 后缀,去掉结尾 \.lnk$ 过滤即可一并递归提取。

3.2 批量解析 LNK

# 单文件快速验证
LECmd.exe -f "C:\evidence\Recent\sample.lnk"

# 整目录解析(-r 只保留指向可移动介质的 LNK)
LECmd.exe -d "C:\evidence\users\jsmith\Recent" -r `
  --csv "C:\evidence\out\lnk" --csvf "lnk.csv" `
  --json "C:\evidence\out\lnk_json" --pretty -q

重点关注输出列:LocalPath、VolumeSerialNumber、DriveType、MachineID、MACAddress、Arguments、WorkingDirectory、RelativePath,以及 TargetCreated/TargetModified/TargetAccessed 三组目标时间戳。

只想快速看 Target ID List 与 Extra 块时用 -f 加 --nid --neb,这两个开关会抑制对应细节输出,反而更快。

3.3 解析 Jump List

# 整目录解析(--all 才会处理非 .automaticDestinations-ms 文件)
JLECmd.exe -d "C:\evidence\users\jsmith\Recent\AutomaticDestinations" `
  --csv "C:\evidence\out\jl" --csvf "autodest.csv" `
  --json "C:\evidence\out\jl_json" --pretty -q

# 只看 DestList 条目、不解全部 LNK 细节时
JLECmd.exe -d "C:\evidence\users\jsmith\Recent" --mp `
  --dt "yyyy-MM-dd HH:mm:ss.fff" --csv "C:\evidence\out\jl_all"

# 抽出嵌入的 LNK 单独深挖
JLECmd.exe -d "C:\evidence\users\jsmith\Recent\AutomaticDestinations" `
  --dumpTo "C:\evidence\out\jl_lnk" -q
LECmd.exe -d "C:\evidence\out\jl_lnk" --all `
  --csv "C:\evidence\out\lnk_from_jl" --csvf "from_jl.csv"

# 对单个可疑 AppID 深挖完整 LNK 信息
JLECmd.exe -f "C:\evidence\...\1b4dd67f29cb1962.automaticDestinations-ms" --fd -q

--fd 会直接输出完整 LNK 信息,代价是慢;样本量大时先 JLECmd 定位可疑 AppID 与条目号,再对单文件深挖效率高得多。

--withDir 显示未被 DestList 条目覆盖的流,用于发现被截断或手工构造的残留。

3.4 手工校验文件头与 droid