Drag And Drop 进阶

Drag And Drop 进阶

上一篇文章我们介绍了基本的Drag And Drop 编程和其运行机制。

为了我们的初衷,自动往打开的explorer 管理窗口发送我们的文件,我们应该想到,如果采用静态的枚举当前所有窗口然后进行独立的操作的话,新生成的资源管理器窗口无法被操作。因此,我们应该想一种方法,即使是新生成的窗口也会运行我们的代码。

答案是:窗口钩子。

窗口钩子简介

SetWindowHookEx

https://msdn.microsoft.com/en-us/library/windows/desktop/ms644990(v=vs.85).aspx

CallWndProc call back function

https://msdn.microsoft.com/en-us/library/windows/desktop/ms644975(v=vs.85).aspx

 

使用WH_CALLWNDPROC钩子

我们的应用程序应该生成一个x64DLL,在装载钩子之后,系统会为x64 进程加载我们的DLL,然后每个进程的原来的过程函数执行之前,操作系统先执行我们自己的函数。DLL 是作为回调函数代码的载体载入的。

 

定义两个导出函数:

装载钩子

IMPORT BOOL InstallHook()
{
    wndProcHook= SetWindowsHookEx(WH_CALLWNDPROCRET, MsgHookProc, hInst, 0);
    return TRUE;
}


卸载钩子

IMPORT void UninstallHook()
{
    if (wndProcHook != NULL) {
        UnhookWindowsHookEx(wndProcHook);
        wndProcHook= NULL;
    }
}


其中用到的两个变量为全局变量

HHOOK wndProcHook = NULL;

HINSTANCE hInst;

 

钩子的成功安装之后就是我们的核心函数:MsHookProc 函数了,该函数应该执行的操作很简单,找到目标窗口,即资源管理器窗口,并模拟发送Drop File 操作,即向其中拷贝目标文件。

LRESULT CALLBACK MsgHookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    if (!gIsTargetProcess){
        returnCallNextHookEx(wndProcHook, nCode, wParam, lParam);
    }
    if(!gIsTargetProcessChecked) {
        gIsTargetProcessChecked= TRUE;
        gIsTargetProcess= CheckTargetProcess(TARGET_PROCESS_NAME);
    }
 
    if (nCode == HC_ACTION) {
        LPCWPRETSTRUCT p = (LPCWPRETSTRUCT)lParam;
        if (GetProp(p->hwnd, _T("OleDropTargetInterface")) != NULL) {
            DWORD   dwStyle = GetWindowLong(p->hwnd, GWL_STYLE);
            if (dwStyle & WS_VISIBLE) {
                if (GetProp(p->hwnd, _T("AutoDrop")) == NULL) {
                    SetProp(p->hwnd, _T("AutoDrop"), p->hwnd);                                      TCHAR className[256] = { _T('\0') };
                    GetClassName(p->hwnd,className, sizeof(className));
                    if (_tcscmp(className, TARGET_WINDOW_CLASS) == 0) {
                       
                        HWND hParent =GetParent(p->hwnd);
                        GetClassName(hParent, className,0x100);
                        if (_tcscmp(className, TARGET_WINDOW_CLASS_PARENT) == 0){
                            ShowInterger((DWORD64)p->hwnd);
                            HookDropTarget(p->hwnd);
                        }
                    }
                   
                }
            }
        }
    }
    returnCallNextHookEx(wndProcHook, nCode, wParam, lParam);
}


那么,上述代码是如何产生的呢?如何判断一个窗口是否可以接收Drop消息,该窗口是否为我们的目标窗口。

要回答上述问题,首先应该了解窗口注册成为Drop目标的过程,上一节我们提到过一个函数,RegisterDraDrop

IDA 逆向得到:

HRESULT __stdcall RegisterDragDrop(HWNDhwnd, LPDROPTARGET pDropTarget)
{
 IDropTarget *v2; // rbx@1
 HWND TargetWnd; // rdi@1
 int v4; // eax@5
 unsigned __int64 v5; // rcx@5
 HRESULT v6; // ebx@5
 
 v2 = pDropTarget;
 TargetWnd = hwnd;
 if ( Microsoft_Windows_OLE_PerfEnableBits & 1 )
   TemplateEventDescriptor((unsigned __int64)hwnd,&OLE_DragDrop_Register_Start);
 if ( !IsOleInitialized() )
   CoVrfShouldCallOleInit("RegisterDragDrop");
 v4 = RegisterDragDropImpl(TargetWnd, v2, 0);
 v6 = v4;
 if ( Microsoft_Windows_OLE_PerfEnableBits & 1 )
   Template_d(v5, &OLE_DragDrop_Register_Stop, v4);
 return v6;
}


我们可以看出,如果没有初始化OLE,会报错提示我们应该初始化OLE

真正的注册操作在RegisterDragDropImpl函数中:

该函数前面为一些检查操作,后面将IDropTarget 与窗口关联在一起的操作为:

顾名思义,该函数设置窗口的属性。

MSDN:
BOOL WINAPI SetProp(
 _In_     HWND    hWnd,
 _In_     LPCTSTR lpString,
 _In_opt_ HANDLE  hData
);


Adds a new entry or changes an existingentry in the property list of the specified window. The function adds a newentry to the list if the specified character string does not exist already inthe list. The new entry contains the string and the handle. Otherwise, thefunction replaces the string's current handle with the specified handle.

 

该函数功能为:添加或改变窗口的属性列表中的一项。

再看第二个参数

lpString [in]

Type: LPCTSTR

A null-terminated string or an atom thatidentifies a string. If this parameter is an atom, it must be a global atomcreated by a previous call to the GlobalAddAtom function. The atom must beplaced in the low-order word of lpString; the high-order word must be zero.

该参数为一个字符串或者atom原子值。很明显,它不是字符串,那这个原子值我们又不知道,或者可以使用extern 声明一下,但是尝试了一下就失败了,符号不认识。上述英文说到,该值如果是原子值,为调用GlobalAddAtom 得到的,而且通过IDA 交叉引用我们发现,其值除了在一个函数中为写操作外,其它全为读操作

很明显,该值关联的字符串为:OleDropTargetInterface

这样,我们就可以通过该字符串对应的窗口属性来判断一个窗口是否可以接收Drop操作。

另外,我们对每一个我们操作过的窗口设置了自己的专属属性“AutoDrop”以避免重复操作,刚开始想着简历一个数据结构,每次都从中判断是否当前窗口操作过,后来发现每个窗口有其自己的属性列表,而且如果简历数据结构,还不好判断当前窗口是否操作,因为窗口句柄和线程ID 都是可重复利用的,利用窗口自己的属性来判断其是否筛选过,特别方便。另外,在判断之后立马设置该属性,防止在调用SetProp之前该窗口又有窗口消息到达,重复拷贝并产生重复文件的窗口,窗口消息死锁,最终导致窗口崩溃。

 

其它的判断窗口的操作都是通过spy++以及自己的枚举窗口的函数,观察窗口类和父类结构来唯一确定目标窗口的方法,可以参考我之前的文章。这里不再赘述。

 

得到了目标窗口之后,我们进入核心的Hook 过程。HookDropTarget(HWND hwnd);再次之前,我们看到一个函数ShowInteger,

调试函数

该函数为调试方便使用,为了显示调试信息并防止窗口阻塞,该函数内部产生线程,然后再显示MessageBox窗口

DWORD WINAPI ThreadProc(LPVOID lpParam)
{
    MessageBoxA(NULL, (LPCSTR)lpParam, NULL, MB_OK);
    return 0;
}
void ShowMessage(CHAR* szShow)
{
    CreateThread(NULL, 0, ThreadProc, szShow, 0, NULL);
}
DWORD WINAPI ThreadProcInteger(LPVOID lpParam)
{
    char    szTemp[0x20];
    sprintf(szTemp,"%x", (DWORD64)lpParam);
    MessageBoxA(NULL, szTemp, NULL, MB_OK);
    return 0;
}
void ShowInterger(DWORD64 dwInput)
{
    CreateThread(NULL, 0,ThreadProcInteger, (LPVOID)dwInput, 0, NULL);
}


HookDropTarget函数

void HookDropTarget(HWND hwnd)
{
    POINTL  point = { 1,1 };
    DWORD   dwEffect = DROPEFFECT_COPY;// | DROPEFFECT_MOVE;
    CHAR    szShow[0x80] = { 0 };
    IDropTarget *target = (IDropTarget *)GetProp(hwnd, _T("OleDropTargetInterface"));
    if (target) {
        target->AddRef();
    }
    if (target) {
        if (g_pDataObject)
        {
            HRESULT hResult =target->DragEnter(g_pDataObject, MK_LBUTTON, point, &dwEffect);
            if (SUCCEEDED(hResult) &&(dwEffect & DROPEFFECT_COPY))
            {
                point.x++;
                point.y++;
                hResult= target->DragOver(MK_LBUTTON, point, &dwEffect);
                if (SUCCEEDED(hResult) &&(dwEffect & DROPEFFECT_COPY))
                {
                    hResult= target->Drop(g_pDataObject, 0, point, &dwEffect);
                }
            }
        }
    }
}
 


该函数现在看起来很简单,但刚开始dwEffect 变量没有初始化,程序出错后添加了大量的调试代码,显示程序的运行信息,后来发现dwEffect 值异常,后来查看参数列表和含义,添加初始化代码后一切正常。

问题根源是不好的编程习惯,变量未初始化就引用,函数参数的含义不清晰就开始编码。

// dllmain.cpp : 定义 DLL 应用程序的入口点。

#include "stdafx.h"
void InitializeDLL(HMODULE hModule);
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    {
        InitializeDLL(hModule);
        break;
    }
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}
 
#include "stdafx.h"
#include <stdio.h>
HHOOK wndProcHook = NULL;
HINSTANCE hInst;
BOOL gIsTargetProcess = TRUE;
BOOL gIsTargetProcessChecked = FALSE;
#define TARGET_PROCESS_NAME                 _T("explorer.exe")
#define TARGET_WINDOW_CLASS_GRANTPA         _T("TXGuiFoundation")
#define TARGET_WINDOW_CLASS                 _T("DirectUIHWND")
#define TARGET_WINDOW_CLASS_PARENT          _T("SHELLDLL_DefView")
#define TARGET_WINDOW_TEXT                  _T("QQ")
BOOL CheckTargetProcess(TCHAR* szTargetProcessName)
{
    TCHAR path[MAX_PATH] = _T("");
    HMODULE hMod = GetModuleHandle(NULL);
    GetModuleFileName(hMod, path, _countof(path));
 
    TCHAR *name = &path[_tcslen(path)];
    while (name > path&& *name != _T('\\')) {
        *name= _totlower(*name);
        name--;
    }
    name++;
    if (_tccmp(name, szTargetProcessName) == 0) {
        return TRUE;
 
    }
    //EnumWindows(EnumWindowsProc_CheckTargetProcess, NULL);
    return FALSE;
}
IDataObject* GetDataObject(CHAR* szFilePath);
CHAR* szFilePath = "D:\\AuToDrop_Test.exe";
IDataObject* g_pDataObject = GetDataObject("D:\\AuToDrop_Test.exe");
 
DWORD WINAPI ThreadProc(LPVOID lpParam)
{
    MessageBoxA(NULL, (LPCSTR)lpParam, NULL, MB_OK);
    return 0;
}
void ShowMessage(CHAR* szShow)
{
    CreateThread(NULL, 0, ThreadProc, szShow, 0, NULL);
}
 
DWORD WINAPI ThreadProcInteger(LPVOID lpParam)
{
    char    szTemp[0x20];
    sprintf(szTemp,"%x", (DWORD64)lpParam);
    MessageBoxA(NULL, szTemp, NULL, MB_OK);
    return 0;
}
void ShowInterger(DWORD64 dwInput)
{
    CreateThread(NULL, 0,ThreadProcInteger, (LPVOID)dwInput, 0, NULL);
}
 
void HookDropTarget(HWND hwnd)
{
    POINTL  point = { 1,1 };
    DWORD   dwEffect = DROPEFFECT_COPY;// | DROPEFFECT_MOVE;
    CHAR    szShow[0x80] = { 0 };
    IDropTarget *target = (IDropTarget *)GetProp(hwnd, _T("OleDropTargetInterface"));
    if (target) {
        if (g_pDataObject)
        {
            HRESULT hResult =target->DragEnter(g_pDataObject, MK_LBUTTON, point, &dwEffect);
            if (SUCCEEDED(hResult) &&(dwEffect & DROPEFFECT_COPY))
            {
                point.x++;
                point.y++;
                hResult= target->DragOver(MK_LBUTTON, point, &dwEffect);
                if (SUCCEEDED(hResult) &&(dwEffect & DROPEFFECT_COPY))
                {
                    hResult= target->Drop(g_pDataObject, 0, point, &dwEffect);
                }
            }
        }
    }
}
LRESULT CALLBACK MsgHookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    if (!gIsTargetProcess){
        returnCallNextHookEx(wndProcHook, nCode, wParam, lParam);
    }
    if(!gIsTargetProcessChecked) {
        gIsTargetProcessChecked= TRUE;
        gIsTargetProcess= CheckTargetProcess(TARGET_PROCESS_NAME);
    }
 
    if (nCode == HC_ACTION) {
        LPCWPRETSTRUCT p = (LPCWPRETSTRUCT)lParam;
 
        /*if (GetProp(p->hwnd,_T("OleDropTargetInterface")) != NULL) {
            DWORD   dwStyle = GetWindowLong(p->hwnd,GWL_STYLE);
            //if(dwStyle & WS_VISIBLE) {
                if(GetProp(p->hwnd, _T("AlterDnD Checked")) == NULL) {
                    TCHARclassName[256] = { _T('\0') };
                    GetClassName(p->hwnd,className, sizeof(className));
                    if(_tcscmp(className, TARGET_WINDOW_CLASS) == 0) {
                        GetWindowText(p->hwnd,className, 0x100);
                        staticint static_i_b = 0;
                        if(_tcscmp(className, TARGET_WINDOW_TEXT) != 0 && !static_i_b) {
                            //ShowInterger((DWORD64)p->hwnd);
                            //static_i_b= 1;
                            HookDropTarget(p->hwnd);
                        }
                    }
                    SetProp(p->hwnd,_T("AlterDnD Checked"), p->hwnd);
                }
            //}
        }*/
        if (GetProp(p->hwnd, _T("OleDropTargetInterface")) != NULL) {
            DWORD   dwStyle = GetWindowLong(p->hwnd, GWL_STYLE);
            if (dwStyle & WS_VISIBLE) {
                if (GetProp(p->hwnd, _T("AutoDrop")) == NULL) {
                    SetProp(p->hwnd, _T("AutoDrop"), p->hwnd);
                    TCHAR className[256] = { _T('\0') };
                    GetClassName(p->hwnd,className, sizeof(className));
                    if (_tcscmp(className, TARGET_WINDOW_CLASS) == 0) {
                       
                        HWND hParent =GetParent(p->hwnd);
                        GetClassName(hParent, className,0x100);
                        if (_tcscmp(className, TARGET_WINDOW_CLASS_PARENT) == 0){
                            ShowInterger((DWORD64)p->hwnd);
                            HookDropTarget(p->hwnd);
                        }
                    }
                   
                }
            }
        }
    }
    returnCallNextHookEx(wndProcHook, nCode, wParam, lParam);
}
IMPORT BOOL InstallHook()
{
    wndProcHook= SetWindowsHookEx(WH_CALLWNDPROCRET, MsgHookProc, hInst, 0);
    return TRUE;
}
 
IMPORT void UninstallHook()
{
    if (wndProcHook != NULL) {
        UnhookWindowsHookEx(wndProcHook);
        wndProcHook= NULL;
    }
}
 
void InitializeDLL(HMODULE hModule)
{
    hInst= hModule;
}


使用:初始化调用:InstallHook() ,程序退出时调用:UninstallHook()

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值