Click here to Skip to main content
15,885,435 members
Articles / Programming Languages / C

FDump - Dumping File Sectors Directly from Disk using Logical Offsets

Rate me:
Please Sign up or sign in to vote.
4.89/5 (23 votes)
8 Jan 2009CPOL5 min read 82.7K   4.5K   55  
An article on reading file sectors directly from disk using logical offsets
// fdump.cpp : Defines the entry point for the console application.
//

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

#include "fs_io.h"

////////////////////////////////////////////////////////////////////////////

typedef BOOL ( CALLBACK* LPFNPARSEPARAM_CALLBACK )(
	IN INT		iArgCount,
	IN INT		iArgIndex,
	IN INT		iArgSubIndex,
	IN LPCTSTR	pszArgument,
	IN BOOL		bArgIsFlag,
	IN LPVOID	lpUserParam 
);

typedef struct _USER_PARAMS
{
	TCHAR	szSrcFile[ _MAX_PATH + 1 ];
	TCHAR	szDstFile[ _MAX_PATH + 1 ];
	BOOL	bVerbose;
	BOOL	bHelp;

} USER_PARAMS, *PUSER_PARAMS, *LPUSER_PARAMS;

////////////////////////////////////////////////////////////////////////////
// Globals:

COORD  g_curPos      = { 0 };
COORD  g_scrRect     = { 0 };
HANDLE g_hEventBreak = NULL;

////////////////////////////////////////////////////////////////////////////

STDAPI_( BOOL ) ConsoleCtrlHandler( IN DWORD dwCtrlType );

VOID ConsoleGetCursorPos	( VOID );
VOID ConsoleRewindCursorPos	( VOID );

VOID PrintUsage			 ( VOID );
VOID PrintLine			 ( IN LPCTSTR pszName, IN LPCTSTR pszValFmt, IN INT nLineMax, IN ... );
VOID PrintVolInfo		 ( IN PFS_VOLUME_INFO pFsVolInfo );
VOID PrintFileInfo		 ( IN PFS_FILE_INFO pFsFileInfo );
VOID PrintPercentComplete( IN DWORD dwLcn, IN DWORD dwPercentComplete, IN BOOL bVerbose );
VOID PrintLastError		 ( IN DWORD dwJobType );

////////////////////////////////////////////////////////////////////////////

VOID ConsoleGetCursorPos( VOID ) 
{
	CONSOLE_SCREEN_BUFFER_INFO csbi = { 0 };

	GetConsoleScreenBufferInfo( 
		GetStdHandle( STD_OUTPUT_HANDLE ), &csbi
	);

	CopyMemory( &g_scrRect, &csbi.dwSize,           sizeof( COORD ) );
	CopyMemory( &g_curPos,  &csbi.dwCursorPosition, sizeof( COORD ) );	
}

VOID ConsoleRewindCursorPos( VOID ) 
{
	// Clear the line:

	FillConsoleOutputCharacter( 
		GetStdHandle( STD_OUTPUT_HANDLE ), 
		_T( ' ' ), g_scrRect.X, g_curPos, NULL
	);

	// Bring cursor back into initial position:

	SetConsoleCursorPosition( 
		GetStdHandle( STD_OUTPUT_HANDLE ), g_curPos
	);
}

// ConsoleCtrlHandler runs in different from main's thread, that is
// why we do not start new thread for FsSectorCopy - we already have
// different ones, one - main and one - ConsoleCtrlHandler:

STDMETHODIMP_( BOOL ) ConsoleCtrlHandler( IN DWORD dwCtrlType )
{
	BOOL bRetVal = FALSE;

	switch( dwCtrlType ) 
	{
	case CTRL_C_EVENT:
	case CTRL_CLOSE_EVENT:
	case CTRL_BREAK_EVENT:
	case CTRL_LOGOFF_EVENT:
	case CTRL_SHUTDOWN_EVENT:
		bRetVal = SetEvent( g_hEventBreak );
		break;
	default:
		_ASSERTE( FALSE ); // Some new control code
		break;
	}

	return bRetVal;
} 

////////////////////////////////////////////////////////////////////////////

VOID PrintUsage( VOID )
{
	_tprintf( _T( "\n" ) );
	_tprintf( _T( " Dumps file to another location reading" )
			  _T( " its sectors directly from disk.\n"      ) );
	_tprintf( _T( "\n" ) );
	_tprintf( _T( " FDUMP [/V] source destination\n" ) );
	_tprintf( _T( "\n" ) );
	_tprintf( _T( " source       Specifies the file to be dumped directly from disk.\n" ) );
	_tprintf( _T( " destination  Specifies the output file name for the dumped data.\n" ) );
	_tprintf( _T( " /V           Produce verbose output.\n" ) );
	_tprintf( _T( " \n" ) );
}

VOID PrintLine( IN LPCTSTR pszName, 
			    IN LPCTSTR pszValFmt, 
			    IN INT	   nLineMax,
			    IN ... )
{
	INT i;
	va_list vaArgList;

	LPTSTR pTmp = NULL;
	TCHAR szText[ 256 ] = { 0 };

	_tprintf( _T( "   %s" ), pszName );

	for( i = 0; i < nLineMax - lstrlen( pszName ) + 2; i++ )
		_tprintf( _T( "." ) );
	
	va_start( vaArgList, nLineMax );
	StringCbVPrintf( szText, sizeof( szText ), pszValFmt, vaArgList );

	pTmp = szText + ( g_scrRect.X - nLineMax - 10 ); // :)
	for( i = 0; *pTmp++; ++i )
	{
		*pTmp = _T( '.' );
		if( 3 == i )
		{
			*pTmp = _T( '\0' );
			break;
		}
	}

	_tprintf( _T( "%s\n" ), szText  );
	va_end( vaArgList );
}

VOID PrintVolInfo( IN PFS_VOLUME_INFO pFsVolInfo )
{
	static LPCTSTR spszDrvType[] = {	
		_T( "Unknown"	  ),
		_T( "No Root Dir" ),
		_T( "Removable"	  ),
		_T( "Fixed"		  ),
		_T( "Remote"	  ),
		_T( "CDROM"		  ),
		_T( "RAM Disk"	  ),
	};

	static LPCTSTR spszFsType[] = {	
		_T( "UNKNOWN" ),
		_T( "NTFS"	  ),
		_T( "exFAT"	  ),
		_T( "FAT12"	  ),
		_T( "FAT16"	  ),
		_T( "FAT32"	  ),
		_T( "HPFS"	  ),
		_T( "CDFS"	  ),	
	};

	TCHAR szTemp[ 256 ] = { 0 };
	LONGLONG llDiskSize = 0;

	_ASSERTE( pFsVolInfo );
	if( !pFsVolInfo )
		return;

	llDiskSize = (LONGLONG)pFsVolInfo->dwTotalNumberOfClusters *
				 (LONGLONG)pFsVolInfo->dwBytesPerCluster;

	StrFormatByteSize64( llDiskSize, szTemp, _countof( szTemp ) );

	_tprintf( _T( "\n" ) );
	_tprintf( _T( " Source Volume Data:\n" ) );
	PrintLine( _T( "Volume Path"			 ), _T( "%s"	 ), 25, pFsVolInfo->pszPath );
	PrintLine( _T( "Unique Name"			 ), _T( "%s"	 ), 25, pFsVolInfo->pszName );
	PrintLine( _T( "Volume Label"			 ), _T( "%s"	 ), 25, pFsVolInfo->pszLabel );
	PrintLine( _T( "Serial Number"			 ), _T( "%d"	 ), 25, pFsVolInfo->dwSerialNumber );
	PrintLine( _T( "Drive Number"			 ), _T( "%d"	 ), 25, pFsVolInfo->iDriveNumber );
	PrintLine( _T( "Drive Type"				 ), _T( "%s"	 ), 25, spszDrvType[ pFsVolInfo->uDriveType ] );
	PrintLine( _T( "Handle"					 ), _T( "%#0.8X" ), 25, pFsVolInfo->hVolume );
	PrintLine( _T( "First Sector Offset"	 ), _T( "%I64d"  ), 25, pFsVolInfo->qwFirstSectorOffset );
	PrintLine( _T( "Total Size"				 ),	_T( "%s"	 ), 25, szTemp );
	PrintLine( _T( "Bytes Per Sector"		 ),	_T( "%d"	 ), 25, pFsVolInfo->dwBytesPerSector );
	PrintLine( _T( "Sector Per Cluster"		 ),	_T( "%d"	 ), 25, pFsVolInfo->dwSectorPerCluster );
	PrintLine( _T( "Number Of Free Clusters" ),	_T( "%d"	 ), 25, pFsVolInfo->dwNumberOfFreeClusters );
	PrintLine( _T( "Number Of Used Cluster"  ),	_T( "%d"	 ),	25, pFsVolInfo->dwNumberOfUsedClusters );
	PrintLine( _T( "File System Type"		 ),	_T( "%s" 	 ),	25, spszFsType[ pFsVolInfo->dwFileSystemType ] );
	PrintLine( _T( "File System Flags"		 ),	_T( "%#0.8X" ), 25, pFsVolInfo->dwFileSystemFlags );
	_tprintf( _T( "\n" ) );
}

VOID PrintFileInfo( IN PFS_FILE_INFO pFsFileInfo )
{
	TCHAR szTemp[ 256 ] = { 0 };

	_ASSERTE( pFsFileInfo );
	if( !pFsFileInfo )
		return;
	
	StrFormatByteSize64( 
		pFsFileInfo->liFileSize.QuadPart, szTemp, _countof( szTemp )
	);

	_tprintf( _T( "\n" ) );
	_tprintf( _T( " Source File Data\n" ) );
	PrintLine( _T( "Input Path"			), _T( "%s"			), 25, pFsFileInfo->pszPath );
	PrintLine( _T( "Resolved path"		), _T( "%s"			), 25, pFsFileInfo->pszResolvedPath );
	PrintLine( _T( "File Handle"		), _T( "%#0.8X"		), 25, pFsFileInfo->hFile );
	PrintLine( _T( "File Size"			), _T( "%s"			), 25, szTemp );
	PrintLine( _T( "NTFS Record Index"	), _T( "%#0.16I64X" ), 25, pFsFileInfo->liFileIndex );
	PrintLine( _T( "File Attributes"	), _T( "%#0.8X"		), 25, pFsFileInfo->dwFileAttributes );
	_tprintf( _T( "\n" ) );
}

VOID PrintPercentComplete( IN DWORD dwLcn,
						   IN DWORD dwPercentComplete,
						   IN BOOL  bVerbose )
{
	if( dwPercentComplete )
	{
		static TCHAR spszProg[] = 
			{ _T( '-' ), _T( '\\' ), _T( '|' ), _T( '/' ) };

		static INT iProg = 0;

		ConsoleRewindCursorPos();

		if( _countof( spszProg ) == iProg )
			iProg = 0;

		if( bVerbose )
		{
			_tprintf( 
				_T( "[%c] #%d, %d%% completed" ),
				spszProg[ iProg++ ], dwLcn, dwPercentComplete
			);
		}
		else
		{
			_tprintf( 
				_T( "[%c] %d%% completed" ),
				spszProg[ iProg++ ], dwPercentComplete
			);
		}

		if( 100 == dwPercentComplete )
			_tprintf( _T( "\n" ) );
	}
	else
	{
		if( bVerbose )
			_tprintf( _T( " Copying logical cluster" ) );
		else
			_tprintf( _T( " Copying data" ) );

		ConsoleGetCursorPos();
	}	
}

VOID PrintLastError( IN DWORD dwJobType )
{
	static LPCTSTR spszJob[] = 
	{
		_T( "complete unknown operation"	),
		_T( "get source volume info"		),
		_T( "get source file info"			),
		_T( "get source file extents"		),
		_T( "get source file info from MFT"	),
		_T( "create destination file"		),
		_T( "copy file data from MFT"		),
		_T( "copy data sectors from disk"	),
	};

	LPTSTR pszMsgBuf = NULL;

	_ASSERTE( dwJobType >= FS_JOB_MIN && dwJobType <= FS_JOB_MAX );
	if( FS_JOB_MIN > dwJobType || FS_JOB_MAX < dwJobType )
		return;

	FormatMessage( 
		FORMAT_MESSAGE_ALLOCATE_BUFFER | 
		FORMAT_MESSAGE_FROM_SYSTEM | 
		FORMAT_MESSAGE_IGNORE_INSERTS,
		NULL,
		GetLastError(),
		0,
		(LPTSTR)&pszMsgBuf,
		0,
		NULL 
	);

	_tprintf( 
		_T( " Cannot %s!\n Last error[%d]: %s\n" ), 
		spszJob[ dwJobType ], GetLastError(), pszMsgBuf
	);

	if( pszMsgBuf )
		LocalFree( pszMsgBuf );
}

////////////////////////////////////////////////////////////////////////////

STDMETHODIMP_( BOOL ) FsProgressCallback( IN LPCVOID lpUserParam,
										  IN DWORD	 dwJobType,
										  IN BOOL	 bJobDone,
										  IN LPCVOID lpJobContext,
										  IN DWORD	 dwPercentComplete )
{
	BOOL bRetVal = bJobDone;

	PUSER_PARAMS pUserParams = (PUSER_PARAMS)lpUserParam;
	_ASSERTE( pUserParams );

	switch( dwJobType )
	{
	case FS_JOB_GET_SRC_VOLUME_INFO:
		{
			PFS_VOLUME_INFO pFsVolInfo = (PFS_VOLUME_INFO)lpJobContext;
			_ASSERTE( pFsVolInfo );

			if( pUserParams->bVerbose )
				PrintVolInfo( pFsVolInfo );

			if( bJobDone &&
				pFsVolInfo->dwFileSystemFlags & FILE_VOLUME_IS_COMPRESSED )
			{
				_tprintf( _T( "Compressed volumes are not supported!\n" ) );
				bRetVal = FALSE;
			}
		}
		break;
	case FS_JOB_GET_SRC_FILE_INFO:
		{
			PFS_FILE_INFO pFsFileInfo = (PFS_FILE_INFO)lpJobContext;
			_ASSERTE( pFsFileInfo );

			if( pUserParams->bVerbose )
				PrintFileInfo( pFsFileInfo );

			if( bJobDone &&
				( pFsFileInfo->dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED ||
				pFsFileInfo->dwFileAttributes & FILE_ATTRIBUTE_SPARSE_FILE ) )
			{
				_tprintf( _T( "Compressed files are not supported!\n" ) );
				bRetVal = FALSE;
			}
		}
		break;	
	case FS_JOB_COPY_DATA_FROM_DISK:
		{
			PrintPercentComplete(
				(DWORD)(DWORD_PTR)lpJobContext, dwPercentComplete, pUserParams->bVerbose
			);
		}
		break;
	case FS_JOB_GET_SRC_FILE_EXTENTS:
	case FS_JOB_GET_SRC_FILE_MFT:
	case FS_JOB_CREATE_FILE_DST:
	case FS_JOB_COPY_DATA_FROM_MFT:
		break;
	default:
		_ASSERTE( FALSE ); // Some new job type
		return FALSE;
	};

	if( !bJobDone )
		PrintLastError( dwJobType );

	return bRetVal;
}

////////////////////////////////////////////////////////////////////////////

STDMETHODIMP_( INT ) ParseCommandLine( IN LPFNPARSEPARAM_CALLBACK lpfnParseParam,
									   IN LPVOID lpUserParam )
{
	INT		iArgIndex   = 0;
	INT		iParamIndex = 0;
	INT		iFlagIndex  = 0;
	INT		iArgCount   = 0;

	BOOL    bCallback   = FALSE;
	BOOL	bArgIsFlag  = FALSE;	
	LPTSTR	pszArgument = NULL;

	_ASSERTE( !IsBadCodePtr( (FARPROC)lpfnParseParam ) );
	if( IsBadCodePtr( (FARPROC)lpfnParseParam ) )
		return -1;

	iArgCount = __argc;

	for( iArgIndex = 1; iArgIndex < iArgCount; iArgIndex++ )
	{
		pszArgument = __targv[ iArgIndex ];
		bArgIsFlag  = ( !ChrCmpI( TEXT( '-' ), *pszArgument ) || 
						!ChrCmpI( TEXT( '/' ), *pszArgument ) );

		if( bArgIsFlag )
			++pszArgument;

		bCallback = lpfnParseParam( 
			iArgCount - 1, iArgIndex - 1,
			( bArgIsFlag ? iFlagIndex++ : iParamIndex++ ),
			pszArgument, bArgIsFlag, lpUserParam
		);

		if( !bCallback )
			break;
	}

	return ( iArgIndex - 1 );
}

STDAPI_( BOOL ) ParseParamCallback( IN INT		iArgCount,
								    IN INT		iArgIndex,
								    IN INT		iArgSubIndex,
								    IN LPCTSTR	pszArgument,
								    IN BOOL		bArgIsFlag,
								    IN LPVOID	lpUserParam )
{
	PUSER_PARAMS pUserParams = (PUSER_PARAMS)lpUserParam;
	_ASSERTE( pUserParams );

	UNREFERENCED_PARAMETER( iArgCount );

	if( !pUserParams )
		return FALSE;

	if( bArgIsFlag )
	{
		if( !pUserParams->bHelp )
		{
			pUserParams->bHelp = 
				!StrCmpI( _T( "?"    ), pszArgument ) ||
				!StrCmpI( _T( "h"    ), pszArgument ) ||
				!StrCmpI( _T( "help" ), pszArgument );
		}

		if( !pUserParams->bVerbose )
		{
			pUserParams->bVerbose = 
				!StrCmpI( _T( "v"       ), pszArgument ) ||
				!StrCmpI( _T( "verbose" ), pszArgument );
		}
	}
	else // if( !bArgIsFlag )
	{
		LPTSTR pszDstBuf = NULL;

		if( 0 == iArgSubIndex )
			pszDstBuf = pUserParams->szSrcFile;
		if( 1 == iArgSubIndex )
			pszDstBuf = pUserParams->szDstFile;

		if( pszDstBuf )
			StringCchCopy( pszDstBuf, MAX_PATH, pszArgument );
	}

	if( ( pUserParams->bHelp ) ||
		( 3 == iArgCount && 
		  2 == iArgIndex ) )
	{
		return FALSE;
	}

	return TRUE;
}

////////////////////////////////////////////////////////////////////////////

INT _tmain( IN INT argc, IN TCHAR* argv[] )
{
	TCHAR szOldTitle[ MAX_PATH + 1 ] = { 0 };

	LPCTSTR pszTitle = _T( "FDUMP version 1.0 by Armen Hakobyan, 2008" );

	OSVERSIONINFO	 osvi			= { 0 };
	USER_PARAMS		 userParams     = { 0 };
	FS_CALLBACK_DATA fsCallbackData = { 0 };

	UNREFERENCED_PARAMETER( argc );
	UNREFERENCED_PARAMETER( argv );

	__try
	{
		GetConsoleTitle( szOldTitle, _countof( szOldTitle ) );	
		SetConsoleTitle( pszTitle );

		_tprintf( _T( "%s\n" ), pszTitle );
		
		osvi.dwOSVersionInfoSize = sizeof( OSVERSIONINFO );

		GetVersionEx( &osvi );

		if( VER_PLATFORM_WIN32_NT != osvi.dwPlatformId ||
			osvi.dwMajorVersion < 5 )
		{
			_tprintf( _T( "Windows 2000 or later is required!\n" ) );
			__leave;
		}

		if( !IsUserAnAdmin() )
			_tprintf( _T( "User must be an administrator to open system volumes!\n" ) );

		if( !ParseCommandLine( &ParseParamCallback, &userParams ) ||
			userParams.bHelp )
		{
			PrintUsage();
			__leave;
		}

		if( !lstrlen( userParams.szSrcFile ) ||
			!lstrlen( userParams.szDstFile ) )
		{
			_tprintf( _T( "Invalid source or destination file!\n" ) );
			__leave;
		}

		if( PathIsNetworkPath( userParams.szSrcFile ) )
		{
			_tprintf( _T( "Source network paths are not supported!\n" ) );
			__leave;
		}

		g_hEventBreak = CreateEvent( NULL, TRUE, FALSE, NULL );
		if( !g_hEventBreak )
			__leave;

		SetConsoleCtrlHandler( &ConsoleCtrlHandler, TRUE );
		ConsoleGetCursorPos();

		fsCallbackData.hEventBreak      = g_hEventBreak;
		fsCallbackData.lpUserParam      = &userParams;
		fsCallbackData.pfnFsProgress    = &FsProgressCallback;

		FsSectorCopy(
			userParams.szSrcFile, userParams.szDstFile, &fsCallbackData
		);

		SetConsoleTitle( szOldTitle );
	}
	__finally
	{
	}

	return 0;
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) SafeNet Inc
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions