如何分析解决COM接口IFileOperation的hook去支持vista、win7、win8、win10 x86 x64系统

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/basketwill/article/details/52102560

 怎么进行COM接口IFileOperation hook我就不讲了,下面这个链接有讲解,
http://blog.csdn.net/wzsy/article/details/17665311 ;
他的hook方法我实验了可用于win8、win10,包括x64位系统,但是他这个有个缺点是在获取源复制文件路径信息的时候,只适用于win7,不适用于win8、win10.
上面那个文章用的是

UINT GetFilesFromDataObject(IUnknown *iUnknown, WPATH **ppPath)

{

    UINT uFileCount = 0;

    IDataObject *iDataObject = NULL;

    HRESULT hr = iUnknown->QueryInterface(IID_IDataObject, (void **)&iDataObject);

 

    do

    {

        if(!SUCCEEDED(hr))

        {

            break;

        }

        FORMATETC fmt = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };

        STGMEDIUM stg = { TYMED_HGLOBAL };

 

        if(!SUCCEEDED(iDataObject->GetData(&fmt, &stg)))

        {

            break;

        }

        HDROP hDrop = (HDROP)GlobalLock(stg.hGlobal);

        if(hDrop == NULL)

        {

            break;

        }

        uFileCount = DragQueryFile(hDrop, 0xFFFFFFFF, NULL, 0);

        if(uFileCount <= 0)

        {

            break;

        }

        *ppPath = new WPATH[uFileCount];

        if(*ppPath != NULL)

        {

            for(UINT uIndex = 0; uIndex < uFileCount; uIndex++)

            {

                DragQueryFile(hDrop, uIndex, (*ppPath)[uIndex], MAX_PATH);

            }

        }

        else

        {

            uFileCount = 0;

        }

 

        GlobalUnlock(stg.hGlobal);

        ReleaseStgMedium(&stg);

    } while (FALSE);

 

    return uFileCount;

} 


注意调用了这个函数
 HRESULT hr = iUnknown->QueryInterface(IID_IDataObject, (void **)&iDataObject);

 
 该函数在win8、win10时返回失败:E_NOINTERFACE, 没有支持的接口。
为什么会这样呢,我们来分析下win8 、win10响应的接口函数的二进制:

(1)
; [thunk]:public: virtual long __stdcall CShellItem::QueryInterface`adjustor{4}' (struct _GUID const &, void * *)
.text:10212890 ?QueryInterface@CShellItem@@W3AGJABU_GUID@@PAPAX@Z proc near
.text:10212890                                         ; DATA XREF: .text:1000A1B8o
.text:10212890                                         ; .text:const CShellItem::`vftable'{for `IShellItem2'}o
.text:10212890
.text:10212890 arg_0           = dword ptr  4
.text:10212890
.text:10212890                 sub     [esp+arg_0], 4
.text:10212895                 jmp     ?QueryInterface@CShellItem@@UAGJABU_GUID@@PAPAX@Z ; CShellItem::QueryInterface(_GUID const &,void * *)
.text:10212895 ?QueryInterface@CShellItem@@W3AGJABU_GUID@@PAPAX@Z endp 

 
virtual long __stdcall CShellItem::QueryInterface`adjustor{4}' (struct _GUID const &, void * *)
函数就是IUnKnow 基类的QuerInterface接口,他继续调用了
jmp     ?QueryInterface@CShellItem@@UAGJABU_GUID@@PAPAX@Z ; CShellItem::QueryInterface(_GUID const &,void * *)
(2)CShellItem类的QueryInterface接口
__int32 __stdcall CShellItem::QueryInterface(CShellItem *this, IID *riid, void **ppv)
.text:1014A960 ?QueryInterface@CShellItem@@UAGJABU_GUID@@PAPAX@Z proc near
.text:1014A960                                         ; CODE XREF: CKnownFoldersFolder::_GetTargetItem(int,int,_ITEMID_CHILD const *,_GUID const &,void * *)+220p
.text:1014A960                                         ; CResultHandlerFactory::BindHandler(IQueryResult *,_GUID const &,_GUID const &,void * *)+1FBp ...
.text:1014A960
.text:1014A960 that            = dword ptr  8
.text:1014A960 riid            = dword ptr  0Ch
.text:1014A960 ppv             = dword ptr  10h
.text:1014A960
.text:1014A960                 mov     edi, edi
.text:1014A962                 push    ebp
.text:1014A963                 mov     ebp, esp
.text:1014A965                 push    [ebp+ppv]       ; ppv
.text:1014A968                 push    [ebp+riid]      ; riid
.text:1014A96B                 push    offset stru_100174D8 ; pqit
.text:1014A970                 push    [ebp+that]      ; that
.text:1014A973                 call    ds:__imp__QISearch@16 ; QISearch(x,x,x,x)
.text:1014A979                 pop     ebp
.text:1014A97A                 retn    0Ch
.text:1014A97A ?QueryInterface@CShellItem@@UAGJABU_GUID@@PAPAX@Z endp
注意该函数中的:
             push    offset stru_100174D8 ; pqit
                       push    [ebp+that]      ; that
                       call    ds:__imp__QISearch@16 ; QISearch(x,x,x,x)
意思是在stru_100174D8 中搜索接口传进来的guid,我们再看看stru_100174D8中有哪些接口,
.text:100174D8                                         ; CShellItem::QueryInterface(_GUID const &,void * *)+Bo ...
.text:100174E0                 dd offset __GUID_b3a4b685_b685_4805_99d9_5dead2873236 //IID_IParentAndItem
.text:100174E4                 dd 10h
.text:100174E8                 dd offset __GUID_43826d1e_e718_42ee_bc55_a1e261c37bfe//IID_IShellItem
.text:100174EC                 dd 4
.text:100174F0                 dd offset __GUID_7e9fb0d3_919f_4307_ab2e_9b1860310c93//IID_IShellItem2
.text:100174F4                 dd 4
.text:100174F8                 dd offset __GUID_e53665fd_f62d_4d9c_8a42_e1166e1dfc3a
.text:100174FC                 dd 8
.text:10017500                 dd offset __GUID_bcc18b79_ba16_442f_80c4_8a59c30c463b//IID_IShellItemImageFactory
.text:10017504                 dd 8
.text:10017508                 dd offset __GUID_00000003_0000_0000_c000_000000000046//IID_IMarshal
.text:1001750C                 align 10h
.text:10017510                 dd offset __GUID_0000010c_0000_0000_c000_000000000046//IID_IPersist
.text:10017514                 dd 14h
.text:10017518                 dd offset __GUID_00000109_0000_0000_c000_000000000046//IID_IPersistStream
.text:1001751C                 dd 14h
.text:10017520                 dd offset __GUID_771e5917_5788_4a36_a276_b0ddbf8e4abf
.text:10017524                 dd 18h
.text:10017528                 dd offset __GUID_c412bf5b_91ea_4904_b34a_855504fbbf0b
.text:1001752C                 dd 1Ch
.text:10017530                 dd offset __GUID_321a6a6a_d61f_4bf3_97ae_14be2986bb36//IID_IObjectWithBackReferences
而我们要Query的接口是IID_IDataObject;
    MIDL_INTERFACE("0000010e-0000-0000-C000-000000000046")
以上结构体里并没有,所以返回的结果是E_NOINTERFACE,问了很多人,而且百度和google了很多都只是以上的方法,没有win8、win10的方法。那该如何获取IShellItem里的信息呢。
我们分析原始的 IFileOperation原始函数MoveItems就能找到答案,我一直认为最好的教材就是微软的原始二进制文件
(1)跟踪CFileOperation::MoveItems
signed int __stdcall CFileOperation::MoveItems(CFileOperation *this, struct IUnknown *a2, struct IShellItem *a3)
{
  return CFileOperation::_AddOperationMulti((CFileOperation *)((char *)this - 8), 1u, a2, a3, 0, 0);
}

(2)继续跟踪CFileOperation::_AddOperationMulti
int __thiscall CFileOperation::_AddOperationMulti(CFileOperation *this, unsigned __int32 a2, struct IUnknown *a3, struct IShellItem *a4, const unsigned __int16 *a5, int a6)
。。。。。。。。
v6 = this;
  v7 = *((_DWORD *)this + 133);
  if ( v7 != -1 && v7 )
  {
    result = 0x8000FFFF;
  }
  else
  {
    *((_DWORD *)this + 133) = 0;
    if ( !*((_BYTE *)this + 0xA2) || (1 << a2) & *((_DWORD *)this + 0x2D) )
    {
      if ( !a4 || IsFolderItem(v22) )
      {
        v25 = 0;
        Microsoft::WRL::ComPtr<IDataObject>::InternalRelease(&v25);
        v8 = a3->lpVtbl;
        __guard_check_icall_fptr(a3->lpVtbl->QueryInterface);
        if ( v8->QueryInterface(a3, &_GUID_0000010e_0000_0000_c000_000000000046, &v25) >= 0 )
          *((_DWORD *)v6 + 11) = DataObj_GetDWORD(v25, word_104773A6, v9);
        v10 = EnumShellItemsFromUnknown((CFreeThreadedItemContainer *)a3, (void **)&v23);
        if ( v10 >= 0 )
        {
          do
          {
            v11 = v23->lpVtbl;
            v12 = v23;
            __guard_check_icall_fptr(v23->lpVtbl->BindToHandler);
            v10 = ((int (__stdcall *)(IShellItem *, signed int, struct IShellItem **, _DWORD))v11->BindToHandler)(
                    v12,
                    1,
                    &v24,
                    0);
我们注意这两个地方
  if ( v8->QueryInterface(a3, &_GUID_0000010e_0000_0000_c000_000000000046, &v25) >= 0 )
          *((_DWORD *)v6 + 11) = DataObj_GetDWORD(v25, word_104773A6, v9);
        v10 = EnumShellItemsFromUnknown((CFreeThreadedItemContainer *)a3, (void **)&v23);
调用了QueryInterface(IID_DATAOBject)接口失败了,然后调用了EnumShellItemsFromUnknown,返回接口结果返回在v23,现在清楚明了了,我们继续分析EnumShellItemsFromUnknown 函数
(3)分析EnumShellItemsFromUnknown
int __fastcall EnumShellItemsFromUnknown(CFreeThreadedItemContainer *a1, void **a2)
{
  CFreeThreadedItemContainer *v2; // ebx@1
  void **v3; // ST08_4@1
  CFreeThreadedItemContainer *v4; // ST00_4@1
  int (__stdcall *v5)(CFreeThreadedItemContainer *, GUID *, void **); // esi@1
  int v6; // edi@1
  int (__stdcall **v8)(CFreeThreadedItemContainer *, GUID *, struct IEnumFullIDList **); // esi@3
  int v9; // ST04_4@4
  ULONG (__stdcall *v10)(IEnumFullIDList *); // esi@4
  struct IEnumFullIDList *v11; // eax@4
  int (__stdcall **v12)(CFreeThreadedItemContainer *, GUID *, struct IEnumFullIDList **); // esi@5
  int (__stdcall **v13)(CFreeThreadedItemContainer *, GUID *, struct IEnumFullIDList **); // esi@7
  struct IEnumFullIDList *v14; // ST08_4@11
  ULONG (__stdcall *v15)(IEnumFullIDList *); // esi@11
  int v16; // ST04_4@14
  ULONG (__stdcall *v17)(IEnumFullIDList *); // esi@14
  int v18; // ST08_4@14
  ULONG (__stdcall *v19)(IEnumFullIDList *); // esi@14
  void **ppv; // [sp+Ch] [bp-18h]@1
  struct IEnumFullIDList *v21; // [sp+10h] [bp-14h]@5
  struct IEnumFullIDList *v22; // [sp+14h] [bp-10h]@7
  struct IEnumFullIDList *v23; // [sp+18h] [bp-Ch]@3
  LPVOID pv; // [sp+1Ch] [bp-8h]@12

  *a2 = 0;
  v2 = a1;
  ppv = a2;
  v3 = a2;
  v4 = a1;
  v5 = **(int (__stdcall ***)(CFreeThreadedItemContainer *, GUID *, void **))a1;
  __guard_check_icall_fptr(**(_DWORD **)a1);
  v6 = v5(v4, &_GUID_70629033_e363_4a28_a567_0db78006e6d7, v3);
  if ( v6 >= 0 )
    return v6;
  v8 = *(int (__stdcall ***)(CFreeThreadedItemContainer *, GUID *, struct IEnumFullIDList **))v2;
  __guard_check_icall_fptr(**(_DWORD **)v2);
  if ( (*v8)(v2, &_GUID_b63ea76d_1f85_456f_a19c_48159efa858b, &v23) >= 0 )
  {
    v9 = (int)v23;
    v10 = v23->lpVtbl[1].Release;
    __guard_check_icall_fptr(v23->lpVtbl[1].Release);
    v6 = ((int (__stdcall *)(int, void **))v10)(v9, ppv);
    v11 = v23;
LABEL_11:
    v14 = v11;
    v15 = v11->lpVtbl->Release;
    __guard_check_icall_fptr(v11->lpVtbl->Release);
    v15(v14);
    return v6;
  }
  v12 = *(int (__stdcall ***)(CFreeThreadedItemContainer *, GUID *, struct IEnumFullIDList **))v2;
  __guard_check_icall_fptr(**(_DWORD **)v2);
  if ( (*v12)(v2, &_GUID_d0191542_7954_4908_bc06_b2360bbe45ba, &v21) >= 0 )
  {
    v6 = EnumShellItemsFromEnumFullIdList(v21, ppv);
    v11 = v21;
    goto LABEL_11;
  }
  v13 = *(int (__stdcall ***)(CFreeThreadedItemContainer *, GUID *, struct IEnumFullIDList **))v2;
  __guard_check_icall_fptr(**(_DWORD **)v2);
  if ( (*v13)(v2, &_GUID_0000010e_0000_0000_c000_000000000046, &v22) >= 0 )
  {
    v6 = EnumShellItemsFromHIDADataObject(v22, ppv);
    if ( v6 < 0 )
      v6 = EnumShellItemsFromHDROPDataObject(v22, ppv);
    v11 = v22;
    goto LABEL_11;
  }
  v6 = SHGetIDListFromObject(v2, (LPITEMIDLIST *)&pv);
  if ( v6 >= 0 )
  {
    v6 = SHCreateShellItemArrayFromIDLists(1, &pv, &v23);
    if ( v6 >= 0 )
    {
      v16 = (int)v23;
      v17 = v23->lpVtbl[1].Release;
      __guard_check_icall_fptr(v23->lpVtbl[1].Release);
      v6 = ((int (__stdcall *)(int, void **))v17)(v16, ppv);
      v18 = (int)v23;
      v19 = v23->lpVtbl->Release;
      __guard_check_icall_fptr(v23->lpVtbl->Release);
      v19((IEnumFullIDList *)v18);
    }
    CoTaskMemFree(pv);
  }
  return v6;
}
分析发现该函数用了很多QueryInterface各种接口尝试一个失败一个就询问下一个这些接口有
_GUID_70629033_e363_4a28_a567_0db78006e6d7、_GUID_b63ea76d_1f85_456f_a19c_48159efa858b、_GUID_d0191542_7954_4908_bc06_b2360bbe45ba、_GUID_0000010e_0000_0000_c000_000000000046,
当这些接口都不支持后,就用最后一个方法:
 v6 = SHGetIDListFromObject(v2, (LPITEMIDLIST *)&pv);
  if ( v6 >= 0 )
  {
    v6 = SHCreateShellItemArrayFromIDLists(1, &pv, &v23);
好了到此我们也就知道怎么获取了,接下来就是自己编码去做了,这里就告诉我们,我们平时在写程序时没找到任何解决方法的时候,我们最好的方法就是去分析二进制程序,这是最好的资料。
阅读更多 登录后自动展开
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页