读取ntfs的usn

上回说到NTFS USN会记录下文件的所有操作,但默认情况下并没有激活这一功能,所以不用担心。在非激活状态,它也只剩下快速查找文件的功能。
这回,假设USN JOURNAL被激活了(你可以用控制台命令fsutil usn,或上回说到的FSCTL_CREATE_USN_JOURNAL来开启这一功能),怎么回窥过去的操作?
为此,写了段代码来进行试验,程序是检索 123.txt 操作历史(记录空间不是无穷大,过旧的信息会被覆盖掉),输出如下

2011-06-24 12:53:53 FILE_CREATE|
  \share\x\新建 文本文档.txt
2011-06-24 12:53:53 FILE_CREATE|CLOSE|
  \share\x\新建 文本文档.txt
2011-06-24 12:53:59 RENAME_OLD_NAME|
  \share\x\新建 文本文档.txt
2011-06-24 12:53:59 RENAME_NEW_NAME|
  \share\x\123.txt
2011-06-24 12:53:59 RENAME_NEW_NAME|CLOSE|
  \share\x\123.txt
2011-06-24 12:55:41 FILE_DELETE|CLOSE|
  \RECYCLER\S-1-5-21-945297121-3308661772-4115227181-3452\Dd3\123.txt

从输出可以看出,先在\share\x目录下创建了"新建 文本文档.txt",后又更名为"123.txt",最后连其父目录一起删进了回收站。

试验代码如下(代码中有好多冗余调用,因为这只是试验代码而已):

#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#include <string>
#include <deque>
using namespace std;

struct MY_USN_RECORD
{
    DWORDLONG FileReferenceNumber;
    DWORDLONG ParentFileReferenceNumber;
    LARGE_INTEGER TimeStamp;
    DWORD Reason;
    WCHAR FileName[MAX_PATH];
};
HANDLE hVol = INVALID_HANDLE_VALUE;
bool EnumUsnRecord( const char* drvname, std::deque<MY_USN_RECORD>& con )
{
    bool ret = false;

    char FileSystemName[MAX_PATH+1];
    DWORD MaximumComponentLength;
    if( GetVolumeInformationA( (std::string(drvname)+":\\").c_str(),0,0,0,&MaximumComponentLength,0,FileSystemName,MAX_PATH+1)
        && 0==strcmp(FileSystemName,"NTFS") ) // 判断是否为 NTFS 格式
    {
        hVol = CreateFileA( (std::string("\\\\.\\")+drvname+":").c_str() // 需要管理员权限,无奈
            , GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
        if( hVol != INVALID_HANDLE_VALUE )
        {
            DWORD br;
            USN_JOURNAL_DATA qujd;
            if( DeviceIoControl( hVol, FSCTL_QUERY_USN_JOURNAL, NULL, 0, &qujd, sizeof(qujd), &br, NULL ) )
            {
                char buffer[0x1000];
                DWORD BytesReturned;
                {
                    READ_USN_JOURNAL_DATA rujd = { 0, -1, 0, 0, 0, qujd.UsnJournalID };
                    for( ; DeviceIoControl(hVol,FSCTL_READ_USN_JOURNAL,&rujd,sizeof(rujd),buffer,_countof(buffer),&BytesReturned,NULL); rujd.StartUsn=*(USN*)&buffer )
                    {
                        DWORD dwRetBytes = BytesReturned - sizeof(USN);
                        PUSN_RECORD UsnRecord = (PUSN_RECORD)((PCHAR)buffer+sizeof(USN));
                        if( dwRetBytes==0 )
                        {
                            ret = true;
                            break;
                        }
               
                        while( dwRetBytes > 0 )
                        {
                            MY_USN_RECORD myur = { UsnRecord->FileReferenceNumber, UsnRecord->ParentFileReferenceNumber, UsnRecord->TimeStamp, UsnRecord->Reason };
                            memcpy( myur.FileName, UsnRecord->FileName, UsnRecord->FileNameLength );
                            myur.FileName[UsnRecord->FileNameLength/2] = L'\0';

                            con.push_back( myur );
            
                            dwRetBytes -= UsnRecord->RecordLength;
                            UsnRecord = (PUSN_RECORD)( (PCHAR)UsnRecord + UsnRecord->RecordLength );
                        }
                    }
                }
            }

            //CloseHandle( hVol );
        }
    }

    return ret;
}

#include <set>
int main()
{
    // 获得所有变化记录
    std::deque<MY_USN_RECORD> con;
    EnumUsnRecord( "D", con );

    // 搜寻文件名为"test.txt"的文件号(可能有多个)
    std::set<DWORDLONG> con2;
    for( std::deque<MY_USN_RECORD>::const_iterator itor=con.begin(); itor!=con.end(); ++itor )
    {
        const MY_USN_RECORD& mur = *itor;
        if( _wcsicmp(mur.FileName,L"123.txt") == 0 )
        {
            con2.insert( mur.FileReferenceNumber );
        }
    }

    // 遍历其历史操作
    setlocale( LC_CTYPE, "chs" );
    for( std::set<DWORDLONG>::const_iterator itor2=con2.begin(); itor2!=con2.end(); ++itor2 )
    {
        for( std::deque<MY_USN_RECORD>::const_iterator itor=con.begin(); itor!=con.end(); ++itor )
        {
            const MY_USN_RECORD& mur = *itor;
            if( *itor2 == mur.FileReferenceNumber )
            {
                FILETIME timestamp;
                FileTimeToLocalFileTime( &(FILETIME&)mur.TimeStamp, &timestamp );
                SYSTEMTIME st;
                FileTimeToSystemTime( &timestamp, &st );
                printf( "%04d-%02d-%02d %02d:%02d:%02d " , st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond );

                if( mur.Reason&USN_REASON_DATA_OVERWRITE )
                    printf( "%s|", "DATA_OVERWRITE" );
                if( mur.Reason&USN_REASON_DATA_EXTEND )
                    printf( "%s|", "DATA_EXTEND" );
                if( mur.Reason&USN_REASON_DATA_TRUNCATION )
                    printf( "%s|", "DATA_TRUNCATION" );
                if( mur.Reason&USN_REASON_NAMED_DATA_OVERWRITE )
                    printf( "%s|", "NAMED_DATA_OVERWRITE" );
                if( mur.Reason&USN_REASON_NAMED_DATA_EXTEND )
                    printf( "%s|", "NAMED_DATA_EXTEND" );
                if( mur.Reason&USN_REASON_NAMED_DATA_TRUNCATION )
                    printf( "%s|", "NAMED_DATA_TRUNCATION" );
                if( mur.Reason&USN_REASON_FILE_CREATE )
                    printf( "%s|", "FILE_CREATE" );
                if( mur.Reason&USN_REASON_FILE_DELETE )
                    printf( "%s|", "FILE_DELETE" );
                if( mur.Reason&USN_REASON_EA_CHANGE )
                    printf( "%s|", "EA_CHANGE" );
                if( mur.Reason&USN_REASON_SECURITY_CHANGE )
                    printf( "%s|", "SECURITY_CHANGE" );
                if( mur.Reason&USN_REASON_RENAME_OLD_NAME )
                    printf( "%s|", "RENAME_OLD_NAME" );
                if( mur.Reason&USN_REASON_RENAME_NEW_NAME )
                    printf( "%s|", "RENAME_NEW_NAME" );
                if( mur.Reason&USN_REASON_INDEXABLE_CHANGE )
                    printf( "%s|", "INDEXABLE_CHANGE" );
                if( mur.Reason&USN_REASON_BASIC_INFO_CHANGE )
                    printf( "%s|", "BASIC_INFO_CHANGE" );
                if( mur.Reason&USN_REASON_HARD_LINK_CHANGE )
                    printf( "%s|", "HARD_LINK_CHANGE" );
                if( mur.Reason&USN_REASON_COMPRESSION_CHANGE )
                    printf( "%s|", "COMPRESSION_CHANGE" );
                if( mur.Reason&USN_REASON_ENCRYPTION_CHANGE )
                    printf( "%s|", "ENCRYPTION_CHANGE" );
                if( mur.Reason&USN_REASON_OBJECT_ID_CHANGE )
                    printf( "%s|", "OBJECT_ID_CHANGE" );
                if( mur.Reason&USN_REASON_REPARSE_POINT_CHANGE )
                    printf( "%s|REPARSE_POINT_CHANGE", "" );
                if( mur.Reason&USN_REASON_STREAM_CHANGE )
                    printf( "%s|", "STREAM_CHANGE" );
                if( mur.Reason&USN_REASON_TRANSACTED_CHANGE )
                    printf( "%s|", "TRANSACTED_CHANGE" );
                if( mur.Reason&USN_REASON_CLOSE )
                    printf( "%s|", "CLOSE" );

                printf( "\n  " );
                bool PrintFullPath( const MY_USN_RECORD& mur, const std::deque<MY_USN_RECORD>& con );
                PrintFullPath(mur,con);

                printf( "\n" );
            }
        }

        printf( "\n" );
    }

    if( hVol != INVALID_HANDLE_VALUE )
        CloseHandle( hVol );

    return 0;
}

bool PrintFullPath( const MY_USN_RECORD& mur, const std::deque<MY_USN_RECORD>& con )
{
    if( (mur.FileReferenceNumber&0x0000FFFFFFFFFFFF) == 5 )
        return true;

    std::deque<MY_USN_RECORD>::const_iterator recent = con.end();
    for( std::deque<MY_USN_RECORD>::const_iterator itor=con.begin(); itor!=con.end() && itor->TimeStamp.QuadPart<=mur.TimeStamp.QuadPart; ++itor )
    {
        if( itor->FileReferenceNumber == mur.ParentFileReferenceNumber )
            recent = itor;
    }
    if( recent != con.end() ) // 它的父目录可能也已被删除,所以要先在记录集中找找
    {
        bool r= PrintFullPath(*recent,con);
        printf( "\\%S", mur.FileName );
        return r;
    }

    bool GetFullPathByFileReferenceNumber( HANDLE hVol, DWORDLONG FileReferenceNumber );
    bool r = GetFullPathByFileReferenceNumber(hVol,mur.ParentFileReferenceNumber); // 如果记录中没有,再去看看这个文件实际存在否
    if( r )
        printf( "\\%S", mur.FileName );
    else
        printf( "???\\%S", mur.FileName );
    return r;
}

bool GetFullPathByFileReferenceNumber( HANDLE hVol, DWORDLONG FileReferenceNumber ) // 根据文件号获得全路径,上篇文章已经说过,共有3中方法,这是其中之一,代码简单但效率不高
{
    typedef ULONG (__stdcall *PNtCreateFile)(
        PHANDLE FileHandle,
        ULONG DesiredAccess,
        PVOID ObjectAttributes,
        PVOID IoStatusBlock,
        PLARGE_INTEGER AllocationSize,
        ULONG FileAttributes,
        ULONG ShareAccess,
        ULONG CreateDisposition,
        ULONG CreateOptions,
        PVOID EaBuffer,
        ULONG EaLength );
    PNtCreateFile NtCreatefile = (PNtCreateFile)GetProcAddress( GetModuleHandle(L"ntdll.dll"), "NtCreateFile" );

    typedef struct _UNICODE_STRING {
        USHORT Length, MaximumLength;
        PWCH Buffer;
    } UNICODE_STRING, *PUNICODE_STRING;
    UNICODE_STRING fidstr = { 8, 8, (PWSTR)&FileReferenceNumber };

    typedef struct _OBJECT_ATTRIBUTES {
        ULONG Length;
        HANDLE RootDirectory;
        PUNICODE_STRING ObjectName;
        ULONG Attributes;
        PVOID SecurityDescriptor;
        PVOID SecurityQualityOfService;
    } OBJECT_ATTRIBUTES;
    const ULONG OBJ_CASE_INSENSITIVE = 0x00000040UL;
    OBJECT_ATTRIBUTES oa = { sizeof(OBJECT_ATTRIBUTES), hVol, &fidstr, OBJ_CASE_INSENSITIVE, 0, 0 };
  
    HANDLE hFile;
    ULONG iosb[2];
    const ULONG FILE_OPEN_BY_FILE_ID = 0x00002000UL;
    const ULONG FILE_OPEN            = 0x00000001UL;
    ULONG status = NtCreatefile( &hFile, GENERIC_ALL, &oa, iosb, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN, FILE_OPEN_BY_FILE_ID, NULL, 0 );
    if( status == 0 )
    {
        typedef struct _IO_STATUS_BLOCK {
            union {
                NTSTATUS Status;
                PVOID Pointer;
            };
            ULONG_PTR Information;
        } IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
        typedef enum _FILE_INFORMATION_CLASS {
            // ……
            FileNameInformation = 9
            // ……
        } FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS;
        typedef NTSTATUS (__stdcall *PNtQueryInformationFile)(
            HANDLE FileHandle,
            PIO_STATUS_BLOCK IoStatusBlock,
            PVOID FileInformation,
            DWORD Length,
            FILE_INFORMATION_CLASS FileInformationClass );
        PNtQueryInformationFile NtQueryInformationFile = (PNtQueryInformationFile)GetProcAddress( GetModuleHandle(L"ntdll.dll"), "NtQueryInformationFile" );

        typedef struct _OBJECT_NAME_INFORMATION {
            UNICODE_STRING Name;
        } OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION;
        IO_STATUS_BLOCK IoStatus;
        size_t allocSize = sizeof(OBJECT_NAME_INFORMATION) + MAX_PATH*sizeof(WCHAR);
        POBJECT_NAME_INFORMATION pfni = (POBJECT_NAME_INFORMATION)operator new(allocSize);
        status = NtQueryInformationFile(hFile, &IoStatus, pfni, allocSize, FileNameInformation);
        if( status == 0 )
        {
            printf( "%.*S", pfni->Name.Length/2, &pfni->Name.Buffer );
        }
        operator delete(pfni);

        CloseHandle(hFile);
    }

    return status == 0;
}

====================================================================================================================================================================================================================================================================================================================================================================================================================

有个名叫Everything的软件,搜索文件飞快,看了一下原理,原来NTFS会记录文件的所有操作,也就是说即便文件被删除了,通过USN记录仍然可以知道文件名等部分信息。于是准备写个程序删掉这个信息,但研究了一下,发现它默认其实是关闭的,也就是没必要担心信息外漏。

#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#include <string>
#include <deque>
using namespace std;

struct FileInfo
{
    DWORDLONG FileRefNo;
    DWORDLONG ParentRefNo;
    DWORD FileAttributes;
    WCHAR Name[MAX_PATH];
};

bool EnumUsnRecord( const char* drvname, std::deque<FileInfo>& con )
{
    bool ret = false;

    char FileSystemName[MAX_PATH+1];
    DWORD MaximumComponentLength;
    if( GetVolumeInformationA( (std::string(drvname)+":\\").c_str(),0,0,0,&MaximumComponentLength,0,FileSystemName,MAX_PATH+1)
        && 0==strcmp(FileSystemName,"NTFS") ) // 判断是否为 NTFS 格式
    {


//CreateFileA这是一个多功能的函数,可打开或创建以下对象,并返回可访问的句柄:控制台,通信资源,目录(只读打开),磁盘驱动器,文件,邮槽,管道。

// DeviceIoControl是直接发送控制代码到指定的设备驱动程序,使相应的移动设备以执行相应的操作的函数。
        HANDLE hVol = CreateFileA( (std::string(" \\\\.\\")+drvname+":").c_str ()  // 需要管理员权限,无奈
            , GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
        if( hVol != INVALID_HANDLE_VALUE )
        {
            DWORD br;
            CREATE_USN_JOURNAL_DATA cujd = { 0, 0 };
            if( DeviceIoControl( hVol, FSCTL_CREATE_USN_JOURNAL, &cujd, sizeof(cujd), NULL, 0, &br, NULL ) )  // 如果创建过,且没有用FSCTL_DELETE_USN_JOURNAL关闭,则可以跳过这一步
            {
                USN_JOURNAL_DATA qujd;
                if( DeviceIoControl( hVol, FSCTL_QUERY_USN_JOURNAL, NULL, 0, &qujd, sizeof(qujd), &br, NULL ) )
                {
                    char buffer[0x1000]; // 缓冲区越大则DeviceIoControl调用次数越少,即效率越高
                    DWORD BytesReturned;
                    //{ // 使用FSCTL_READ_USN_JOURNAL可以只搜索指定change reason的记录,比如下面的代码只搜索被删除的文件信息,但即便rujd.ReasonMask设为-1,也列不出所有文件
                    //    READ_USN_JOURNAL_DATA rujd = { 0, USN_REASON_FILE_DELETE, 0, 0, 0, qujd.UsnJournalID };
                    //    for( ; DeviceIoControl(hVol,FSCTL_READ_USN_JOURNAL,&rujd,sizeof(rujd),buffer,_countof(buffer),&BytesReturned,NULL); rujd.StartUsn=*(USN*)&buffer )
                    //    {
                    //        DWORD dwRetBytes = BytesReturned - sizeof(USN);
                    //        PUSN_RECORD UsnRecord = (PUSN_RECORD)((PCHAR)buffer+sizeof(USN));
                    //        if( dwRetBytes==0 )
                    //        {
                    //            ret = true;
                    //            break;
                    //        }
                    //
                    //        while( dwRetBytes > 0 )
                    //        {
                    //            printf( "FRU %016I64x, PRU %016I64x, %.*S\n", UsnRecord->FileReferenceNumber, UsnRecord->ParentFileReferenceNumber
                    //                , UsnRecord->FileNameLength/2, UsnRecord->FileName );
                    //
                    //            dwRetBytes -= UsnRecord->RecordLength;
                    //            UsnRecord = (PUSN_RECORD)( (PCHAR)UsnRecord + UsnRecord->RecordLength );
                    //        }
                    //    }
                    //}
                    { // 使用FSCTL_ENUM_USN_DATA可以列出所有存在的文件信息,但UsnRecord->Reason等信息是无效的
                        MFT_ENUM_DATA med = { 0, 0, qujd.NextUsn };
                        for( ; DeviceIoControl(hVol,FSCTL_ENUM_USN_DATA,&med,sizeof(med),buffer,_countof(buffer),&BytesReturned,NULL); med.StartFileReferenceNumber=*(USN*)&buffer )
                        {
                            DWORD dwRetBytes = BytesReturned - sizeof(USN);
                            PUSN_RECORD UsnRecord = (PUSN_RECORD)((PCHAR)buffer+sizeof(USN));

                            while( dwRetBytes > 0 )
                            {
                                FileInfo finf;
                                finf.FileRefNo = UsnRecord->FileReferenceNumber;
                                finf.ParentRefNo = UsnRecord->ParentFileReferenceNumber;
                                finf.FileAttributes = UsnRecord->FileAttributes;
                                memcpy( finf.Name, UsnRecord->FileName, UsnRecord->FileNameLength );
                                finf.Name[UsnRecord->FileNameLength/2] = L'\0';
                                con.push_back( finf );

                                dwRetBytes -= UsnRecord->RecordLength;
                                UsnRecord = (PUSN_RECORD)( (PCHAR)UsnRecord + UsnRecord->RecordLength );
                            }
                        }
                        ret = GetLastError()==ERROR_HANDLE_EOF;
                    }
                    DELETE_USN_JOURNAL_DATA dujd = { qujd.UsnJournalID, USN_DELETE_FLAG_DELETE };
                    DeviceIoControl( hVol, FSCTL_DELETE_USN_JOURNAL, &dujd, sizeof(dujd), NULL, 0, &br, NULL ); // 关闭USN记录。如果是别人的电脑,当然可以不关^_^
                }
            }
        }
        CloseHandle( hVol );
    }

    return ret;
}

/// 以下为测试代码,输出D盘文件树结构 ///

#include <vector>
#include <algorithm>
#include <cstdio>

struct FileNode
{
    WCHAR name[MAX_PATH];
    DWORD FileAttributes;
    std::vector<FileNode> subs;

    FileNode( const WCHAR* filename, DWORD fileattr ) : FileAttributes(fileattr)
    {
        wcscpy( name, filename );
    }
};

int main()
{
    std::deque<FileInfo> con;// list不利于折半查找
    EnumUsnRecord( "D", con );

    // 整理成树
    struct foo1
    {
        bool operator()( const FileInfo& a, const FileInfo& b ) const
        {
            if( a.ParentRefNo != b.ParentRefNo )
                return a.ParentRefNo<b.ParentRefNo;
            if( (a.FileAttributes&FILE_ATTRIBUTE_DIRECTORY) != (b.FileAttributes&FILE_ATTRIBUTE_DIRECTORY) )
                return (a.FileAttributes&FILE_ATTRIBUTE_DIRECTORY) > (b.FileAttributes&FILE_ATTRIBUTE_DIRECTORY);
            return _wcsicmp(a.Name,b.Name)<0;
        }
    };
    std::sort( con.begin(), con.end(), foo1() );
    FileNode root( L"D:\\", 0 );
    std::deque< std::pair<DWORDLONG,std::vector<FileNode>*> > tmp;
    tmp.push_back( std::make_pair(0x5000000000005,&root.subs) );
    for( ; !tmp.empty(); )
    {
        DWORDLONG ParentRefNo = tmp.front().first;
        std::vector<FileNode>& subs = *tmp.front().second;
        tmp.pop_front();

        struct foo2 {
            bool operator()( DWORDLONG prn, const FileInfo& fi ) const { return prn < fi.ParentRefNo; }
            bool operator()( const FileInfo& fi, DWORDLONG prn ) const { return fi.ParentRefNo < prn; }
            bool operator()( const FileInfo& a, const FileInfo& b ) const { return a.ParentRefNo < b.ParentRefNo; }
        };
        std::pair<std::deque<FileInfo>::iterator,std::deque<FileInfo>::iterator> r = std::equal_range( con.begin(), con.end(), ParentRefNo, foo2() );
        subs.reserve( std::distance(r.first,r.second) );
        for( std::deque<FileInfo>::iterator itor=r.first; itor!=r.second; ++itor )
        {
            FileNode fn( itor->Name, itor->FileAttributes );
            subs.push_back( fn );
            tmp.push_front( std::make_pair(itor->FileRefNo, &subs.back().subs) ); // 深度优先
        }
    }
    con.clear();

    // 输出树
    setlocale( LC_CTYPE, "chs" );
    std::vector< std::pair<std::vector<FileNode>::iterator,std::vector<FileNode>::iterator> > path;
    printf( "%s\n", "D:" );
    path.push_back( std::make_pair(root.subs.begin(),root.subs.end()) );
    for( ; !path.empty(); )
    {
        if( path.back().first != path.back().second )
        {
            printf( "%*s%S\n", path.size()*2, "", path.back().first->name );
            path.push_back( std::make_pair(path.back().first->subs.begin(),path.back().first->subs.end()) );
        }
        else
        {
            path.pop_back();
            if( path.empty() ) break;
            ++path.back().first;
        }
    }

    return 0;
}

// 根据 FileReferenceNumber 直接获得全路径 的方法二
使用 NtCreatefile 和 NtQueryInformationFile ,但要求这个文件必须存在(in-used)
void GetFullPathByFileReferenceNumber( HANDLE hVol, DWORDLONG FileReferenceNumber )
{
    typedef ULONG (__stdcall *PNtCreateFile)(
        PHANDLE FileHandle,
        ULONG DesiredAccess,
        PVOID ObjectAttributes,
        PVOID IoStatusBlock,
        PLARGE_INTEGER AllocationSize,
        ULONG FileAttributes,
        ULONG ShareAccess,
        ULONG CreateDisposition,
        ULONG CreateOptions,
        PVOID EaBuffer,
        ULONG EaLength );
    PNtCreateFile NtCreatefile = (PNtCreateFile)GetProcAddress( GetModuleHandle(L"ntdll.dll"), "NtCreateFile" );

    typedef struct _UNICODE_STRING {
        USHORT Length, MaximumLength;
        PWCH Buffer;
    } UNICODE_STRING, *PUNICODE_STRING;
    UNICODE_STRING fidstr = { 8, 8, (PWSTR)&FileReferenceNumber };

    typedef struct _OBJECT_ATTRIBUTES {
        ULONG Length;
        HANDLE RootDirectory;
        PUNICODE_STRING ObjectName;
        ULONG Attributes;
        PVOID SecurityDescriptor;
        PVOID SecurityQualityOfService;
    } OBJECT_ATTRIBUTES;
    const ULONG OBJ_CASE_INSENSITIVE = 0x00000040UL;
    OBJECT_ATTRIBUTES oa = { sizeof(OBJECT_ATTRIBUTES), hVol, &fidstr, OBJ_CASE_INSENSITIVE, 0, 0 };
    
    HANDLE hFile;
    ULONG iosb[2];
    const ULONG FILE_OPEN_BY_FILE_ID = 0x00002000UL;
    const ULONG FILE_OPEN            = 0x00000001UL;
    ULONG status = NtCreatefile( &hFile, GENERIC_ALL, &oa, iosb, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN, FILE_OPEN_BY_FILE_ID, NULL, 0 );
    if( status == 0 )
    {
        typedef struct _IO_STATUS_BLOCK {
            union {
                NTSTATUS Status;
                PVOID Pointer;
            };
            ULONG_PTR Information;
        } IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
        typedef enum _FILE_INFORMATION_CLASS {
            // ……
            FileNameInformation = 9
            // ……
        } FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS;
        typedef NTSTATUS (__stdcall *PNtQueryInformationFile)(
            HANDLE FileHandle,
            PIO_STATUS_BLOCK IoStatusBlock,
            PVOID FileInformation,
            DWORD Length,
            FILE_INFORMATION_CLASS FileInformationClass );
        PNtQueryInformationFile NtQueryInformationFile = (PNtQueryInformationFile)GetProcAddress( GetModuleHandle(L"ntdll.dll"), "NtQueryInformationFile" );

        typedef struct _OBJECT_NAME_INFORMATION {
            UNICODE_STRING Name;
        } OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION;
        IO_STATUS_BLOCK IoStatus;
        size_t allocSize = sizeof(OBJECT_NAME_INFORMATION) + MAX_PATH*sizeof(WCHAR);
        POBJECT_NAME_INFORMATION pfni = (POBJECT_NAME_INFORMATION)operator new(allocSize);
        status = NtQueryInformationFile(hFile, &IoStatus, pfni, allocSize, FileNameInformation);
        if( status == 0 )
        {
            printf( "%.*S\n", pfni->Name.Length/2, &pfni->Name.Buffer );
        }
        operator delete(pfni);

        CloseHandle(hFile);
    }
}

// 根据 FileReferenceNumber 直接获得全路径 的方法三
使用 FSCTL_GET_NTFS_FILE_RECORD,但要求这个文件必须存在(in-used)
typedef struct {
    ULONG Type;
    USHORT UsaOffset; 
    USHORT UsaCount;
    USN Usn;
} NTFS_RECORD_HEADER, *PNTFS_RECORD_HEADER;

typedef struct {
    NTFS_RECORD_HEADER Ntfs;
    USHORT SequenceNumber;
    USHORT LinkCount;
    USHORT AttributesOffset;
    USHORT Flags;               // 0x0001 = InUse, 0x0002 = Directory
    ULONG BytesInUse;
    ULONG BytesAllocated;
    ULONGLONG BaseFileRecord; 
    USHORT NextAttributeNumber;
} FILE_RECORD_HEADER, *PFILE_RECORD_HEADER;

typedef enum {
    AttributeStandardInformation = 0x10,
    AttributeAttributeList = 0x20,
    AttributeFileName = 0x30,
    AttributeObjectId = 0x40,
    AttributeSecurityDescriptor = 0x50,
    AttributeVolumeName = 0x60,
    AttributeVolumeInformation = 0x70,
    AttributeData = 0x80,
    AttributeIndexRoot = 0x90,
    AttributeIndexAllocation = 0xA0,
    AttributeBitmap = 0xB0,
    AttributeReparsePoint = 0xC0,
    AttributeEAInformation = 0xD0,
    AttributeEA = 0xE0,
    AttributePropertySet = 0xF0,
    AttributeLoggedUtilityStream = 0x100
} ATTRIBUTE_TYPE, *PATTRIBUTE_TYPE;

typedef struct {
    ATTRIBUTE_TYPE AttributeType; 
    ULONG Length;
    BOOLEAN Nonresident;
    UCHAR NameLength;
    USHORT NameOffset;
    USHORT Flags;               // 0x0001 = Compressed
    USHORT AttributeNumber;
} ATTRIBUTE, *PATTRIBUTE;

typedef struct {
    ATTRIBUTE Attribute; 
    ULONGLONG LowVcn;
    ULONGLONG HighVcn;
    USHORT RunArrayOffset;
    UCHAR CompressionUnit;
    UCHAR AlignmentOrReserved[5];
    ULONGLONG AllocatedSize;
    ULONGLONG DataSize;
    ULONGLONG InitializedSize;
    ULONGLONG CompressedSize;    // Only when compressed
} NONRESIDENT_ATTRIBUTE, *PNONRESIDENT_ATTRIBUTE;

typedef struct {
    ATTRIBUTE Attribute;
    ULONG ValueLength;
    USHORT ValueOffset;
    USHORT Flags;               // 0x0001 = Indexed
} RESIDENT_ATTRIBUTE, *PRESIDENT_ATTRIBUTE;

typedef struct {
    ULONGLONG CreationTime; 
    ULONGLONG ChangeTime;
    ULONGLONG LastWriteTime; 
    ULONGLONG LastAccessTime; 
    ULONG FileAttributes; 
    ULONG AlignmentOrReservedOrUnknown[3];
    ULONG QuotaId;                        // NTFS 3.0 only
    ULONG SecurityId;                     // NTFS 3.0 only
    ULONGLONG QuotaCharge;                // NTFS 3.0 only
    USN Usn;                              // NTFS 3.0 only
} STANDARD_INFORMATION, *PSTANDARD_INFORMATION;

typedef struct {
    ULONGLONG DirectoryFileReferenceNumber;
    ULONGLONG CreationTime;   // Saved when filename last changed
    ULONGLONG ChangeTime;     // ditto
    ULONGLONG LastWriteTime;  // ditto
    ULONGLONG LastAccessTime; // ditto
    ULONGLONG AllocatedSize;  // ditto
    ULONGLONG DataSize;       // ditto
    ULONG FileAttributes;     // ditto
    ULONG AlignmentOrReserved;
    UCHAR NameLength;
    UCHAR NameType;           // 0x01 = Long, 0x02 = Short
    WCHAR Name[1];
} FILENAME_ATTRIBUTE, *PFILENAME_ATTRIBUTE;

bool GetFullPathByFileReferenceNumber( HANDLE hVol, DWORDLONG FileReferenceNumber )
{
    if( (FileReferenceNumber&0x0000FFFFFFFFFFFF) == 5 )
        return true;

    bool ret = false;
    DWORD BytesReturned;
    NTFS_VOLUME_DATA_BUFFER nvdb;


//DeviceIoControl是直接发送控制代码到指定的设备驱动程序,使相应的移动设备以执行相应的操作的函数。


    if( DeviceIoControl( hVol, FSCTL_GET_NTFS_VOLUME_DATA, NULL, 0
        , &nvdb, sizeof(nvdb), &BytesReturned, NULL ) ) // 仅是事例,没有作优化 1.作为递归调用,这一步应当提取出来 2.如果多次调用,DirectoryFileReferenceNumber没必要被重复获取
    {
        NTFS_FILE_RECORD_INPUT_BUFFER nfrib;
        nfrib.FileReferenceNumber.QuadPart = FileReferenceNumber;
        size_t len = sizeof(NTFS_FILE_RECORD_OUTPUT_BUFFER)+nvdb.BytesPerFileRecordSegment-1;
        NTFS_FILE_RECORD_OUTPUT_BUFFER* nfrob = (PNTFS_FILE_RECORD_OUTPUT_BUFFER)operator new(len);
        if( DeviceIoControl( hVol, FSCTL_GET_NTFS_FILE_RECORD, &nfrib, sizeof(nfrib)
            , nfrob, len, &BytesReturned, NULL ) )
        {
            if( (nfrib.FileReferenceNumber.QuadPart&0x0000FFFFFFFFFFFF) == nfrob->FileReferenceNumber.QuadPart ) // a 48-bit index and a 16-bit sequence number 
            {
                PFILE_RECORD_HEADER frh = (PFILE_RECORD_HEADER)nfrob->FileRecordBuffer;
                for( PATTRIBUTE attr=(PATTRIBUTE)((LPBYTE)frh+frh->AttributesOffset); attr->AttributeType!=-1; attr=(PATTRIBUTE)((LPBYTE)attr+attr->Length) )
                {
                    if( attr->AttributeType == AttributeFileName )
                    {
                        PFILENAME_ATTRIBUTE name = (PFILENAME_ATTRIBUTE)( (LPBYTE)attr + PRESIDENT_ATTRIBUTE(attr)->ValueOffset );
                        if( (name->NameType&1) == 1 ) // long name
                        {

//// GetFullPathByFileReferenceNumber如果记录中没有,再去看看这个文件实际存在否
                            if( GetFullPathByFileReferenceNumber( hVol, name->DirectoryFileReferenceNumber ) )
                            {
                                printf( "\\%.*S", name->NameLength, name->Name );
                                ret = true;
                            }
                        }
                    }
                }
            }
        }
        operator delete( nfrob );
    }
    return ret;
}

====================================================================================================================================================================================================================================================================================================================================================================================================================



  硬盘序列号: 英文名 Hard Disk Serial Number, 该号是出厂时生产厂家为区别产品而设置的, 是唯一的, 是只读的, 利用硬盘序列号的加密往往是利用其唯一和只读的特性, 大多是针对有序列号的 IDE HDD而言, 对于没有序列号或SCSI HDD硬盘则无能为力, 这也是利用它进行加密的局限性.
       卷的序列号: 英文名 Volume Serial Number, 该号既可指软磁盘要得, 如: A:盘和B:盘的, 又可以指硬盘的逻辑盘, 如: C:, D:...的, 是高级格式化时随机产生的, 是可以修改的, 所以利用其进行 加密, 其唯一性还可, 而其可修改性对于安全而言就大打折扣了. 

        如何得到磁盘序列号和卷标及其它信息主要就是要调用GetVolumeInformation这个API函数,函数声明如下
BOOL GetVolumeInformation(
  LPCTSTR lpRootPathName,        // address of root directory of the 
                                 // file system
  LPTSTR lpVolumeNameBuffer,     // address of name of the volume
  DWORD nVolumeNameSize,         // length of lpVolumeNameBuffer
  LPDWORD lpVolumeSerialNumber,  // address of volume serial number
  LPDWORD lpMaximumComponentLength,
                                 // address of system's maximum 
                                 // filename length
  LPDWORD lpFileSystemFlags,     // address of file system flags
  LPTSTR lpFileSystemNameBuffer, // address of name of file system
  DWORD nFileSystemNameSize      // length of lpFileSystemNameBuffer
);

[VB]
Public Declare Function GetVolumeInformation& Lib "kernel32" Alias "Ge
tVolumeInformationA" (ByVal lpRootPathName As String, ByVal pVolumeNam
eBuffer As String, ByVal nVolumeNameSize As Long, lpVolumeSerialNumber
As Long, lpMaximumComponentLength As Long, lpFileSystemFlags As Long,
ByVal lpFileSystemNameBuffer As String, ByVal nFileSystemNameSize As
Long)

用API 函数GetVolumeInformation得到的不是硬盘的序列号!!! 看看英文啊:VolumeSerialNumber!得到的只是卷区序列号!

硬盘的序列号应该是
Drive Model Number________________: WDC WD400EB-00CPF0
Drive Serial Number_______________: WD-WCAATF083586
Drive Controller Revision Number__: 06.04G06
Controller Buffer Size on Drive___: 2097152 bytes
Drive Type________________________: Fixed
Drive Size________________________: 40020664320 bytes
中:

Drive Serial Number_______________: WD-WCAATF083586 这才是硬盘Serial Number!!!!


这个号是不会因为你格式化硬盘而改动,也不是网上流传的修改工具能改的,(其实网上流传的修改工具的也不过是卷区号而已,真是哭笑不得!)

硬盘序列号与磁盘序列号不同,磁盘序列号是硬盘分区中某个卷(volumn)也就是我们说的磁盘的序列号。而硬盘序列号是整块硬盘的序列号,不能被修改。

 

 

 

-----------------------

DeviceIoControl可以获取硬盘序列号。其实现在CSDN上有下。摘录部分

  BYTE IdOutCmd [sizeof (SENDCMDOUTPARAMS) + IDENTIFY_BUFFER_SIZE - 1];
   BOOL bFlag = FALSE;
   int  drive = 0;
   char driveName [256];
   HANDLE hPhysicalDriveIOCTL = 0;   
     
   sprintf (driveName, "\\\\.\\PhysicalDrive%d", drive);
   //  Windows NT/2000/XP下创建文件需要管理员权限
   hPhysicalDriveIOCTL = CreateFile (driveName,
                            GENERIC_READ | GENERIC_WRITE,
                            FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
                            OPEN_EXISTING, 0, NULL);

   if (hPhysicalDriveIOCTL != INVALID_HANDLE_VALUE)
   {
       GETVERSIONOUTPARAMS VersionParams;
       DWORD               cbBytesReturned = 0;

       // 得到驱动器的IO控制器版本
       memset ((void*) &VersionParams, 0, sizeof(VersionParams));
       if(DeviceIoControl (hPhysicalDriveIOCTL, IOCTL_GET_VERSION,
                               NULL, 0, &VersionParams,
                               sizeof(VersionParams),
                               &cbBytesReturned, NULL) )
    {       
          if (VersionParams.bIDEDeviceMap > 0)
    {
              BYTE             bIDCmd = 0;   // IDE或者ATAPI识别命令
              SENDCMDINPARAMS  scip;
 
              // 如果驱动器是光驱,采用命令IDE_ATAPI_IDENTIFY, command,
              // 否则采用命令IDE_ATA_IDENTIFY读取驱动器信息
              bIDCmd = (VersionParams.bIDEDeviceMap >> drive & 0x10)?
                      IDE_ATAPI_IDENTIFY : IDE_ATA_IDENTIFY;

              memset (&scip, 0, sizeof(scip));
              memset (IdOutCmd, 0, sizeof(IdOutCmd));
              // 获取驱动器信息
              if (WinNTGetIDEHDInfo (hPhysicalDriveIOCTL,
                                      &scip,
                                      (PSENDCMDOUTPARAMS)&IdOutCmd,
                                      (BYTE) bIDCmd,
                                      (BYTE) drive,
                                      &cbBytesReturned))
     {
                  int m = 0;
                  USHORT *pIdSector = (USHORT *)
                             ((PSENDCMDOUTPARAMS) IdOutCmd) -> bBuffer;

                  for (m = 0; m < 256; m++)
                       buffer[m] = pIdSector [m];
                  bFlag = TRUE;  // 读取硬盘信息成功
     }
    }
    }
       CloseHandle (hPhysicalDriveIOCTL);  // 关闭句柄
   }
   return bFlag;

 

 

 

 

BOOL CGetHDSerial::WinNTGetIDEHDInfo (HANDLE hPhysicalDriveIOCTL, PSENDCMDINPARAMS pSCIP,
                 PSENDCMDOUTPARAMS pSCOP, BYTE bIDCmd, BYTE bDriveNum,
                 PDWORD lpcbBytesReturned)
{
   // 为读取设备信息准备参数
   pSCIP -> cBufferSize = IDENTIFY_BUFFER_SIZE;
   pSCIP -> irDriveRegs.bFeaturesReg = 0;
   pSCIP -> irDriveRegs.bSectorCountReg = 1;
   pSCIP -> irDriveRegs.bSectorNumberReg = 1;
   pSCIP -> irDriveRegs.bCylLowReg = 0;
   pSCIP -> irDriveRegs.bCylHighReg = 0;

   // 计算驱动器位置
   pSCIP -> irDriveRegs.bDriveHeadReg = 0xA0 | ((bDriveNum & 1) << 4);

   // 设置读取命令
   pSCIP -> irDriveRegs.bCommandReg = bIDCmd;
   pSCIP -> bDriveNumber = bDriveNum;
   pSCIP -> cBufferSize = IDENTIFY_BUFFER_SIZE;
  
   // 读取驱动器信息
   return ( DeviceIoControl (hPhysicalDriveIOCTL, IOCTL_GET_DRIVE_INFO,
               (LPVOID) pSCIP,
               sizeof(SENDCMDINPARAMS) - 1,
               (LPVOID) pSCOP,
               sizeof(SENDCMDOUTPARAMS) + IDENTIFY_BUFFER_SIZE - 1,
               lpcbBytesReturned, NULL) );
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
在Windows系统中,MTF(Master File Table)是NTFS文件系统中的一个重要组成部分。MTF可以看作是一个索引表,记录了NTFS文件系统中所有文件和目录的元数据信息。如果你想要读取MTF表,可以使用Python的Win32 API模块来实现。 下面是一个使用Win32 API模块读取MTF表的示例代码: ```python import win32file import winioctlcon # 打开NTFS卷 volume = r"\\.\C:" handle = win32file.CreateFile(volume, win32file.GENERIC_READ, win32file.FILE_SHARE_READ | win32file.FILE_SHARE_WRITE, None, win32file.OPEN_EXISTING, 0, None) # 获取NTFS卷的属性 attributes = win32file.GetFileInformationByHandle(handle) # 获取NTFS卷的设备句柄 device = r"\\.\PHYSICALDRIVE%s" % attributes[8] device_handle = win32file.CreateFile(device, win32file.GENERIC_READ, win32file.FILE_SHARE_READ | win32file.FILE_SHARE_WRITE, None, win32file.OPEN_EXISTING, 0, None) # 使用DeviceIOControl函数读取MTF表 buffer = win32file.DeviceIoControl(device_handle, winioctlcon.FSCTL_ENUM_USN_DATA, None, 4096, None, 0) print(buffer) ``` 在上面的代码中,我们首先使用`win32file.CreateFile`函数打开NTFS卷,并使用`win32file.GetFileInformationByHandle`函数获取NTFS卷的属性。接着,我们使用NTFS卷的设备句柄打开设备,并使用`win32file.DeviceIoControl`函数读取MTF表。最后,我们将读取到的MTF表内容打印出来。 需要注意的是,读取MTF表需要管理员权限。如果你没有管理员权限,可以尝试使用`pywin32`模块的`elevate`函数来提升权限。示例代码如下: ```python import elevate elevate.elevate() ``` 希望这能帮助到你。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值