你有没有遇到过这样的遭遇:在Windows里看某个文件,右键属性一切正常,大小、修改时间都对得上,但磁盘空间却莫名其妙少了几个GB;又或者你在某个目录下用dir命令翻来翻去,明明看到一堆文件,可某个安全工具一扫描,却突然报出一串带着冒号的“幽灵文件名”。这十有八九就是NTFS的备用数据流在作祟,也就是标题里说的ADS(Alternate Data Streams)。
NTFS里的ADS并不是什么新东西,从Windows NT时代就存在了,但在普通用户甚至不少运维老手眼里,它依然是既神秘又容易被忽略的一块。说实话,我最早接触ADS还是因为一次“灵异事件”:公司一台文件服务器上,某个共享文件夹的空间占用不断上涨,但把所有可见文件加起来根本对不上账,最后用streams一扫,才发现某个不起眼的Excel文件里挂着几百MB的隐藏数据流。后来我花了不少时间系统折腾NTFS数据流,从命令行创建、读取、隐藏,到用脚本批量检测、清理,再到配合数据恢复工具去还原被藏起来的内容,踩过不少坑。这篇文章就把这些折腾记录下来,把ADS的原理、操作、检测、清理和边界一次性讲透,希望能帮你少走弯路。
1. NTFS里那个看不见的“夹层”——ADS到底是什么
1.1 先从FAT32到NTFS:文件结构进化带来的“隐藏维度”
要理解ADS,得先理解NTFS和FAT32在文件组织方式上的根本差异。FAT32的年代,文件系统是“一个目录项对应一个文件数据”的平面结构:目录里有文件名、起始簇号、文件大小这些信息,文件数据就是一连串簇,没有多余的空间去表达“一个文件有多份数据”。
NTFS则完全重构了这套逻辑。NTFS把每个文件和目录都抽象成MFT(主文件表,Master File Table)里的一条记录,每条记录里以“属性”的形式存放各种信息。常见的有$STANDARD_INFORMATION(标准信息)、$FILE_NAME(文件名)、$DATA(数据)等。其中$DATA属性就是文件内容本身。而ADS之所以出现,是因为NTFS允许同一条文件记录里存在多个$DATA属性——默认的那个就是你在资源管理器里看到的文件内容,额外增加的$DATA属性就是“备用数据流”。
打个比方,FAT32就像一间只有一个抽屉的柜子,文件内容只能放这个抽屉;NTFS则是一个带多个抽屉的柜子,主数据流是明面上那个抽屉,ADS就是那些带锁甚至不带锁的暗格。系统默认只展示主抽屉,暗格里放什么、放多少,普通情况下你根本看不见。
这个设计在微软官方的语境里叫“Alternate Data Streams”,中文常译作“备用数据流”或“替代数据流”。它的语法也很特别:在文件路径后面用冒号加上流名称来访问。比如C:\test\demo.txt:hidden.txt,表示访问demo.txt文件里名为hidden.txt的那个数据流。这种冒号路径在Windows的API层面是原生支持的,所以很多系统自带的命令,像type、more、wmic,其实都能和ADS打交道,只是很少人专门去用。
1.2 ADS的诞生背景与正经用途
微软当年设计ADS,主要是为了兼容老Mac系统的资源分支(Resource Fork)和OS/2的一些文件特性。后来在Windows系统自身里,ADS也被广泛使用。最典型的例子就是“文件来自网络”的标记。
你用浏览器从网上下载一个zip包,系统会自动往这个文件里写一个名为Zone.Identifier的数据流,里面记录着下载来源的ZoneId。所以你会看到:右键点击这类文件,属性对话框底部会出现一个“解除锁定”的复选框。一旦你点击“解除锁定”,系统要做的就是删掉Zone.Identifier这个数据流。这就是ADS在系统里的常规应用场景之一。
另外,Windows搜索索引服务、Office文档的属性存储、NTFS压缩和加密相关的$EFS属性,也都依赖附加的数据流或附加属性。这些都是ADS的“正经用途”,不是说只要出现多流就一定是恶意行为。所以我在后面讲检测时,也会强调:看到ADS不要立刻当成病毒,先看流名称和内容再判断。
1.3 先说清楚:此ADS非彼ADS
写这篇文章之前我看了一眼相关搜索词,发现有不少人在搜“ADS仿真”“ADS安装”“ADS版图优化”这类内容。这里必须分个流:那些话题说的是Advanced Design System,是Keysight出品的电磁/射频仿真软件,和NTFS数据流完全是两个领域。如果你是想做射频电路仿真点进来的,看到这里就可以止步了,本篇聊的是文件系统层的NTFS备用数据流。以后搜索时也建议用“NTFS ADS”或“备用数据流”作为关键词,能直接过滤掉仿真软件那一大堆无关结果。
2. 亲手造一条隐形流:命令行全流程实操
2.1 基础创建与读取:echo、dir /r、more的组合拳
纸上谈兵没意思,直接上命令行实操。我建议你在测试机上建一个干净的文件夹,比如C:\ads_test,避免误操作影响真实数据。打开cmd,执行下面这几步。
第一步,创建普通文件和隐藏流。
cd /d C:\ads_test echo 这是明面上的正文内容 > demo.txt echo 这是藏在备用数据流里的秘密 > demo.txt:hidden.txt第二步,查看结果。
dir dir /r你会发现,第一条dir只显示demo.txt这一个文件,大小也很正常;而第二条dir /r会多出一行类似demo.txt:hidden.txt:$DATA的条目。这里的/r参数在Windows 7及之后版本的cmd里都能用,专门用来显示备用数据流。在Windows XP及更早的系统上,这个参数还不存在,识别ADS就得靠第三方工具。
第三步,读取流内容。这一步很多人会下意识敲type demo.txt:hidden.txt,但在某些Windows版本里直接这么打会报错,因为cmd的type对冒号路径的解析不太稳定。更稳妥的做法是用more加重定向:
more < demo.txt:hidden.txt执行后终端会输出“这是藏在备用数据流里的秘密”。你看,文件列表里看不出异常,但内容实打实存在,这就是ADS最让人防不胜防的地方。
2.2 无文件名关联的独立流:直接把流挂在目录上
ADS还能玩得更“花”一点:不一定非要挂在某个文件上,它可以直接挂在目录上。命令是这样:
cd /d C:\ads_test echo 目录级隐藏流 > :folder_stream.txt注意这里的写法,冒号前没有文件名,直接把流挂在了当前目录的MFT记录上。用dir /r查看当前目录时,同样会显示这个流。这种“目录级流”在实战中很受恶意程序欢迎,因为传统安全管理员的巡检思路大多是“扫文件”,很少会专门去扫描目录本身的附加流。我在测试环境里复现过这个场景,很多普通杀软在工作站上的静态扫描根本不会碰这类数据,只有主动用工具枚举目录流才能发现。
读取目录级流的方式和文件流类似:
more < :folder_stream.txt这种隐藏方式的清理也更麻烦一些,需要先用工具确认,再想办法删掉。我后面专门写清理方法。
2.3 移动、复制、备份时的“静默丢失”问题
ADS有一个非常坑爹的特性,跨文件系统、跨传输方式时极易被静默剥离。我最初就是在这上面栽了跟头。把demo.txt从NTFS分区复制到FAT32格式的U盘,或者exFAT移动硬盘上,文件主内容在,但hidden.txt这个备用流会在复制过程中悄无声息地消失,系统不会弹任何警告。
这种“静默丢流”还存在于很多场景里:用WinRAR默认参数打包,zip格式必然丢ADS(RAR格式在某些设置下可以保留);把文件传到网盘再下载下来,同步客户端通常只认主数据流;邮件附件、IM传输这类通道基本百分百丢流;甚至某些备份软件如果不勾选“备份备选数据流”选项,也会把ADS丢得干干净净。
所以当你在排查“为什么文件属性页显示的大小和磁盘占用量不一致”时,除了怀疑稀疏文件和硬链接,也一定要把ADS考虑进去。尤其是做安全取证的人,如果直接把带ADS的原始文件复制到FAT32介质上拿去送检,那隐藏数据早就没了,后续分析等于白做。
对于这类多点复制、压缩传输的问题,我在实际工作中总结了一条规矩:凡是怀疑涉及ADS的文件,宁可整盘打包成E01镜像或vhd虚拟磁盘,也别直接复制单文件。这样能确保包括ADS在内的NTFS元数据完整保留下来。
3. 双面性:ADS在隐藏、检测与取证中的攻防实战
3.1 攻击者为什么偏爱ADS
ADS在安全领域的名声,一半来自攻击者的“偏爱”。原因很简单:它利用的是文件系统自身的合法机制,不需要额外驱动,不触发常见文件扫描的路径,还能把恶意代码塞进看起来完全正常的文件里。
举个例子,早期很多恶意脚本喜欢这么做:把一个VBS或者PowerShell脚本写进C:\Windows\System32\某个合法exe文件:hidden.ps1,然后在注册表的Run键里写一条启动命令:powershell.exe -ExecutionPolicy Bypass -File "C:\Windows\System32\某个合法exe文件:hidden.ps1"。系统重启后,计划任务或注册表启动项会用PowerShell去读取那个冒号路径里的流,恶意代码就被执行了。由于主文件本身是系统自带的合法程序,大小、数字签名都没变(修改时间有时会变),普通管理员很难发现问题。
还有更隐蔽的玩法:直接利用ADS隐藏勒索说明、远控木马的配置信息,或者作为C2通道的临时数据缓存。我处理过的一台失陷设备上,攻击者把一段用于内网扫描的Python脚本藏在了某个日志文件的时间属性流里,要不是做深度取证时把整个NTFS分区逐条MFT记录过了一遍,根本发现不了。
这也解释了为什么防病毒软件对ADS的查杀效率不稳定:很多杀软实时监控的“文件访问”钩子主要拦主数据流的读写,对备用流的扫描覆盖并不完整。某些知名EDR产品虽然能枚举ADS,但在默认策略下也只是记录,并不会自动清除。
3.2 检测ADS的实用命令与工具
检测ADS的方法分三个梯队,从系统自带到专业工具,覆盖不同场景。
第一梯队是用dir /r看单个目录。优点是零依赖,缺点是效率低、信息少,它只会列出目录下的ADS,不会递归子目录,也不会显示流大小和内容。适合快速确认某个目录有没有流,不适合全盘排查。
第二梯队是微软Sysinternals套件里的streams工具。命令行格式如下:
streams64.exe -s C:\ads_test-s参数表示递归子目录。跑完之后,它会列出每个文件关联的ADS路径和大小。如果要直接清除扫描到的流,加上-d参数:
streams64.exe -s -d C:\ads_test注意,-d是带删除动作的,建议先不加-d扫描一次,确认结果再动手。streams工具虽然年头久远,但至今依然好用,我手头常备一份64位版本。
第三梯队是PowerShell脚本。streams工具在应急响应时会遇到一个尴尬问题:它显示信息不够结构化,不好批量导出。PowerShell的Get-Item命令天生支持-Stream参数,可以这样枚举:
Get-ChildItem -Path C:\ads_test -Recurse -ErrorAction SilentlyContinue | ForEach-Object { $item = Get-Item -Path $_.FullName -Stream * -ErrorAction SilentlyContinue if ($item) { $item | Where-Object { $_.Stream -ne ':$DATA' } | Select-Object FileName, Stream, Length } }这段脚本把每个文件的所有流都列出来,过滤掉默认的:$DATA主数据流,剩下的就是ADS条目。在实操中,全盘递归扫描的性能开销很大,尤其是碰到大型文件服务器时,我建议先锁定几个重点目录,比如C:\Users、C:\Windows\Temp、共享目录根,分片扫描,避免I/O打满。
3.3 把藏起来的流“打回原形”进行取证分析
检测出ADS之后,直接分析“冒号路径”里的流并不方便,尤其是文件内容为二进制或脚本时。我的做法是把流导出为独立文件再分析,相当于把暗格里的东西搬出来摊在桌上。
在cmd里,用重定向方式导出:
more < C:\ads_test\demo.txt:hidden.txt > C:\analysis\hidden_export.txt更推荐用PowerShell,因为Get-Content原生支持-Stream参数:
Get-Content -Path C:\ads_test\demo.txt -Stream hidden.txt > C:\analysis\hidden_export.txt如果你怀疑流里面是加密数据或另一层压缩包,导出后直接用file命令(Windows下可用Dependencies或CyberChef辅助判断)识别文件头,再决定后续的解析手段。在真实取证里,还有一个细节容易被忽略:导出流的时候尽量不要在原始盘上直接操作,最好先做一份NTFS级别的镜像,再从镜像上提取,否则会污染原始介质上的时间戳等元数据,给后续司法鉴定带来麻烦。
4. 清理、固化与磁盘空间“对不上账”的真相
4.1 如何正确删除ADS
清理ADS最容易走进的误区是试图用del命令直接删流。我试验过很多次,del file.txt:hidden.txt在cmd下通常会报“文件名、目录名或卷标语法不正确”,根本没有用。原因在于cmd的del没有实现流级别的删除逻辑。正确方法分三种,按场景选择。
场景一,目标文件整个都不要了。直接删文件,主数据流和所有备用数据流会一起被清掉。这是最干净彻底的方式。
场景二,文件要保留,但某个特定的流必须删掉。用PowerShell:
Remove-Item -Path C:\ads_test\demo.txt -Stream hidden.txt执行后可以用Get-Item -Stream *确认一下,确保流没了。
场景三,需要批量清理整个目录下的所有ADS,同时不想误删文件本身。用Sysinternals的streams工具:
streams64.exe -s -d C:\ads_test需要注意的是,这个操作会把Zone.Identifier这种系统正常使用的数据流也一起删掉。在某些企业环境里,去掉Zone.Identifier会导致Office的Mark-of-the-Web防护失效,用户打开带宏的文档时不再弹安全警告,这反而增加风险。所以批量清理之前,建议先导出流清单,分辨一下哪些是系统正常用途、哪些是可疑内容,再决定是否一刀切。
4.2 日常安全巡检:多久扫一次ADS比较合适
ADS的检测不应该只在出事之后才想起来。我自己在管理文件服务器时,会把ADS巡检放进月度安全基线里,大体上做三件事。
第一件,检查重点目录的流数量是否异常增长。如果某个目录平时只有零星几个Zone.Identifier,某天突然多出大量带ps1、vbs这类扩展名的流,基本可以判定为“有人搞事情”的强信号。
第二件,把ADS清单的哈希值也纳入记录。文件主内容的哈希很多人会存,但流的哈希容易被忽略。我给关键文件做完整性基线时,会用PowerShell把每个流的内容算一遍哈希,跟主文件哈希分开存。这样就算攻击者把payload藏进流里,只要内容发生变化,基线比对立刻就能发现。
第三件,定期抽查备份/同步链路是否保留ADS。操作很简单,在源端用echo test > ready.txt:check.txt造一个带流的文件,走一遍备份流程,再到备份目标上用dir /r看还在不在。这个方法很土,但能有效发现那些“宣称支持ADS备份”的软件到底有没有偷懒。
4.3 文件大小正常但磁盘占用狂涨的真相
“文件大小对不上账”大概是被问到最多的问题。这里要把概念厘清:资源管理器属性页里显示的大小,通常只算主数据流的长度;如果某个文件被附加了一个巨大的ADS,磁盘占用会显著增加,但属性页显示的文件大小基本不变。于是我见过不少案例,服务器上某个共享目录里的可见文件总大小只有50GB,但卷上空间被吃掉了200GB,一查才发现是个别文件挂了巨大的流。
反过来也存在“流占了空间但删不掉”的假象。某些第三方工具在删除文件时,只删了MFT记录里的主数据流引用,没有把备用流对应的簇回收到空闲簇链表,NTFS就会认为这些簇仍然被占用。表现就是文件明明删了,磁盘可用空间却不回升。遇到这种情况,通常要先把文件彻底删除,再在磁盘管理里做一次“压缩磁盘”或把卷离线再上线,让NTFS完成簇回收。如果还不行,可以用chkdsk /f扫一遍卷,让文件系统把孤儿簇重新标记为空闲。
5. 兼容性边界、数据恢复与常见误区速查
5.1 兼容性铁律:哪些场景会丢ADS
ADS虽强,但它的“存在边界”也很明显。最核心的铁律就是:ADS依赖NTFS,一旦文件离开了NTFS,流几乎必然丢失。我把常见场景整理成了一张表,方便你对照查阅。
| 传输/存储方式 | 是否保留ADS | 说明 |
|---|---|---|
| NTFS分区内复制/移动 | 保留 | 同卷内移动甚至不复制数据,只改MFT引用 |
| NTFS到FAT32/exFAT | 丢失 | 目标文件系统不支持多流,静默剥离 |
| SMB共享(同为NTFS卷) | 保留 | 局域网内常用场景,流能完整传过去 |
| SMB共享(目标为非NTFS) | 丢失 | 取决于服务端文件系统,常见于老NAS |
| zip压缩包 | 丢失 | ZIP格式没有流的概念,WinRAR生成zip同样丢失 |
| rar压缩包 | 可保留 | WinRAR对NTFS ADS有专用存储支持 |
| 网盘/IM传输 | 基本丢失 | 绝大多数同步客户端不处理流 |
| 磁盘镜像(E01/vhd) | 保留 | 整盘级镜像保留所有NTFS元数据 |
这里特别提醒两点。第一,给NAS做备份时,如果NAS端的文件系统是ext4或btrfs,Windows往NAS共享复制文件很可能把流丢掉,这对那些需要保留“文件来源标记”的企业来说是个隐蔽合规风险。第二,企业内部走SMB共享时,传输大文件可以保留ADS,但如果中间经过某些防火墙自带的DLP网关或文件过滤设备,它把文件重新落盘一遍,流也可能被剥离。最稳妥的方式还是定期做流保留性抽检。
5.2 数据恢复视角:GetDataBack for NTFS这类工具如何对待ADS
提到ADS,就绕不开数据恢复。我见过有人在论坛问:“我的文件被删了,用GetDataBack for NTFS扫描,恢复回来的文件怎么好像少了隐藏数据?”这其实不是工具坏了,而是很多恢复软件把恢复的焦点放在主数据流上,ADS属于附加属性,恢复时如果不专门针对MFT记录里的多$DATA属性做解析,很容易被忽略。
从NTFS原理看,删除文件时系统只是把MFT记录标记为空闲,记录里的属性包括备用数据流在内都还留在磁盘上,理论上是可以恢复的。但具体能不能恢复,取决于三个因素:MFT记录是否被复用、备用流占用的簇是否被分配给新文件、恢复工具是否实现了ADS级别的解析逻辑。GetDataBack for NTFS在扫描NTFS时,主数据流的恢复成功率很高;对于ADS,新版工具也一直在跟进支持,但恢复出来的流通常不会自动还原为“隐藏属性”的形态,需要你在恢复设置里手动选择并留意输出文件列表。
我的建议是:如果你确认某个被删文件带有重要ADS,先立刻停止对原分区的读写,用只读方式做整盘镜像;再用支持RAW和NTFS解析的取证工具(比如X-Ways Forensics、EnCase这类)去镜像文件里提取MFT记录,而不是直接在原盘上反复扫描。普通用户如果手头只有GetDataBack这种工具,恢复完后一定要在“高级恢复/额外文件类型”里找找是否有按流名命名的输出文件,别只盯着主文件名找。
5.3 常见认知误区速查
折腾ADS这么多年,我发现很多问题都来自几个根深蒂固的误区,专门列出来一起说清楚。
| 误区 | 真相 |
|---|---|
| dir看不到流,就说明文件没有流 | 普通dir只看主数据流,必须dir /r、streams或PowerShell枚举 |
| 文件属性页大小不变,说明没有隐藏数据 | 属性页大小通常只反映主数据流,ADS大小不计入 |
| ADS一定是恶意行为 | Zone.Identifier等系统合法流大量存在,需要结合流名称和内容判断 |
| 文件复制到U盘,隐藏流也会跟着走 | 非NTFS目标上ADS会被静默丢弃 |
| 杀软没报警,说明没有恶意流 | 部分杀软对ADS扫描不完整,流内容可绕过静态查杀 |
| del file:stream可以删除流 | cmd的del不支持流级删除,要用PowerShell Remove-Item -Stream或streams -d |
| ADS只能藏在文件里,目录上是空的 | 目录MFT记录同样可以挂备用数据流,扫描时要覆盖目录本身 |
还有一件事值得提一下:NTFS的压缩和加密属性会在MFT记录里生成额外的属性条目,比如$EFS、$Reparse、$EA之类的,用PowerShell的-Stream *枚举时,这些系统内部属性有时也会以类似流的形式露面。新手看到这些容易误判“中招了”。区分方法很简单:系统属性流一般名字固定,集中在特定区域;真正的ADS通常是你自己或程序主动创建的,文件名和扩展名更“像正常文件”。拿不准的时候,先在测试机上创建一个已知的ADS样本,对比观察一下差异,再下结论。
最后再分享一个我个人在实际操作中的体会:ADS这套机制,单看觉得冷门,但在排查隐蔽问题、做应急响应、设计备份方案时,它常常是那条“最后一根稻草”。建议每个Windows环境的管理员都把检测ADS的PowerShell脚本存一份,无需等到出事再翻文档。测流的时候,记得先在测试目录里做实验,别直接在正式服务器上乱删。对这个话题,我自己的态度是“不迷信、不轻视”:既不要见流就删,也不能只看主文件大小就放松警惕。希望这篇折腾记录,能帮你把NTFS备用数据流这块盲区补上。