[轉]Windows系統中監控文件復制操作的幾種方式


1. ICopyHook

作用: 監視文件夾和打印機移動,刪除, 重命名, 復制操作. 可以得到源和目標文件名. 可以控制拒絕操作.

 

缺點: 不能對文件進行控制. 只對Shell文件操作有效, 對原生Api MoveFile, CopyFile之類的操作無效.

 

用法: 從ICopyHook派生一個COM對象, 重載CopyCallbackA和CopyCallbackW, 然后把COM注冊到HKRC\Directory\ShellEx\CopyHookHandlers\中去

 

 

2. 文件改變通知

作用: 監視一個文件夾下的文件修改(寫入, 刪除, 重命名), 並可以注冊到一個窗口來處理通知.

 

缺點: 只是通知, 不可以拒絕操作. 不能區分是否文件復制操作還是移動操作, 不能拿到源文件名. 只對Shell文件操作有效, 對原生Api MoveFile, CopyFile之類的操作無效.

 

用法: SHChangeNotifyRegister 注冊一個窗口接收文件改變同; 或者FindFirstChangeNotification 結合FindNextChangeNotification 的方式處理.

 

 

3.IShellExtInit

作用: 每一個Shell擴展對象創建都會觸發IShellExInit::Initialize調用, 在Shell中, 用戶的對文件的復制粘貼操作, 最終會被解析成文件對象的拖拽操作, 然后觸發拖拽目標對象的Shell擴展對象的調用. 所以在文件夾和盤符對象上注冊一個IShellExtInit可以監視到拖拽到文件夾對象的事件. 也就是可以監視到文件復制或移動到文件夾的操作. 並且同時可以從IShellExitInit::Initialize中可以獲取到源文件名.

 

缺點: 同通知一樣, 不能拒絕文件操作. 只對Shell文件操作有效, 對原生Api MoveFile, CopyFile之類的操作無效.

用法: 從IShellExtInit中派生一個COM對象, 重載Initialize, 在Initialize傳來的第一個參數是目標目錄名, 第二個參數中可以獲取所有源文件名, 第三個參數是一個注冊表對象句柄.

下面給一段處理樣例:

HRESULT STDMETHODCALLTYPE CKCopyHook::Initialize(
 
 __in_opt  PCIDLIST_ABSOLUTE pidlFolder,
 
 __in_opt  IDataObject *pdtobj,
 
 __in_opt  HKEY hkeyProgID)
{
 HRESULT ret = E_INVALIDARG;
 if (NtQueryObject == 0)
 {
  NtQueryObject = (PFNtQueryObject)GetProcAddress(LoadLibraryA("ntdll.dll"), "NtQueryObject");
 }

 if (NtQueryObject == 0)
  return E_FAIL;

 // 獲取 hkeyProgID 的名稱
 std::auto_ptr<WCHAR> buffer(new WCHAR[4096]);
 DWORD retlen = 0;
 if (NtQueryObject(hkeyProgID, ObjectNameInformation, buffer.get(), 4096, &retlen) > 0)
  return E_INVALIDARG;

 POBJECT_NAME_INFORMATION poni = (POBJECT_NAME_INFORMATION)buffer.get();
 poni->Name.Buffer[poni->Name.Length] = 0;

 DbgOutPutMessageW(L"[%s] hkeyProgID=0x%x=[%s]", __FUNCTIONW__, hkeyProgID, poni->Name.Buffer);

 // 當 hkeyProgID 是 Folder 項時才進行文件處理
 if (wcsnicmp(PathFindFileNameW(poni->Name.Buffer), L"Folder", 6) != 0)
  return S_OK;

 if (!SHGetPathFromIDListW(pidlFolder, buffer.get()))
  return E_INVALIDARG;

 std::wstring DestStr = buffer.get();

 ret = E_INVALIDARG;

 COleDataObject oledo;
 oledo.Attach(pdtobj, FALSE);
 HGLOBAL  GlobalData;
 GlobalData = oledo.GetGlobalData(CF_HDROP);
 if (GlobalData)
 {
  HDROP hDrop = (HDROP)GlobalLock(GlobalData);
  if (hDrop)
  {
   // 枚舉拖拽的源文件
   int nFiles = DragQueryFileW(hDrop, 0xFFFFFFFF, NULL, 0);
   std::vector<std::wstring> SrcStrs;
   for (int i=0; i<nFiles; ++i)
   {
    if (DragQueryFile(hDrop, i, buffer.get(), 4096))
    {
     SrcStrs.push_back(buffer.get());
    }
   }
   
   if (OnCopyFile(SrcStrs, DestStr, 0))
    ret = S_OK;

   GlobalUnlock(hDrop);
  }
  GlobalFree(GlobalData);
 }

 return ret;
}

 

4. 文件過濾驅動

作用: 控制所有文件原子操作

 

缺點: 不能(或者說很難)追蹤文件復制, 移動操作.

 

用法: 反正沒用, 不寫了.

 

5. API Hooking

作用: 攔截CopyFile, MoveFile等Api, 可以任意控制文件復制操作, 可以拒絕文件操作, 也可以在復制前后插入自定義的操作, 相當靈活.

 

缺點: 麻煩, 相當麻煩, 兼容性差.

 

用法: Api Hooking的技術這里就不再陳述了.

需要攔截的API相當多, 從kernel32.dll中導出的 MoveFile* CopyFile* 系列函數, Vista之前的系統中, Shell都是使用ShFileOperation進行文件操作的, ShFileOperation 內部也是調用kernel32中的這些函數, 所以可以不處理ShFileOperation.

但是Vista之后的系統, Shell改為調用ShFileOperationEx, ShFilerOperationEx內部並不使用CopyFile, MoveCopy等的函數, 而是使用CreateFile, ReadFile, WriteFile 重疊IO進行文件操作, 並且ShFileOperationEx沒有在任何dll中導出. 這樣就對攔截ShFilerOperationEx帶來很大的麻煩.

不過可以利用搜索特征代碼的方式從內存中, 搜索到ShFilerOperationEx的地址.

下面這個是32位系統中ShFilerOperationEx的開頭的特征代碼, 在Shell32.dll內存空間中, 32位的Vista, Win7適用 

const BYTE SHFileOperationExCodeMark[] = {
0x8B, 0xFF, 0x55, 0x8B, 0xEC, 0x83, 0xEC, 0x18, 0xA1, 0xFF, 0xFF, 0xFF, 0xFF, 0x33, 0xC5, 0x89,
0x45, 0xFC, 0x8B, 0x45, 0x0C, 0x8B, 0x4D, 0x1C, 0x53, 0x8B, 0x5D, 0x10, 0x56, 0x8B, 0x75, 0x20,
0x57, 0xFF, 0x75, 0x08, 0x89, 0x45, 0xF0, 0x8B, 0x45, 0x14, 0x89, 0x45, 0xEC, 0xBF, 0xFF, 0xFF,
0xFF, 0xFF, 0xE8, 0xFF, 0xFF, 0xFF, 0xFF, 0x85, 0xC0, 0x0F, 0x84, 0xFF, 0xFF, 0xFF, 0xFF, 0x8D,
};

//其中的0xFF的位置跳過比對


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM