上回说到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, ×tamp );
SYSTEMTIME st;
FileTimeToSystemTime( ×tamp, &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);
}
}
====================================================================================================================================================================================================================================================================================================================================================================================================================
有个名叫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这是一个多功能的函数,可打开或创建以下对象,并返回可访问的句柄:控制台,通信资源,目录(只读打开),磁盘驱动器,文件,邮槽,管道。
, 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) );
}