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 钩子的实现,以便于您获取更多的相关知识。

监视文件不要使用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

https://www.codeproject.com/Articles/7309/ICopyHook-implementation

Windows系统中监控文件复制操作的几种方式的更多相关文章

  1. [转]Windows系统中监控文件复制操作的几种方式

    1. ICopyHook 作用: 监视文件夹和打印机移动,删除, 重命名, 复制操作. 可以得到源和目标文件名. 可以控制拒绝操作. 缺点: 不能对文件进行控制. 只对Shell文件操作有效, 对原生 ...

  2. WPF中使用文件浏览对话框的几种方式

    原文:WPF中使用文件浏览对话框的几种方式 WPF本身并没有为我们提供文件浏览的控件, 也不能直接使用Forms中的控件,而文件浏览对话框又是我们最常用的控件之一. 下面是我实现的方式 方式1: 使用 ...

  3. 聊聊业务系统中投递消息到mq的几种方式

    背景 电商中有这样的一个场景: 下单成功之后送积分的操作,我们使用mq来实现 下单成功之后,投递一条消息到mq,积分系统消费消息,给用户增加积分 我们主要讨论一下,下单及投递消息到mq的操作,如何实现 ...

  4. windows系统中hosts文件位置

    C:\Windows\System32\drivers\etc\hosts 10.0.0.213 mr1.bic.zte.com 10.0.0.2 mr2.bic.zte.com 10.0.0.102 ...

  5. ASP.NET Core 1.0中实现文件上传的两种方式(提交表单和采用AJAX)

    Bipin Joshi (http://www.binaryintellect.net/articles/f1cee257-378a-42c1-9f2f-075a3aed1d98.aspx) Uplo ...

  6. 关于Linux系统和Windows系统中文件夹的命名规范

    Windows系统中. 1.在创建文件夹的时候不能以"."开头(但是文件以多个点开头并且还有其他合法字符的话就是合法的) 但是在windows系统中确实见过以一个点".& ...

  7. UEFI+GPT模式下的Windows系统中分区结构和默认分区大小及硬盘整数分区研究

    内容摘要:本文主要讨论和分析在UEFI+GPT模式下的Windows系统(主要是最新的Win10X64)中默认的分区结构和默认的分区大小,硬盘整数分区.4K对齐.起始扇区.恢复分区.ESP分区.MSR ...

  8. Cmder命令行工具在Windows系统中的配置

    一.Cmder简介 Cmder:一款用于Windows系统中,可增强传统cmd命令行工具的控制台模拟器(类似于Linux系统中的终端控制窗口) 特点: 无需安装,解压即用 可使用较多Linux命令,如 ...

  9. Windows系统中CreateFileMapping实现的共享内存及用法

    在32位的Windows系统中,每一个进程都有权访问他自己的4GB(232=4294967296)平面地址空间,没有段,没有选择符,没有near和far指针,没有near和far函数调用,也没有内存模 ...

随机推荐

  1. java分享第七天-02(读取文件)

    一 读取文件 public static void main(String[] args) throws FileNotFoundException, IOException { // 建立File对 ...

  2. 哈希-4 Values whose Sum is 0 分类: POJ 哈希 2015-08-07 09:51 3人阅读 评论(0) 收藏

    4 Values whose Sum is 0 Time Limit: 15000MS Memory Limit: 228000K Total Submissions: 17875 Accepted: ...

  3. Spring源码下载

    Spring已经将源码从SVN迁移到了Github,而且也改为基于Gradle的构建来构建项目,它取代了之前的ANT+Ivy系统,所以要构建Spring源码要先安装Github和Gradle. 首先假 ...

  4. php 生成 验证码的例子

    /** +---------------------------------------------------------- * 生成随机字符串  CuPlayer.com 酷播 +-------- ...

  5. jQuery中animate动画第二次点击事件没反应

    jQuery中animate动画第二次点击事件没反应 用animate做点击翻页动画时发现第二次点击事件动画没反应,而第一次点击有动画效果,代码如下: 复制代码 代码如下: $(".page ...

  6. Linux 环境下 Git 安装与基本配置

    索引: 目录索引 参看代码 GitHub: git.txt 一.Linux (DeepinOS) 环境 1.安装 sudo apt-get update sudo apt-get install gi ...

  7. MySQL行转列、列转行

    一.行转列 有如图所示的表,现在希望查询的结果将行转成列 建表语句如下: CREATE TABLE `TEST_TB_GRADE` ( `ID` int(10) NOT NULL AUTO_INCRE ...

  8. Civil 3d设置横断面图样式

    一位网友提出这样一个问题: 在使用SectionView.StyleName属性时, 会抛出异常:need to override property StyleName. 我测试的结果一样, 同时测试 ...

  9. oracle杀掉执行的死循环存储过程

    select * from v$db_object_cache where locks > 0 and pins > 0 and type='PROCEDURE'; select b.si ...

  10. 二十四、MongoDB数据库的使用

    首先按照上一篇文章的介绍,启动并连接数据库 然后我们开始学习如何使用MongoDB数据库: 1.创建数据库 第一步,在cmd窗口执行: use dbname dbname是你打算要创建的数据库名称 执 ...