c - 如何用C读取主文件表(MFT)?

标签 c windows winapi

我想使用 C 读取本地系统的 MFT。 我在互联网上寻找解决方案,但没有找到。 我希望有人为我提供一个教程,或者通过代码示例提供有关如何执行此操作的良好解释。 提前致谢。

最佳答案

首先,我们需要使用 FILE_READ_DATA 权限打开卷句柄。

然后我们需要用NTFS_VOLUME_DATA_BUFFER控制代码来查询FSCTL_GET_NTFS_VOLUME_DATA

从这里我们得到了单个 MFT 记录的大小 - BytesPerFileRecordSegmentMFT 的总大小 - MftValidDataLength.因此最大记录数为(MftValidDataLength.QuadPart/BytesPerFileRecordSegment)

正确的方法(与 NTFS 同步)是通过 FSCTL_GET_NTFS_FILE_RECORD 读取单个记录。

如果您想一次读取多条记录 - 当然可以直接从卷中读取。我们有 MFT 的起始 LCN - MftStartLcn。但是MFT可以有几个不连续的片段。所以如果我们想获取所有片段位置,我们需要使用FSCTL_GET_RETRIEVAL_POINTERS。要将LCN转换为卷偏移量,我们需要将其乘以BytesPerCluster

演示代码:

void ReadMft(PCWSTR szVolume)
{
    HANDLE hVolume = CreateFileW(szVolume, FILE_READ_DATA, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, FILE_OPEN_FOR_BACKUP_INTENT, 0);

    if (hVolume != INVALID_HANDLE_VALUE)
    {
        NTFS_VOLUME_DATA_BUFFER nvdb;

        OVERLAPPED ov = {};

        if (DeviceIoControl(hVolume, FSCTL_GET_NTFS_VOLUME_DATA, 0, 0, &nvdb, sizeof(nvdb), 0, &ov))
        {
            NTFS_FILE_RECORD_INPUT_BUFFER nfrib;

            nfrib.FileReferenceNumber.QuadPart = nvdb.MftValidDataLength.QuadPart / nvdb.BytesPerFileRecordSegment - 1;

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

            PNTFS_FILE_RECORD_OUTPUT_BUFFER pnfrob = (PNTFS_FILE_RECORD_OUTPUT_BUFFER)alloca(cb);

            do 
            {
                if (!DeviceIoControl(hVolume, FSCTL_GET_NTFS_FILE_RECORD, 
                    &nfrib, sizeof(nfrib), pnfrob, cb, 0, &ov))
                {
                    break;
                }
                
                // pnfrob->FileRecordBuffer :
                // here pnfrob->FileReferenceNumber FileRecord

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

            ReadMft2(szVolume, hVolume, &nvdb);
        }

        CloseHandle(hVolume);
    }
}

void ReadMft2(PCWSTR szVolume, HANDLE hVolume, PNTFS_VOLUME_DATA_BUFFER nvdb)
{
    static PCWSTR MFT = L"\\$MFT";

    static STARTING_VCN_INPUT_BUFFER vcn {};

    static volatile UCHAR guz;

    PVOID stack = alloca(guz);

    union {
        PVOID buf;
        PWSTR lpFileName;
        PRETRIEVAL_POINTERS_BUFFER rpb;
    };

    buf = alloca(wcslen(szVolume) * sizeof(WCHAR) + sizeof(MFT));

    wcscat(wcscpy(lpFileName, szVolume), MFT);

    HANDLE hFile = CreateFileW(lpFileName, 0, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, FILE_OPEN_FOR_BACKUP_INTENT, 0);

    if (hFile != INVALID_HANDLE_VALUE)
    {
        OVERLAPPED ov{};

        ULONG cb = RtlPointerToOffset(buf, stack), rcb, ExtentCount = 2;

        do 
        {
            rcb = __builtin_offsetof(RETRIEVAL_POINTERS_BUFFER, Extents[ExtentCount]);

            if (cb < rcb)
            {
                cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
            }

            if (DeviceIoControl(hFile, FSCTL_GET_RETRIEVAL_POINTERS, &vcn, sizeof(vcn), buf, cb, 0, &ov))
            {
                if (rpb->Extents->Lcn.QuadPart != nvdb->MftStartLcn.QuadPart)
                {
                    __debugbreak();
                }

                if (ExtentCount = rpb->ExtentCount)
                {
                    auto Extents = rpb->Extents;

                    ULONG BytesPerCluster = nvdb->BytesPerCluster;
                    ULONG BytesPerFileRecordSegment = nvdb->BytesPerFileRecordSegment;

                    LONGLONG StartingVcn = rpb->StartingVcn.QuadPart, NextVcn, len;

                    PVOID FileRecordBuffer = alloca(BytesPerFileRecordSegment);

                    do 
                    {
                        NextVcn = Extents->NextVcn.QuadPart;
                        len = NextVcn - StartingVcn, StartingVcn = NextVcn;

                        DbgPrint("%I64x %I64x\n", Extents->Lcn.QuadPart, len);

                        if (Extents->Lcn.QuadPart != -1)
                        {
                            Extents->Lcn.QuadPart *= BytesPerCluster;
                            ov.Offset = Extents->Lcn.LowPart;
                            ov.OffsetHigh = Extents->Lcn.HighPart;

                            // read 1 record
                            ReadFile(hVolume, FileRecordBuffer, BytesPerFileRecordSegment, 0, &ov);
                        }

                    } while (Extents++, --ExtentCount);
                }
                break;
            }

            ExtentCount <<= 1;

        } while (GetLastError() == ERROR_MORE_DATA);

        CloseHandle(hFile);
    }
}

关于c - 如何用C读取主文件表(MFT)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54606760/

相关文章:

windows - Windows 上 git log 中的颜色和作者?

c# - 如何计算 1 个特定类(class)的所有开放形式?

c++ - 当调用原始版本的 DLL Hook 函数时,我得到无限递归

C 编程 Char 可能的值

c++ - 如何获取在对 VirtualAlloc 的初始分配调用中保留的区域大小

windows - 在 Perl for win32 中获取 CPU ID 序列号

c++ - 使用父窗口的控件打开新窗口

java - Netty 使用标准套接字获取响应

c - C 中的求和无法解决

c - 如何在源代码中正确定位不平衡的括号?