本文介紹通過ReadDirectoryChangesW()來編寫一個監視目錄變化的程序。
對目錄及目錄中的文件實時監控,可以有效地發現文件被改動的情況。就好像在本地安裝IIS服務器,并搭建一個網站平臺,有時候會遭到黑客的篡改,而程序員無法及時地恢復被篡改的頁面,導致出現了非常不好的影響。如果能及時地發現網頁被篡改,并及時地恢復本來的頁面就好了,那么該如何做呢?
下面通過一個簡單的例子來介紹如何監控某目錄及目錄下文件的變動情況。首先需要了解的函數為ReadDirectoryChangesW(),其定義如下:
BOOL ReadDirectoryChangesW(
HANDLE hDirectory,
LPVOID lpBuffer,
DWORD nBufferLength,
BOOL bWatchSubtree,
DWORD dwNotifyFilter,
LPDWORD lpBytesReturned,
LPOVERLAPPED lpOverlapped,
LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
);
參數說明如下。
hDirectory:該參數指向一個要監視目錄的句柄。該目錄需要用 FILE_LIST_DIRECTORY的訪問權限打開。
lpBuffer:該參數指向一個內存的緩沖區,它用來存放返回的結果。結果為一個 FILE_NOTIFY_INFORMATION 的數據結構。
nBufferLength:表示緩沖區的大小。
bWatchSubtree:該參數為 TRUE 時,表示監視指定目錄下的文件及子目錄下的文件操作。如果該參數為 FALSE,則只監視指定目錄下的文件,不包含子目錄下的文件。
dwNotifyFilter:該參數指定要返回何種文件變更后的類型,該參數的常量值參見 MSDN。
lpBytesReturned:該參數返回傳給 lpBuffer 結果的字節數。
lpOverlapped:該參數執行一個 OVERLAPPED 結構體,該結構體用于異步操作,否則該數據為 NULL。
ReadDirectoryChangesW()函數的使用非常簡單,下面通過一個例子介紹其使用。該例子是對E盤目錄進行監控,將程序編寫完成后對E盤進行簡單的文件操作,以觀察程序的輸出結構。完整的代碼如下:
#include <windows.h>
#include <stdio.h>
extern “C”
BOOL
WINAPI
ReadDirectoryChangesW(
__in HANDLE hDirectory,
__out_bcount_part(nBufferLength, *lpBytesReturned) LPVOIDlpBuffer,
__in DWORD nBufferLength,
__in BOOL bWatchSubtree,
__in DWORD dwNotifyFilter,
__out LPDWORD lpBytesReturned,
__inout LPOVERLAPPED lpOverlapped,
__in_opt LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
);
DWORD WINAPI ThreadProc(LPVOID lpParam)
{
BOOL bRet = FALSE;
BYTE Buffer[1024] = { 0 };
FILE_NOTIFY_INFORMATION *pBuffer = (FILE_NOTIFY_INFORMATION *)Buffer;
DWORD BytesReturned = 0;
HANDLE hFile = CreateFile(“e:\\”,
FILE_LIST_DIRECTORY,
FILE_SHARE_READ|FILE_SHARE_DELETE|FILE_SHARE_WRITE,
NULL,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,NULL);
if ( INVALID_HANDLE_VALUE == hFile )
{
return 1;
}
printf(“monitor… \r\n”);
while ( TRUE )
{
ZeroMemory(Buffer, 1024);
bRet = ReadDirectoryChangesW(hFile,&Buffer,sizeof(Buffer),TRUE,
FILE_NOTIFY_CHANGE_FILE_NAME | // 修改文件名
FILE_NOTIFY_CHANGE_ATTRIBUTES | // 修改文件屬性
FILE_NOTIFY_CHANGE_LAST_WRITE , // 最后一次寫入
&BytesReturned,NULL, NULL);
if ( bRet == TRUE )
{
char szFileName[MAX_PATH] = { 0 };
// 寬字符轉換多字節
WideCharToMultiByte(CP_ACP,0,pBuffer->FileName,
pBuffer->FileNameLength / 2,szFileName,
MAX_PATH,NULL,NULL);
switch(pBuffer->Action)
{
// 添加
case FILE_ACTION_ADDED:
{
printf(“添加 : %s\r\n”, szFileName);
break;
}
// 刪除
case FILE_ACTION_REMOVED:
{
printf(“刪除 : %s\r\n”, szFileName);
break;
}
// 修改
case FILE_ACTION_MODIFIED:
{
printf(“修改 : %s\r\n”, szFileName);
break;
}
// 重命名
case FILE_ACTION_RENAMED_OLD_NAME:
{
printf(“重命名 : %s”, szFileName);
if ( pBuffer->NextEntryOffset != 0 )
{
FILE_NOTIFY_INFORMATION *tmpBuffer = (FILE_NOTIFY_INFORMATION *)
((DWORD)pBuffer + pBuffer->NextEntryOffset);
switch ( tmpBuffer->Action )
{
case FILE_ACTION_RENAMED_NEW_NAME:
{
ZeroMemory(szFileName, MAX_PATH);
WideCharToMultiByte(CP_ACP,0,
tmpBuffer->FileName,tmpBuffer->FileNameLength / 2,
szFileName,MAX_PATH,NULL,NULL);
printf(“ -> : %s \r\n”, szFileName);
break;
}
}
}
break;
}
case FILE_ACTION_RENAMED_NEW_NAME:
{
printf(“重命名(new) : %s\r\n”, szFileName);
}
}
}
}
CloseHandle(hFile);
return 0;
}
int main(int argc, char* argv[])
{
HANDLE hThread = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);
if ( hThread == NULL )
{
return -1;
}
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
return 0;
}
將程序編譯連接并運行,在E盤下進行簡單的操作,查看程序對E盤的監視輸出記錄,如圖1所示。
圖片
圖1 目錄監控輸出記錄
對于目錄監視的這個例子,可以將其改為一個簡單的文件防篡改程序。首先將要監視的文件目錄進行備份,然后對文件目錄進行監視,如果有文件發生了修改,那么就使用備份目錄下的指定文件恢復被修改的文件。