RestoreFile调用(文件管理API-fmapi.dll)返回无效的句柄

时间:2018-07-16 07:07:46

标签: c++

背景

我正在尝试编写一个C ++应用程序,该应用程序可以扫描并尝试从WinPE环境中恢复已删除的文件,主要是作为学习练习。此应用程序利用了FMAPI库(fmapi.dll),这是一个文献很少的库,仅在WinPE环境中可用(在完整的Windows操作系统中不可用)。我一直使用MS(可用here发布)的ScanRestorableFiles示例作为起点。

现在,我已经进行了大量的挖掘工作,并且在涉及FMAPI文档时几乎什么都没发现-仅是上面提到的示例和一些基本的MSDN文档here。 MSDN页面提供API函数的定义,并在几个提供提示的功能上提供了一些额外的注释,仅此而已。所以我以为我会来这里,希望能得到一些帮助。

还请注意,就开发语言而言,C ++不是我的强项-我认为自己最多还是新手。

现在要解决的问题:

我的应用程序能够成功加载库,创建文件还原上下文并扫描可恢复的文件。但是,一旦尝试在ScanRestorableFiles()调用返回的可恢复项之一上调用RestoreFile()函数,就会收到无效句柄错误。 “还原的文件”最终是一个0字节的文件(确实在正确的位置成功创建了文件),其中没有数据。

有趣的是,即使返回了Invalid Handle错误代码,我的应用程序也会在文件上保持打开句柄,直到关闭文件还原上下文为止(我知道这一点,因为如果我尝试在还原文件后立即读取已还原的文件,我收到“文件正在被另一个进程使用”错误。)

下面发布的是我应用程序的全部代码(它只是一个源文件,不包括标题等)-由于这似乎是很少使用的API,我觉得我应该发布整个文件以添加上下文每个函数调用(也是因为我不太确定到底是什么与我遇到的问题有关)。

代码:

#include <windows.h>
#include <stdio.h>
#include <iostream>
#include <string>
#include <sstream>
#include <stdlib.h>

#define SCAN_PATH L"\\"

//
//Define the needed FMAPI structures as documented in FMAPI
//
#define FILE_RESTORE_MAJOR_VERSION_2    0x0002
#define FILE_RESTORE_MINOR_VERSION_2    0x0000
#define FILE_RESTORE_VERSION_2          ((FILE_RESTORE_MAJOR_VERSION_2 << 16) | FILE_RESTORE_MINOR_VERSION_2)

using namespace std;

//External API function declarations

//We don't have an import library or
//header for the FMAPI functions, so
//we must dynamically link to them at
//runtime.

typedef PVOID PFILE_RESTORE_CONTEXT;

typedef enum  {
    ContextFlagVolume                   = 0x00000001,
    ContextFlagDisk                     = 0x00000002,
    FlagScanRemovedFiles                = 0x00000004,
    FlagScanRegularFiles                = 0x00000008,
    FlagScanIncludeRemovedDirectories   = 0x00000010 
} RESTORE_CONTEXT_FLAGS;

typedef enum {
    FileRestoreProgressInfo = 100,
    FileRestoreFinished     = 101
} FILE_RESTORE_PACKET_TYPE, *PFILE_RESTORE_PACKET_TYPE;

typedef BOOL (WINAPI *FuncCreateFileRestoreContext) (
    _In_  PCWSTR                Volume,
    _In_  RESTORE_CONTEXT_FLAGS Flags,
    _In_  LONGLONG              StartSector,
    _In_  LONGLONG              BootSector,
    _In_  DWORD                 Version,
    _Out_ PFILE_RESTORE_CONTEXT* Context
    );

typedef BOOL (WINAPI *FuncCloseFileRestoreContext) (
  _In_  PFILE_RESTORE_CONTEXT Context
);

typedef struct _RESTORABLE_FILE_INFO
{
    ULONG           Size;
    DWORD           Version;
    ULONGLONG       FileSize;
    FILETIME        CreationTime;
    FILETIME        LastAccessTime;
    FILETIME        LastWriteTime;
    DWORD           Attributes;
    BOOL            IsRemoved;
    LONGLONG        ClustersUsedByFile;
    LONGLONG        ClustersCurrentlyInUse;
    ULONG           RestoreDataOffset;
    WCHAR           FileName[1]; // Single-element array indicates a variable-length structure
} RESTORABLE_FILE_INFO, *PRESTORABLE_FILE_INFO;

typedef struct _FILE_RESTORE_PROGRESS_INFORMATION {
    LONGLONG    TotalFileSize;
    LONGLONG    TotalBytesCompleted;
    LONGLONG    StreamSize;
    LONGLONG    StreamBytesCompleted;
    PVOID       ClbkArg;
} FILE_RESTORE_PROGRESS_INFORMATION, *PFILE_RESTORE_PROGRESS_INFORMATION;

typedef struct _FILE_RESTORE_FINISHED_INFORMATION {
    BOOL    Success;
    ULONG   FinalResult;
    PVOID   ClbkArg;
} FILE_RESTORE_FINISHED_INFORMATION, *PFILE_RESTORE_FINISHED_INFORMATION;

typedef BOOL (WINAPI *FuncScanRestorableFiles) (
    _In_                        PFILE_RESTORE_CONTEXT   Context,
    _In_                        PCWSTR                  Path,
    _In_                        ULONG                   FileInfoSize,
    _Out_bytecap_(FileInfoSize) PRESTORABLE_FILE_INFO   FileInfo,
    _Out_                       PULONG                  FileInfoUsed
);

typedef BOOLEAN (*FILE_RESTORE_CALLBACK) (
    _In_    FILE_RESTORE_PACKET_TYPE    PacketType,
    _In_    ULONG                       PacketLength,
    _In_    PVOID                       PacketData
);

typedef BOOL (WINAPI *FuncRestoreFile) (
    _In_        PFILE_RESTORE_CONTEXT   Context,
    _In_        PRESTORABLE_FILE_INFO   RestorableFile,
    _In_        PCWSTR                  DstFile,
    _In_opt_    FILE_RESTORE_CALLBACK   Callback,
    _In_opt_    PVOID                   ClbkArg
);

HMODULE hLib;
wchar_t VOLUME[255];

BOOLEAN FuncRestoreCallback(_In_ FILE_RESTORE_PACKET_TYPE pType, _In_ ULONG pLength, _In_ PVOID pData)
{
    // This is the callback that is passed to RestoreFile(), which
    // returns data about the status of an attempted restoration.
    if (pType == FileRestoreProgressInfo)
    {
        wcout << L"FILE RESTORE PROGRESS INFO:" << L"\n";
        wprintf(L"Length of status data: %lu\n", pLength);
        wcout << L"Location of data: " << pData << L"\n";
        PFILE_RESTORE_PROGRESS_INFORMATION restoreProgressInfo = static_cast<PFILE_RESTORE_PROGRESS_INFORMATION>(pData);
        wprintf(L"Total file size: %lld\n", restoreProgressInfo->TotalFileSize);
        wprintf(L"Total bytes completed: %lld\n", restoreProgressInfo->TotalBytesCompleted);
        wprintf(L"Stream size: %lld\n", restoreProgressInfo->StreamSize);
        wprintf(L"Stream bytes completed: %lld\n", restoreProgressInfo->StreamBytesCompleted);
        //wcout << L"Callback arg data: " << restoreProgressInfo->ClbkArg << L"\n";
        wprintf(L"Callback arg: %p\n", restoreProgressInfo->ClbkArg);
    }
    else if (pType == FileRestoreFinished)
    {
        wcout << L"FILE RESTORE FINISHED INFO:" << L"\n";
        wprintf(L"Length of status data: %lu\n", pLength);
        wcout << L"Location of data: " << pData << L"\n";
        // Obtain the struct
        PFILE_RESTORE_FINISHED_INFORMATION restoreFinishedInfo = static_cast<PFILE_RESTORE_FINISHED_INFORMATION>(pData);
        // Try to read some data from it
        wprintf(L"Success data: %d\n", restoreFinishedInfo->Success);
        wprintf(L"Final result data: %lu\n", restoreFinishedInfo->FinalResult);
        wprintf(L"Callback arg: %p\n", restoreFinishedInfo->ClbkArg);
    }
    return TRUE;
}

void Scan(_In_ PFILE_RESTORE_CONTEXT context, _In_ LPCWSTR path)
{
    // This is the main function that scans the files
    // Dynamically link to the needed FMAPI functions
    FuncScanRestorableFiles ScanRestorableFiles;
    ScanRestorableFiles = reinterpret_cast<FuncScanRestorableFiles>( GetProcAddress( hLib, "ScanRestorableFiles" ) );

    ULONG neededBufferSize = 0;
    BOOL success = TRUE;
    RESTORABLE_FILE_INFO tempFileInfo;

    // Call ScanRestorableFiles the first time with a size of 0 to get the required buffer size
    if ( ! ScanRestorableFiles(context, path, 0, &tempFileInfo, &neededBufferSize) )
    {
        wprintf(L"Failed to retrieve required buffer size, Error: #%u\n", GetLastError());
        return;
    }

    // Create the buffer needed to hold restoration information
    BYTE *buffer = new BYTE[neededBufferSize];
    wcout << L"Initial buffer size is: " << neededBufferSize << L"\n";

    // Loops until an error occurs or no more files found
    while (success)
    {        
        // Cast the byte buffer pointer into a structure pointer
        PRESTORABLE_FILE_INFO fileInfo = reinterpret_cast<PRESTORABLE_FILE_INFO>(buffer);
        #pragma warning( push ) 
        #pragma warning( disable : 6386 ) /* warning is ignored since fileInfo grows in size by design */
        success = ScanRestorableFiles(context, path, neededBufferSize, fileInfo, &neededBufferSize);
        #pragma warning( pop )
        wcout << L"Current buffer size is: " << neededBufferSize << L"\n";
        if (success)
        {
            wcout << L"Call returned success! Required buffer size from latest call is " << neededBufferSize <<
                L" bytes." L"\n";
            if (fileInfo->IsRemoved)
            {
                // Found restorable file
                wprintf(L"Restorable file found: %s\n", fileInfo->FileName);

                // Echo size of char array containing file name
                wcout << L"Restorable file name size: " <<
                    (sizeof(fileInfo->FileName) / sizeof(fileInfo->FileName[0])) << L"\n";

                // Echo RESTORABLE_FILE_INFO structure info to console
                wcout << L"Restorable file info structure memory address: " << fileInfo << L"\n";
                wcout << L"Restorable file info structure size (returned via RESTORABLE_FILE_INFO): " << fileInfo->Size << L"\n";
                wcout << L"Restorable file info structure size (returned via sizeof()): " << sizeof(*fileInfo) << L"\n";

                // Echo restorable file (FMAPI) version to console
                wcout << L"Restorable file version: " << fileInfo->Version << L"\n";

                // Retrieve creation, write and access times for the file
                // Define temp FILETIME, SYSTEMTIME and TIME_ZONE_INFORMATION
                // structure vars
                // All of these types and functions are defined in windows.h
                FILETIME tmpFT;
                SYSTEMTIME tmpST;
                TIME_ZONE_INFORMATION tmpTZI;
                // Initialize empty char arrays
                wchar_t szLocalDate[255] = {0}, szLocalTime[255] = {0};
                // Get local time zone info
                SetTimeZoneInformation(&tmpTZI);
                // Get file creation time
                FileTimeToLocalFileTime(&(fileInfo->CreationTime), &tmpFT);
                FileTimeToSystemTime(&tmpFT, &tmpST);
                // Format to readable output and store in char arrays
                GetDateFormatEx(LOCALE_NAME_SYSTEM_DEFAULT, DATE_LONGDATE, &tmpST, NULL, szLocalDate, 255, NULL);
                GetTimeFormatEx(LOCALE_NAME_SYSTEM_DEFAULT, 0, &tmpST, NULL, szLocalTime, 255);
                wcout << L"Restorable file created: " << szLocalDate << " " << szLocalTime << L"\n";
                // Clear array for re-use
                fill(begin(szLocalDate), end(szLocalDate), 0);
                // Get last write time
                FileTimeToLocalFileTime(&(fileInfo->LastWriteTime), &tmpFT);
                FileTimeToSystemTime(&tmpFT, &tmpST);
                // Format to readable output and store in char arrays
                GetDateFormatEx(LOCALE_NAME_SYSTEM_DEFAULT, DATE_LONGDATE, &tmpST, NULL, szLocalDate, 255, NULL);
                GetTimeFormatEx(LOCALE_NAME_SYSTEM_DEFAULT, 0, &tmpST, NULL, szLocalTime, 255);
                wcout << L"Restorable file last written: " << szLocalDate << " " << szLocalTime << L"\n";
                // Clear array for re-use
                fill(begin(szLocalDate), end(szLocalDate), 0);
                // Get last access time
                FileTimeToLocalFileTime(&(fileInfo->LastAccessTime), &tmpFT);
                FileTimeToSystemTime(&tmpFT, &tmpST);
                // Format to readable output and store in char arrays
                GetDateFormatEx(LOCALE_NAME_SYSTEM_DEFAULT, DATE_LONGDATE, &tmpST, NULL, szLocalDate, 255, NULL);
                GetTimeFormatEx(LOCALE_NAME_SYSTEM_DEFAULT, 0, &tmpST, NULL, szLocalTime, 255);
                wcout << L"Restorable file last accessed: " << szLocalDate << " " << szLocalTime << L"\n";

                // Output the rest of the file info
                wcout << L"Restorable file attributes: " << fileInfo->Attributes << L"\n";
                wcout << L"Restorable file size: " << fileInfo->FileSize << L"\n";
                wcout << L"Restorable file ClustersUsedByFile: " << fileInfo->ClustersUsedByFile << L"\n";
                wcout << L"Restorable file ClustersCurrentlyInUse: " << fileInfo->ClustersCurrentlyInUse << L"\n";
                wcout << L"Restorable file RestoreDataOffset: " << fileInfo->RestoreDataOffset << L"\n";

                // Attempt to restore the file
                wstring tmpStr;
                getline(wcin, tmpStr);
                // Convert input to uppercase
                for (wstring::size_type i = 0; i < tmpStr.size(); i++)
                {
                    towupper(tmpStr[i]);
                }
                wcout << L"tmpStr is: " << tmpStr << L"\n";
                if (tmpStr == L"RESTORE")
                {
                    // Attempt to restore the file
                    wcout << L"Attempting to restore file " << fileInfo->FileName << L"..." << L"\n";
                    FuncRestoreFile RestoreFile;
                    //RestoreFile = (FuncRestoreFile)GetProcAddress( hLib, "RestoreFile" );
                    RestoreFile = reinterpret_cast<FuncRestoreFile>(GetProcAddress(hLib, "RestoreFile"));
                    wcout << L"RestoreFile address: " << RestoreFile << L"\n";
                    BOOL tmpRetVal = false;
                    PCWSTR restoredFileName = L"X:\\testfile.txt";
                    wcout << L"New file name: " << restoredFileName << L"\n";
                    PVOID cbArg = NULL;
                    tmpRetVal = RestoreFile(context, fileInfo, restoredFileName, &FuncRestoreCallback, cbArg);
                    wcout << L"Return value: " << tmpRetVal << L" ; cbArg: " << cbArg << L"\n";
                    if (tmpRetVal == 0)
                    {
                        wcout << L"Error was: " << GetLastError() << L"\n";
                    }
                }
                else if (tmpStr == L"CLOSE")
                {
                    // Abort the scanning process and close the file restore context
                    wcout << L"Aborting scan and closing file restore context..." << L"\n";
                    success = false;
                }
            }
        } 
        else
        {
            DWORD err = GetLastError();
            if (ERROR_INSUFFICIENT_BUFFER == err)
            {
                wcout << L"Insufficient buffer size! Current size is " << sizeof(buffer) << L" bytes; " <<
                    L" required size is " << neededBufferSize << L" bytes. Resizing..." << L"\n";
                delete [] buffer;
                buffer = new BYTE[neededBufferSize];
                success = true;
            } 
            else if (ERROR_NO_MORE_FILES == err)
            {
                wprintf(L"Scanning Complete.\n");
                success = false;
            }
            else
            {
                wprintf(L"ScanRestorableFiles, Error #%u.\n", err);
            }
        }
    }

    delete [] buffer;
    buffer = NULL;
}

//
// Program entry point
//
void __cdecl wmain(int argc, wchar_t *argv[])
{

    HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0); 

    // Load the FMAPI DLL
    hLib = ::LoadLibraryEx(L"fmapi.dll", NULL, NULL);    
    if ( !hLib )
    {
        wprintf(L"Could not load fmapi.dll. Error #%u.\n", GetLastError());
        return;
    }

    // Dynamically link to the needed FMAPI functions
    FuncCreateFileRestoreContext CreateFileRestoreContext;
    CreateFileRestoreContext = reinterpret_cast<FuncCreateFileRestoreContext>( GetProcAddress( hLib, "CreateFileRestoreContext" ) );

    FuncCloseFileRestoreContext CloseFileRestoreContext;
    CloseFileRestoreContext = reinterpret_cast<FuncCloseFileRestoreContext>( GetProcAddress( hLib, "CloseFileRestoreContext" ) );

    // Set the flags value for which kind of items we want to scan for
    RESTORE_CONTEXT_FLAGS flags;
    flags = (RESTORE_CONTEXT_FLAGS)(ContextFlagVolume | FlagScanRemovedFiles);
    switch (argc)
    {
        case 2:
        {
            if (wcslen(argv[1]) < (sizeof(VOLUME) / sizeof(VOLUME[0]))) // ensure that argv[1] is not >255 chars long
            {
                wcscpy_s(VOLUME, argv[1]);
            }
            else
            {
                wcscpy_s(VOLUME, 255, argv[1]);
            }
            wcout << L"Volume set to: " << VOLUME << L"\n";
            wcout << L"Defaulting to VOLUME search type; setting flags accordingly..." << L"\n";
            flags = (RESTORE_CONTEXT_FLAGS)(ContextFlagVolume | FlagScanRemovedFiles);
            break;
        }
        case 3:
        {
            if (wcslen(argv[1]) < (sizeof(VOLUME) / sizeof(VOLUME[0]))) // ensure that argv[1] is not >255 chars long
            {
                wcscpy_s(VOLUME, argv[1]);
            }
            else // if it is, only copy the first 255 chars
            {
                wcscpy_s(VOLUME, 255, argv[1]);
            }
            int len;
            // Get length of second argument passed by the user
            len = wcslen(argv[2]);
            // Allocate new wchar_t array big enough to hold it
            wchar_t *tmpArg = new (nothrow) wchar_t[len + 1];
            // Ensure that the memory was allocated successfully
            if (tmpArg == nullptr)
            {
                // Error allocating memory - bail out
                wcout << L"Error allocating memory! Bailing out..." << L"\n";
                return;
            }
            // Copy the argument string from argv to tmpArg
            wcscpy_s(tmpArg, len + 1, argv[2]);
            // Convert to uppercase
            _wcsupr_s(tmpArg, len + 1);
            // Check whether VOLUME or DISK search type was specified and set flags accordingly
            if (wcscmp(tmpArg, L"VOLUME") == 0)
            {
                wcout << L"Volume set to: " << VOLUME << L"\n";
                if (wcschr(VOLUME, L':') != NULL)
                {
                    wcout << L"VOLUME search type specified; setting flags accordingly..." << L"\n";
                    flags = (RESTORE_CONTEXT_FLAGS)(ContextFlagVolume | FlagScanRemovedFiles);
                }
                else
                {
                    wcout << L"VOLUME search type specified, but the volume identifier given doesn't appear " <<
                        L"to be a valid mounted volume! Please check your arguments and try again." << L"\n";
                    delete [] tmpArg;
                    return;
                }
            }
            else if (wcscmp(tmpArg, L"DISK") == 0)
            {
                wcout << L"Disk set to: " << VOLUME << L"\n";
                if (wcschr(VOLUME, L':') == NULL)
                {
                    wcout << L"DISK search type specified; setting flags accordingly..." << L"\n";
                    flags = (RESTORE_CONTEXT_FLAGS)(ContextFlagDisk | FlagScanRemovedFiles);
                }
                else
                {
                    wcout << L"DISK search type specified, but the disk identifier given doesn't appear " <<
                        L"to be a valid physical disk! Please check your arguments and try again." << L"\n";
                    delete [] tmpArg;
                    return;
                }
            }
            else
            {
                wcout << L"UNKNOWN search type specified! Defaulting to VOLUME; setting flags accordingly..." << L"\n";
                flags = (RESTORE_CONTEXT_FLAGS)(ContextFlagVolume | FlagScanRemovedFiles);
            }
            delete [] tmpArg;
            break;
        }
        default:
        {
            // Set the default volume to scan here
            wcscpy_s(VOLUME, L"\\\\.\\D:");
            wcout << L"No arguments specified! Defaulting to VOLUME search of \\\\.\\D:" << L"\n";
            break;
        }
    }

    // Create the FileRestoreContext
    PFILE_RESTORE_CONTEXT context = NULL;
    if ( ! CreateFileRestoreContext(VOLUME, flags, 0, 0, FILE_RESTORE_VERSION_2, &context) )
    {        
        DWORD err = GetLastError();        
        wprintf(L"Failed to Create FileRestoreContext, Error #%u.\n", err);
        return;
    }
    else
    {
        wcout << L"Success! File restore context created! Value of context is: " << context << L"\n";
    }

    // Find restorable files starting at the given directory
    Scan(context, SCAN_PATH);

    // Close the context
    if (context)
    {
        CloseFileRestoreContext(context);
        context = NULL;
    }
}

请注意,这在开始阶段仍然是一个非常初级的应用程序。它基本上开始扫描,默认情况下(如果未提供args)扫描D:\驱动器,然后尝试将已删除的文件还原到X:\ testfile.txt。对于可能不熟悉WinPE的用户,X:是WinPE环境中的系统驱动器,而D:通常是分配给在PE环境中引导到PE的计算机的OS驱动器的字母,具体取决于设置,其中C:是系统保留的分区。

对于找到的每个可恢复文件,它将暂停,并且如果用户输入文本“ RESTORE”并按ENTER键,它将尝试恢复该文件(此操作在第247行发生)。指示恢复状态的回调函数(通过FinalResult)返回错误代码6,GetLastError()也是如此。

对此非常感谢!

0 个答案:

没有答案