应用程序的 DIY__(给程序加上新功能)

来源:互联网 发布:达芬奇14mac怎么改中文 编辑:程序博客网 时间:2024/05/01 07:14

原始排版:应用程序的 DIY__(给程序加上新功能)

标签(空格分隔): R3 窗口编程


开篇

之前的考试中最后一个实操题就是要在记事本上加入一个功能。(通过 按钮 或者 菜单项来实现)

动还是静

从静开始

这个问题的由来是 这个控件本身到底应该动态创建还是静态创建,本身是取决于原有应用程序的资源, 如果原本的应用程序是存在有窗口资源或者是菜单资源,那么我们静态创建也是无妨的,通过 ResHacker 等工具可以轻易做到,过程比较简单,找到窗口的资源后,写入一个控件资源即可。

这是 Windows Xp 下的计算器的窗口资源

   CONTROL "1/x", 107, BUTTON, BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE, 140, 85, 24, 18    CONTROL "sqt", 103, BUTTON, BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE, 140, 45, 24, 18    CONTROL "%", 109, BUTTON, BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE, 142, 64, 20, 18    CONTROL "help", 333, BUTTON, BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE, 148, 23, 20, 19    CONTROL "", 1000, STATIC, SS_LEFT | WS_CHILD | WS_VISIBLE | WS_GROUP, 0, 127, 1, 1 

我在其中添加了一个 Caption 为 “help” 的按钮,资源ID为333

在最新版本的 ResHacker 也支持可视化的资源编辑器,可以直接右键 “Insert control”。

菜单资源的话也是同样的道理。

到动结束

换一个例子,当我们去读取 Win7 的计算器时就会发现,其静态资源区是找不到 窗口资源和菜单资源的,反正我是没找到。

这个时候怎么办?

最简单的方法 莫过于 调用 CreateWidowsEx 来创建一个按钮出来。
不过后来我发现创建按钮的话由于种种原因并不可靠。
不过也有应对的方法就是添加 菜单项,这个就我目前测试来说比较可靠。

        HWND hWnd = FindWindow(L"SciCalc", L"计算器");        AppendMenu(GetMenu(hWnd), MF_STRING, 123, L"Hello");

当然这个过程并不是由本地调用,而是由应用程序去调用

驻进目标应用程序___dll注入

动还是静

驻进也分为动静两种:

一、 远程线程注入
二、 导入表注入

两种各有优劣,自行斟酌。

从远程线程注入说起,也就是从动说起:

bool InjectDll(DWORD dwPid, char *pszLibFileName){    HANDLE hProcess = OpenProcess(        PROCESS_QUERY_INFORMATION | // Required by Alpha        PROCESS_CREATE_THREAD |     // For CreateRemoteThread        PROCESS_VM_OPERATION |      // For VirtualAllocEx/VirtualFreeEx        PROCESS_VM_WRITE,           // For WriteProcessMemory        //, PROCESS_VM_READ ,       //  For CreateRemoteThread        /*  PROCESS_ALL_ACCESS,*/        FALSE, dwPid);    PTHREAD_START_ROUTINE pfnStartAddr = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(L"Kernel32"), "LoadLibraryA");    //计算DLL路径名需要的内存空间    int cb = (1 + lstrlenA(pszLibFileName)) * sizeof(char);    //使用VirtualAllocEx函数在远程进程的内存地址空间分配DLL文件名缓冲区    char *pszLibFileRemote = (char *)VirtualAllocEx(hProcess, NULL, cb, MEM_COMMIT, PAGE_READWRITE);    //使用WriteProcessMemory函数将DLL的路径名复制到远程进程的内存空间    bool iReturnCode = WriteProcessMemory(hProcess, pszLibFileRemote, (PVOID)pszLibFileName, cb, NULL);    CreateRemoteThread(hProcess, NULL, 0, pfnStartAddr, pszLibFileRemote, 0, NULL);    CloseHandle(hProcess);    return true;}

这个代码的 健壮性很差,代码中并没有 return false 的处理,是临时拼凑出来解释动这一情况的。

接下来说静:

这个说起来就很简单了,
使用 LoadPE 这一类型的工具,直接将 DLL 添加到 原应用程序的导入表就行了。
这里需要注意的是,这个dll的写法。

DWORD WINAPI ThreadProc(    _In_  LPVOID lpParameter    ){    //    // 延时 500 毫秒 等待窗口创建完成    //    Sleep(500);    HWND hWnd = FindWindow(L"SciCalc", L"计算器");    // AppendMenu(GetMenu(hWnd), MF_STRING, 123, L"Hello");    g_uOldProc = GetWindowLong(hWnd, GWL_WNDPROC);    SetWindowLong(hWnd, GWL_WNDPROC, (LONG)MyWindowProc);    return 0;}

DllMain如下:

    switch (ul_reason_for_call)    {    case DLL_PROCESS_ATTACH:    {        //        // 延时几秒 等待窗口创建成功        //        CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);    }    case DLL_THREAD_ATTACH:    case DLL_THREAD_DETACH:    case DLL_PROCESS_DETACH:        break;    }

需要多创建一条线程出来,等待窗口创建完成。

尾声,消息的拦截

这里消息拦截是 使用的 SetWindowLong 这个API。 这个API 可以用来替换掉原来的消息回调函数地址,我们就可以实现一个中间过滤的功能。
GetWindowLong 是用来获取 消息回调函数地址,把老的地址记录,替换上新的地址,
使用 CallWindowProc 继续下发消息,看起来是不是有点像是 SSDT Inline HOOK呢?

LONG g_uOldProc;LRESULT CALLBACK MyWindowProc(    _In_  HWND hwnd,    _In_  UINT uMsg,    _In_  WPARAM wParam,    _In_  LPARAM lParam    ){    if (uMsg == WM_COMMAND && LOWORD(wParam) == 333)    {        MessageBox(0, 0, 0, 0);    }    return CallWindowProc((WNDPROC)g_uOldProc, hwnd, uMsg, wParam, lParam);}

后话

实现方法远远不止上面列举的几种,例如还可以设置全局消息钩子等,各种各样的方法,不应该局限于眼前。

0 0
原创粉丝点击