NTFS系统的ADS交换数据流
VC++ 基于NTFS的数据流创建与检测
What are Alternate Streams?(交换数据流)
NTFS alternate streams , 或者叫streams,或者叫ADS(which stands for Alternate Data Streams)是NTFS文件系统中一个非常有用的特性,但很少被人知道。和早期文件系统比如FAT相比,NTFS对描述一个数据文件的名称方面进行了内容上的扩展,如下图所示:
未命名流(The unnamed stream)是NTFS中的强制元素,总是存在的。如果创建一个交换数据流但文件并不存在的话,系统将自动创建一个0字节长度的未命名流。如果对一个未命名流实施删除操作,则删除将针对整个文件,因此,所有的交换数据流将被删除。
安全描述符和文件的属性是文件整体的一部分,而不是未命名数据流的一部分。比如,没有流能够被打开,如果属性设置为read-only的话。
注意:不是所有的attributes都是基于文件的。有一些是基于流的,比如,encrypted,compressed和sparse。
当一个程序(Program)打开一个NTFS文件时,它事实上打开的是未命名流。为了指定一个交换流,需要使用“:”和字符串来将流名对应到文件名。也就是说,filename.exe指定的是文件的未命名流,filename.exe:strname指定的是交换流的文件名strname。
一个目录也可以有交换流,并以与文件流相同的方法进行访问。然而,目录没有未命名流。因此任何未指定流名称的访问将报告Access Denied Error。
由于“:字符”这种格式也被用于驱动器的指定,因此容易引起歧义。比如,“A:B”,既可以表示文件B在驱动器A中,也可以标识文件A的流B。为了避免这一问题,使用“.\A:B”的格式进行前一种表示。
System Support for Stream Operations
好消息是Windows Explorer和命令行copy命令对于交换流和多流文件(multi-stream files)copy操作是接受的。坏消息是系统限制了对这些操作的支持。Windows Explorer不允许任何的流操作。如果在命令行下试图指定流名,则会返回错误信息。
有一些命令能够激活流命令。比如echo和more。比如下面例子:
勿庸置疑,上面命令能够很好的工作,但对于理解交换流的技术来说仍然很困难。当然,借助Hex编辑器能够执行任何流操作。但对于copy或者rename操作来说,使用Hex编辑器不是最好的工具。
因此,一套用户操作交换流的工具被开发出来。
So When to Use Alternate Streams?
当然,在存储任何关键信息时都不需要用到交换流。就的文件系统仍然广泛在使用且不支持NTFS的高级特性。如果Copy一个NTFS文件到USB驱动器、flash Card、CDR/RW,或者其它非NTFS驱动器上的时候,系统将仅仅Copy主文件流(main stream),而忽略所有的交换流。对于FTP和HTTP传输也是如此。没有任何警告信息,一个依赖交换流的用户,可能得到吃惊的信息。因此,Microsoft也不希望提供给用户关于交换流的访问工具。
然而,交换流的确非常有用。交换流是存储一些非关键信息的最正常的位置。比如,极小的图形文件,程序代码的分析信息,文档的拼写检查和数据格式化,或者其它一些信息能够通过交换流轻而易举地获得。这种方式得到的文件能够被存放在任何文件系统上,但存放在NTFS上效率更高。
Programming Considerations
检测驱动器
使用GetVolumeInformation函数检测驱动器是否支持alternate streams:
char szVolName[MAX_PATH], szFSName[MAX_PATH];
DWORD dwSN, dwMaxLen, dwVolFlags;
::GetVolumeInformation("C:\\", szVolName, MAX_PATH, &dwSN, &dwMaxLen, &dwVolFlags, szFSName, MAX_PATH);
if (dwVolFlags & FILE_NAMED_STREAMS) {
// File system supports named streams
}
else {
// Named streams are not supported
}
可以使用更安全的检测文件系统名的方法来代替标志:
if (_stricmp(szFSName, "NTFS") == 0) // If NTFS
创建和打开一个流
创建和打开命名流和未命名流的方法相同:
HANDLE hFile = ::CreateFile("file.dat:alt", ...
主要,如果文件不存在,则创建命名流也将同样创建一个0长度的未命名流。
删除一个流
API函数DeleteFile支持交换流的删除:
::DeleteFile("file.dat:alt");
注意,不能单独删除未命名流,而必须删除所有的交换流。
Copy一个流
可以使用CopyFile/CopyFileEx函数进行交换流的Copy。但这些函数常被用来进行文件拷贝,因此结果往往是用户非期望的。它们执行的是命名流到未命名流的Copy。应该明白下面的不同:
Unnamed stream to unnamed stream:就象操作一个普通文件,所有命名流也会一起被拷贝,如果目标存在,则被替换。
Named stream to unnamed stream:也象操作一个文件一样,但仅仅是一个流被拷贝。存在的目标文件将被删除。功能相当于将整个目标文件替换成一个新的单流(single-stream)文件。
下面是例子代码:
HANDLE hInFile = ::CreateFile(szFromStream, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
HANDLE hOutFile = ::CreateFile(szToStream, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
BYTE buf[64*1024];
DWORD dwBytesRead, dwBytesWritten;
do {
::ReadFile(hInFile, buf, sizeof(buf), &dwBytesRead, NULL);
if (dwBytesRead) ::WriteFile(hOutFile, buf, dwBytesRead, &dwBytesWritten, NULL);
} while (dwBytesRead == sizeof(buf));
::CloseHandle(hInFile);
::CloseHandle(hOutFile);
重命名一个流
It seems there is no way - documented or undocumented - to rename a stream short of directly modifying the appropriate MFT entry.
Enumerating Streams
只有Win32 API函数BackupRead能用来列举流。但用起来却存在问题。即,为了得到流的名字,BackupRead必须读到所有文件的流才行。幸运的是,虽然文档没有记载,但却证明是有效的另一个函数能够被用来获得流信息,就是NtQueryInformationFile (or ZwQueryInformationFile).
// Open a file and obtain stream information
BYTE InfoBlock[64 * 1024]; // Buffer must be large enough
PFILE_STREAM_INFORMATION pStreamInfo = (PFILE_STREAM_INFORMATION)InfoBlock;
IO_STATUS_BLOCK ioStatus;
HANDLE hFile = ::CreateFile(szPath, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
NtQueryInformationFile(hFile, &ioStatus, InfoBlock, sizeof(InfoBlock), FileStreamInformation);
::CloseHandle(hFile);
如果打开一个目录,则代码有一些细微的要求。首先,程序必须具备 SE_BACKUP_NAME权限。第二,必须在调用CreateFile函数时指定 FILE_FLAG_BACKUP_SEMANTICS参数。第三,必须时刻明白这一事实,即目录和普通文件不同,目录可能根本就没有流。对这种情况程序处理也应该考虑。
// Open a directory and obtain stream information
// Obtain backup privilege in case we don't have it
HANDLE hToken;
TOKEN_PRIVILEGES tp;
::OpenProcessToken(::GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken);
::LookupPrivilegeValue(NULL, SE_BACKUP_NAME, &tp.Privileges[0].Luid);
tp.PrivilegeCount = 1;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
::AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL);
::CloseHandle(hToken);
HANDLE hFile = ::CreateFile(szPath, 0, FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
BYTE InfoBlock[64 * 1024]; // Buffer must be large enough
PFILE_STREAM_INFORMATION pStreamInfo = (PFILE_STREAM_INFORMATION)InfoBlock;
IO_STATUS_BLOCK ioStatus;
pStreamInfo->StreamNameLength = 0; // Zero in this field means empty info block
NtQueryInformationFile(hFile, &ioStatus, InfoBlock,
sizeof(InfoBlock), FileStreamInformation);
::CloseHandle(hFile);
函数NtQueryInformationFile在InfoBlock Buffer中存储一个FILE_STREAM_INFORMATION结构的序列。FILE_STREAM_INFORMATION是一个可变长的结构。它的大小存储在NextEntryOffset 中 ,序列的最后一个结构的NextEntryOffset field为0.
StreamName field 包含了流名称,采用UNICODE编码; StreamNameLength是名称的长度,单位是Byte。
现在,已经成功获得了流信息记录的队列,并且能够实现打印了:
WCHAR wszStreamName[MAX_PATH];
for (;;) {
// Check if stream info block is empty (directory may have no stream)
if (pStreamInfo->StreamNameLength == 0) break; // No stream found
// Get null-terminated stream name
memcpy(wszStreamName, pStreamInfo->StreamName, pStreamInfo->StreamNameLength);
wszStreamName[pStreamInfo->StreamNameLength / sizeof(WCHAR)] = L'\0';
print("%S", wszStreamName);
if (pStreamInfo->NextEntryOffset == 0) break; // No more stream records
pStreamInfo = (PFILE_STREAM_INFORMATION)
((LPBYTE)pStreamInfo + pStreamInfo->NextEntryOffset); // Next stream record
}
如果不打算处理目录,则可以将if (pStreamInfo->StreamNameLength == 0)一句移除。每个文件至少有一个流,因此,这一句不是必须的。
注意,流名字包括属性名。未命名看起来是::$DATA的样子,命名流看起来是:alt:$DATA 的样子。
如果安装了DDK ,则所有的头文件和lib文件都有了。在调用函数 NtQueryInformationFile 以前,应包括下面头文件和链接库:
NTQUERYINFORMATIONFILE NtQueryInformationFile;
(FARPROC&)NtQueryInformationFile = ::GetProcAddress(
::GetModuleHandle("ntdll.dll"), "NtQueryInformationFile");
完整的例子参考下面的下载链接。
Command Line Tools
这些工具均有源代码和编译后的工具提供下载。
All our stream-enabled command line tools are free and can be downloaded from the download section. You cannot distribute these tools separately, however you can distribute the original zip archive freely.
Copy Stream
Usage:
cs from_stream to_stream
This command copies separate streams, for example
cs C:\SomeFile.dat:str stream.dat:alt
If the stream is not specified, the command assumes the unnamed stream. For instance, the command
cs c:\report.txt reports.txt:r20
will copy the file's primary stream. If the file report.txt has any alternate streams, they will be ignored (use the standard copy command to copy the file as a whole).
Delete Stream
Usage:
ds stream
Delete the specified stream, for example
ds stream.dat:alt
If no stream name is specified, the command deletes the whole file (deleting the unnamed stream causes all the streams to be deleted).
The command don't ask for confirmation, so be careful.
Rename Stream
There is no known method of renaming a stream, so we have to use the copy/delete sequence. While this method will do the trick, renaming a large stream may take considerable time.
Usage:
rs file oldname newname
Rename the stream oldname of the file file to newname. For example, the command
rs stream.dat alt text
renames stream.dat:alt to stream.dat:text.
List Streams
This command lists all streams of the specified file and their size.
Usage:
ls file
Example:
The LS command returns the standard success code 0 only if at least one alternate stream was found. See the topic "Calling From a Batch File" below for a usage example.
Calling From a Batch File
Like most standard command line commands, the stream commands return the standard exit codes that can be analyzed with the if errorlevel batch command. There are two possible exit codes: 0 means success, and 1 means error. The technique is illustrated by the following example batch file:
@echo off
echo Copying stream...
cs c:\report.txt reports.txt:20
if errorlevel 1 goto cmderr
echo Successfully copied!
goto exitbatch
:cmderr
echo Some error occured.
:exitbatch
rem Exiting the batch file....
The LS command returns the standard success code 0 when at least one alternate stream present. The standard error code 1 is returned if the file contains an unnamed stream only or if I/O error occured. The following example shows how to check for presence of alternate streams:
@echo off
rem This batch file finds and list all files with ADS in the current directory
echo Files containing alternate streams:
for %%f in (*.*) do call :checkf %%f
goto exitbatch
:checkf
rem We don't want to list streams so throw out the output
ls %1 >nul
if not errorlevel 1 echo %1
:exitbatch
This batch file FS.bat can be downloaded as a part of the stream tools package.
Please refer to the Windows Help to learn more about Windows batch files and batch commands.
Downloads
Streamtools download
http://www.flexhex.com/docs/articles/download/streamtools.zip
Streams download
http://www.flexhex.com/docs/articles/download/streams.zip
NTFS系统的ADS交换数据流的更多相关文章
- NTFS的交换数据流ADS应用
NTFS的交换数据流ADS应用 NTFS是Windows常用的文件系统格式.该格式支持交换数据流(Alternate Data Streams,缩写ADS)特性.该特性可以让多个文件流使用同一个文 ...
- NTFS交换数据流隐写的应用
by Chesky ##目录 ####一.NTFS交换数据流(ADS)简介 ####二.ADS应用 写入隐藏文件(文本\图像\可执行文件) ADS在Windows平台下的利用--写入后门 ADS在We ...
- 利用NTFS交换数据流隐藏文件
利用NTFS交换数据流隐藏文件 发表于 2012 年 12 月 15 日 由 晴刃 这篇文章介绍一下Windows的NTFS文件系统的ADS(alternate data streams,交换数据流) ...
- SVN二次开发——让SVN、TSVN(TortoiseSVN)支持windows的访问控制模型、NTFS ADS(可选数据流、NTFS的安全属性)
SVN二次开发 ——让SVN.TSVN(TortoiseSVN)支持windows的访问控制模型.NTFS ADS (可选数据流.NTFS的安全属性) SVN secondary developmen ...
- UNCTF杂项题Hidden secret 之NTFS交换数据流隐写
---恢复内容开始--- 做这道题目的经历比较坎坷,题目中用于隐藏flag的jpg文件出了问题,导致不能被交换数据流隐写所以出题人换了一次题目,最后做法也换了,不过出题人一开始的考察点还是基于NTFS ...
- NTFS中的ADS的一些问题
有关ADS的简单说明请看http://www.xfocus.net/articles/200212/466.html 可以看到ADS在很久以前就被一些安全人员所关注,并且也提出了一些经典的利用,比如隐 ...
- WIN7 不用格式化磁盘怎么把FAT32系统改成NTFS系统
开始-运行,输入cmd回车.假设你要转换D盘.输入convert d: /fs:NTFS回车. [ 此时可能会提示: 访问被拒绝 因为你没有足够的特权 是权限不够的原因 开始--程序--附件 右键&q ...
- Qt编写安防视频监控系统3-通道交换
一.前言 最开始写通道交换的功能的时候,走了很多弯路,比如最开始用最初级的办法,触发交换的时候,先关闭视频,然后设置新的url重新打开视频,这样处理非常低级而且耗内存还卡还很慢,毕竟重新打开视频都需要 ...
- TCP交换数据流——Nagle算法简单记录
Nagle算法: 该算法提出的目的是想解决网络中大量的小的TCP数据包造成网络拥塞的问题,举个例子,当客户端要发送一个字节的TCP数据包到服务器时,我们实际上产生了41字节长的分组:包括20字节的IP ...
随机推荐
- Select Tree Node
这里用到了Oracle的一个树形结构查询函数select * from record START WITH A.TREE_NODE IN ('COST_CTR_10053')CONNECT BY P ...
- 笔记本中的archlinux调节亮度
参考了archlinux的wiki:https://wiki.archlinux.org/index.php/Backlight 说一下情况:/sys/class/backlight/acpi_vid ...
- JavaScript中的apply和call函数详解(转)
每个JavaScript函数都会有很多附属的(attached)方法,包括toString().call()以及apply().听起来,你是否会感到奇怪,一个函数可能会有属于它自己的方法,但是记住,J ...
- 基于配置文件(xml)的S2S3H3搭建
本次环境选择:JDK1.6+MySQL数据库+C3P0连接池+(struts2,spring3,hibernate3) 首先,创建WEB工程 然后倒入相关jar包(maven项目,在pom.xml中导 ...
- 删除变长列字段后使用DBCC CLEANTABLE回收空间
标签:SQL Server Reclaim space 收缩表 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://lzf328.bl ...
- CSU 1597 薛XX后代的IQ
Description 薛XX的低IQ是个令人头疼的问题,他的队友深受其害.幸运的是,薛XX非常有钱,所以他买了一些可以提高他的后代的IQ的药.这种药有三个属性,A,B和P.当薛XX使用这种药的时候, ...
- Android项目架构之业务组件化
前言: 从个人经历来说的话,从事APP开发这么多年来,所接触的APP的体积变得越来越大,业务的也变得越来越复杂,总来来说只有一句话:这是一个APP臃肿的时代!所以为了告别APP臃肿的时代,让我们进入一 ...
- 2017年1月1日 星期日 --出埃及记 Exodus 21:27
2017年1月1日 星期日 --出埃及记 Exodus 21:27 And if he knocks out the tooth of a manservant or maidservant, he ...
- Chapter 1: 随机事件及其概率
1. 随机试验,样本点,样本空间 若试验具有下列特点: 在相同条件下可重复进行 每次试验的可能结果不止一个,且所有可能结果在实验前是已知的 实验前不能确定哪一个结果会发生 则称该试验为随机试验,常记为 ...
- Android first --- 单元测试框架junit
#单元测试junit* 定义一个类继承AndroidTestCase,在类中定义方法,即可测试该方法 * 在指定指令集时,targetPackage指定你要测试的应用的包名 <instrumen ...