探索NTFS
出处:论文网
时间:2007-04-13
file
$STANDARD_INFORMATION (resident)
$FILE_NAME (resident)
$DATA (resident)
D:>echo testforattr>file:ATTR
D:>nfi d:file
NTFS File Sector Information Utility.
Copyright (C) Microsoft Corporation 1999. All rights reserved.
file
$STANDARD_INFORMATION (resident)
$FILE_NAME (resident)
$DATA (resident)
$DATA ATTR (resident)
nfi的输出结果$STANDARD_INFORMATION、$FILE_NAME、$DATA等在NTFS中称为属性(Attribute)。属性分为常驻属性(Resident Attribute)与非常驻属性(Nonresident Attribute)。文件的数据也包含在属性中,似乎与属性这个名称有点混谣。不过这又让NTFS有了更加统一的组织文件的形式。这也同时让NTFS有MultiStreams的特性(上面也演示了这个特性)。通过指定的File Record定位给定的Attribute的实现代码如下:
template <class T1, class T2> inline
T1* Padd(T1* p, T2 n) { return (T1*)((char *)p + n); }
PATTRIBUTE FindAttribute(PFILE_RECORD_HEADER file,
ATTRIBUTE_TYPE type, PWSTR name)
{
for (PATTRIBUTE attr = PATTRIBUTE(Padd(file, file->AttributesOffset));
attr->AttributeType != -1;
attr = Padd(attr, attr->Length)) {
if (attr->AttributeType == type) {
if (name == 0 && attr->NameLength == 0) return attr;
if (name != 0 && wcslen(name) == attr->NameLength
&& _wcsicmp(name, PWSTR(Padd(attr, attr->NameOffset))) == 0) return attr;
}
}
return 0;
}
Gary Nebbett提供的这个FindAttribute函数在Attribute name(即第三个参数)不为空串时可能会出现bug,主要原因是_wcsicmp对UNICODE字符串比较时应该是以 结束的标准的C字符串。我在提供的代码中已经纠正了这个错误。
下面我将通过使用SoftICE来分析这段代码得到$MFT的$FILE_NAME属性来得到$MFT的file name。这个示例同样适用于得到其它文件的$FILE_NAME(如上面的file)、还有其它的属性如$DATA等等。
:bpx FindAttribute
Break due to BPX FindAttribute (ET=6.89 seconds)
:locals
[EBP-4] +struct ATTRIBUTE * attr = 0x00344D68 <{...}>
[EBP+8] +struct FILE_RECORD_HEADER * file = 0x00344D38 <{...}>
[EBP+C] enum ATTRIBUTE_TYPE type = AttributeFileName (30)
[EBP+10] +unsigned short * name = 0x004041BC <"$MFT">
:?file
struct FILE_RECORD_HEADER * = 0x00344D38 <{...}>
struct NTFS_RECORD_HEADER Ntfs = {...}
unsigned short SequenceNumber = 0x1, " x01"
unsigned short LinkCount = 0x1, " x01"
unsigned short AttributesOffset = 0x30, " 0"
unsigned short Flags = 0x1, " x01"
unsigned long BytesInUse = 0x2D8, " x02xD8"
unsigned long BytesAllocated = 0x400, " x04 "
unsigned quad BaseFileRecord = 0x0, " "
unsigned short NextAttributeNumber = 0x6, " x06"
file参数我传入的是$MFT,从$MFT的LCN=4可以得到其在卷中的物理地址,这在上面已说明。你也可以使用dskprobe(我机子中为第LCN*SectorsPerCluster=4*8扇区)得到底下SoftICE的输出结果:
:dd @file //以下的注释可对照文中开头列出的FILE_RECORD_HEADER定义。
0023:00344D38 454C4946 0003002A 6D4AC04D 00000000 FILE*...M.Jm....
0023:00344D48 00010001 00010030 000002D8 00000400 ....0...........
----
|__AttributeOffset
0023:00344D58 00000000 00000000 04340006 0000FA0D ..........4.....
0023:00344D68 00000010 00000060 00180000 00000000 ....`...........
-------- --------
| |_指出这个Attribute的长度。定义如下。
|_根据AttributeOffset得到的Attribute头,定义如下。00000010指出这个Attribute为StandardInformation
0023:00344D78 00000048 00000018 2C1761D0 01BFB03C H........a.,<...
Attribute头如下定义:
typedef struct {
ATTRIBUTE_TYPE AttributeType;
ULONG Length;
BOOLEAN Nonresident;
UCHAR NameLength;
USHORT NameOffset;
USHORT Flags; // 0x0001 = Compressed
USHORT AttributeNumber;
} ATTRIBUTE, *PATTRIBUTE;
typedef struct {
ATTRIBUTE Attribute;
ULONG ValueLength;
USHORT ValueOffset;
USHORT Flags; // 0x0001 = Indexed
} RESIDENT_ATTRIBUTE, *PRESIDENT_ATTRIBUTE;
typedef struct {
ULONGLONG DirectoryFileReferenceNumber;
ULONGLONG CreationTime; // Saved when filename last changed
ULONGLONG ChangeTime; // ditto
ULONGLONG LastWriteTime; // ditto
ULONGLONG LastAccessTime; // ditto
ULONGLONG AllocatedSize; // ditto
ULONGLONG DataSize; // ditto
ULONG FileAttributes; // ditto
ULONG AlignmentOrReserved;
UCHAR NameLength;
UCHAR NameType; // 0x01 = Long, 0x02 = Short
WCHAR Name[1];
} FILENAME_ATTRIBUTE, *PFILENAME_ATTRIBUTE;
ATTRIBUTE_TYPE是一个Enum型定义。其中00000010为StandardInformation。30为FileName。因为FileNameAttribute总是一个常驻Attribute,所以我将RESIDENT_ATTRIBUTE定义也给出。OK,现在可以继续Dump下一个Attribute:
// dd @file+file->AttributeOffset+length(StandardInformationAttribute)
:dd @file+30+60
0023:00344DC8 00000030 00000068 00180000 00030000 0...h...........
-------- ------
| |___这里的NameLength与NameOffset指FileNameAttribute名。不要与$MFT FileName混谣。
|_指出这是一个FileNameAttribute。
0023:00344DD8 0000004A 00010018 00000005 00050000 J...............
-------- ---- --------
| | |_根据ValueOffset的值,得到FILENAME_ATTRIBUTE的具体位置。
| |_ValueOffset值
|_ValueLength值
0023:00344DE8 2C1761D0 01BFB03C 2C1761D0 01BFB03C .a.,<....a.,<...
0023:00344DF8 2C1761D0 01BFB03C 2C1761D0 01BFB03C .a.,<....a.,<...
0023:00344E08 00004000 00000000 00004000 00000000 .@.......@......
0023:00344E18 00000006 00000000 00240304 0046004D ..........$.M.F.
-- --------
| |___找到$MFT的FileName了吧。
|_NameLength
0023:00344E28 00000054 00000000 00000080 00000190 T...............
0023:00344E38 00400001 00010000 00000000 00000000 ..@.............
这儿给出了Dump Attribute的一个具体方法。最后我将给出遍历File Record的代码,在给出代码前应该说明一下$MFT中$BITMAP属性。NTFS的这个Attribute相当于LINUX EXT2的s_inode_bitmap数组(Linux 2.0版本)。所以很容易明白$BITMAP的作用,即每bit指出相应File Record的在用情况。以下是DumpAllFileRecord的代码:
BOOL bitset(PUCHAR bitmap, ULONG i)
{
return (bitmap[i >> 3] & (1 << (i & 7))) != 0;
}
VOID DumpAllFileRecord()
{
PATTRIBUTE attr = FindAttribute(MFT, AttributeBitmap, 0);
PUCHAR bitmap = new UCHAR[AttributeLengthAllocated(attr)];
ReadAttribute(attr, bitmap);
ULONG n = AttributeLength(FindAttribute(MFT, AttributeData, 0)) / BytesPerFileRecord;
PFILE_RECORD_HEADER file = PFILE_RECORD_HEADER(new UCHAR[BytesPerFileRecord]);
for (ULONG i = 0; i < n; i++) {
if (!bitset(bitmap, i)) continue;
ReadFileRecord(i, file);
if (file->Ntfs.Type == 'ELIF' && (file->Flags & 3 )) {
attr = FindAttribute(file, AttributeFileName, 0);
if (attr == 0) continue;
PFILENAME_ATTRIBUTE name
= PFILENAME_ATTRIBUTE(Padd(attr, PRESIDENT_ATTRIBUTE(attr)->ValueOffset));
printf("%8lu %.*wsn", i, int(name->NameLength),name->Name)
}
}
}
本文引用Gary Nebbett的些定义可能对Windows 2000版本有些很小的出入,不过Internet有其神奇的地方,虽然Microsoft不提供这些信息,但诸如linux-ntfs GNU工程等均是学习NTFS的一个很好的资料,本文也参考了很多它提供的文档。另外Mark Russinovich的《Inside Win2K NTFS》、《Inside NTFS》、《Exploring NTFS On-disk Structures》等也是很好的NTFS资料。本文仍未涉及NTFS中目录的组织(B+树)等等,可能的话我会另行介绍。文中介绍的完整代码可到http://webcrazy.yeah.net下载。出现的错误也欢迎来信指教(tsu00@263.net)!
最后感谢Anton Altaparmakov,感谢我的同事在出差时抽空给我买到Gary Nebbett的书。感谢我看到的所有资料的原作者们。感谢他们!
参考资料:
1.Gary Nebbett《Windows NT/2000 Native API Reference》
2.Linux-NTFS Project NTFS Documentation Version 0.4
3.Mark Russinovich相关文档
4.David Solomom《Inside Windows NT,2nd Edition》
- 上一篇:监视、控制计算机的使用
- 下一篇:消息钩子函数入门篇