During the previous summer I worked on a research project that focused heavily on data mining and interpolation. The nature of the project required data to be constantly switched from physical memory to RAM. As a result, I looked into a variety of read techniques for a variety of file systems. One piece of code I wrote used the Master File Table to directly index file locations from disk. This technique was of course completely inefficient for fast lookups, but it was useful in that it gave me a better understanding of the NTFS file system.
Anyways, I took this code and cannibalized it so that it will just read a systems volume boot sector and then give you the master file table’s byte offset. The code just uses a series of ReadFile calls to the physical drive in order to read the volume boot sector. At which point we just uses some arithmetic to determine where the master file table is located.
Once you know the location of the master file table you can iterate through the segments in order to read files on the system. I didn’t include the code that accomplishes that task as it’s simply a matter of reading headers and manipulating the offset into the disk.
// Get Master File Table Offset
// Aaron Burrow (burrows@mit.edu)
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <windows.h>
#include <tchar.h>
typedef struct _Volume_Boot_Sector { // Offsets
BYTE jmp_boot_ldr[3]; // 0x00
UCHAR system_id[8]; // 0x03
WORD bytes_per_sector; // 0x0B
BYTE sectors_per_cluster; // 0x0D
WORD reserved_sectors; // 0x0E
BYTE reserved1[3]; // 0x10
WORD unused1; // 0x13
BYTE media_desc; // 0x15
WORD reserved2; // 0x16
WORD sectors_per_track; // 0x18
WORD number_of_heads; // 0x1A
DWORD hidden_sectors; // 0x1C
DWORD unused2; // 0x20
DWORD unused3; // 0x24
DWORDLONG total_sectors; // 0x28
DWORDLONG mft_logical_cluster_number; // 0x30
DWORDLONG mft_mirror_logical_cluster_number; // 0x38
DWORD cluters_per_file_record_seg; // 0x40
DWORD cluster_per_index_block; // 0x44
DWORDLONG volume_serial_number; // 0x48
DWORD checksum; // 0x50
} __attribute__ ((__packed__)) Volume_Boot_Sector, *PVolume_Boot_Sector;
// This is just a container we use to simplify code
typedef struct _Physical_Drive_Descriptor{
HANDLE physical_handle;
USHORT sector_size; // In bytes
USHORT cluster_size; // In bytes
} Physical_Drive_Descriptor, *PPhysical_Drive_Descriptor;
Physical_Drive_Descriptor* init_physical_drive_descriptor(VOID);
VOID cleanup_physical_drive_descriptor(Physical_Drive_Descriptor* ptr_phys_desc);
DWORD read_physical_volume(Physical_Drive_Descriptor* ptr_phys_desc, LPTSTR drive_path, BYTE** buffer);
BOOL get_volume_boot_sector(Physical_Drive_Descriptor* ptr_phys_desc, BYTE* buffer, Volume_Boot_Sector* vol_boot_sector);
LONGLONG get_master_file_table_offset(Physical_Drive_Descriptor* ptr_phys_desc, Volume_Boot_Sector vol_boot_sector);
INT main(INT argc, LPTSTR* argv)
{
LPTSTR drive = TEXT("C");
BYTE* buffer;
TCHAR path[MAX_PATH + 1];
INT size;
LONG file_size;
LONGLONG mft_offset;
Physical_Drive_Descriptor* phys_desc;
Volume_Boot_Sector vol_boot_sector;
_stprintf(path, TEXT("\\\\.\\%s:"), drive);
if ((phys_desc = init_physical_drive_descriptor())) {
if ((size = read_physical_volume(phys_desc, path, &buffer)) > 0) {
if (get_volume_boot_sector(phys_desc, buffer, &vol_boot_sector)) {
mft_offset = get_master_file_table_offset(phys_desc, vol_boot_sector);
printf("MFT Offset: %llX\n", mft_offset);
}
free (buffer);
}
cleanup_physical_drive_descriptor(phys_desc);
}
return 0;
}
Physical_Drive_Descriptor* init_physical_drive_descriptor(VOID)
{
Physical_Drive_Descriptor* temp = calloc(1, sizeof(Physical_Drive_Descriptor));
if (temp)
temp->physical_handle = INVALID_HANDLE_VALUE;
return temp;
}
VOID cleanup_physical_drive_descriptor(Physical_Drive_Descriptor* ptr_phys_desc)
{
if (ptr_phys_desc->physical_handle != INVALID_HANDLE_VALUE)
CloseHandle(ptr_phys_desc->physical_handle);
free (ptr_phys_desc);
return;
}
DWORD read_physical_volume(Physical_Drive_Descriptor* ptr_phys_desc, LPTSTR drive_path, BYTE** buffer) // Thanks to Napalm@netcore2k, he originally helped me with this function
{
TCHAR physical_drive_path[MAX_PATH+1];
DWORD bytes_read;
*buffer = NULL;
ptr_phys_desc->physical_handle = INVALID_HANDLE_VALUE;
ptr_phys_desc->physical_handle = CreateFile(drive_path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if (ptr_phys_desc->physical_handle == INVALID_HANDLE_VALUE)
goto cleanup_exit;
if (!(*buffer = calloc(512, sizeof(BYTE))))
goto cleanup_exit;
if (SetFilePointer(ptr_phys_desc->physical_handle, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
goto cleanup_exit;
if (!ReadFile(ptr_phys_desc->physical_handle, *buffer, 512, &bytes_read, NULL))
goto cleanup_exit;
return bytes_read;
cleanup_exit:
if (ptr_phys_desc->physical_handle != INVALID_HANDLE_VALUE) {
CloseHandle(ptr_phys_desc->physical_handle);
ptr_phys_desc->physical_handle = INVALID_HANDLE_VALUE;
}
if (*buffer)
free (*buffer);
return -1;
}
BOOL get_volume_boot_sector(Physical_Drive_Descriptor* ptr_phys_desc, BYTE* buffer, Volume_Boot_Sector* vol_boot_sector)
{
memcpy((void*)vol_boot_sector, (void*)buffer, sizeof(Volume_Boot_Sector));
if (!(_tcsncmp(vol_boot_sector->system_id, TEXT("NTFS"), 4))) {
ptr_phys_desc->sector_size = vol_boot_sector->bytes_per_sector;
ptr_phys_desc->cluster_size = vol_boot_sector->bytes_per_sector * vol_boot_sector->sectors_per_cluster;
return TRUE;
}
else
return FALSE;
}
LONGLONG get_master_file_table_offset(Physical_Drive_Descriptor* ptr_phys_desc, Volume_Boot_Sector vol_boot_sector)
{
return (LONGLONG)vol_boot_sector.bytes_per_sector * vol_boot_sector.sectors_per_cluster * vol_boot_sector.mft_logical_cluster_number;
}
Until later.
no shit…. I had it all wrong…. lol…dad
Comment by burrow — October 19, 2010 @ 9:49 pm