Burrows Code Blog

October 16, 2010

Get Master File Table Offset

Filed under: General Programming — Tags: , , , , , , — burrowscode @ 1:31 pm

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.

Advertisement

1 Comment »

  1. no shit…. I had it all wrong…. lol…dad

    Comment by burrow — October 19, 2010 @ 9:49 pm


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Theme: WordPress Classic. Blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.