How to Programmatically Retrieve NTFS File Sector Allocation Data on Windows


4 views

When dealing with low-level file system operations or forensic analysis, determining the physical sectors occupied by a specific file on NTFS is a common requirement. The NTFS Master File Table (MFT) contains all necessary metadata, but accessing this information programmatically requires specific Windows API calls.

While nfi.exe from the Windows 2000 OEM toolkit can map sectors to files, we need the reverse operation. Modern Windows versions include built-in capabilities:

fsutil file queryextents C:\\path\\to\\file.txt

This command returns the Logical Cluster Numbers (LCNs) which can be converted to physical sectors.

Here's a C++ implementation using DeviceIoControl with FSCTL_GET_RETRIEVAL_POINTERS:

#include <windows.h>
#include <stdio.h>

void GetFileSectors(LPCWSTR filename) {
    HANDLE hFile = CreateFile(filename, GENERIC_READ, 
        FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
    
    if (hFile == INVALID_HANDLE_VALUE) {
        printf("Error opening file: %d\n", GetLastError());
        return;
    }

    STARTING_VCN_INPUT_BUFFER inputBuffer;
    RETRIEVAL_POINTERS_BUFFER outputBuffer;
    DWORD bytesReturned;

    inputBuffer.StartingVcn.QuadPart = 0;

    if (!DeviceIoControl(hFile, FSCTL_GET_RETRIEVAL_POINTERS,
        &inputBuffer, sizeof(inputBuffer),
        &outputBuffer, sizeof(outputBuffer),
        &bytesReturned, NULL)) {
        printf("Error: %d\n", GetLastError());
        CloseHandle(hFile);
        return;
    }

    printf("Extent Count: %d\n", outputBuffer.ExtentCount);
    for (DWORD i = 0; i < outputBuffer.ExtentCount; i++) {
        printf("LCN: %I64d, Length: %I64d sectors\n",
            outputBuffer.Extents[i].Lcn.QuadPart,
            outputBuffer.Extents[i].NextVcn.QuadPart - 
            (i ? outputBuffer.Extents[i-1].NextVcn.QuadPart : 0));
    }

    CloseHandle(hFile);
}

For Python developers, the pywin32 package provides access to these APIs:

import win32file

def get_file_sectors(path):
    hfile = win32file.CreateFile(
        path,
        win32file.GENERIC_READ,
        win32file.FILE_SHARE_READ,
        None,
        win32file.OPEN_EXISTING,
        0,
        None
    )
    
    try:
        lcn_info = win32file.DeviceIoControl(
            hfile,
            win32file.FSCTL_GET_RETRIEVAL_POINTERS,
            b'\x00'*8,  # StartingVcn = 0
            512
        )
        # Process the binary data here
        return parse_lcn_data(lcn_info)
    finally:
        win32file.CloseHandle(hfile)

Remember that sectors are typically 512 bytes or 4K in modern drives. To convert LCNs to physical sector addresses:

physical_sector = (volume_start_sector) + (lcn * sectors_per_cluster)

Use IOCTL_DISK_GET_DRIVE_GEOMETRY_EX to get sector size information.

For those preferring GUI tools:

  • WinHex (forensic tool with sector view)
  • Active@ Disk Editor
  • Runtime's DiskExplorer for NTFS

When performing low-level disk operations or forensic analysis, developers often need to determine the exact physical sectors occupied by specific files. NTFS (New Technology File System) maintains complex metadata structures that map logical file positions to physical disk locations.

The most reliable method involves using the Windows FSCTL_GET_RETRIEVAL_POINTERS control code with DeviceIoControl:


#include <windows.h>
#include <stdio.h>

void PrintFileClusters(LPCWSTR filename) {
    HANDLE hFile = CreateFileW(filename, 
                              GENERIC_READ, 
                              FILE_SHARE_READ, 
                              NULL, 
                              OPEN_EXISTING, 
                              FILE_FLAG_NO_BUFFERING, 
                              NULL);

    if (hFile == INVALID_HANDLE_VALUE) {
        printf("Error opening file: %d\n", GetLastError());
        return;
    }

    STARTING_VCN_INPUT_BUFFER inputBuffer;
    inputBuffer.StartingVcn.QuadPart = 0;

    RETRIEVAL_POINTERS_BUFFER outputBuffer;
    DWORD bytesReturned;

    if (!DeviceIoControl(hFile, 
                        FSCTL_GET_RETRIEVAL_POINTERS, 
                        &inputBuffer, 
                        sizeof(inputBuffer), 
                        &outputBuffer, 
                        sizeof(outputBuffer), 
                        &bytesReturned, 
                        NULL)) {
        printf("DeviceIoControl failed: %d\n", GetLastError());
        CloseHandle(hFile);
        return;
    }

    // Output the cluster information
    printf("File clusters:\n");
    for (DWORD i = 0; i < outputBuffer.ExtentCount; i++) {
        printf("Cluster %lld to %lld\n", 
               outputBuffer.Extents[i].Lcn.QuadPart,
               outputBuffer.Extents[i].Lcn.QuadPart + 
               outputBuffer.Extents[i].NextVcn.QuadPart - 1);
    }

    CloseHandle(hFile);
}

For quick checks without writing code, these tools can help:

  • fsutil: fsutil file queryextents C:\path\to\file
  • defrag: defrag C: /A /V (provides fragmentation analysis)

To convert cluster numbers to physical sectors, you'll need additional information:


// After getting cluster info from above
DWORD sectorsPerCluster;
DWORD bytesPerSector;
GetDiskFreeSpaceW(L"C:\\", NULL, §orsPerCluster, NULL, NULL);
GetDiskFreeSpaceW(L"C:\\", NULL, NULL, &bytesPerSector, NULL);

LONGLONG physicalSector = clusterNumber * sectorsPerCluster;

Several specialized tools provide this functionality:

  • WinHex: Includes sector-level file mapping
  • Active@ Disk Editor: Shows file-to-sector mapping
  • NTFSInfo (Sysinternals): Provides detailed NTFS metadata

Files with multiple extents require handling each fragment separately. The RETRIEVAL_POINTERS_BUFFER structure's ExtentCount field indicates how many fragments exist.