Слежение за объектами файловой системой
От: Аноним  
Дата: 19.01.06 08:02
Оценка:
Привет всем.

Недавно мне поставили задачу создать файловый монитор наподобие
filemon, но работающий исключительно в UserMode. Пока в голову приходит
только использование возможнойстей shell, но они достаточно ограничены и
не полны. Я слышал, что помимо shell в Винде есть еще два механизма слежения
за файлами и директориями, но не знаю, как их зовут и где искать. Покорнейше прошу,
натолкните на мысль. Куда идти, где копать.
Re: Слежение за объектами файловой системой
От: _Dreamer Россия  
Дата: 19.01.06 08:46
Оценка:
Здравствуйте, Аноним, Вы писали:

А>натолкните на мысль. Куда идти, где копать.


ReadDirectoryChangesW не подойдет ? но она только для NT — Requires Windows XP, Windows 2000 Professional, or Windows NT Workstation 4.0.
Re: Слежение за объектами файловой системой
От: ekamaloff Великобритания  
Дата: 19.01.06 09:00
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Привет всем.


А>Недавно мне поставили задачу создать файловый монитор наподобие

А>filemon, но работающий исключительно в UserMode. Пока в голову приходит
А>только использование возможнойстей shell, но они достаточно ограничены и
А>не полны.

Имеешь ввиду FindFirstChangeNotification, FindNextChangeNotification, ReadDirectoryChangesW?

A>Я слышал, что помимо shell в Винде есть еще два механизма слежения

А>за файлами и директориями, но не знаю, как их зовут и где искать. Покорнейше прошу,
А>натолкните на мысль. Куда идти, где копать.

Видимо под этими двумя способами подразумеваются фильтр файловой системы и создание драйвера, перехватывающего вызовы системных функций (типа ZwReadFile, ZwWriteFile). Вот по этим ключевым словам и попробуй поискать.

Вот несколько ссылок с этого навскидку:

здесь
Автор: ASGS
Дата: 20.11.04

здесь
Автор: Jungle
Дата: 18.08.03

здесь
Автор: Carc
Дата: 08.04.03

здесь
Автор: jsmax
Дата: 27.05.05

здесь
Автор:
Дата: 26.12.05

здесь
Автор: Lonely Dog
Дата: 07.09.02
It is always bad to give advices, but you will be never forgiven for a good one.
Oscar Wilde
Re: Слежение за объектами файловой системой
От: SeregaLBN Украина  
Дата: 19.01.06 17:39
Оценка: 10 (3)
Здравствуйте, Аноним, Вы писали:
А>Недавно мне поставили задачу создать файловый монитор

Вот писал для себя. Узнавал кто же удаляет мои файлы.
Использую и ReadDirectoryChangesW и FindFirstChangeNotification/FindNextChangeNotification и перехват API и native ф-ций удаления файлов внутри своего просесса.

#include "stdafx.h"
//#define _WIN32_WINNT 0x0500 // must defined in the project
#include <detours.h> // download from http://research.microsoft.com/research/downloads/download.aspx?FUID={10E5D78C-592C-419D-A53E-BAE8DBD81801}&CodeName=Detours&Version=1.5
#pragma comment(lib, "detours")

#ifdef _DEBUG
   #define new DEBUG_NEW
   #undef THIS_FILE
   static char THIS_FILE[] = __FILE__;
#endif

//#define __NT_DDK__ // можно определить, если установлено DDK
#ifdef __NT_DDK__
   namespace NT {
      extern "C" {
         #pragma warning(disable:4005)
         #include <ntddk.h> // need DDK
         #pragma warning(default:4005)
      }
   }
   using NT::NTSTATUS;
#endif // __NT_DDK__


/*******************************************************************************************/
//
//          Вспомогательные ф-ции
//
/*******************************************************************************************/
inline CString Format(LPCTSTR szFormat, ...) {
   va_list arglist;
   va_start(arglist, szFormat);
   CString strResult;
   strResult.FormatV(szFormat, arglist);
   va_end(arglist);
   return strResult;
}
inline CString ErrorCode2Str(DWORD dwErrCode) {
   CString strRes;
   LPTSTR lpMsgBuf = NULL;
   DWORD dwRes = ::FormatMessage(
      FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
      NULL,
      dwErrCode,
      MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
      (LPTSTR)&lpMsgBuf,
      0,
      NULL
   );
   if (dwRes != 0) {
      strRes = lpMsgBuf;
      strRes.Replace(_T("\r\n"), _T(" "));
      //strRes.Replace(_T('\r'), _T(' '));
      //strRes.Replace(_T('\n'), _T(' '));
      strRes.TrimRight();
   } else {
      DWORD dwErrCode2 = ::GetLastError(); // must be ERROR_MR_MID_NOT_FOUND ???
   }
   ::LocalFree(lpMsgBuf);
   return strRes;
}

/*******************************************************************************************/
//
//          LOGGER
//
/*******************************************************************************************/
namespace nsLogger {
   inline void Put(IN LPCTSTR szStr, ...) {
      va_list argList;
      va_start(argList, szStr);
      CString strRes; strRes.FormatV(szStr, argList);
      ::OutputDebugString(strRes);
      ::OutputDebugString(_T("\r\n"));
      va_end(argList);
   }

   inline void PutErrorCode(IN LPCTSTR szStr, IN DWORD dwErrCode) {
      CString strErr = ErrorCode2Str(dwErrCode);
      nsLogger::Put(_T("%sError code is %i%s"), szStr ? szStr : _T(""), dwErrCode, (LPCTSTR)(strErr.IsEmpty() ? _T("") : (LPCTSTR)(_T(" (")+strErr+_T(")"))));
   }
} // namespace nsLogger
#define LOG_PUT(funcName, logLevel) nsLogger::funcName(

/*******************************************************************************************/
//
//          Types defenitions
//
/*******************************************************************************************/

#ifndef  __NT_DDK__
   // описываю сам нужные структры если нет установленного DDK
   namespace NT {
      typedef LONG NTSTATUS;
      #define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0)

      typedef struct _UNICODE_STRING {
          USHORT Length;
          USHORT MaximumLength;
          PWSTR  Buffer;
      } UNICODE_STRING;
      typedef UNICODE_STRING *PUNICODE_STRING;

      typedef struct _OBJECT_ATTRIBUTES {
         ULONG Length;
         HANDLE RootDirectory;
         PUNICODE_STRING ObjectName;
         ULONG Attributes;
         PVOID SecurityDescriptor;        // Points to type SECURITY_DESCRIPTOR
         PVOID SecurityQualityOfService;  // Points to type SECURITY_QUALITY_OF_SERVICE
      } OBJECT_ATTRIBUTES;
      typedef OBJECT_ATTRIBUTES *POBJECT_ATTRIBUTES;

      typedef struct _IO_STATUS_BLOCK {
         union {
            NTSTATUS Status;
            PVOID Pointer;
         };

         ULONG_PTR Information;
      } IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;

      typedef enum _FILE_INFORMATION_CLASS {
         // end_wdm
         FileDirectoryInformation       = 1,
         FileFullDirectoryInformation,   // 2
         FileBothDirectoryInformation,   // 3
         FileBasicInformation,           // 4  wdm
         FileStandardInformation,        // 5  wdm
         FileInternalInformation,        // 6
         FileEaInformation,              // 7
         FileAccessInformation,          // 8
         FileNameInformation,            // 9
         FileRenameInformation,          // 10
         FileLinkInformation,            // 11
         FileNamesInformation,           // 12
         FileDispositionInformation,     // 13
         FilePositionInformation,        // 14 wdm
         FileFullEaInformation,          // 15
         FileModeInformation,            // 16
         FileAlignmentInformation,       // 17
         FileAllInformation,             // 18
         FileAllocationInformation,      // 19
         FileEndOfFileInformation,       // 20 wdm
         FileAlternateNameInformation,   // 21
         FileStreamInformation,          // 22
         FilePipeInformation,            // 23
         FilePipeLocalInformation,       // 24
         FilePipeRemoteInformation,      // 25
         FileMailslotQueryInformation,   // 26
         FileMailslotSetInformation,     // 27
         FileCompressionInformation,     // 28
         FileObjectIdInformation,        // 29
         FileCompletionInformation,      // 30
         FileMoveClusterInformation,     // 31
         FileQuotaInformation,           // 32
         FileReparsePointInformation,    // 33
         FileNetworkOpenInformation,     // 34
         FileAttributeTagInformation,    // 35
         FileTrackingInformation,        // 36
         FileIdBothDirectoryInformation, // 37
         FileIdFullDirectoryInformation, // 38
         FileMaximumInformation
         // begin_wdm
      } FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS;

      typedef struct _FILE_DISPOSITION_INFORMATION {                  
         BOOLEAN DeleteFile;                                         
      } FILE_DISPOSITION_INFORMATION, *PFILE_DISPOSITION_INFORMATION; 

      #define STATUS_UNSUCCESSFUL ((NTSTATUS)0xC0000001L) // The requested operation was unsuccessful.
   } // namespace NT
   using NT::NTSTATUS;
#endif // __NT_DDK__

/*******************************************************************************************/
//
//          Перехваченные ф-ции
//
/*******************************************************************************************/

typedef BOOL (WINAPI* PF_DeleteFileA)(IN LPCSTR lpFileName);
PF_DeleteFileA g_pfDeleteFileA = DeleteFileA;
BOOL WINAPI MyDeleteFileA(IN LPCSTR lpFileName) {
   LOG_PUT(Put, LL_DEBUG) _T("> DeleteFileA: Файл [%s]: "), (LPCTSTR)CString(lpFileName));
   BOOL bRes = g_pfDeleteFileA(lpFileName);
   DWORD dwErrCode = ::GetLastError();
   LOG_PUT(Put, LL_DEBUG) _T("< DeleteFileA: return %s: "), bRes ? _T("TRUE") : _T("FALSE"));
   ::SetLastError(dwErrCode);
   return bRes;
}

typedef BOOL (WINAPI* PF_DeleteFileW)(IN LPCWSTR lpFileName);
PF_DeleteFileW g_pfDeleteFileW = DeleteFileW;
BOOL WINAPI MyDeleteFileW(IN LPCWSTR lpFileName) {
   LOG_PUT(Put, LL_DEBUG) _T("> DeleteFileW: Файл [%s]: "), (LPCTSTR)CString(lpFileName));
   BOOL bRes = g_pfDeleteFileW(lpFileName);
   DWORD dwErrCode = ::GetLastError();
   LOG_PUT(Put, LL_DEBUG) _T("< DeleteFileW: return %s: "), bRes ? _T("TRUE") : _T("FALSE"));
   ::SetLastError(dwErrCode);
   return bRes;
}

typedef NTSTATUS (NTAPI* PF_ZwDeleteFile)(IN NT::POBJECT_ATTRIBUTES pObjectAttributes);
PF_ZwDeleteFile g_pfZwDeleteFile = NULL;
NTSTATUS NTAPI MyZwDeleteFile(IN NT::POBJECT_ATTRIBUTES pObjectAttributes) {
   LOG_PUT(Put, LL_DEBUG) _T("> ZwDeleteFile: "));
   CString strFileName;
   if (pObjectAttributes && pObjectAttributes->ObjectName) {
      DWORD ulLength = pObjectAttributes->ObjectName->Length>>1; // для WCHAR строки длину в байтах преобразую в длину в символах
      try {
#ifdef UNICODE
         strFileName.Format(_T("%.*s"), ulLength, pObjectAttributes->ObjectName->Buffer);
#else
         strFileName.Format(_T("%.*S"), ulLength, pObjectAttributes->ObjectName->Buffer);
#endif
      } catch(...) {
         LOG_PUT(Put, LL_ERROR) _T("  ZwDeleteFile: Exception при выборке имени файла из структуры OBJECT_ATTRIBUTES. Длина строки %i байт, адрес строки 0x%08X. "), pObjectAttributes->ObjectName->Length, pObjectAttributes->ObjectName->Buffer);
      }
   }
   LOG_PUT(Put, LL_DEBUG) _T("  ZwDeleteFile: Файл [%s]: "), (LPCTSTR)strFileName);
   NTSTATUS lRes = g_pfZwDeleteFile ? g_pfZwDeleteFile(pObjectAttributes) : STATUS_UNSUCCESSFUL;
   DWORD dwErrCode = ::GetLastError();
   LOG_PUT(Put, LL_DEBUG) _T("< ZwDeleteFile: return 0x%08X: "), lRes);
   ::SetLastError(dwErrCode);
   return lRes;
}

typedef NTSTATUS (NTAPI* PF_NtDeleteFile)(IN NT::POBJECT_ATTRIBUTES pObjectAttributes);
PF_NtDeleteFile g_pfNtDeleteFile = NULL;
NTSTATUS NTAPI MyNtDeleteFile(IN NT::POBJECT_ATTRIBUTES pObjectAttributes) {
   LOG_PUT(Put, LL_DEBUG) _T("> NtDeleteFile: "));
   CString strFileName;
   if (pObjectAttributes && pObjectAttributes->ObjectName) {
      DWORD ulLength = pObjectAttributes->ObjectName->Length>>1; // для WCHAR строки длину в байтах преобразую в длину в символах
      try {
#ifdef UNICODE
         strFileName.Format(_T("%.*s"), ulLength, pObjectAttributes->ObjectName->Buffer);
#else
         strFileName.Format(_T("%.*S"), ulLength, pObjectAttributes->ObjectName->Buffer);
#endif
      } catch(...) {
         LOG_PUT(Put, LL_ERROR) _T("  NtDeleteFile: Exception при выборке имени файла из структуры OBJECT_ATTRIBUTES. Длина строки %i байт, адрес строки 0x%08X. "), pObjectAttributes->ObjectName->Length, pObjectAttributes->ObjectName->Buffer);
      }
   }
   LOG_PUT(Put, LL_DEBUG) _T("  NtDeleteFile: Файл [%s]: "), (LPCTSTR)strFileName);
   NTSTATUS lRes = g_pfNtDeleteFile ? g_pfNtDeleteFile(pObjectAttributes) : STATUS_UNSUCCESSFUL;
   DWORD dwErrCode = ::GetLastError();
   LOG_PUT(Put, LL_DEBUG) _T("< NtDeleteFile: return 0x%08X: "), lRes);
   ::SetLastError(dwErrCode);
   return lRes;
}

typedef NTSTATUS (NTAPI* PF_ZwSetInformationFile)(IN HANDLE FileHandle, OUT NT::PIO_STATUS_BLOCK IoStatusBlock, IN PVOID FileInformation, IN ULONG Length, IN NT::FILE_INFORMATION_CLASS FileInformationClass);
PF_ZwSetInformationFile g_pfZwSetInformationFile = NULL;
NTSTATUS NTAPI MyZwSetInformationFile(IN HANDLE FileHandle, OUT NT::PIO_STATUS_BLOCK IoStatusBlock, IN PVOID FileInformation, IN ULONG Length, IN NT::FILE_INFORMATION_CLASS FileInformationClass) {
   if (FileInformationClass == NT::FileDispositionInformation) {
      if (Length == sizeof(NT::FILE_DISPOSITION_INFORMATION)) {
         NT::PFILE_DISPOSITION_INFORMATION pFileDispositionInformation = (NT::PFILE_DISPOSITION_INFORMATION)FileInformation;
         if (pFileDispositionInformation->DeleteFile) {
            LOG_PUT(Put, LL_DEBUG) _T("  ZwSetInformationFile: Удаление файла "));
         }
      }
   }
   NTSTATUS lRes = g_pfZwSetInformationFile ? g_pfZwSetInformationFile (FileHandle, IoStatusBlock, FileInformation, Length, FileInformationClass) : STATUS_UNSUCCESSFUL;
   //DWORD dwErrCode = ::GetLastError();
   //::SetLastError(dwErrCode);
   return lRes;
}

typedef NTSTATUS (NTAPI* PF_NtSetInformationFile)(IN HANDLE FileHandle, OUT NT::PIO_STATUS_BLOCK IoStatusBlock, IN PVOID FileInformation, IN ULONG Length, IN NT::FILE_INFORMATION_CLASS FileInformationClass);
PF_NtSetInformationFile g_pfNtSetInformationFile = NULL;
NTSTATUS NTAPI MyNtSetInformationFile(IN HANDLE FileHandle, OUT NT::PIO_STATUS_BLOCK IoStatusBlock, IN PVOID FileInformation, IN ULONG Length, IN NT::FILE_INFORMATION_CLASS FileInformationClass) {
#ifdef _DEBUG
   //::AfxTrace(_T("> NtSetInformationFile: \r\n"));
#endif
   if (FileInformationClass == NT::FileDispositionInformation) {
      if (Length == sizeof(NT::FILE_DISPOSITION_INFORMATION)) {
         NT::PFILE_DISPOSITION_INFORMATION pFileDispositionInformation = (NT::PFILE_DISPOSITION_INFORMATION)FileInformation;
         if (pFileDispositionInformation->DeleteFile) {
            LOG_PUT(Put, LL_DEBUG) _T("  NtSetInformationFile: Удаление файла "));
         }
      }
   }
   NTSTATUS lRes = g_pfNtSetInformationFile ? g_pfNtSetInformationFile (FileHandle, IoStatusBlock, FileInformation, Length, FileInformationClass) : STATUS_UNSUCCESSFUL;
   //DWORD dwErrCode = ::GetLastError();
#ifdef _DEBUG
   //::AfxTrace(_T("> NtSetInformationFile: return 0x%08X \r\n"), lRes);
#endif
   //::SetLastError(dwErrCode);
   return lRes;
}
/*******************************************************************************************/
//
//          Собственно главная ф-ция очереди мониторинга удаления файлов
//
/*******************************************************************************************/

UINT ControllingFunction(LPVOID) {

   /////////////////////////////////////////////////////////////////////////
   //      Перехватываю ф-ции удаления с помощью Detours API
   /////////////////////////////////////////////////////////////////////////

   HMODULE hModule = ::GetModuleHandle(_T("ntdll.dll"));
   BOOL bRes = (hModule != NULL);
   DWORD dwErrCode = ERROR_SUCCESS;
   if (!bRes) {
      dwErrCode = ::GetLastError();
      LOG_PUT(PutErrorCode, LL_ERROR) _T("  ControllingFunction: GetModuleHandle('ntdll.dll'): "), dwErrCode);
      hModule = ::LoadLibrary(_T("ntdll.dll")); // ну это уже лишнее, конечно
      bRes = (hModule != NULL);
      if (!bRes) {
         dwErrCode = ::GetLastError();
         LOG_PUT(PutErrorCode, LL_ERROR) _T("  ControllingFunction: LoadLibrary('ntdll.dll'): "), dwErrCode);
      }
   }
   if (bRes) {
#define GET_PROC_ADDRESS_NTDLL(FuncName, TextFuncNameA, TextFuncNameT) \
      g_pf##FuncName = (PF_##FuncName)::GetProcAddress(hModule, TextFuncNameA); \
      bRes = (g_pf##FuncName != NULL); \
      if (!bRes) { \
         dwErrCode = ::GetLastError(); \
         LOG_PUT(PutErrorCode, LL_ERROR) _T("  ControllingFunction: GetProcAddress('ntdll.dll', '") TextFuncNameT _T("'): "), dwErrCode); \
      }

      GET_PROC_ADDRESS_NTDLL(ZwDeleteFile        , "ZwDeleteFile"        , _T("ZwDeleteFile"        ))
      GET_PROC_ADDRESS_NTDLL(NtDeleteFile        , "NtDeleteFile"        , _T("NtDeleteFile"        ))
      GET_PROC_ADDRESS_NTDLL(ZwSetInformationFile, "ZwSetInformationFile", _T("ZwSetInformationFile"))
      GET_PROC_ADDRESS_NTDLL(NtSetInformationFile, "NtSetInformationFile", _T("NtSetInformationFile"))
   }

   g_pfDeleteFileA          = (PF_DeleteFileA         )DetourFunction((PBYTE)::DeleteFileA           , (PBYTE)MyDeleteFileA         );
   g_pfDeleteFileW          = (PF_DeleteFileW         )DetourFunction((PBYTE)::DeleteFileW           , (PBYTE)MyDeleteFileW         );
   g_pfZwDeleteFile         = (PF_ZwDeleteFile        )DetourFunction((PBYTE)g_pfZwDeleteFile        , (PBYTE)MyZwDeleteFile        );
 //g_pfNtDeleteFile         = (PF_NtDeleteFile        )DetourFunction((PBYTE)g_pfNtDeleteFile        , (PBYTE)MyNtDeleteFile        );
   g_pfZwSetInformationFile = (PF_ZwSetInformationFile)DetourFunction((PBYTE)g_pfZwSetInformationFile, (PBYTE)MyZwSetInformationFile);
 //g_pfNtSetInformationFile = (PF_NtSetInformationFile)DetourFunction((PBYTE)g_pfNtSetInformationFile, (PBYTE)MyNtSetInformationFile);

   /////////////////////////////////////////////////////////////////////////
   
#pragma message(">  Незабудь поменять каталог!\n")
   CString strDirCache(_T("c:\\"));

   HANDLE hEventNotifyCloseThread = NULL;
   try {
#pragma message(">  Незабудь раскоментировать эвент окончания главногой очереди процесса!\n      Иначе WaitForMultipleObjects будет вылетать на ошибке ERROR_INVALID_HANDLE...\n")
      //hEventNotifyCloseThread = ((CMyProject*)::AfxGetApp())->m_hEventNotifyCloseThread;
   } catch(...) {}

   HANDLE hDirCache = ::CreateFile(
      strDirCache,                        // pointer to the file name
      FILE_LIST_DIRECTORY,                // access (read/write) mode
      FILE_SHARE_READ|FILE_SHARE_DELETE,  // share mode
      NULL,                               // security descriptor
      OPEN_EXISTING,                      // how to create
      FILE_FLAG_OVERLAPPED | FILE_FLAG_BACKUP_SEMANTICS,//FILE_ATTRIBUTE_DIRECTORY,//         // file attributes
      NULL                                // file with attributes to copy
   );
   if (hDirCache == INVALID_HANDLE_VALUE) {
      dwErrCode = ::GetLastError();
      LOG_PUT(PutErrorCode, LL_ERROR) ::Format(_T("  ControllingFunction: Не сумел открыть для контроля директорию: CreateFile('%s', FILE_LIST_DIRECTORY, ...): "), (LPCTSTR)strDirCache), dwErrCode);
   }

   HANDLE hEventChangeDirCache = ::CreateEvent(NULL, FALSE, FALSE, NULL);
   if (hEventChangeDirCache == NULL) {
      dwErrCode = ::GetLastError();
      LOG_PUT(PutErrorCode, LL_ERROR) _T("  ControllingFunction: Не сумел создать эвент открыть для контроля директорию: CreateEvent: "), dwErrCode);
   }
   OVERLAPPED ovChangeDirCache = {0,0,0,0, hEventChangeDirCache};

   BYTE pBuffer[1024];
   const DWORD dwBufferLength = sizeof(pBuffer);
   bRes = ::ReadDirectoryChangesW(
     hDirCache,
     pBuffer,
     dwBufferLength,
     TRUE,                          // BOOL bWatchSubtree,
     FILE_NOTIFY_CHANGE_FILE_NAME,  // DWORD dwNotifyFilter,
     NULL,                          // PDWORD lpBytesReturned // [out] For synchronous calls
     &ovChangeDirCache,             // LPOVERLAPPED lpOverlapped,
     NULL                           // LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
   );
   dwErrCode = ::GetLastError();
   if (!bRes) {
      LOG_PUT(PutErrorCode, LL_ERROR) _T("  ControllingFunction: ReadDirectoryChangesW: "), dwErrCode);
   }

   HANDLE hChangeNotifyCache = ::FindFirstChangeNotification(
      strDirCache,                 // directory to watch
      TRUE,                        // do not watch the subtree
      FILE_NOTIFY_CHANGE_FILE_NAME // watch file name changes
   );
   if (hChangeNotifyCache == INVALID_HANDLE_VALUE) {
      dwErrCode = ::GetLastError();
      LOG_PUT(PutErrorCode, LL_ERROR) ::Format(_T("  ControllingFunction: Не сумел открыть для контроля директорию: FindFirstChangeNotification('%s', TRUE, FILE_NOTIFY_CHANGE_FILE_NAME): "), (LPCTSTR)strDirCache), dwErrCode);
   }
 
   if ((hDirCache != INVALID_HANDLE_VALUE) && (hChangeNotifyCache != INVALID_HANDLE_VALUE)) {
      const HANDLE pH[3] = {hEventNotifyCloseThread, hEventChangeDirCache, hChangeNotifyCache};
      do {
         BOOL bBreak = TRUE;
         DWORD dwRes = ::WaitForMultipleObjects(3, pH, FALSE, INFINITE);
         dwErrCode = ::GetLastError();
         switch (dwRes) {
         case WAIT_OBJECT_0+0:
            LOG_PUT(Put, LL_INFO) _T("  ControllingFunction: Главная очередь процесса завершилась!"));
            break;
         case WAIT_OBJECT_0+1:
            bBreak = FALSE;
            {
               LOG_PUT(Put, LL_DEBUG) _T("  ControllingFunction: Пришло уведомление (ReadDirectoryChangesW) о изменени в директории. "));
               DWORD dwNumberOfBytesTransferred = 0;
               bRes = ::GetOverlappedResult(hDirCache, &ovChangeDirCache, &dwNumberOfBytesTransferred, FALSE);
               if (!bRes) {
                  dwErrCode = ::GetLastError();
                  LOG_PUT(PutErrorCode, LL_ERROR) _T("  ControllingFunction: GetOverlappedResult: "), dwErrCode);
               } else {
                  if (dwNumberOfBytesTransferred == 0) {
                     LOG_PUT(Put, LL_ERROR) _T("  ControllingFunction: ReadDirectoryChangesW & GetOverlappedResult: dwNumberOfBytesTransferred == 0 "));
                  } else {
                     const FILE_NOTIFY_INFORMATION *pPNI = (FILE_NOTIFY_INFORMATION*)pBuffer;
                     int i = 0;
                     DWORD dwCountByte = 0;
                     do {
                        i++;

                        if (dwCountByte + pPNI->FileNameLength > dwNumberOfBytesTransferred) {
                           LOG_PUT(Put, LL_ERROR) _T("  ControllingFunction: ReadDirectoryChangesW & GetOverlappedResult: Неверно работаю с FILE_NOTIFY_INFORMATION - залажу за её пределы: dwCountByte + pPNI->FileNameLength > dwNumberOfBytesTransferred (%i+%i > %i) "), dwCountByte, pPNI->FileNameLength, dwNumberOfBytesTransferred);
                           break;
                        }

                        CString strFileName;
                        DWORD dwFileNameLength = pPNI->FileNameLength>>1; // для WCHAR строки длину в байтах преобразую в длину в символах
                        try {
               #ifdef UNICODE
                           strFileName.Format(_T("%.*s"), dwFileNameLength, pPNI->FileName);
               #else
                           strFileName.Format(_T("%.*S"), dwFileNameLength, pPNI->FileName);
               #endif
                        } catch(...) {
                           LOG_PUT(Put, LL_ERROR) _T("  ControllingFunction: Exception при выборке имени файла из структуры FILE_NOTIFY_INFORMATION. Длина строки %i байт, адрес строки 0x%08X. "), pPNI->FileNameLength, pPNI->FileName);
                        }
                        switch (pPNI->Action) {
                        case FILE_ACTION_ADDED: // The file was added to the directory. 
                           LOG_PUT(Put, LL_DEBUG) _T("  ControllingFunction: Операция FILE_ACTION_ADDED. Файл [%s]"), (LPCTSTR)strFileName);
                           break;
                        case FILE_ACTION_REMOVED: // The file was removed from the directory. 
                           LOG_PUT(Put, LL_DEBUG) _T("  ControllingFunction: Операция FILE_ACTION_REMOVED. Файл [%s]"), (LPCTSTR)strFileName);
                           break;
                        case FILE_ACTION_MODIFIED: // The file was modified. This can be a change in the time stamp or attributes. 
                           LOG_PUT(Put, LL_DEBUG) _T("  ControllingFunction: Операция FILE_ACTION_MODIFIED. Файл [%s]"), (LPCTSTR)strFileName);
                           break;
                        case FILE_ACTION_RENAMED_OLD_NAME: // The file was renamed and this is the old name. 
                           LOG_PUT(Put, LL_DEBUG) _T("  ControllingFunction: Операция FILE_ACTION_RENAMED_OLD_NAME. Файл [%s]"), (LPCTSTR)strFileName);
                           break;
                        case FILE_ACTION_RENAMED_NEW_NAME: // The file was renamed and this is the new name.
                           LOG_PUT(Put, LL_DEBUG) _T("  ControllingFunction: Операция FILE_ACTION_RENAMED_NEW_NAME. Файл [%s]"), (LPCTSTR)strFileName);
                           break;
                        default:
                           LOG_PUT(Put, LL_ERROR) _T("  ControllingFunction: Неизвестный код (%i) действия над файлом [%s] (цикл #%i обработки структуры FILE_NOTIFY_INFORMATION) "), pPNI->Action, (LPCTSTR)strFileName, i);
                           break;
                        }

                        if (pPNI->NextEntryOffset == 0) break;
                        pPNI = (FILE_NOTIFY_INFORMATION*)(((BYTE*)pPNI)+pPNI->NextEntryOffset);

                        dwCountByte += pPNI->NextEntryOffset;
                        if (dwCountByte >= dwNumberOfBytesTransferred) {
                           LOG_PUT(Put, LL_ERROR) ::Format(_T("  ControllingFunction: ReadDirectoryChangesW & GetOverlappedResult: Неверно работаю с FILE_NOTIFY_INFORMATION - залажу за её пределы: dwCountByte >= dwNumberOfBytesTransferred (%i >= %i). Последний pPNI->NextEntryOffset == %i "), dwCountByte, dwNumberOfBytesTransferred, pPNI->NextEntryOffset));
                           break;
                        }
                     } while(true);
                  }
               }
               bRes = ::ReadDirectoryChangesW(
                 hDirCache,
                 pBuffer,
                 dwBufferLength,
                 TRUE,                          // BOOL bWatchSubtree,
                 FILE_NOTIFY_CHANGE_FILE_NAME,  // DWORD dwNotifyFilter,
                 NULL,                          // PDWORD lpBytesReturned // [out] For synchronous calls
                 &ovChangeDirCache,             // LPOVERLAPPED lpOverlapped,
                 NULL                           // LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
               );
               dwErrCode = ::GetLastError();
               if (!bRes) {
                  LOG_PUT(PutErrorCode, LL_ERROR) _T("  ControllingFunction: ReadDirectoryChangesW: "), dwErrCode);
               }

            }
            break;
         case WAIT_OBJECT_0+2:
            bBreak = FALSE;
            {
               LOG_PUT(Put, LL_DEBUG) _T("  ControllingFunction: Пришло уведомление (FindFirstNextChangeNotification) о изменени в директории. "));
               //RefreshDirectory(); // application-defined function
               bRes = ::FindNextChangeNotification(hChangeNotifyCache);
               if (!bRes) {
                  dwErrCode = ::GetLastError();
                  LOG_PUT(PutErrorCode, LL_ERROR) ::Format(_T("  ControllingFunction: Запрос на следующую нотификацию изменения директории [%s]: FindNextChangeNotification: "), (LPCTSTR)strDirCache), dwErrCode);
               }
            }
            break;
         case WAIT_TIMEOUT:
            LOG_PUT(PutErrorCode, LL_ERROR) _T("  ControllingFunction: WaitForMultipleObjects return WAIT_TIMEOUT: "), dwErrCode);
            break;
         case WAIT_FAILED:
            LOG_PUT(PutErrorCode, LL_ERROR) _T("  ControllingFunction: WaitForMultipleObjects return WAIT_FAILED: "), dwErrCode);
            break;
         default:
            LOG_PUT(PutErrorCode, LL_ERROR) ::Format(_T("  ControllingFunction: WaitForMultipleObjects return %i: "), dwRes), dwErrCode);
            break;
         }

         if (bBreak) break;

      } while(true);
   }

   if (hDirCache != INVALID_HANDLE_VALUE) {
      bRes = ::CloseHandle(hDirCache); hDirCache = NULL;
      if (!bRes) {
         LOG_PUT(PutErrorCode, LL_ERROR) _T("  ControllingFunction: Закрытие дескриптора директории: CloseHandle: "), ::GetLastError());
      }
   }
   if (hChangeNotifyCache == INVALID_HANDLE_VALUE) {
      bRes = ::FindCloseChangeNotification(hChangeNotifyCache); hChangeNotifyCache = NULL;
      if (!bRes) {
         LOG_PUT(PutErrorCode, LL_ERROR) _T("  ControllingFunction: Закрытие дескриптора нотификации изменений директории: FindCloseChangeNotification: "), ::GetLastError());
      }
   }

   LOG_PUT(Put, LL_INFO) _T("  ControllingFunction: Выхожу из очереди. "));

   return 0;
}
Серёга, любит баранью ногу.
Re: Слежение за объектами файловой системой
От: Spark  
Дата: 25.01.06 14:42
Оценка:
Здравствуйте, Аноним. Вы писали:
> Недавно мне поставили задачу создать файловый монитор наподобие
> filemon, но работающий исключительно в UserMode. Пока в голову приходит
> только использование возможнойстей shell, но они достаточно ограничены и
> не полны. Я слышал, что помимо shell в Винде есть еще два механизма слежения
> за файлами и директориями, но не знаю, как их зовут и где искать. Покорнейше прошу,
> натолкните на мысль. Куда идти, где копать.

В UserMode это можно сделать, как мне кажется, путем перехвата вызовов функций API по работе с файлами у всех приложений UserMode. Подобные технологии используют UserMode rootkit`s. Посмотри на www.rootkit.com
Posted via RSDN NNTP Server 2.0
Re[2]: Слежение за объектами файловой системой
От: Alexander__S  
Дата: 25.01.06 19:28
Оценка:
Здравствуйте, ekamaloff, Вы писали:

E>Имеешь ввиду FindFirstChangeNotification, FindNextChangeNotification, ReadDirectoryChangesW?

Да, наверное, это и имелось в виду, хотя для "монитора" они вряд ли пойдойдут. Нет способа узнать, где именно и что создалось, удалилось и т.п. Ны Вы знаете, наверное.

E>Видимо под этими двумя способами подразумеваются фильтр файловой системы и создание драйвера, перехватывающего вызовы системных функций (типа ZwReadFile, ZwWriteFile). Вот по этим ключевым словам и попробуй поискать.


Ну это уже FileMon . Вроде просили исключительно в пользовательском режиме. В конце концов вызовы из kernel32.dll CreateFile, WriteFile и проч. приводят к вызовам соттв-но ZwReadFile, ZwWriteFile в ntdll.dll и последующему переходу в режим ядра для вызова одномиенного системного сервиса (INT2E/SYSCALL). Так что я бы хукнул все функции работы с файлами в ntdll, техника уже не раз описвалась в разных источниках (Рихтер, форумы).
Re[3]: Слежение за объектами файловой системой
От: Alexander__S  
Дата: 25.01.06 22:48
Оценка:
E>>Имеешь ввиду FindFirstChangeNotification, FindNextChangeNotification, ReadDirectoryChangesW?
Сорри, имелись в виду первые две. Вообще зря написал, ниже привден добротный код, выполняющий нужные действия.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.