峥嵘岁月

joshua_yu的网络空间

俞峥ID:joshua_yu
97370次访问,排名842好友0人,关注者7
多年网络安全产品项目管理和研发经验
joshua_yu的文章
原创 43 篇
翻译 2 篇
转载 34 篇
评论 18 篇
joshua_yu的公告
联系方式: QQ:404271575 MSN:joshua_yu@263.net
最近评论
mohroq:wow gold,
owen870215:好帖
不过和我用的版本的源码还是不一样
继续学习
joshua_yu:对不起,好久没有上来了,才看到你的留言。
编译驱动程序需要用DDK的编译环境,你可能直接在VC当中编译了。
zh517:提示"cannot open file "wbemuuid.lib" "
zh517:大哥.我下了你的代码,可以编译不过去啊!先是提示找不到wbemcli.h文件,后来我从DDK下找到了这个文件放到了James\Exe\inc\下.继续编译,不提示"cannot open file "wbemuuid.lib" "怎么回事啊?我是初学者,是不是VC和DDK需要配置一下啊?
文章分类
收藏
    相册
    08年第一期儿子照片
    过年
    交大新面貌
    我的可爱儿子
    周末烧烤之众生相
    关注的Blog
    EVA的回收站
    joyfire的space
    Kendiv的专栏
    PJF的Blog
    WebCrazy的Blog
    野路子(http://wulujia.com)
    铁卷大成天下
    网络收藏夹
    China CISSP论坛(文档保护)
    China Uniix
    developerWorks Linux 专栏
    docshow
    linux伊甸园
    OSR在线论坛
    PKI论坛
    reactos
    rootkit论坛
    Sysinternals论坛
    中国Linux公社
    中国Linux论坛
    协议分析网论坛
    安全焦点
    看雪技术学院论坛
    驱动开发网
    存档
    软件项目交易
    订阅我的博客
    XML聚合  FeedSky
    订阅到鲜果
    订阅到Google
    订阅到抓虾
    订阅到BlogLines
    订阅到Yahoo
    订阅到GouGou
    订阅到飞鸽
    订阅到Rojo
    订阅到newsgator
    订阅到netvibes

    转载 探索NTFS收藏

    新一篇: 关于文件系统设备堆栈的说明

        NTFS是Windows NT引入的新型文件系统,它具有许多新特性。本文旨在探索NTFS的底层结构,所叙述的也仅是文件在NTFS卷上的分布。NTFS中,卷中所有存放的数据均在一个叫$MFT的文件中,叫主文件表(Master File Table)。而$MFT则由文件记录(File Record)数组构成。File Record的大小一般是固定的,通常情况下均为1KB,这个概念相当于Linux中的inode。File Record在$MFT文件中物理上是连续的,且从0开始编号。$MFT仅供File System本身组织、架构文件系统使用,这在NTFS中称为元数据(Metadata)。以下列出Windows 2000 Release出的NTFS的元数据文件(我将要给出的示例代码的部分输出结果)。

        File Record(inode) FileName
        ------------------ --------
              0             $MFT
              1             $MFTMirr
              2             $LogFile
              3             $Volume
              4             $AttrDef
              5             .
              6             $Bitmap
              7             $Boot
              8             $BadClus
              9             $Secure
             10             $UpCase
             11             $Extend

        Windows 2000中不能使用dir命令(甚至加上/ah参数)像普通文件一样列出这些元数据文件。实际上File System Driver(ntfs.sys)维护了一个系统变量NtfsProtectSystemFiles用于隐藏这些元数据。默认情况下,这个变量被设为TRUE,所以使用dir /ah将得不到任何文件。知道这个行为后使用i386kd修改NtfsProtectSystemFiles后即可以列出元数据文件:

        kd> x ntfs!NtfsProtect*
        fe213498  Ntfs!NtfsProtectSystemFiles
        fe21349c  Ntfs!NtfsProtectSystemAttributes
        kd> dd ntfs!NtfsProtectSystemFiles l 2
        fe213498  00000001 00000001
        kd> ed ntfs!NtfsProtectSystemFiles 0
        kd> dd ntfs!NtfsProtectSystemFiles l 2
        fe213498  00000000 00000001
        kd>

        D:\>ver

        Microsoft Windows 2000 [Version 5.00.2195]

        D:\>dir /ah $*
         驱动器 D 中的卷是 W2KNTFS
         卷的序列号是 E831-9D04

         D:\ 的目录

        2000-04-27  19:31               36,000 $AttrDef
        2000-04-27  19:31                    0 $BadClus
        2000-04-27  19:31               67,336 $Bitmap
        2000-04-27  19:31                8,192 $Boot
        2000-04-27  19:31       <DIR>          $Extend
        2000-04-27  19:31           13,139,968 $LogFile
        2000-04-27  19:31           27,575,296 $MFT
        2000-04-27  19:31                4,096 $MFTMirr
        2000-04-27  19:31              131,072 $UpCase
        2000-04-27  19:31                    0 $Volume
                       9 个文件     40,961,960 字节
                       1 个目录     51,863,552 可用字节

        需要指出的是ntfs.sys将元数据文件以一种特殊的方式打开,所以在打开NtfsProtectSystemFiles后,如果使用ReadFile等产生IRP_MJ_READ等IRP包时将会导致Page Fault(详见Gary Nebbett的《Windows NT/2000 ative API Reference》)。

        以上的讨论均是基于$MFT文件而讨论的,即基于$MFT中的File Record(inode)讨论的。
    为更好的继续以下的讨论,这儿我列出File Record Header的结构:

        typedef struct {
            ULONG Type;
            USHORT UsaOffset;
            USHORT UsaCount;
            USN Usn;
        } NTFS_RECORD_HEADER, *PNTFS_RECORD_HEADER;

        typedef struct {
            NTFS_RECORD_HEADER Ntfs;
            USHORT SequenceNumber;
            USHORT LinkCount;
            USHORT AttributesOffset;
            USHORT Flags;               // 0x0001 = InUse, 0x0002 = Directory
            ULONG BytesInUse;
            ULONG BytesAllocated;
            ULONGLONG BaseFileRecord;
            USHORT NextAttributeNumber;
        } FILE_RECORD_HEADER, *PFILE_RECORD_HEADER;

        下面我将讨论如何定位$MFT。稍微有点操作系统知识的人都会知道引导扇区(Boot Sector),其物理位置为卷中的第一个扇区。以下由dskprobe.exe(Windows 2000 Resource Kit中的一个小工具)分析的第一个扇区(当然也可以使用WinHex等其他应用程序):

        File: d:\Sector00.bin
        Size: 0x00000200 (512)

        Address  | 00 01 02 03-04 05 06 07 : 08 09 0A 0B-0C 0D 0E 0F | 0123456789ABCDEF
        ---------|-------------------------:-------------------------|-----------------
        00000000 | EB 52 90 4E-54 46 53 20 : 20 20 20 00-02 08 00 00 | ?R?NTFS    .....
        00000010 | 00 00 00 00-00 F8 00 00 : 3F 00 F0 00-3F 00 00 00 | .....?..?.e.?...
        00000020 | 00 00 00 00-80 00 80 00 : 90 C0 41 00-00 00 00 00 | ....?.?.?懒.....
        00000030 | 04 00 00 00-00 00 00 00 : 09 1C 04 00-00 00 00 00 | ................
        00000040 | F6 00 00 00-01 00 00 00 : 04 9D 31 E8-BB 31 E8 94 | ?.......?杌1??
                            .                         .
                            .                         .
                            .                         .
        000001F0 | 00 00 00 00-00 00 00 00 : 83 A0 B3 C9-00 00 55 AA | ........??成..U?

     

        这512字节为如下的格式:(摘自Gary Nebbett书中,本文许多代码均来自或参考此书。)
     
        #pragma pack(push, 1)

        typedef struct {
            UCHAR Jump[3];
            UCHAR Format[8];
            USHORT BytesPerSector;
            UCHAR SectorsPerCluster;
            USHORT BootSectors;
            UCHAR Mbz1;
            USHORT Mbz2;
            USHORT Reserved1;
            UCHAR MediaType;
            USHORT Mbz3;
            USHORT SectorsPerTrack;
            USHORT NumberOfHeads;
            ULONG PartitionOffset;
            ULONG Reserved2[2];
            ULONGLONG TotalSectors;
            ULONGLONG MftStartLcn;
            ULONGLONG Mft2StartLcn;
            ULONG ClustersPerFileRecord;
            ULONG ClustersPerIndexBlock;
            ULONGLONG VolumeSerialNumber;
            UCHAR Code[0x1AE];
            USHORT BootSignature;
        } BOOT_BLOCK, *PBOOT_BLOCK;

        #pragma pack(pop)

        各个字段的详细意义从字段名中即可大致清楚。在linux-ntfs的GNU工程(http://sf.net/projects/linux-ntfs)中也有详细的文档,限于篇幅我不将其列出。可以使用如下代码读出卷中的第一个扇区:

        hVolume = CreateFile(drive, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0,
                             OPEN_EXISTING, 0, 0);

        ReadFile(hVolume, &bootb, sizeof(bootb), &n, 0);

        bootb是一个BOOT_BLOCK结构,在我的卷中如下格式(请对应Sector00.bin分析):

        Dump BootBlock at below:
            BytesPerSector:200
            SectorsPerCluster:8
            BootSectors:0
            SectorsPerTrack:3F
            NumberOfHeads:F0
            PartitionOffset:3F
            TotalSectors:41C090
            MftStartLcn:4
            Mft2StartLcn:41C09
            ClustersPerFileRecord:F6
            ClustersPerIndexBlock:1
            VolumeSerialNumber:E8319D04
            BootSignature:AA55


        以上的MftStartLcn其实是$MFT在卷中的簇(Cluster)号。簇是NTFS的基本单位,最小单位。一个只有1Byte的文件也要占用一簇的空间。NTFS使用LCN(Logical Cluster Number)来代表NTFS卷中的物理位置,其简单的从0到卷中的总簇数减一进行编号。对于一个特定的文件NTFS则使用VCN(Virtual Cluster Number)来映射LCN实现文件的组织。从MftStartLcn的值4可以知道$MFT的LCN为4与SectorsPerCluster、BytesPerSector的大小即可定位$MFT的位置。得到$MFT的位置后,如果遍历$MFT中所有的File Record即可以得到卷中所有的文件列表(前面已
    经提到File Record只是简单的从0开始编号)。也就是说到目前为止已经可以对文件组织有最简单的认识,但如何得到文件的信息呢,如文件名等等。NTFS中所有文件包括普通的用户文件、元数据文件均用同样的方式组织数据、属性等。我将nfi.exe(来自Windows NT/2000 OEM Support Tools)的输出结果列出,作为我叙述的开始:

        D:\>copy con file
        testforntfs^Z
        已复制         1 个文件。

        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)

        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字符串比较时应该是以\0结束的标准的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, "\0\x01"
         unsigned short LinkCount = 0x1, "\0\x01"
         unsigned short AttributesOffset = 0x30, "\00"
         unsigned short Flags = 0x1, "\0\x01"
         unsigned long BytesInUse = 0x2D8, "\0\0\x02\xD8"
         unsigned long BytesAllocated = 0x400, "\0\0\x04\0"
         unsigned quad BaseFileRecord = 0x0, "\0\0\0\0\0\0\0\0"
         unsigned short NextAttributeNumber = 0x6, "\0\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 %.*ws\n", 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》

    发表于 @ 2006年02月04日 13:03:00|评论(loading...)|编辑

    旧一篇: Windows文件系统的过滤器驱动程序设计

    评论:没有评论。

    发表评论  


    当前用户设置只有注册用户才能发表评论。如果你没有登录,请点击登录
    Csdn Blog version 3.1a
    Copyright © joshua_yu