有个名叫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 格式
    {
        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;
    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
                        {
                            if( GetFullPathByFileReferenceNumber( hVol, name->DirectoryFileReferenceNumber ) )
                            {
                                printf( "\\%.*S", name->NameLength, name->Name );
                                ret = true;
                            }
                        }
                    }
                }
            }
        }
        operator delete( nfrob );
    }
    return ret;
}

读取NTFS的USN(快速检索文件)相关推荐

  1. 读取NTFS的USN(获取文件的历史操作记录,即使这个文件已被删除)

    原文转载于:http://blog.okbase.net/bruceteen/archive/242.html 上回说到NTFS USN会记录下文件的所有操作,但默认情况下并没有激活这一功能,所以不用 ...

  2. 读取ntfs的usn

    上回说到NTFS USN会记录下文件的所有操作,但默认情况下并没有激活这一功能,所以不用担心.在非激活状态,它也只剩下快速查找文件的功能. 这回,假设USN JOURNAL被激活了(你可以用控制台命令 ...

  3. Listary-不仅仅是快速检索文件

    为什么80%的码农都做不了架构师?>>>    Listary是windows下一款特别的文件检索工具,它不仅仅使文件浏览变得真正的灵活,它那紧凑的UI重新定义了极简主义.在保持轻量 ...

  4. Everything研究之读取NTFS下的USN日志文件(1)

    转自:http://univasity.iteye.com/blog/805234 http://univasity.iteye.com/blog/805235 我在第一次使用 Everything  ...

  5. Everything研究之快速获取USN记录的文件路径

    转自:http://univasity.iteye.com/blog/860847 <!-- 发觉越是没事干,记忆越差,乘还记得点什么,记录下以备份 --> 继上一篇关于USN的探索,我们 ...

  6. 【利用Arcpy快速检索矢量文件和栅格文件并进行统计和删除工作】

    这里写目录标题 场景分析 解决思路和方法 解决思路 代码 拓展的知识及应用分析 快速获取文件夹中的所有矢量文件和栅格文件名称 矢量文件 栅格文件 快速数据库中的矢量文件和栅格文件名称 快速读取每个矢量 ...

  7. Elasticsearch 如何做到快速检索 - 倒排索引的秘密

    欢迎关注方志朋的博客,回复"666"获面试宝典 来源:https://ricstudio.top/archives/es-lucene-reverted-index 一.前言 最近 ...

  8. Elasticsearch 如何做到快速检索?

    " 最近接触的几个项目都使用到了 Elasticsearch (以下简称 ES ) 来存储数据和对数据进行搜索分析,就对 ES 进行了一些学习.本文整理自我自己的一次技术分享. 本文不会关注 ...

  9. 快速检索并引用你在CSDN上所有的博文笔记

    简 介: 利用CSDN提供的博文的列表,编写了一个快速检索自己博文,并自动插入索引连接的程序.这样就可以大大提高博文写作的效率,将之前记录学习.工作的内容更好的进行连接.最后,感谢CSDN技术人员的大 ...

最新文章

  1. 后端 消息 转发_小程序转发探索示例
  2. 交叉验证的意义和目的_干货:详解原料药的工艺验证
  3. 模型蒸馏(Distillation)
  4. .net组件开发系列之武术系列 武术招数 控件生命周期与控件事件机制
  5. 超600人!近5小时直播!录屏+彩蛋+PPT…你要的都在这!
  6. owncloud8 php,owncloud-8.2.3
  7. 基于CUDA的粒子系统的实现
  8. 全栈性能测试修炼宝典jmeter实战电子版_JMeter实战(一) 体系结构
  9. Eclipse Class Decompiler——Java反编译插件
  10. Dxg——AD(Altium Designer) 开发笔记整理分类合集【所有的相关记录,都整理在此】
  11. vsCode 快捷键、插件
  12. 了解一下Redis队列【缓兵之计-延时队列】
  13. 51单片机之TMOD寄存器
  14. matlab三维绘图函数plot3【matlab图行绘制四】
  15. 操作系统原理课程 期末考试复习重点
  16. 开源GIS的一些理解和介绍
  17. 银广夏事件--中国股票财务作假事件
  18. Vue 集成 stylus和stylus-loader
  19. ucb计算机硕士专业,美国CS、CE、ECE硕士项目你能区分开吗?
  20. PAT-2019年冬季考试-乙级-7-3String复读机

热门文章

  1. 电脑解压文件丢失怎么找回来?四种恢复方法
  2. 巴菲特致股东的一封信:1998年
  3. js 解析GET 请求参数
  4. Mysql8.0.17压缩包安装——超详细简单教程
  5. 我的世界java邮箱和密码_java实现邮箱找密码
  6. python学习-进阶
  7. C++编程规范 头文件格式 和 函数注释格式
  8. 基音提取之短时自相关法
  9. catalog英文翻译_Catalog.是什么意思
  10. Android小心心动画