jjzjj

c++ - 以非特权用户身份查询 NTFS 特殊文件的元数据?

coder 2024-06-20 原文

在非特权用户上下文中,如何查询 NTFS 特殊文件的大小?

大小对我来说是最重要的元数据,但如果我能得到通常在 WIN32_FIND_DATA 中找到的所有内容,我就不会介意了。

我指的 NTFS 特殊文件是(除其他外):$Mft$MftMirr$LogFile$BadClus 等等。

为了打开 MFT,我必须获得某些权限,打开卷然后解析 MFT。这样就结束了。

而且似乎不可能通过名称打开这些文件(对于其中的大多数),这排除了 NtQueryInformationFile()GetFileInformationByHandle()。或者可能存在我未尝试过的标志组合,可以以某种方式打开它们以查询文件信息?

最后但并非最不重要的一点是,在使用相应的 Win32 API(FindFirstFile() 等)或 NtQueryDirectoryFile() 时,我都没有返回这些文件也不直接使用 IRP_MN_QUERY_DIRECTORY


是的,我知道我可以使用 FSCTL_GET_NTFS_VOLUME_DATA 有效地获取 MFT 的大小,但这只是这些特殊文件之一。

最佳答案

ntfs 卷上,我们可以使用 FSCTL_GET_NTFS_FILE_RECORD 枚举所有文件记录。不幸的是,FileRecordBuffer 的格式在 Windows header 中未记录/未声明。但这是常见的 ntfs 结构。缓冲区以 NTFS_RECORD_HEADER(基类)开头,之后是几个 NTFS_ATTRIBUTE 记录。部分和自定义定义:

union NTFS_FILE_ID 
{
    LONGLONG IndexNumber;

    struct  
    {
        LONGLONG MftRecordIndex : 48;
        LONGLONG SequenceNumber : 16;
    };
};

struct NTFS_RECORD_HEADER 
{
    enum {
        FILE = 'ELIF',
        INDX = 'XDNI',
        BAAD = 'DAAB',
        HOLE = 'ELOH',
        CHKD = 'DKHC'
    } Type;
    USHORT UsaOffset;
    USHORT UsaCount;
    USN Usn;
};

struct NTFS_FILE_RECORD_HEADER : public NTFS_RECORD_HEADER
{
    USHORT SequenceNumber;
    USHORT LinkCount;
    USHORT AttributesOffset;
    USHORT Flags;
    ULONG BytesInUse;
    ULONG BytesAllocated;
    ULONGLONG BaseFileRecord;
    USHORT NextAttributeNumber;

    enum{
        flgInUse = 1, flgDirectory = 2
    };
};

struct NTFS_ATTRIBUTE 
{
    enum ATTRIBUTE_TYPE {
        StandardInformation = 0x10,
        AttributeList = 0x20,
        FileName = 0x30,
        ObjectId = 0x40,
        SecurityDescriptor = 0x50,
        VolumeName = 0x60,
        VolumeInformation = 0x70,
        Data = 0x80,
        IndexRoot = 0x90,
        IndexAllocation = 0xa0,
        Bitmap = 0xb0,
        ReparsePoint = 0xc0,
        EAInformation = 0xd0,
        EA = 0xe0,
        PropertySet = 0xf0,
        LoggedUtilityStream = 0x100,
        StopTag = MAXDWORD
    } Type;
    ULONG Length;
    BOOLEAN Nonresident;
    UCHAR NameLength;
    USHORT NameOffset;
    USHORT Flags;// 1 = Compresed
    USHORT AttributeNumber;
};

struct NTFS_RESIDENT_ATTRIBUTE : public NTFS_ATTRIBUTE 
{
    ULONG ValueLength;
    USHORT ValueOffset;
    USHORT Flags;
};

struct NTFS_NONRESIDENT_ATTRIBUTE : public NTFS_ATTRIBUTE 
{
    LONGLONG LowVcn;
    LONGLONG HighVcn;
    USHORT RunArrayOffset;
    UCHAR CompressionUnit;
    UCHAR Unknown[5];
    LONGLONG AllocationSize;
    LONGLONG DataSize;
    LONGLONG InitializedSize;
    LONGLONG CompressedSize;
};

struct NTFS_ATTRIBUTE_LIST
{
    NTFS_ATTRIBUTE::ATTRIBUTE_TYPE Type;
    USHORT Length;
    UCHAR NameLength;
    UCHAR NameOffset;
    LONGLONG LowVcn;
    LONGLONG FileReferenceNumber : 48;
    LONGLONG FileReferenceNumber2 : 16;
    USHORT AttributeNumber;
    USHORT Unknown[3];
};

struct NTFS_STANDARD_ATTRIBUTE 
{
    LONGLONG CreationTime;
    LONGLONG ChangeTime;
    LONGLONG LastWriteTime;
    LONGLONG LastAccessTime;
    ULONG FileAttributes;
    ULONG Unknown[3];
    ULONG QuotaId;
    ULONG SecurityId;
    ULONGLONG QuotaChange;
    USN Usn;
};

struct NTFS_FILENAME_ATTRIBUTE
{
    NTFS_FILE_ID DirectoryId;
    LONGLONG CreationTime;
    LONGLONG ChangeTime;
    LONGLONG LastWriteTime;
    LONGLONG LastAccessTime;
    LONGLONG AllocationSize;
    LONGLONG DataSize;
    ULONG FileAttributes;
    ULONG EaSize;
    UCHAR FileNameLength;// in symbols !!
    UCHAR NameType;
    WCHAR FileName[];

    enum {
        systemName , longName, shortName, systemName2
    };
};

枚举所有文件的代码如下:

inline ULONG BOOL_TO_ERROR(BOOL f)
{
    return f ? NOERROR : GetLastError();
}

ULONG QFMD(PCWSTR szVolumeName)
{
    HANDLE hVolume = CreateFile(szVolumeName, FILE_GENERIC_READ, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);

    if (hVolume == INVALID_HANDLE_VALUE)
    {
        return GetLastError();
    }

    ULONG cb, BytesReturned;
    NTFS_VOLUME_DATA_BUFFER nvdb;

    ULONG err = BOOL_TO_ERROR(DeviceIoControl(hVolume, FSCTL_GET_NTFS_VOLUME_DATA, 0, 0, &nvdb, sizeof(nvdb), &BytesReturned, 0));

    if (err == NOERROR)
    {
        NTFS_FILE_RECORD_INPUT_BUFFER nfrib;

        cb = FIELD_OFFSET(NTFS_FILE_RECORD_OUTPUT_BUFFER, FileRecordBuffer[nvdb.BytesPerFileRecordSegment]);

        PNTFS_FILE_RECORD_OUTPUT_BUFFER pnfrob = (PNTFS_FILE_RECORD_OUTPUT_BUFFER)alloca(cb);

        // search for maximum valid FileReferenceNumber
        LONG a = 0, b = MAXLONG, o;
        do 
        {
            nfrib.FileReferenceNumber.QuadPart = o = (a + b) >> 1;

            err = BOOL_TO_ERROR(DeviceIoControl(hVolume, FSCTL_GET_NTFS_FILE_RECORD, 
                &nfrib, sizeof nfrib, pnfrob, cb, &BytesReturned, 0));

            err ? b = o : a = o + 1;

        } while(a < b);

        nfrib.FileReferenceNumber.QuadPart--;

        DbgPrint("MftRecordCount=%u\n", nfrib.FileReferenceNumber.LowPart);


        union {
            PVOID FileRecordBuffer;
            PBYTE pb;
            NTFS_RECORD_HEADER* pnrh;
            NTFS_FILE_RECORD_HEADER* pnfrh;
            NTFS_ATTRIBUTE* pna;
            NTFS_RESIDENT_ATTRIBUTE* pnra;
            NTFS_NONRESIDENT_ATTRIBUTE* pnaa;
        };

        NTFS_FILE_ID nfi;
        UNICODE_STRING us = { sizeof (nfi), sizeof (nfi), (PWSTR)&nfi };
        OBJECT_ATTRIBUTES oa = { sizeof(oa), hVolume, &us };

        do 
        {
            FileRecordBuffer = pnfrob->FileRecordBuffer;

            if (err = BOOL_TO_ERROR(DeviceIoControl(hVolume, FSCTL_GET_NTFS_FILE_RECORD, 
                &nfrib, sizeof nfrib, pnfrob, cb, &BytesReturned, 0)))
            {
                break;
            }

            // are really file
            if (
                pnrh->Type != NTFS_RECORD_HEADER::FILE ||
                !(pnfrh->Flags & NTFS_FILE_RECORD_HEADER::flgInUse) ||
                pnfrh->BaseFileRecord
                )
            {
                continue;
            }

            ULONG FileAttributes = INVALID_FILE_ATTRIBUTES;
            ULONGLONG FileSize = 0;

            nfi.MftRecordIndex = pnfrob->FileReferenceNumber.QuadPart;
            nfi.SequenceNumber = pnfrh->SequenceNumber;

            pb += pnfrh->AttributesOffset;

            for( ; ; ) 
            {
                NTFS_FILENAME_ATTRIBUTE* pnfa;
                NTFS_STANDARD_ATTRIBUTE* pnsa;

                switch (pna->Type)
                {
                case NTFS_ATTRIBUTE::StopTag:
                    goto __end;

                case NTFS_ATTRIBUTE::FileName:

                    pnfa = (NTFS_FILENAME_ATTRIBUTE*)RtlOffsetToPointer(pnra, pnra->ValueOffset);

                    if (pnfa->NameType == NTFS_FILENAME_ATTRIBUTE::longName)
                    {
                        //DbgPrint("<< %.*S\n", pnfa->FileNameLength, pnfa->FileName);
                    }
                    break;

                case NTFS_ATTRIBUTE::StandardInformation:

                    pnsa = (NTFS_STANDARD_ATTRIBUTE*)RtlOffsetToPointer(pnra, pnra->ValueOffset);
                    FileAttributes = pnsa->FileAttributes;
                    break;

                case NTFS_ATTRIBUTE::Data:
                    FileSize += pna->Nonresident ? pnaa->DataSize : pnra->ValueLength;
                    break;
                }

                pb += pna->Length;
            }

__end:;

            //HANDLE hFile;
            //IO_STATUS_BLOCK iosb;

            //NTSTATUS status = NtOpenFile(&hFile, FILE_READ_ATTRIBUTES, &oa, &iosb, FILE_SHARE_VALID_FLAGS,
            //  FILE_OPEN_REPARSE_POINT| FILE_OPEN_BY_FILE_ID | FILE_OPEN_FOR_BACKUP_INTENT);

            //if (0 <= status)
            //{
            //  NtClose(hFile);
            //}


        } while (0 <= (nfrib.FileReferenceNumber.QuadPart = pnfrob->FileReferenceNumber.QuadPart - 1));

    }

    CloseHandle(hVolume);

    return err;
}

一些 NTFS System Files ,但是这个列表已经很旧了,存在更多的系统文件。如果要查询具体的系统文件需要将其编号分配给NTFS_FILE_RECORD_INPUT_BUFFER。仅查询 sys 文件的代码变化不大:

ULONG QFMD(PCWSTR szVolumeName)
{
    HANDLE hVolume = CreateFile(szVolumeName, FILE_GENERIC_READ, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);

    if (hVolume == INVALID_HANDLE_VALUE)
    {
        return GetLastError();
    }

    ULONG cb, BytesReturned;
    NTFS_VOLUME_DATA_BUFFER nvdb;

    ULONG err = BOOL_TO_ERROR(DeviceIoControl(hVolume, FSCTL_GET_NTFS_VOLUME_DATA, 0, 0, &nvdb, sizeof(nvdb), &BytesReturned, 0));

    if (err == NOERROR)
    {
        NTFS_FILE_RECORD_INPUT_BUFFER nfrib;

        nfrib.FileReferenceNumber.QuadPart = 0x30;

        cb = FIELD_OFFSET(NTFS_FILE_RECORD_OUTPUT_BUFFER, FileRecordBuffer[nvdb.BytesPerFileRecordSegment]);

        PNTFS_FILE_RECORD_OUTPUT_BUFFER pnfrob = (PNTFS_FILE_RECORD_OUTPUT_BUFFER)alloca(cb);

        union {
            PVOID FileRecordBuffer;
            PBYTE pb;
            NTFS_RECORD_HEADER* pnrh;
            NTFS_FILE_RECORD_HEADER* pnfrh;
            NTFS_ATTRIBUTE* pna;
            NTFS_RESIDENT_ATTRIBUTE* pnra;
            NTFS_NONRESIDENT_ATTRIBUTE* pnaa;
        };

        NTFS_FILE_ID nfi;
        UNICODE_STRING us = { sizeof (nfi), sizeof (nfi), (PWSTR)&nfi };
        OBJECT_ATTRIBUTES oa = { sizeof(oa), hVolume, &us };

        do 
        {
            FileRecordBuffer = pnfrob->FileRecordBuffer;

            if (err = BOOL_TO_ERROR(DeviceIoControl(hVolume, FSCTL_GET_NTFS_FILE_RECORD, 
                &nfrib, sizeof nfrib, pnfrob, cb, &BytesReturned, 0)))
            {
                break;
            }

            // are really file
            if (
                pnrh->Type != NTFS_RECORD_HEADER::FILE ||
                !(pnfrh->Flags & NTFS_FILE_RECORD_HEADER::flgInUse) ||
                pnfrh->BaseFileRecord
                )
            {
                continue;
            }

            ULONG FileAttributes = INVALID_FILE_ATTRIBUTES;
            ULONGLONG FileSize = 0;
            PCWSTR ShortName = 0, LongName = 0, SystemName = 0;
            UCHAR ShortNameLength = 0, LongNameLength = 0, SystemNameLength = 0;

            nfi.MftRecordIndex = pnfrob->FileReferenceNumber.QuadPart;
            nfi.SequenceNumber = pnfrh->SequenceNumber;

            pb += pnfrh->AttributesOffset;

            BOOL bSysFile = FALSE;

            for( ; ; ) 
            {
                union {
                    NTFS_FILENAME_ATTRIBUTE* pnfa;
                    NTFS_STANDARD_ATTRIBUTE* pnsa;
                };

                switch (pna->Type)
                {
                case NTFS_ATTRIBUTE::StopTag:
                    goto __end;

                case NTFS_ATTRIBUTE::FileName:

                    pnfa = (NTFS_FILENAME_ATTRIBUTE*)RtlOffsetToPointer(pnra, pnra->ValueOffset);

                    switch (pnfa->NameType)
                    {
                    case NTFS_FILENAME_ATTRIBUTE::systemName:
                    case NTFS_FILENAME_ATTRIBUTE::systemName2:
                        bSysFile = TRUE;
                        SystemName = pnfa->FileName, SystemNameLength = pnfa->FileNameLength;
                        break;
                    case NTFS_FILENAME_ATTRIBUTE::longName:
                        LongName = pnfa->FileName, LongNameLength = pnfa->FileNameLength;
                        break;
                    case NTFS_FILENAME_ATTRIBUTE::shortName:
                        ShortName = pnfa->FileName, ShortNameLength = pnfa->FileNameLength;
                        break;
                    }
                    break;

                case NTFS_ATTRIBUTE::StandardInformation:

                    pnsa = (NTFS_STANDARD_ATTRIBUTE*)RtlOffsetToPointer(pnra, pnra->ValueOffset);
                    FileAttributes = pnsa->FileAttributes;
                    break;

                case NTFS_ATTRIBUTE::Data:
                    FileSize += pna->Nonresident ? pnaa->DataSize : pnra->ValueLength;
                    break;
                }

                pb += pna->Length;
            }

__end:;

            if (bSysFile)
            {
                HANDLE hFile;
                IO_STATUS_BLOCK iosb;

                NTSTATUS status = NtOpenFile(&hFile, FILE_READ_ATTRIBUTES, &oa, &iosb, FILE_SHARE_VALID_FLAGS,
                    FILE_OPEN_REPARSE_POINT| FILE_OPEN_BY_FILE_ID | FILE_OPEN_FOR_BACKUP_INTENT);

                if (0 <= status)
                {
                    NtClose(hFile);
                }

                char sz[32];
                StrFormatByteSize64A(FileSize, sz, RTL_NUMBER_OF(sz));
                DbgPrint("%I64u: %08x %s [%x] %.*S\n", pnfrob->FileReferenceNumber.QuadPart, 
                    FileAttributes, sz, status, SystemNameLength, SystemName);
            }


        } while (0 <= (nfrib.FileReferenceNumber.QuadPart = pnfrob->FileReferenceNumber.QuadPart - 1));

    }

    CloseHandle(hVolume);

    return err;
}

有了它我得到了下一个结果:

38: 10000006 0 bytes [0] $Deleted
34: 00000020 10.0 MB [0] $TxfLogContainer00000000000000000002
33: 00000020 10.0 MB [0] $TxfLogContainer00000000000000000001
32: 00000020 64.0 KB [0] $TxfLog.blf
31: 00000026 1.00 MB [0] $Tops
30: 80000006 0 bytes [0] $Txf
29: 00000006 0 bytes [0] $TxfLog
28: 00000026 27.0 MB [0] $Repair
27: 00000006 0 bytes [0] $RmMetadata
26: 20000026 0 bytes [c0000034] $Reparse
25: 20000026 0 bytes [c0000034] $ObjId
24: 20000026 0 bytes [c0000034] $Quota
11: 00000006 0 bytes [0] $Extend
10: 00000006 128 KB [0] $UpCase
9: 20000006 0 bytes [c0000034] $Secure
8: 00000006 237 GB [c0000022] $BadClus
7: 00000006 8.00 KB [c0000022] $Boot
6: 00000006 7.42 MB [c0000022] $Bitmap
5: 00000806 0 bytes [0] .
4: 00000006 2.50 KB [0] $AttrDef
3: 00000006 0 bytes [0] $Volume
2: 00000006 64.0 MB [c0000022] $LogFile
1: 00000006 4.00 KB [0] $MFTMirr
0: 00000006 212 MB [0] $MFT

关于c++ - 以非特权用户身份查询 NTFS 特殊文件的元数据?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52928593/

有关c++ - 以非特权用户身份查询 NTFS 特殊文件的元数据?的更多相关文章

  1. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

    我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

  2. ruby - 其他文件中的 Rake 任务 - 2

    我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时

  3. ruby-on-rails - 在 Rails 中将文件大小字符串转换为等效千字节 - 2

    我的目标是转换表单输入,例如“100兆字节”或“1GB”,并将其转换为我可以存储在数据库中的文件大小(以千字节为单位)。目前,我有这个:defquota_convert@regex=/([0-9]+)(.*)s/@sizes=%w{kilobytemegabytegigabyte}m=self.quota.match(@regex)if@sizes.include?m[2]eval("self.quota=#{m[1]}.#{m[2]}")endend这有效,但前提是输入是倍数(“gigabytes”,而不是“gigabyte”)并且由于使用了eval看起来疯狂不安全。所以,功能正常,

  4. ruby-on-rails - Rails 3 中的多个路由文件 - 2

    Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题

  5. ruby - 将差异补丁应用于字符串/文件 - 2

    对于具有离线功能的智能手机应用程序,我正在为Xml文件创建单向文本同步。我希望我的服务器将增量/差异(例如GNU差异补丁)发送到目标设备。这是计划:Time=0Server:hasversion_1ofXmlfile(~800kiB)Client:hasversion_1ofXmlfile(~800kiB)Time=1Server:hasversion_1andversion_2ofXmlfile(each~800kiB)computesdeltaoftheseversions(=patch)(~10kiB)sendspatchtoClient(~10kiBtransferred)Cl

  6. ruby - ECONNRESET (Whois::ConnectionError) - 尝试在 Ruby 中查询 Whois 时出错 - 2

    我正在用Ruby编写一个简单的程序来检查域列表是否被占用。基本上它循环遍历列表,并使用以下函数进行检查。require'rubygems'require'whois'defcheck_domain(domain)c=Whois::Client.newc.query("google.com").available?end程序不断出错(即使我在google.com中进行硬编码),并打印以下消息。鉴于该程序非常简单,我已经没有什么想法了-有什么建议吗?/Library/Ruby/Gems/1.8/gems/whois-2.0.2/lib/whois/server/adapters/base.

  7. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  8. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  9. ruby - 使用 Vim Rails,您可以创建一个新的迁移文件并一次性打开它吗? - 2

    使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta

  10. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

随机推荐