SecurityRonin/lnk-forensic
GitHub: SecurityRonin/lnk-forensic
一款纯 Rust 编写的 Windows .lnk 快捷方式和 Jump List 取证分析器,能将快捷方式元数据转化为分级取证发现,揭示可移动媒体、网络共享和跨机器使用痕迹。
Stars: 0 | Forks: 0
# lnk-forensic
[](https://crates.io/crates/lnk-core)
[](https://crates.io/crates/lnk-forensic)
[](https://docs.rs/lnk-core)
[](https://www.rust-lang.org)
[](LICENSE)
[](https://github.com/sponsors/h4x0r)
[](https://github.com/SecurityRonin/lnk-forensic/actions/workflows/ci.yml)
[](https://github.com/SecurityRonin/lnk-forensic/actions/workflows/ci.yml)
[](https://github.com/rust-secure-code/safety-dance/)
[](deny.toml)
**将一个 Windows `.lnk` 快捷方式 —— 或整个 Jump List —— 转化为分级取证发现 —— 揭示从 USB 闪存盘打开的文件、其来源的共享位置,以及创作该文件的机器,并附带将其关联回物理设备的卷序列号。**
`.lnk` 是一种内容丰富的 `[MS-SHLLINK]` 取证产物:它记录了目标路径、卷
序列号和 MAC 时间戳、来源机器的 NetBIOS 名称,以及一个分布式
链接跟踪 droid GUID —— 通常是已不存在的文件的证据。
`lnk-forensic` 从**任何** Windows 主机上创建的链接中读取这些信息,并对
对分诊至关重要的内容进行分级。它还解析 **Jump Lists** —— 任务栏/“开始”菜单的 MRU
产物 —— 包括 `*.automaticDestinations-ms`(一个 OLE/CFB 复合文件,包含一个
`DestList` MRU 流 + 每个条目嵌入一个 `.lnk`)和
`*.customDestinations-ms`(一段连续的嵌入式 `.lnk`),在每个嵌入式链接上复用相同的
shell-link 审计。
## 在 30 秒内审计 Shell Link
```
[dependencies]
lnk-forensic = "0.2" # pulls in lnk-core
```
```
use lnk_core::parse_shell_link;
use lnk_forensic::{audit_findings};
// .lnk bytes off disk; a malformed header yields None, never a panic.
if let Some(link) = parse_shell_link(lnk_bytes) {
for f in audit_findings(&link, "volume: E:") {
println!("[{:?}] {} — {}", f.severity, f.code, f.note);
// e.g. [Some(Medium)] LNK-REMOVABLE-MEDIA-TARGET — the link target resolves to a removable …
}
}
```
想要原始类型化流而不是分级发现?`audit(&link)` 返回
`Vec`;每个异常通过
`to_finding(source)` 生成一个 `forensicnomicon::report::Finding`。
## 异常代码
每个异常都是一个**观察结果**(“符合……”);由检查人员得出
结论。这些代码是稳定、公开的契约。
| 代码 | 严重程度 | 类别 | 观察内容 |
|---|---|---|---|
| `LNK-REMOVABLE-MEDIA-TARGET` | 中 | 威胁 | `VolumeID` 描述了一个 `DRIVE_REMOVABLE` 卷 —— 符合从外部媒体打开的文件的特征 (MITRE T1052.001 / T1091)。**卷序列号**被作为连接外部设备连接的关联键值呈现。 |
| `LNK-NETWORK-TARGET` | 低 | 威胁 | 链接携带了一个 `CommonNetworkRelativeLink` —— 符合从网络共享打开的文件的特征 (MITRE T1021)。 |
| `LNK-TRACKER-MACHINE` | 信息 | 来源 | `TrackerDataBlock` 记录了来源机器的 NetBIOS 名称 —— 符合链接是在该机器上创建的特征(归属)。 |
## Jump Lists —— 自动 + 自定义目标
`parse_automatic_destinations(bytes, filename)` 将 `*.automaticDestinations-ms`
作为 CFB 复合文件打开,读取 `DestList` MRU 流(Windows 7 v1 和
Windows 10/11 v2+ 布局),并解码每个嵌入的 `.lnk` 子流;
`parse_custom_destinations(bytes, filename)` 通过 `[MS-SHLLINK]` CLSID
和 `0xBABFFBAB` 页脚,将扁平的
`*.customDestinations-ms` 拆分为其嵌入的 `.lnk`。`audit_jumplist(&jl, acquisition_host, scope)` 运行
**现有的逐链接审计,覆盖每个嵌入的链接**(因此上面的代码会免费触发),
外加四个 Jump List 级别的代码:
| 代码 | 严重程度 | 类别 | 观察内容 |
|---|---|---|---|
| `JUMPLIST-PINNED-TARGET` | 低 | 来源 | 一个 `DestList` 条目被**固定** —— 符合用户故意将此目标固定到应用程序的 Jump List 的特征。 |
| `JUMPLIST-CROSS-MACHINE` | 低 | 来源 | 一个 `DestList` 条目的来源主机名(或 droid 卷 GUID)**与采集主机不匹配** —— 符合目标/产物源自另一台机器的特征。 |
| `JUMPLIST-MRU-RECENCY` | 信息 | 历史 | 一个 `DestList` 条目的最后访问时间 + 访问计数 —— 应用程序自身针对该目标的使用历史。 |
| `JUMPLIST-APPID-IDENTIFIED` | 信息 | 来源 | Jump List 的 `AppID` 通过 `forensicnomicon::jumplist::appid_name` 解析为已知应用程序。 |
DestList 偏移量表、`0xBABFFBAB` 页脚、嵌入式 LNK 的 CLSID
边界,以及 `AppID` 映射均来自
[`forensicnomicon::jumplist`](https://crates.io/crates/forensicnomicon)。
## 卷序列号是跨产物的关联键
`.lnk` 的 `VolumeID.DriveSerialNumber` 与 USB
大容量存储设备在注册表 / setupapi 日志中记录的 32 位卷序列号相同。`lnk-forensic`
将其在可移动媒体异常中作为一等公民呈现,以便检查人员能够将
从外部媒体打开的文件(此链接)与携带它的**物理
设备**(一个
[`peripheral-forensic`](https://github.com/SecurityRonin/peripheral-forensic)
`DeviceConnection`)进行**关联**。该序列号就是关联键 —— 链接提供数值,由
检查人员进行核对。
## 双 crate 拆分
- **`lnk-core`** —— 读取器。解析 0x4C `ShellLinkHeader`(LinkFlags,
FileAttributes,三个目标 FILETIMEs → Unix epoch,文件大小,图标索引,
显示命令,热键),`LinkInfo` 块(`VolumeID` 驱动器类型 + 序列号 +
标签,本地基路径,`CommonNetworkRelativeLink`),ANSI/Unicode `StringData`,
原始 `LinkTargetIDList` PIDL blob(完整的 PIDL 解码是 shellbag 解析器的
工作),`ExtraData` `TrackerDataBlock`,以及 **Jump Lists**(自动 + 自定义
目标)。格式常量来自
[`forensicnomicon::shlink`](https://crates.io/crates/forensicnomicon) 和
[`forensicnomicon::jumplist`](https://crates.io/crates/forensicnomicon);
解析算法存放在这里。无发现。
- **`lnk-forensic`** —— 分析器。将 `ShellLink` 或
`JumpList` 审计为分级的 `forensicnomicon::report::Finding`s。依赖于 `lnk-core`。
### 第三方依赖说明
`lnk-core` 依赖于成熟的、基于 MIT 许可证的
[`cfb`](https://crates.io/crates/cfb) crate 来读取存储 `*.automaticDestinations-ms` Jump Lists 的 OLE Compound-File
容器 —— 这是“优先使用我们自己的解析器”的一个
记录在案的例外,其地位与 NTFS 的 `lznt1` 相同:
重用一个正确、维护良好、范围更窄的读取器胜过
重新发明一个 OLE/CFB 解析器。我们自己的代码保持 `#![forbid(unsafe_code)]`。
## 信任,但要验证
专为处理来自可能已受损系统的不可信 `.lnk` 文件而构建:
- **`#![forbid(unsafe_code)]`** 覆盖两个 crate —— 无 FFI,无 C 绑定。
- **在恶意输入下不会引发 panic** —— 每个整数/长度/偏移量的读取都进行了
边界检查;工作空间在生产代码中拒绝 `clippy::unwrap_used` 和
`clippy::expect_used`。截断或乱码链接会
导致子结构缺失或返回 `None`,绝不会崩溃。
- **经过模糊测试** —— `cargo-fuzz` 针对 `shelllink`(读取器)、`forensic`(完整的
解析 → 审计管道)和 `jumplist`(CFB/DestList + 自定义
目标解析 → 审计)进行测试;`fuzz.yml` CI 工作流会构建并对
每一个进行冒烟测试。
- **根据精确规格产物进行了验证** —— 管道经过了
端到端的测试,使用了手工编写的测试夹具:`[MS-SHLLINK]` 链接(带有卷序列号的可移动
媒体链接 + 一个网络共享链接;
`forensic/tests/real_data.rs`)和 Jump Lists(一个真实的 CFB
`*.automaticDestinations-ms`,包含固定的、跨机器的可移动条目 + 一个
扁平的 `*.customDestinations-ms`;`forensic/tests/jumplist.rs`),核对了
呈现出的序列号和发现。
```
cargo test
cargo +nightly fuzz run forensic # requires nightly + cargo-fuzz
```
## 适用场景
`lnk-forensic` 是 SecurityRonin 取证工具舰队中的一个解析器/分析器:每个
crate 都是某一产物家族的资深专家,发出共享的
`forensicnomicon::report` 词汇,以便发现结果能够在磁盘、
内存、日志和注册表产物中统一聚合。
[隐私政策](https://securityronin.github.io/lnk-forensic/privacy/) · [服务条款](https://securityronin.github.io/lnk-forensic/terms/) · © 2026 Security Ronin Ltd
标签:Rust, 可视化界面, 快捷方式解析, 数字取证, 文件分析, 网络流量审计, 自动化脚本, 通知系统