標籤:api函數 sof 狀態 root rcfile direct win32 控制代碼 shc
http://blog.sina.com.cn/s/blog_4596beaa0100lp4y.html
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 鉤子的實現,以便於您擷取更多的相關知識。
參考連結:
https://msdn.microsoft.com/en-us/library/cc144063(VS.85).aspx
https://www.codeproject.com/Articles/7309/ICopyHook-implementation
Windows系統中監控檔案複製操作的幾種方式