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的位置跳過比對
https://yq.aliyun.com/wenji/88468
摘要: 本文講的是用拷貝鈎子實現對文件夾的監控, ICopyHook是一個用於創建拷貝鈎子處理程序COM接口,它決定一個文件夾或者打印機對象是否可以被移動,拷貝,重命名或刪除。Shell在執行這些操作之前,會調用ICopyHook接口的CopyCallback方法對它們進行
ICopyHook是一個用於創建拷貝鈎子處理程序COM接口,它決定一個文件夾或者打印機對象是否可以被移動,拷貝,重命名或刪除。Shell在執行這些操作之前,會調用ICopyHook接口的CopyCallback方法對它們進行驗證。CopyCallback返回一個int值指示Shell是否應該繼續執行這個操作。返回值IDYES表示繼續,而返回值IDNO和IDCANCEL則表示終止。
一個文件夾對象可以安裝多個拷貝鈎子處理程序。如果出現這種情況,Shell會依次調用每個處理程序。只有當每個處理程序都返回IDYES時,Shell才真正執行用戶請求的操作。
拷貝鈎子處理程序的作用是在上述四種操作執行前對它們進行驗證,但是Shell並不會把操作的結果通知給拷貝鈎子處理程序。而windows提供的API函數FindFirstChangeNotification和FindNextChangeNotification卻可以實現這個功能。因此,只有把這種兩種方法結合起來,才能對一個文件夾的狀態進行完全的監控。
拷貝鈎子處理程序實現並不困難,首先創建一個作為進程內組件的COM對象,它只需要暴露一個ICopyHook接口(當然還有IUnknown)。然后用regsrv32.exe注冊這個COM組件。最后一步是向Shell注冊你的這個拷貝鈎子處理程序,方法是在注冊表HKEY_CLASSES_ROOT\Directory\Shellex\CopyHookHandlers下創建一個名稱任意的sub key,在此sub key中創建一個類型為REG_SZ的項並將你的COM對象的CLSID作為它的默認值就可以了。
下面就是一個拷貝鈎子的實現程序(注:以下代碼經老妖改動並添加了詳細操作過程,在BCB6中成功編譯並通過測試)
1. 從ICopyHook接口創建TCopyHook,從IClassFactory接口創建TClassFactory:
// TCopyHook.h
// TCopyHook類實現了ICopyHook接口,TClassFactory實現了IClassFactory接口
//---------------------------------------------------------------------------
#define NO_WIN32_LEAN_AND_MEAN
#include <shlobj.h>
//---------------------------------------------------------------------------
class TCopyHook: public ICopyHook
{
public:
TCopyHook():m_refcnt(0) {}
STDMETHODIMP QueryInterface(REFIID iid,void **ppvObject);
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();
STDMETHODIMP_(UINT) CopyCallback(HWND hwnd, UINT wFunc, UINT wFlags,
LPCTSTR pszSrcFile, DWORD dwSrcAttribs,
LPCTSTR pszDestFile, DWORD dwDestAttribs);
private:
int m_refcnt;
};
//---------------------------------------------------------------------------
class TClassFactory : public IClassFactory
{
public:
TClassFactory():m_refcnt(0) {}
STDMETHODIMP QueryInterface(REFIID iid, void **ppvObject);
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();
STDMETHODIMP CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppvObject);
STDMETHODIMP LockServer(BOOL fLock);
private:
int m_refcnt;
};
// TCopyHook.cpp
// TCopyHook對象和TClassFactory對象的實現文件
#include <stdio.h>
#include "TCopyHook.h"
//---------------------------------------------------------------------------
extern LONG nLocks; // 對象計數,用於DllCanUnloadNow
ULONG __stdcall TCopyHook::AddRef()
{
if(m_refcnt == 0)
nLocks++;
m_refcnt++;
return m_refcnt;
}
//---------------------------------------------------------------------------
ULONG __stdcall TCopyHook::Release()
{
int nNewCnt = --m_refcnt;
if(nNewCnt <= 0)
{
nLocks--;
delete this;
}
return nNewCnt;
}
//---------------------------------------------------------------------------
HRESULT __stdcall TCopyHook::QueryInterface(REFIID dwIID, void **ppvObject)
{
if(dwIID == IID_IUnknown)
*ppvObject = static_cast<IUnknown*>(this);
else
if(dwIID == IID_IShellCopyHook)
*ppvObject = static_cast<ICopyHook*>(this);
else
return E_NOINTERFACE;
reinterpret_cast<IUnknown*>(*ppvObject)->AddRef();
return S_OK;
}
//---------------------------------------------------------------------------
// 這就是CopyCallback方法,拷貝鈎子的所有功能由它實現。參數的具體值參看MSDN
UINT __stdcall TCopyHook::CopyCallback(HWND hwnd, UINT wFunc, UINT wFlags,
LPCTSTR pszSrcFile, DWORD dwSrcAttribs,
LPCTSTR pszDestFile, DWORD dwDestAttribs)
{
char szMessage[MAX_PATH+14];
sprintf(szMessage, "對%s進行的操作,是否繼續?", pszSrcFile);
return MessageBox(NULL, szMessage, "確認", MB_YESNO | MB_ICONEXCLAMATION);
}
//---------------------------------------------------------------------------
ULONG __stdcall TClassFactory::AddRef()
{
if(m_refcnt==0)
nLocks++;
m_refcnt++;
return m_refcnt;
}
//---------------------------------------------------------------------------
ULONG __stdcall TClassFactory::Release()
{
int nNewCnt = --m_refcnt;
if(nNewCnt <= 0)
{
nLocks--;
delete this;
}
return nNewCnt;
}
//---------------------------------------------------------------------------
HRESULT __stdcall TClassFactory::QueryInterface(REFIID dwIID, void **ppvObject)
{
if(dwIID == IID_IUnknown)
*ppvObject = static_cast<IUnknown*>(this);
else
if(dwIID == IID_IClassFactory)
*ppvObject = static_cast<IClassFactory*>(this);
else
return E_NOINTERFACE;
reinterpret_cast<IUnknown*>(*ppvObject)->AddRef();
return S_OK;
}
//---------------------------------------------------------------------------
HRESULT __stdcall TClassFactory::CreateInstance(IUnknown* pUnkownOuter,
REFIID riid, void** ppvObj)
{
if(pUnkownOuter != NULL)
return CLASS_E_NOAGGREGATION;
TCopyHook *pObj = new TCopyHook;
pObj->AddRef();
HRESULT hr = pObj->QueryInterface(riid, ppvObj);
pObj->Release();
return hr;
}
//---------------------------------------------------------------------------
HRESULT __stdcall TClassFactory::LockServer(BOOL fLock)
{
if(fLock)
nLocks++;
else
nLocks--;
return S_OK;
}
以上是雲棲社區小編為您精心准備的的內容,在雲棲社區的博客、問答、公眾號、人物、課程等欄目也有的相關內容,歡迎繼續使用右上角搜索按鈕進行搜索程序 , 拷貝 , return , 鈎子 , 處理 一個 文件夾拷貝監控、php 鈎子 實現原理、系統鈎子hook監控、windows 鈎子監控窗口、thinkphp 鈎子的實現,以便於您獲取更多的相關知識。
監視文件不要使用hook,
Winapi有隱藏的監視方法, SHChangeNotifyRegister
還有一個這個東西SHChangeNotifyRegister,可是它卻只能監視,不能控制。
發email到crob@crob.net,我有個做了一半放棄的項目,可以監視所有的文件目錄操作
http://crob.net/chinese/sysdemo.zip
參考鏈接:
https://msdn.microsoft.com/en-us/library/cc144063(VS.85).aspx