最早接触 NTFS 里的 ADS,是在一次内网排查的现场。当时同事在共享目录里发现了一个名为 report.doc 的文件,大小只有几十 KB,属性看起来完全正常。可当我在命令行敲下dir /r的瞬间,问题立刻浮出水面:这个文件下面还挂着一条report.doc:docdata附加流,里面躺着一个接近两百 MB 的数据块。那一刻我才真正意识到,NTFS 的这些“看不见的角落”绝不只是教科书上的冷门概念,而是会真真切切影响数据安全、容量统计和运维决策的关键机制。
ADS 的完整叫法是 NTFS Alternate Data Streams,也就是 NTFS 替代数据流,中文圈子里大家更习惯叫它 NTFS 数据流、附加流,或者直接说 ADS。简单来说,它允许你在同一个文件或目录上挂载多份互相独立的数据内容,这些内容共用同一个文件名,但对普通 API 和资源管理器来说,通常只有默认的那个流是可见的。我们今天就把这个“冰山下的部分”从原理到实操彻底聊透,内容里所有命令我都建议你自己动手敲一遍,尤其是系统管理员、安全分析人员和做数据恢复的朋友,这部分知识是真的能派上用场。
1. ADS 到底是什么?先搞懂 NTFS 里“文件”的真实结构
1.1 一个文件不是一个文件
在 FAT32 时代,文件的概念很简单:一个文件对应一块存储区域,名字、大小、属性都明明白白。但 NTFS 从一开始就把“文件”设计得更复杂,也更灵活。一个 NTFS 文件并不只是一块字节数据,而是一个由多个属性组成的逻辑实体,其中最常见的是文件名属性、时间属性、安全描述符,以及存放真正内容的$DATA属性。
关键就在这里:NTFS 的$DATA属性可以出现多次,而且每次都有不同的名字。默认的那份数据叫“未命名数据流”,也就是我们平时用记事本、资源管理器看到的全部内容。而额外创建的数据流则属于“已命名数据流”,它的格式通常写作文件名:流名。你在资源管理器里看不到这些已命名流,在大多数编程语言的标准文件读写接口里也读不到它们,但它们实实在在地占据磁盘空间,并且可以承载任意数据。
用大白话打个比方:把一个 NTFS 文件想象成一个人,主数据流是这个人公开的身份证,附加数据流则是他口袋里另外揣着的私人物品。别人看到身份证就知道这个人存在,但完全不知道口袋里装了什么东西,除非专门去搜身。
1.2 从 Mac OS 继承来的历史包袱
很多人可能不知道,NTFS 的 ADS 设计初衷是为了兼容早期 Macintosh 文件系统的“资源分支”概念。当年的 Mac OS 文件里,数据和元数据是分开存放的,一个文件既有数据分支,也有资源分支,里面存放图标、字体和程序资源。Windows NT 3.1 时代要支持 Mac 共享文件,NTFS 就把这种双分支结构吸收了过来,用多个数据流来模拟 Mac 的资源分支。
后来 Windows 和 Mac 的协作方式几经变迁,但这个底层的多数据流机制一直保留到了今天。可以说,ADS 是 NTFS 为了兼容性留下的一个设计“活化石”,但它没有随着历史使命的结束而消失,反而在后续的 Windows 生态里找到了新用途。理解这段历史,你就不难明白为什么 ADS 在文件系统层面如此底层,也为什么它对普通用户隐藏得这么深。
2. 实操:创建、读写、查看和删除数据流
2.1 用命令行给文件“挂一个分身”
创建数据流听起来很玄乎,实际操作却简单到让人意外。只要你掌握一个规则——在文件名后面加冒号和流名——就可以通过命令行直接写入附加流。注意,这里说的冒号是英文半角冒号。
比如我准备创建一个普通的文本文件作为宿主:
echo 这是可见数据 > sample.txt接着给它挂上一个叫 hidden 的附加流:
echo 这是藏在数据流里的内容 > sample.txt:hidden这时候用type sample.txt只能看到“这是可见数据”,但你用下面这条命令就能读到附加流内容:
type sample.txt:hidden如果你用的是 PowerShell,还可以用更结构化一点的写法。例如创建带数据流的文件:
Set-Content -Path sample.txt -Value "这是可见数据" Set-Content -Path sample.txt -Stream hidden -Value "这是藏在数据流里的内容"读取的时候用:
Get-Content -Path sample.txt -Stream hidden这里需要注意,echo >其实会带一个回车换行符,所以你读出来的内容末尾会多一行空行。如果做数据校验或哈希计算,最好用 PowerShell 的Set-Content配合-NoNewline,或者直接用十六进制编辑器写入,避免因为结尾换行符导致哈希对不上。
2.2 查看盘上的所有附加流
对运维和排查来说,怎么快速发现数据流才是核心能力。Windows 自带命令里最朴素也最有效的就是dir /r。在命令行进入目标目录后执行:
dir /r它会列出当前目录下所有文件的主数据流信息,并把每个文件对应的已命名流单独列一行。比如前面创建的sample.txt,输出里会多出一行类似sample.txt:hidden,后面还有这行各自的大小。这个命令虽然老旧,但直到 Windows 11 依然可用,是我排查时的第一板斧。
不过dir /r有个缺点:它只显示当前目录,不支持递归扫描子目录。如果你的共享目录有几万个文件,一个个进目录太不现实。这种情况我一般直接用 PowerShell 的流枚举命令,一行就能递归搜出整个目录树下的所有流:
Get-ChildItem -Path C:\share -Recurse -File | ForEach-Object { Get-Item -Path $_.FullName -Stream * } | Where-Object { $_.Stream -ne ':$DATA' }这段命令的意思是先递归找到所有文件,再逐个拿到它们的流信息,最后过滤掉主数据流,只留下命名流。运行完之后你会发现,很多平时根本察觉不到的流都会浮出水面,比如下载文件自动产生的Zone.Identifier流。
2.3 删除与误删,边界在哪
清理数据流有两种常见方式。如果你确定某个附加流没有用,可以直接用命令删除:
del sample.txt:hidden在 PowerShell 里对应这条:
Remove-Item -Path sample.txt -Stream hidden如果你想把整个文件连同所有流一起删掉,直接删除文件本身就行。这里要提醒一句:很多人的第一反应是“把数据流删了,文件就能瘦身”,但实际在 Explorer 里你看不到数据流的存在,文件属性对话框中显示的“大小”和“占用空间”通常也只统计主数据流。所以,用资源管理器查看容量统计时,数据流占用的空间是隐藏在幕后、很容易被忽略的,这也是共享目录经常“莫名变大”的常见原因之一。
另外,复制文件时要特别小心。默认情况下,Windows 的资源管理器复制操作会保留 NTFS 数据流,但如果你用copy命令复制到 FAT32、exFAT 或 U 盘等非 NTFS 文件系统上,数据流会被静默丢弃。而且普通的copy命令在 NTFS 分区之间也不一定复制所有流,比如复制单个文件时,未命名数据流会被复制,但已命名流不一定全部跟着走。这个坑我在备份共享目录时踩过不下三次,所以后来备份文件和重要数据前,我都会先用dir /r看一眼源目录的流情况。
3. 现实中 ADS 在正当地“干活”
3.1 Zone.Identifier:你每天都在接触的 ADS
ADS 听起来像一个冷门特性,但实际上普通 Windows 用户每天都在和它打交道。最典型的就是浏览器下载文件的“安全标记”,也就是Zone.Identifier数据流。
从互联网下载一个 exe 文件,再右键打开文件属性,如果能看到“解除锁定”按钮,那这个文件就一定带有一条名为Zone.Identifier的附加流。用命令行看一下:
Get-Content -Path C:\downloads\setup.exe -Stream Zone.Identifier运气好的话,你会看到类似下面的内容:
[ZoneTransfer] ZoneId=3 ReferrerUrl=https://example.com/ HostUrl=https://example.com/setup.exe这个数据流里记录的是文件来源区域和下载地址。Windows 的 SmartScreen、Microsoft Defender 以及其他安全软件会读取这个流,判断文件是不是来自“不受信任的互联网区域”,从而决定是否弹出安全警告。如果你把这个流删除,文件就会立刻变成“本地生成的文件”,安全警告也随之消失。
我自己做安全测试时经常利用这个机制模拟“用户下载文件”的场景。但也因此吃过一次亏:有一次我清理临时文件,顺手把整个下载目录的所有Zone.Identifier流都批量删掉了,结果当天下午同事下载一个安装包,Windows 竟然没有弹出任何提示,我这才意识到自己的清理脚本把安全机制也给“清理”了。从那以后,我处理下载目录时只做记录,不再默认删除 Zone.Identifier。
3.2 软件元数据和文件标记的另一个家
除了浏览器,很多 Windows 软件也把 ADS 当作存放元数据的“口袋”。比如老版本的 Office 和 Windows 资源管理器,会利用数据流保存文件的作者信息、自定义属性或缩略图缓存;部分文档管理系统会在文件上挂额外的业务编号、审批状态字段;某些同步工具则会用数据流记录云端文件版本或本地同步标记。
这里最有意思的是“文件标记”这个用途。Windows 的“标记”功能允许用户给文件设置不同颜色的标签,而这些标签在旧版 NTFS 上就是通过一个叫Oplock或文件别名的机制存储的,虽然现代版本实现方式有变化,但“把附属性信息放在文件旁边而不是单独建库”这种思路,在 Windows 生态里一直存在。
为什么这些软件不把这些信息单独存一个配置文件呢?因为零散信息放进数据库容易丢失关联,放进主数据流又会破坏文件原始内容的一致性,而放进 ADS 里就可以做到“内容不变,附加信息跟着走”。比如一个 PDF 文件要拷贝给外部客户看,主数据流是清白的正文,附加流里的审批记录和员工 ID 就不会泄露出去——除非接收方专门去枚举流。从设计角度说,这其实是相当聪明的做法,只是安全性上存在隐患。
4. 恶意场景与排查思路
4.1 攻击者为什么盯上 ADS
ADS 在安全领域之所以臭名昭著,是因为它天然具备“隐蔽性”和“嵌套性”。攻击者拿到一台 Windows 主机的一定权限后,经常会把工具、脚本、密码字典、临时收集的数据藏进系统文件或常用文件夹的附加流里。表面看目录里只有几个正常文件,属性、时间戳、大小都不会被普通用户察觉,但流里面可能已经藏了完整的小型工具包。
举个例子:攻击者可以把一个木马文件的二进制内容塞进一张正常 JPG 图片的附加流里:
type evil.exe > vacation.jpg:payload.exe表面上vacation.jpg仍然是一张可以正常打开的图片,文件大小也几乎没变,但payload.exe的完整字节都藏在vacation.jpg:payload.exe这个流里。如果没有专门的流扫描工具,很难被发现。而且流的数据可以通过一些合法程序被执行或读取,比如 Windows 脚本宿主、PowerShell 等针对流的路径语法都能读取其内容,这就给了攻击者“藏得深、执行时再还原”的操作空间。
更隐蔽的是,攻击者还会把 ADS 藏在文件夹上。NTFS 不仅允许文件带流,目录也可以有命名数据流,所以攻击者可以把一堆数据打包藏进某个系统目录的附加流里,连宿主文件都不需要,这给取证分析增加了不少难度。
4.2 防御视角的检测与处置
面对 ADS 带来的风险,防御方绝对不是说把 ADS 功能禁用就万事大吉,因为 NTFS 本身不支持关闭多流机制,你最多只能做到充分感知、及时清理。
我的检测思路分三步。第一步是“全盘摸底”,定期对关键服务器、共享目录跑一次递归流枚举,把可疑的命名流全部记录下来。第二步是“白名单对比”,通常合法的流名相对有限,比如Zone.Identifier、Oplock、部分厂商自己的命名规则。把白名单之外的流单独拉出来分析,命中率会非常高。第三步是“内容分析”,对可疑流做哈希和大小记录,再用十六进制工具或文件类型识别工具判断里边到底是什么格式的内容。
处置可疑流时,建议先把宿主文件连同数据流一起复制到一个隔离的取证环境中再分析,不要急于在线上环境删除。因为攻击者可能已经在流里藏了后门入口,贸然删流可能导致痕迹丢失或程序崩溃。另外要检查系统计划任务、启动项和服务中是否有脚本指向类似C:\Windows\Temp\file.txt:payload.ps1的路径,这类路径特征是排查 ADS 攻击的强信号。
我复盘自己遇到的真实案例时发现,很多流量或进程层面无异常的告警,其实最终都能回溯到“藏在流里”的恶意载荷。安全团队如果只盯着网络流量和进程命令行,忽略了文件系统底层的数据流,排查范围会少一大块。这也是我强烈建议所有从事终端安全、取证分析的朋友把“枚举 ADS”写进日常巡检脚本的原因。
5. 工具链和个人工作流
5.1 我常用的三个工具组合
说到实战工具,我自己常年保留三件套:Sysinternals 的 Streams 命令、PowerShell 原生流接口,以及一个十六进制编辑器。
Streams 是老牌瑞士军刀,可以递归列出目录下的所有数据流,还支持清理。它的优点是小巧、上手快,适合在不出网的内网环境里快速跑一遍。基本用法:
streams.exe -s C:\share后面加-d参数会删除匹配到的流。不过我建议平时先只列出,不要急着删,避免误伤合法流。需要注意的是,Streams 工具在部分场景下对大型目录树会遍历比较慢,所以我的习惯是用它做抽样检查,真正全量扫描还是交给 PowerShell 来完成。
PowerShell 的原生接口虽然读取流的速度一般,但胜在灵活。我可以把扫描结果直接导出成 CSV,按文件名、流名、大小排序,再和上周的基线做对比,任何新冒出来的命名流都会被标记出来。下面这段命令会递归枚举 C 盘上所有非 Zone.Identifier 的命名流:
Get-ChildItem -Path C:\ -Recurse -Force -ErrorAction SilentlyContinue | ForEach-Object { Get-Item -Path $_.FullName -Stream * -ErrorAction SilentlyContinue } | Where-Object { $_.Stream -ne ':$DATA' -and $_.Stream -ne 'Zone.Identifier' } | Select-Object FileName, Stream, Length | Export-Csv -Path streams_report.csv -NoTypeInformation十六进制编辑器则是“最后一锤定音”的角色。当某个流的大小和特征都很可疑时,我会用类似 HxD 这种工具直接打开该文件的原始流区域,人工看一眼文件头魔数,判断它到底是个 PE 文件、脚本、压缩包还是加密数据。
5.2 一次“不留死角”的实际体检流程
下面是我在接管一台陌生服务器时必走的流程,也分享给各位参考。
第一步,先看分区文件系统类型。如果服务器盘是 FAT32 或 exFAT,那 ADS 不会存在,整个步骤可以跳过。如果是 NTFS,继续往下走。
第二步,运行一次成本可控的全局枚举。先用dir /r快速检查根目录,再用 PowerShell 递归枚举全盘命名流,把结果保存到审计日志。这一步耗时取决于文件数量,一台几十万文件的文件服务器可能要跑十几分钟,建议放在业务低峰期执行。
第三步,筛选并分类可疑流。我会把流名分成三类:合法已知类、暂未知名类、明显可疑类。合法已知类用脚本自动忽略;明显可疑类直接进隔离区做深度检查;暂未知名类人工核对厂商文档和系统版本信息后补充进白名单。
第四步,把可疑流的内容提取出来做哈希比对。比如用 PowerShell 的Get-FileHash配合流路径,把哈希扔进本地病毒库或第三方威胁情报平台查一下,命中直接处置。这里要特别提醒:提取流内容时不要直接还原成可执行文件放在可访问目录,最好放在只读的取证文件夹或者加密容器里,防止误触发或二次感染。
整套流程走完,我会给服务器留一个“基线快照”,把正常文件的流名、大小和哈希记下来。之后每隔固定周期自动跑一次对比,新出现的流就是最值得怀疑的异常信号,这个方法比单纯全盘扫描更高效,也更容易发现潜伏问题。
6. 踩坑记录与写在最后
6.1 容易翻车的几个细节
先说一个最容易踩的坑:在命令行里引用流路径时,冒号经常会“捣乱”。比如目标文件路径本身带有空格,或者你在 PowerShell 里直接把整个路径当成字符串传入,系统可能会把冒号之后的字符串解析成另一个盘符或参数。我建议所有流路径都用引号包起来,或者在 PowerShell 里用$()和-Stream参数这种显式方式,而不是依赖file:stream的简便写法。
第二个坑是大小写敏感。NTFS 在默认情况下对文件名大小写不敏感,但数据流名在某些 API 调用里大小写可能被视为不同流。这意味着你创建的hidden流,用Hidden去访问时行为可能不一致,扫描和取证时报错也是常事。保险起见,创建流和记录流名时统一使用小写,不要靠大小写“玩花样”。
第三个坑和数据恢复有关。如果你用软件做误删文件恢复,恢复出来的文件很可能只有主数据流,而不带附加流。这意味着源文件是一张带有隐藏数据的图片,恢复后隐藏数据可能丢失,或者反过来,恢复物里留下了一个无法对应到主数据的孤立流信息。所以需要做数据恢复时,尽量使用支持 NTFS 流恢复的取证工具,并提前说明你要保留所有数据流。
第四个坑是杀毒软件的误报与漏报并存。如果你把数据流提取成独立的可执行文件加载到内存分析,部分杀毒引擎可能报毒,但报毒原因是流内容本来就包含恶意特征;另一些杀毒引擎对 ADS 内部的内容扫描深度不够,恶意文件藏在附加流里可能直接躲过查杀。所以在测试环境中,我会关闭实时监控再做流提取,避免中途被拦截或者误判。
6.2 一点个人习惯
做运维和安全的年头久了,我形成了一些比较“保守”的习惯,这里挑几个对大家有价值的分享一下。
一是在文件服务器和关键业务盘上,我从不关闭也不试图“禁用”ADS,但我会在所有共享目录的说明文档里提醒用户:无论上传还是下载,尽量使用公司内部标准的压缩格式,不用网上下载的未知工具直接操作共享文件,避免把带危险流的文件扩散到整个目录树。
二是我给团队里的新同学培训时,要求他们必须掌握dir /r和 PowerShell 流枚举。不要求每个人都能背出 MFT 属性编号,但至少要能在十秒内判断一个目录是否存在异常流。长期看,这个基本功比临时装一堂“高级威胁分析课”实用得多。
三是所有从我手里交接出去的服务器,我一定会附一份“流基线清单”。清单里会写明这台机器上出现过哪些合法流、命名规律是什么、哪些目录出现新流必须上报。很多人觉得这是小题大做,但真实案例告诉我,绝大部分 ADS 相关的安全事故其实都能通过“提前建立基线 + 定期对比”的方式在前中期发现。事后的逆向分析固然重要,但前置的规则和习惯更能省钱省力。
如果你现在手上正好有一台 Windows 机器,建议立刻打开命令行,敲一个dir /r看看你的下载目录。那些平时看不见的Zone.Identifier流,就是理解 NTFS ADS 最便捷的一个入口。从这个小流开始,你会慢慢发现整个 NTFS 底层设计的精妙,也会更理解为什么文件系统安全始终是终端安全绕不开的基础课。