怎么进行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);
好了到此我们也就知道怎么获取了,接下来就是自己编码去做了,这里就告诉我们,我们平时在写程序时没找到任何解决方法的时候,我们最好的方法就是去分析二进制程序,这是最好的资料。