Windows注入与拦截(5)-- 实现对指定窗口控件的挂载

一. 挂载原理

本文主要介绍如何通过DLL注入的方式来实现在指定的窗口控件上挂载自定义窗口。

何谓挂载?
和舰载机挂载导弹类似,将我们自己的窗口挂到原有程序的窗口之上,可以实现对原有窗口功能的覆盖和扩展。

结合本实例的代码,挂载的实现原理大致如下:

  1. 通过远程线程的方式(其他方式也可以)将DLL注入到指定的进程(当然是被挂载窗口所属的进程)。
  2. DllMainDLL_PROCESS_ATTACH条件分支中创建一个新线程NewThread,后面的处理逻辑将放到NewThread中,防止DllMain阻塞。
  3. 在NewThread线程中查找被挂载的窗口(注意是窗口不是窗口上的控件)的句柄(查找方式:EnumWindows结合FindWindowEx实现,详见FindProcessWindow函数)。
  4. 使用SetWindowLong修改窗体的默认的消息处理过程(假设将窗口处理过程修改为我们DLL中的WndProc_Trampoline函数),然后向窗体发送一个自定义消息,这时WndProc_Trampoline函数就可以获取到该消息通知,我们在收到该消息通知后就可以开始我们的挂载逻辑了。之所以要通过发送一个自定义消息的方式来做,而不是直接在新线程NewThread中开始我们的挂载逻辑,是因为这样做可以保证我们的挂载逻辑(如创建窗口)是在主线程中进行的。
  5. 挂载逻辑主要包含:查找需要挂载控件的句柄、创建挂载窗口。创建挂载窗口的时候要设置WS_CHILD子窗口属性,并设置父窗体的WS_CLIPCHILDREN属性来裁剪子窗口,防止我们挂载的子窗口闪烁。
  6. WndProc_Trampoline函数的最后不要忘记调用之前老的消息处理过程,否则原窗口的消息将无法得到正确的响应。

二. 实例代码

2.1 DllInjecter工程

DllInjecter.exe实现将DLL注入到指定的进程之中,是一个通用的DLL注入器。包含了对前面文章介绍的“使用远程线程的方式注入”和“使用钩子方式注入”这两种注入方式的实现。

Windows注入与拦截(5)-- 实现对指定窗口控件的挂载_第1张图片

2.2 Test工程

Test.exe是一个模拟的被挂载程序,程序非常简单,只包含一个窗体和大按钮,不包含任何逻辑。本实例主要是将我们的窗口挂载到这个大按钮之上。
Windows注入与拦截(5)-- 实现对指定窗口控件的挂载_第2张图片

2.3 Troy工程

Troy.dll被注入到Test.exe中的DLL文件(名称来源于“特洛伊木马”)。挂载的主要逻辑大都在该工程之中。

我们可以在WndProc_Trampoline函数的WUM_CREATE_USER_WINDOW消息处理分支中分别调用CreateUserWindowCreateUserWindowByDuilib函数来创建挂载窗口:
- CreateUserWindow使用原始的Windows API(CreateWindow)创建挂载窗口。
- CreateUserWindowByDuilib使用duilib界面库来创建挂载窗口。

LRESULT CALLBACK WndProc_Trampoline(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
    switch (message) {
    case WUM_CREATE_USER_WINDOW:
        CreateUserWindowByDuilib();
        break;
    }

    return CallWindowProc(g_oldProc, hwnd, message, wParam, lParam);
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
    switch (message) {
    case WM_PAINT:
        RECT rect;
        GetClientRect(hwnd, &rect);
        DrawText(GetDC(hwnd), TEXT("test"), 4, &rect, DT_CENTER);
        break;
    }

    return  DefWindowProc(hwnd, message, wParam, lParam);
}
unsigned int __stdcall PluginProc(LPVOID pArg) {
    MessageBox(NULL, TEXT("我已经被注入啦"), TEXT("信息"), MB_OK | MB_ICONASTERISK);

    HWND hMainWindow = InjectHelper::FindProcessWindow(GetCurrentProcessId(), TEXT("#32770"), TEXT("Test"), TRUE);

    g_oldProc = (WNDPROC)SetWindowLong(hMainWindow, DWL_DLGPROC, (LONG)WndProc_Trampoline);


    ::PostMessage(hMainWindow, WUM_CREATE_USER_WINDOW, 0, 0);

    return 0;
}
BOOL APIENTRY DllMain(HMODULE hModule, DWORD  fdwReason, LPVOID lpReserved) {
    HANDLE hThread = NULL;

    switch(fdwReason) {
        case DLL_PROCESS_ATTACH:
        {
            g_hDllModule = hModule;
            // 使用注册表方式和CreateRemoteThread方式注入时,一般在此处创建线程
            //

            hThread = (HANDLE)_beginthreadex(NULL, 0, PluginProc, NULL, 0, NULL);
            if (hThread) {
                CloseHandle(hThread); // 关闭句柄,防止句柄泄漏
            }
            break;
        }
        case DLL_THREAD_ATTACH:
        {
            break;
        }
        case DLL_THREAD_DETACH:
        {
            break;
        }
        case DLL_PROCESS_DETACH:
        {
            break;
        }
    }
    return TRUE;
}

下面是使用duilib创建的挂载窗口的效果如图:
Windows注入与拦截(5)-- 实现对指定窗口控件的挂载_第3张图片


实例完整代码见:https://gitee.com/china_jeffery/inject_sample
选择x86平台编译,x64平台未配置

你可能感兴趣的:(☆,Windows,Via,C/C++)