中国人民解放军91515部队 贾文博(海南三亚 572016)
摘要:NTFS是Microsoft公司开发的一种有着良好安全性和稳定性的高性能文件系统,NTFS的文件或文件夹中附加多个额外的数据流,但是其访问一直没有很好的解决办法,本文使用VB2003实现NTFS文件附加数据流的读写类,提供.Net框架下NTFS文件附加数据流的完整解决方案。
关键词:VB.Net NTFS 数据流 类
NTFS(New Technology File System)是Microsoft公司开发的一种有着良好安全性和稳定性的高性能文件系统,广泛用于WindowsNT、Windows2000、WindowsXP、Windows2003等操作系统中。为了使NTFS能访问Macintosh文件服务器的HFS(Hierarchical File System),Microsoft在NTFS中引入了交换数据流ADS(Alternate Data Streams),并提供了访问ADS的命令方式和编程API(Application Programming Interface)。这种机制允许在一个基于NTFS的文件或文件夹中附加多个额外的数据流,但其中只有一个是主数据流(Main Data Stream),其余为附加数据流,每个数据流都可以看作一个独立的文件,其内容可以是任何数据或程序代码,并能通过相应的接口访问,这种访问包括读写和执行。(图1.1)
图1.1
MSDN中已经演示了一种使用C++的MFC访问NTFS文件附加数据流的方法,由于对数据流的访问需要借助API函数,因此到目前为止,尚未有VB.Net程序能够完整地解决操作数据流的问题,为了跨越API与VB.Net的鸿沟,特写了此类访问NTFS文件附加数据流,提供.Net框架下文件附加数据流的完整解决方案。
NTFS文件附加数据流的访问类,应具有如下功能:
对流文件访问需要已知流文件的完整文件名,其完整文件名为:
主文件名:流文件名
声明如下:
Dim sFileStreamName As String = FileName & “:” & sStreamName
相对于.Net,获取文件名后我们需要借助于API函数CreateFile()创建流文件的操作句柄,然后串接到FileStream类进行数据的读取,其关键代码如下:
声明CreateFile函数:
Private Declare Function CreateFile Lib "kernel32" Alias "CreateFileA"
(ByVal lpFileName As String, ByVal dwDesiredAccess As Integer, ByVal dwShareMode As Integer,
ByVal lpSecurityAttributes As Integer, ByVal dwCreationDisposition As Integer,
ByVal dwFlagsAndAttributes As Integer, ByVal hTemplateFile As Integer) As Integer
获得文件操作句柄:
Dim hfile As Integer ‘定义文件操作句柄
Dim tmpfilename As String = sFileStreamName ‘传递完整流文件名
hfile = CreateFile(tmpfilename, GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)
由文件操作句柄串接至FileStream类:
Dim sFile As New FileStream(IntPtr.op_Explicit(hfile), FileAccess.ReadWrite)
其中IntPtr.op_Explicit()函数功能是把API文件操作句柄Integer类型转换为.Net文件操作句柄IntPtr类型。串接之后按照.Net下二进制数据的读取方法操作即可。
对文件写入特定数据流方法与读取特定数据流大体相同,把操作属性改为Write即可。(本文后附完整类代码,可参照实现)
对所有附加数据流名称的枚举需要借助API函数BackupRead()和BackupSeek(),具体功能请查阅《Windows API参考手册完全版》,该函数主要用来在磁带机上读取备份。
Function ReadNTFSStreamsName() As String()
Dim returnNames() As String
'获取文件操作句柄
Dim tmpfilename As String = _str_sfilename ‘待枚举流文件名的NTFS文件
Dim hfile As Integer = CreateFile(tmpfilename, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0)
Try
Dim Sid As New WIN32_STREAM_ID ‘定义流文件头部的格式,此结构后面介绍
Dim dwStreamHeaderSize As Integer = Marshal.SizeOf(Sid) ‘获得占用字节大小
Dim lpContext = 0
Dim bContinue As Boolean = True ‘执行成功标志
Dim num As Integer = -1 ‘附加数据流总数
Dim sum As Integer
‘循环读取文件数据流名称,直到全部读取完毕
While (bContinue)
Dim lRead As Integer = 0
bContinue = BackupRead(hfile, Sid, dwStreamHeaderSize, lRead, False, False, lpContext)
If (bContinue AndAlso lRead = dwStreamHeaderSize) Then
sum += 1
If (Sid.dwStreamNameSize > 0) Then
num += 1
lRead = 0
‘获得数据流名称地址
Dim pName As Int32 = Marshal.AllocHGlobal(Sid.dwStreamNameSize).ToInt32
Try
bContinue = BackupRead(hfile, pName, Sid.dwStreamNameSize, lRead, False, False, lpContext)
Dim bName(Sid.dwStreamNameSize - 1) As Char
‘传递地址数据到字符数组bName
Marshal.Copy(IntPtr.op_Explicit(pName), bName, 0, Sid.dwStreamNameSize)
Dim sName As String = bName
‘分析数据流名称
Dim i As Integer = sName.IndexOf(STREAM_SEP, 1)
If (i > -1) Then
sName = sName.Substring(1, i - 1)
Else
i = sName.IndexOf("/0")
If (i > -1) Then
sName = sName.Substring(1, i - 1)
End If
End If
ReDim Preserve returnNames(num)
returnNames(num) = sName
Catch ex As Exception
Marshal.FreeHGlobal(IntPtr.op_Explicit(pName)) ‘释放内存
End Try
End If
Dim l As Integer = 0
Dim h As Integer = 0
‘移动读取指针
BackupSeek(hfile, Sid.Size.Low, Sid.Size.High, l, h, lpContext)
Else
Exit While
End If
End While
Catch ex As Exception
MsgBox(Hex(GetLastError()))
End Try
CloseHandle(hfile)
Return returnNames
End Function
Function GetNTFSStreamSize(ByVal sStreamName As String) As Long
Try
Dim returnSize As Long
‘获取文件操作句柄同上
Dim sFileStreamName As String = FileName & STREAM_SEP & sStreamName
Dim hfile As Integer
Dim tmpfilename As String = sFileStreamName
Try
hfile = CreateFile(tmpfilename, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0)
Catch ex As Exception
MsgBox(Hex(GetLastError()))
End Try
If hfile = INVALID_HANDLE_VALUE Then
Return Nothing
End If
Dim returnStream As FileStream = New FileStream(IntPtr.op_Explicit(hfile), FileAccess.Read)
returnSize = returnStream.Length
CloseHandle(hfile)
Return returnSize
Catch ex As Exception
Return -1
End Try
End Function
Function DeleteNTFSStream(ByVal sStreamName As String) As Boolean
Try
Dim sFileStreamName As String = FileName & STREAM_SEP & sStreamName
Dim hfile, fsuccess As Integer
fsuccess = DeleteFile(sFileStreamName)
‘返回0值时删除失败
If fsuccess <> 0 Then
Return True
Else
MsgBox(Hex(GetLastError()))
End If
Catch ex As Exception
Return False
End Try
End Function
3.1.1 文中主要用到的结构声明如下:
Private Structure WIN32_STREAM_ID ‘流文件头的格式
Dim dwStreamId As Integer ‘数据流类型
Dim dwStreamAttributes As Integer ‘数据流属性
Dim Size As LARGE_INTEGER
Dim dwStreamNameSize As Integer ‘数据流名称大小
End Structure
Private Structure LARGE_INTEGER
Dim Low As Integer
Dim High As Integer
End Structure
3.1.2 用到的常数声明如下:
Private Const STREAM_SEP = ":" ‘数据流名分隔符
Private Const INVALID_HANDLE_VALUE = -1 ‘文件句柄获取失败
‘CreateFile()函数参数
Private Const CREATE_NEW = 1
Private Const CREATE_ALWAYS = 2
Private Const OPEN_EXISTING = 3
Private Const OPEN_ALWAYS = 4
Private Const TRUNCATE_EXISTING = 5
‘数据流类型,dwStreamId的值(后面的英文说明请参考文献[4])
Private Const BACKUP_DATA = 1 'Standard data
Private Const BACKUP_EA_DATA = 2 'Extended attribute data
Private Const BACKUP_SECURITY_DATA = 3 'Security descriptor data
Private Const BACKUP_ALTERNATE_DATA = 4 'Alternative data streams
Private Const BACKUP_LINK = 5 'Hard link information
Private Const BACKUP_PROPERTY_DATA = 6 'Property data
Private Const BACKUP_OBJECT_ID = 7 'Objects identifiers
Private Const BACKUP_REPARSE_DATA = 8 'Reparse points
Private Const BACKUP_SPARSE_BLOCK = 9 'Sparse file.
‘文件属性常数
Private Const FILE_ATTRIBUTE_READONLY = 1
Private Const FILE_ATTRIBUTE_HIDDEN = 2
Private Const FILE_ATTRIBUTE_SYSTEM = 4
Private Const FILE_ATTRIBUTE_ARCHIVE = 32
Private Const FILE_ATTRIBUTE_NORMAL = 128
Private Const FILE_ATTRIBUTE_TEMPORARY = 256
‘文件读写共享常数
Private Const FILE_NONE_SHARE = 0
Private Const FILE_SHARE_READ = 1
Private Const FILE_SHARE_WRITE = 2
‘其他
Private Const FILE_FLAG_NO_BUFFERING = &H20000000
Private Const FILE_FLAG_WRITE_THROUGH = &H80000000
Private Const GENERIC_READ = &H80000000
Private Const GENERIC_WRITE = &H40000000
3.1.3 A PI函数声明如下:
Private Declare Function CreateFile Lib "kernel32" Alias "CreateFileA" _
(ByVal lpFileName As String, ByVal dwDesiredAccess As Integer, ByVal dwShareMode As Integer, ByVal lpSecurityAttributes As Integer, ByVal dwCreationDisposition As Integer, ByVal dwFlagsAndAttributes As Integer, ByVal hTemplateFile As Integer) As Integer
Private Declare Function BackupRead Lib "kernel32" Alias "BackupRead" _
(ByVal hFile As Integer, ByRef lpBuffer As WIN32_STREAM_ID, ByVal nNumberOfBytesToRead As Integer, ByRef lpNumberOfBytesRead As Integer, ByVal bAbort As Boolean, ByVal bProcessSecurity As Boolean, ByRef lpContext As Integer) As Boolean
Private Declare Function BackupRead Lib "kernel32" Alias "BackupRead" _
(ByVal hFile As Integer, ByVal hName As Integer, ByVal nNumberOfBytesToRead As Integer, ByRef lpNumberOfBytesRead As Integer, ByVal bAbort As Boolean, ByVal bProcessSecurity As Boolean, ByRef lpContext As Integer) As Boolean
Private Declare Function BackupSeek Lib "kernel32" Alias "BackupSeek" _
(ByVal hFile As Integer, ByVal dwLowBytesToSeek As Integer, ByVal dwHighBytesToSeek As Integer, ByRef lpdwLowByteSeeked As Integer, ByRef lpdwHighByteSeeked As Integer, ByRef lpContext As Integer) As Boolean
Private Declare Function DeleteFile Lib "kernel32" Alias "DeleteFileA" (ByVal lpFileName As String) As Integer
‘获取API执行失败代码
Private Declare Function GetLastError Lib "kernel32" Alias "GetLastError" () As Integer
‘关闭打开的文件
Private Declare Function CloseHandle Lib "kernel32" Alias "CloseHandle" (ByVal hObject As Integer) As Integer
在.Net框架下不支持对含有“:”字符的流文件名进行直接访问,因此必须使用CreateFile()函数取得文件操作句柄hfile(integer)然后通过IntPtr.op_Explicit()函数转化为.Net可操作的文件句柄IntPtr类型,用FileStream 的Public Sub New(ByVal handle As System.IntPtr, ByVal access As System.IO.FileAccess)重载函数就可以像正常文件一样操作了。
定义头格式WIN32_STREAM_ID(参考文献[4]),integer类型占位4字节,long类型占位8字节,WIN32_STREAM_ID总共占位20字节,使用BackupRead()函数的两个变形函数读取数据,再结合Marshal类对其中的数据类型进行处理,得到流名称,之后用BackupSeek()移动指针,循环读取文件头,直到枚举出所有附加数据流的名称。
在WIN32_STREAM_ID结构中,dwStreamAttributes(integer)说明了该数据流的类型,具体值所代表的数据类型请参考文献[4],本文不再深入探讨。
在每次读取流文件名称成功后,必须用BackupSeek()移动读取指针,虽然该函数返回执行结果(布尔值),但是实际使用中发现其返回值为False时仍有数据流名称没有被枚举,因此可忽略它的返回值,根据BackupRead()返回值退出循环即可。
在项目中选择添加引用->浏览->选择“JWBStreamOP.dll”文件->确定,即可成功引用。
Dim myStreamOP As New ClassJWBStreamOP(“NTFS文件完整路径”)
该类共有3个只读属性
属性名
返回值类型
备注
FileName
String
只读,在成功声明后使用
Ready
Boolean
只读,该类可操作时为True
Ver
String
只读,类版本、版权信息
该类共有6个方法:
4.3.1 OpenNTFSStream(ByVal sStreamName As String) As System.IO.FileStream 打开指定文件(声明时指定)的指定数据流,返回值为指定数据流的FileStream接口。
参数列表
类型
传递方式
参数说明
sStreamName
String
Byval
流文件名
4.3.2 GetNTFSStreamSize(ByVal sStreamName As String) As Long 获取指定数据流的大小,返回实际大小,执行失败返回-1
参数列表
类型
传递方式
参数说明
sStreamName
String
Byval
流文件名
4.3.3 A ddNTFSStream(ByVal toHidName As String, ByRef percentDone As Double) As Boolean 添加附加数据流,返回执行结果。
参数列表
类型
传递方式
参数说明
toHidName
String
ByVal
待添加的文件路径
percentDone
Double
ByRef
传递一个完成百分比的参数
4.3.4 SaveNTFSStream(ByVal sStreamName As String, ByVal outFileName As String, ByRef percentDone As Double) As Boolean将指定的数据流保存为文件,返回执行结果。
参数列表
类型
传递方式
参数说明
sStreamName
String
ByVal
流文件名
outFileName
String
ByVal
保存文件路径
percentDone
Double
ByRef
传递一个完成百分比的参数
4.3.5 ReadNTFSStreamsName() As String() 获取文件的所有附加数据流名称,返回名称数组。
4.3.6 DeleteNTFSStream(ByVal sStreamName As String) As Boolean 删除指定数据流,返回执行结果。
参数列表
类型
传递方式
参数说明
sStreamName
String
Byval
流文件名
图4.1
Microsoft在.Net中努力构建一个只能访问上层文件系统的框架,但是NTFS文件系统的附加数据流却不能依赖.Net的内置函数访问,不能不说是.Net设计的一大遗憾。附加数据流中的数据通过Windows自带的程序无法浏览和写入,但是又切切实实地占用着可用磁盘空间,如一件文件的“隐身衣”,完全不尊重计算机用户的“知情权”。随着计算机病毒技术的发展,NTFS文件附加数据流也必会成为病毒代码的栖息之地;在涉及敏感信息的领域,使用附加数据流隐藏机密数据轻而易举地就可以躲过过滤系统的侦查,因此对NTFS文件附加数据流的清理已应提上日程,清理其中的“污垢”已迫在眉睫!(如图5.1)
图5.1
本文演示了使用VB.Net操作NTFS文件附加数据流的方法类的关键代码,该类可以直接使用,出于安全考虑,该类屏蔽了对扩展名为.EXE、.COM、.VBS文件的导入,该类对于NTFS文件夹附加数据流的操作暂时没有找到方法。限于本人水平有限,不足之处望广大同仁批评指正!
[1]程序员对 NTFS 2000 的看法第一部分:流与硬链接, Dino Esposito,2000 年 3 月, ms-help://MS.VSCC.2003/MS.MSDNQTR.2003FEB.2052/dntaloc/html/ntfs5.htm
[2] Lester Yu 的思考空间,C#实现对 NTFS 命名流访问的程序,http://www.cnblogs.com/Threading/archive/2004/06/04/13476.html
[3]NTFS数据流存在安全缺陷的原因与对策,西华师范大学,戚淮兵 刁永锋 闫正洲,《计算机安全》 2006.12
[4]《Windows API参考手册完全版》(英文帮助文件),Microsoft 公司
[5]MSDN 2003
[6] Windows API 函数 for Visual Basic(CHM版),陈国强, 1999-12-05
_______________________________________
提供例程
资源名称:文件隐身衣(NTFS文件数据流读写)附VB源代码!下载地址:http://download.csdn.net/source/161510
屏蔽了.EXE,.COM,.VBS文件作为附加数据流的导入,防止用于非法目的。限于本人水平有限,关于文件夹的附加数据流本人没有找到很好的解决办法(不能枚举流名称),请各位同仁不吝赐教!本人QQ:84686E-mail:Jamesjia@tom.com