zoukankan      html  css  js  c++  java
  • [转]Windows系统中监控文件复制操作的几种方式

    1. ICopyHook

    作用: 监视文件夹和打印机移动,删除, 重命名, 复制操作. 可以得到源和目标文件名. 可以控制拒绝操作.

    缺点: 不能对文件进行控制. 只对Shell文件操作有效, 对原生Api MoveFile, CopyFile之类的操作无效.

    用法: 从ICopyHook派生一个COM对象, 重载CopyCallbackA和CopyCallbackW, 然后把COM注册到HKRCDirectoryShellExCopyHookHandlers中去

    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的位置跳过比对

  • 相关阅读:
    HttpServletResponse
    非易失性FRAM中的预充电操作
    Everspin MRAM优化系统能耗
    工业和消费者HMI系统中的扩展内存
    STT结构涡轮增压MRAM
    将FRAM存储器芯片集成到汽车EDR设计中
    ros编译时报Invoking "make -j4 -l4" failed
    python安装第三方库xlrd失败、更新pip失败
    python的pip命令安装request库失败、在pycharm里File>setting...>Python Interpreter里右上角点击加号搜索request点击安装也失败
    selenium之unitest库
  • 原文地址:https://www.cnblogs.com/adylee/p/4615893.html
Copyright © 2011-2022 走看看