
USNLowestValidUsn; //此日志中的最小USN不一定为零.
USN MaxUsn; //根据最大大小计算的最大日志NextUsn大于它,则需要清除记录.
DWORDLONGMaximumSize; //最大尺寸
DWORDLONGAllocationDelta; //增加大小. 如果增加量超过MaximuSize,则开始清除记录.
} USN_JOURNAL_DATA,* PUSN_JOURNAL_DATA;
= USN记录=
以下是USN记录结构. 请注意,磁盘的特定数据不是以这种方式存储的,因此结构始终由系统填充. 成员的解释如下:
C ++:
//版本2.0USN_RECORD结构
typedef struct {
DWORD RecordLength;
WORD MajorVersion;
WORD MinorVersion;
DWORDLONG FileReferenceNumber;
DWORDLONG ParentFileReferenceNumber;
USNUsn;
LARGE_INTEGERTimeStamp;
DWORD原因;
DWORD SourceInfo;
DWORD SecurityId;
DWORD FileAttributes;
WORD FileNameLength;
WORD FileNameOffset;
WCHAR文件名[1];
} USN_RECORD,* PUSN_RECORD;
系统一次读取多个记录缓冲区. RecordLength是总记录长度,包括文件名. 因此怎么解决usn journal,请使用长度来计算下一个记录位置.
C ++:

PUSN_RECORDpNext;
pNext =(PUSN_RECORD)((((PBYTE)pRecord)+
pRecord-> RecordLength);
请不要忽略这两个参数MajorVersion和MinorVersion. 毕竟,NTFS在不断发展. ChangeJournal具有自己的版本控制. 要了解最新的结构,您可以参考winioctl.h中的语句,并且可能要确定程序中的版本,以区分处理以避免错误. 看,2.3看起来像这样:
C ++:
// HYPOTHETICALVersion 2.3 USN_RECORD结构
typedef struct {
DWORDRecordLength;
WORD MajorVersion;
WORD MinorVersion;
DWORDLONGFileReferenceNumber;
DWORDLONGParentFileReferenceNumber;
USNUsn;
LARGE_INTEGERTimeStamp;
DWORDReason;
DWORDSourceInfo;
DWORDSecurityId;
DWORDFileAttributes;
WORDFileNameLength;
WORDFileNameOffset; //原始版本的倒数第二个
DWORD ExtraInfo1; //假设是在2.1版中添加的
DWORD ExtraInfo2; //假设是在2.2版中添加的
DWORD ExtraInfo3; //假设是在版本2.3中添加的
WCHARFileName [1]; //可变长度总是在结尾
} USN_RECORD,* PUSN_RECORD;
记录本身不会记录文件或目录的完整路径,并且文件名由上述结构中的三个参数确定,FileNameOffset文件名偏移,FileNameLength文件名长度和FileName不能直接使用.
C ++:

WCHARszName [MAX_PATH];
CopyMemory(szName,
(((PBYTE)pRecord)+ pRecord-> FileNameOffset,
pRecord-> FileNameLength);
//让我们零终止它
szName [pRecord-> FileNameLength / sizeof(WCHAR)] = 0;
文件参考号(FRN)是NTFS卷上文件和目录的唯一标识符. 完整路径可以通过ParentFileReferenceNumber获取.
C ++:
TCHARszFullPath [MAX_PATH];
//填写父目录的路径
PathFromParentFRN(pRecord-> ParentFileReferenceNumber,
szFullPath); //使用Win32 functionPathAppend将名称附加到路径
PathAppend(szFullPath怎么解决usn journal,szName);
不幸的是,没有名为PathFromParentFRN的API,否则可以直接读取目录名称. 现在您可能想知道FileReferenceNumber是做什么的. 如果我们可以通过FRN获取完整的路径信息,则不需要偏移量+长度来获取文件名. 实际上,查找目录的FRN比文件容易得多. FileReferenceNumber不一定是文件或目录,但是ParentFileReferenceNumber必须是目录,因此请使用offset + length获取真实名称,然后使用Parent获取目录. 完整的路径到了.
是的,Usn是记录标识符; TimeStamp是64位UTC时间戳;原因成员指示文件或目录发生了什么. 打开文件后,系统将Reason变量设置为零,但不写入USN记录. 发生更改时,如果这是新的ReasonCode,请设置Reason变量并将一条记录写入日志. 如果多个程序同时操作同一文件,则可能会发生同一记录的原因具有多个原因代码,直到设置了USN_REASON_CLOSE并关闭了该文件.
C ++: 您也可以通过调用DeviceIoControl传入FSCTL_WRITE_USN_CLOSE_RECORD,以便在打开文件时系统将Reason变量清除为0.
DWORDcb;
USNusn;
//强制关闭记录
//指定的打开文件
//通过'hFile'
DeviceIoControl(hFile,FSCTL_WRITE_USN_CLOSE_RECORD,
NULL,0,&usn,sizeof(usn),&cb,NULL);
唯一的特殊ReasonCode是USN_REASON_RENAME_OLD_NAME. 重命名文件时,两条记录将被写入日志,一条记录用于旧文件/目录名,另一条记录用于新文件/目录名. 当然,其ReasonCode是USN_REASON_RENAME_NEW_NAME.
如果SourceInfo成员不为零,则表明文件已更改,此文件和Season之间有什么区别. 例如,“防病毒软件删除了文件中的病毒”. 防病毒软件需要打开文件并覆盖受感染的部分. 这将生成一个原因为USN_REASON_DATA_OVERWRITE的记录,并且由于数据覆盖操作(原因),该记录将被完成以用于防病毒(SourceInfo). 换句话说,SourceInfo更合乎逻辑. 该信息不是系统指出的,而是由操作文件的程序设置的.
SecurityId是系统用来描述文件安全性的成员,并且与设备I / O控制FSCTL_SECURITY_ID_CHECK仪器一起使用; FileAttributes可以通过GetFileAttributes调用获取文件/目录的属性.
=读取记录=

在了解了记录结构之后,让我们阅读ChangeJournal记录. 首先准备两个变量,分别是卷句柄和日志结构(通过FSCTL_QUERY_USN_JOURNAL获得):
C ++:
HANDLEhcj;
USN_JOURNAL_DATAujd;
通过调用FSCTL_READ_USN_JOURNAL再次调用DeviceIoControl. 需要填写以下结构作为参数输入:
C ++:
typedef struct {
USNStartUsn;
DWORDReasonMask;
DWORDReturnOnlyOnClose;
DWORDLONGTimeout;
DWORDLONGBytesToWaitFor;
DWORDLONGUsnJournalID;
} READ_USN_JOURNAL_DATA,* PREAD_USN_JOURNAL_DATA;
StartUsn,您要访问的第一个Usn. 如果该ID存在,则返回ID,否则返回下一个ID. 如果StartUsn为0,系统将返回第一条记录. ReasonMask和ReturnOnlyOnClose可以按字面理解(稍后说明). StartUsn不保证同时满足这两个条件,因此调用者需要自己进行验证. 系统使用4KB作为块(USN_PAGE_SIZE)写入日志. 所有ujd.FirstUsn至ujd.NextUsn都将按照4KB对齐.
系统将仅返回满足ReasonMask条件的记录. 换句话说,您可以指定所需的ReasonCode. 不符合条件的记录将不包含在缓冲区中. ReturnOnlyOnClose是另一个可以过滤记录的成员. 如果其值不为零,则仅返回原因= USN_REASON_CLOSE记录. 此条件需要与ReasonMask保持一致.
超时与BytesToWaitFor一起用作查询时间的限制. 这并不意味着DeviceIoControl在指定的超时时间内返回,而是用来指定系统检查请求的数据是否可用的时间. 该成员不像其他win32超时参数那样使用毫秒级计时,而是使用FILETIME结构. 将“超时”设置为0时,未指定任何超时. 使用负数指定超时,例如25秒超时可以表示为-2500000000. 如果DeviceIoControl是异步调用的,则超时成员将被忽略.
请勿将BytesToWaitFor成员与输出缓冲区的大小或DeviceIoControl的返回值混淆. 如果将其设置为零,则意味着即使没有找到匹配的日志,该函数也会立即返回. 如果它不为零,则至少找到一个匹配数据,然后将其返回. BytesToWaitFor定义系统检查其是否与数据创建匹配的时间段. 例如,如果定义16384,系统将在创建16KB数据后进行验证. 这样可以防止进程在读取记录时使用过多的资源. 超时/ BytesToWaitFor仅在使用ReasonMask / ReturnOnlyOnClose时有效,但找不到数据.
UsnJournalID应该设置为ujd.UsnJournalID. 如果更改了日记ID,则DeviceIoControl调用将失败(如前所述,禁用后将删除数据,并且在重新启动后将更改此ID).
调用FSCTL_READ_USN_JOURNAL来填充输出缓冲区.
C ++:
DeviceIOControl(hcj,FSCTL_READ_USN_JOURNAL和InBuf,
sizeof(InBuf),pOut,cbOut和cbReturned,NULL);
但是,不可能知道要填充多少数据. 具体安排如下:
以下代码使用usnStart和usnEnd确定数据的有效性:
C ++:
///从usnStart读取但不包括usnEnd的USN原始数据

//这可以用于通过使用读取所有可用记录
// theUSN_JOURNAL_DATA成员FirstUsn和NextUsn
voidGetRawRecordData(HANDLE hcj,DWORDLONG journalId,
USN usnStart,USNusnEnd){
READ_USN_JOURNAL_DATArujd;
rujd.StartUsn = usnStart;
rujd.ReasonMask = 0xFFFFFFFF; // Allbits
rujd.ReturnOnlyOnClose = FALSE; //所有条目
rujd.Timeout = 0; //没有超时
rujd.BytesToWaitFor = 0; //如果没有记录,请不要等待
rujd.UsnJournalID = journalId; //我们希望从中读取的日记(rujd.StartUsn DWORDcbRead; BYTE pData [8192 + sizeof(USN)]; //读取8 KB块 BOOL fOk = DeviceIoControl(hcj,FSCTL_READ_USN_JOURNAL, &rujd,sizeof(rujd),pData,sizeof(pdata),&cbRead,NULL); 如果(!fOK) 休息; // handleerror //获取下一次请求的第一个USN rujd.StartUsn = *((PUSN)pData); PUSN_RECORDpRecord =(PUSN_RECORD)&pData [sizeof(USN)]; while(((PBYTE)pRecord <(pData + cbRead)){ // ...对记录进行操作 > pRecord =(PUSN_RECORD)(((PBYTE)pRecord + pRecord-> RecordLength) } }} ==代码== ChangeJournal.exe ==参考== 1. 密切关注NTFS驱动器: Windows 2000 Change Journal Explained 2. 密切关注NTFS驱动器,第二部分: 构建更改JournalApplication 3. 维基百科: USN_Journal
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/ruanjian/article-155942-1.html
巴菲特不是人吗