首页 > 其他分享 >一个使用HOOK/WH_GETMESSAGE解决软件交互问题的案例

一个使用HOOK/WH_GETMESSAGE解决软件交互问题的案例

时间:2024-04-10 23:36:20浏览次数:14  
标签:wParam lParam exe pt pMsg HOOK GETMESSAGE WH szBuf

一、问题背景:
1、某设计软件AD,可以打开/编辑二维电路设计界面,其交互方式如下:

(1)鼠标右键按下拖动

(2)鼠标滚轮上下移动

(3)鼠标滚轮按下+鼠标移动 缩放

(4)ctrl + 鼠标滚轮 缩放 

    该软件的交互方式可以通过软件设置修改,但是基本的使用习惯就是如此,用户的一个场景是通过每台机器mstsc到另一台机器S上,而软件AD是部署运行在S上,在S上进行以上操作是没有问题的,而使用机器远程到S上进行操作的时候就会出现(3)的交互有问题,表现出来是缩放过程中设计图会突然变小,这个就很不好用,所以需要解决此问题。

 

二、方案设想

(1)研究windows远程桌面rdp协议,通过了解协议细节去解决

大概看了一下rdp协议,比较繁琐,没有仔细研究,pass

(2)尝试在S上安装一台显示器,推测是否为两边显示器分辨率不一致问题

因为开始的时候S上是没有连接显示器的,尝试以后发现问题仍然存在,pass

(3)通过HOOK MOUSE操作,将以上交互(3)修改为交互(4)

即打算把 MButtonDown + MouseMove  修改为 Ctrl + MouseWheel

在使用MOUSE HOOK的过程中,发现以下结论:

①Mouse Low Level Hook 可以不试用dll实现,直接在一个宿主exe中实现即可,因为不管是否使用dll,钩子过程函数的执行上下文都是安装钩子的线程。

②同时,局部钩子能hook的线程只能是安装钩子的进程所在的线程,无法跨进程安装局部钩子;

③又,鼠标钩子无法添加键盘按键属性,即无法通过给鼠标钩子添加CTRL按下这个键值,遂pass。

(4)通过 hook WH_GETMESSAGE 来解决

以下以这个方案来叙述。

 

三、解决过程

首先明确一点:WH_GETMESSAGE  全局钩子通过dll实现。

思路:

(1)设置目标进程名字xxx.exe

后面会根据xxx.exe查找进程id

(2)在钩子过程函数GetMsgProc中判断当前窗口对应的进程是否为目标进程

GetMsgProc原型如下

LRESULT CALLBACK GetMsgProc(int code, WPARAM wParam, LPARAM lParam)

 

参数意义如下:(from  https://learn.microsoft.com/zh-cn/windows/win32/winmsg/getmsgproc )

代码 [in]

类型:int

指定钩子过程是否必须处理消息。 如果代码是 HC_ACTION,则钩子过程必须处理消息。 如果代码小于零,则钩子过程必须将消息传递给 CallNextHookEx 函数,而无需进一步处理,并且应返回 CallNextHookEx 返回的值。

wParam [in]

类型:WPARAM

指定是否已从队列中删除消息。 此参数的取值可为下列值之一。

含义
PM_NOREMOVE 0x0000 消息尚未从队列中删除。 (一个应用程序调用了 PeekMessage 函数,且指定 PM_NOREMOVE 标志。)
PM_REMOVE 0x0001 消息已从队列中删除。 (一个应用程序调用了 GetMessage,或者它调用了 PeekMessage 函数,且指定 PM_REMOVE 标志。)

lParam [in]

类型:LPARAM

指向包含消息详细信息的 MSG 结构的指针。

-returns

如果代码小于零,则钩子过程必须返回 CallNextHookEx 返回的值。

如果代码大于或等于零,则强烈建议调用 CallNextHookEx 并返回它返回的值;否则,安装有 WH_GETMESSAGE 钩子的其他应用程序不会收到钩子通知,可能会因此出现错误行为。 如果钩子过程不调用 CallNextHookEx,则返回值应为零。

 

因此主要是处理lParam参数,其为MSG结构 (https://learn.microsoft.com/zh-cn/windows/win32/api/winuser/ns-winuser-msg)

typedef struct tagMSG {
  HWND   hwnd;
  UINT   message;
  WPARAM wParam;
  LPARAM lParam;
  DWORD  time;
  POINT  pt;
  DWORD  lPrivate;
} MSG, *PMSG, *NPMSG, *LPMSG;

 

其结构体成员如下

成员

hwnd

类型:HWND

其窗口过程接收消息的窗口的句柄。 当消息是线程消息时,此成员为 NULL 。

message

类型: UINT

消息的标识符。 应用程序只能使用低字;高字由系统保留。

wParam

类型:WPARAM

关于消息的附加信息。 确切含义取决于 消息 成员的值。

lParam

类型:LPARAM

关于消息的附加信息。 确切含义取决于 消息 成员的值。

time

类型:DWORD

消息的发布时间。

pt

类型: POINT

发布消息时的光标位置(以屏幕坐标表示)。

lPrivate

 

(3)通过hwnd成员获取起所在进程id,通过与xxx.exe获取的进程id对比,如果一致则进行处理,否则不需要处理。

(4)几个关键点

①windows窗口过程函数如下,需要知道GetMsgProc到WndProc,他们的参数如何对应。

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

 

 

②对于消息循环来说,msg.lParam包含了鼠标位置,对mousemove来说是客户区坐标,msg.pt包含的是桌面坐标,对于mousewheel来说,两个坐标值都是桌面坐标。

 1  while (auto ret = GetMessage(&msg, nullptr, 0, 0))
 2  {
 3      if (-1 == ret)
 4      {
 5          exit(1);
 6      }
 7      TCHAR szBuf[200] = { 0 };     
 8      if (msg.message >= WM_MOUSEFIRST && msg.message <= WM_MOUSELAST)
 9      {
10          wsprintf(szBuf, TEXT("message=%0x, lParam.pt(%d, %d), msg.pt(%d, %d)\r\n"),
11              msg.message, GET_X_LPARAM(msg.lParam), GET_Y_LPARAM(msg.lParam), msg.pt.x, msg.pt.y);
12          OutputDebugString(szBuf);
13      }
14     
15      if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
16      {
17          TranslateMessage(&msg);
18          DispatchMessage(&msg);
19      }
20  }

 

 

③GetMsgProc中的MSG结构与下游消息循环如何对应

结论就是MSG结构即为消息循环的GetMessage中的第一个参数!(字段值是一样的!!!)

因此转换过程就是 改鼠标消息值、添加CTRL消息,修改鼠标坐标值、根据鼠标移动方向计算为滚轮向上还是向下滚动。

  

  1 #include "pch.h"
  2 #include "CHook.h" 
  3 #include "utility.h"
  4 #include <string> 
  5 #include <windowsx.h>
  6 
  7 #pragma data_seg("hookrws")
  8 HHOOK           g_hookLLKeyboard = NULL;
  9 HHOOK           g_hookLLMouse = NULL; 
 10 HHOOK           g_hookGetMsg = NULL;
 11 bool            g_bMouseMBtnDown = false;
 12 HMODULE         g_hDll = NULL;
 13 wchar_t         g_szExeName[MAX_PATH] = { 0 };
 14 
 15 #pragma data_seg()
 16 #pragma comment(linker,"/SECTION:hookrws,RWS")
 17 
 18 int             g_nLastMousePosY = 0;
 19 char            g_nLastMouseDirectionFlag = 1;
 20 int             g_nLastPosX = 0;
 21 int             g_nLastPosY = 0;
 22 
 23 CHook& CHook::Instance()
 24 {
 25     static CHook hook;
 26     return hook; 
 27 }
 28 
 29 bool CHook::Install(const wchar_t* exe)
 30 { 
 31     OutputDebugString(__FUNCTIONW__);
 32     if (exe)
 33     {
 34         wsprintf(g_szExeName, TEXT("%s"), exe);
 35     }
 36 
 37     HWND hwnd = GetWindowHandlerByName(exe);
 38     DWORD dwThread = 0, dwProcessId = 0;
 39     dwThread = GetWindowThreadProcessId(hwnd, &dwProcessId);
 40     TCHAR szBuf[200] = { 0 };
 41     wsprintf(szBuf, TEXT("[CHook::Install]thread=%u, exe=%s, hwnd=%0x, dwProcessId=%0x, dwProcessId=%d, dwThread=%0x, \r\n"),
 42         GetCurrentThreadId(), exe, hwnd, dwProcessId, dwProcessId, dwThread);
 43     OutputDebugString(szBuf);
 44 
 45     return InstallGetMsgHook(0); 
 46 }
 47 
 48 void CHook::UnInstall()
 49 {
 50     OutputDebugString(__FUNCTIONW__);
 51     UnInstallLowLevelKeyboardHook();
 52     UnInstallLowLevelMouseHook(); 
 53     UnInstallGetMsgHook();
 54  }
 55 
 56  
 57 
 58 bool CHook::InstallLowLevelKeyboardHook(DWORD dwThread) // 此案例不需要关注
 59 {
 60     if (!g_hookLLKeyboard)
 61     {
 62         g_hookLLKeyboard = SetWindowsHookEx(WH_KEYBOARD, LowLevelKeyboardProc, NULL, dwThread);
 63     }
 64     return (NULL != g_hookLLKeyboard);
 65 }
 66 
 67 bool CHook::InstallLowLevelMouseHook(DWORD dwThread) // 此案例无需关注
 68 {
 69     DWORD dwErr = ERROR_SUCCESS;
 70     if (!g_hookLLMouse)
 71     {
 72         g_hookLLMouse = SetWindowsHookEx(WH_MOUSE_LL, LowLevelMouseProc, NULL, dwThread);
 73         dwErr = GetLastError();
 74     }
 75     return (NULL != g_hookLLMouse);
 76 }
 77 
 78 bool CHook::InstallGetMsgHook(DWORD dwThread)
 79 {
 80     DWORD dwErr = ERROR_SUCCESS;
 81     if (!g_hookGetMsg)
 82     {
 83         g_hookGetMsg = SetWindowsHookEx(WH_GETMESSAGE, GetMsgProc, g_hDll, dwThread);
 84         dwErr = GetLastError();
 85     }
 86     return (NULL != g_hookGetMsg); 
 87 }
 88 
 89 void CHook::UnInstallLowLevelKeyboardHook()
 90 {
 91     if (g_hookLLKeyboard)
 92     {
 93         UnhookWindowsHookEx(g_hookLLKeyboard);
 94         g_hookLLKeyboard = NULL;
 95     }
 96 }
 97 
 98 void CHook::UnInstallLowLevelMouseHook()
 99 {
100     if (g_hookLLMouse)
101     {
102         UnhookWindowsHookEx(g_hookLLMouse);
103         g_hookLLMouse = NULL;
104     }
105 }
106 
107 void CHook::UnInstallGetMsgHook()
108 {
109     if (g_hookGetMsg)
110     {
111         UnhookWindowsHookEx(g_hookGetMsg);
112         g_hookGetMsg = NULL;
113     }
114 }
115 
116 
117 
118 
119 LRESULT CALLBACK LowLevelKeyboardProc(int code, WPARAM wParam, LPARAM lParam)
120 {
121     if (HC_ACTION == code)
122     {
123         TCHAR szBuf[100] = { 0 };
124         wsprintf(szBuf, TEXT("[Keyboard]thread=%u, code=%d, w=%u, l=%ld\r\n"), GetCurrentThreadId(), code, wParam, lParam);
125         OutputDebugString(szBuf);
126     }
127     
128     return CallNextHookEx(g_hookLLKeyboard, code, wParam, lParam);
129 }
130 
131 LRESULT CALLBACK LowLevelMouseProc(int code, WPARAM wParam, LPARAM lParam)
132 {
133     if (HC_ACTION == code)
134     {
135         TCHAR szBuf[200] = { 0 };
136         std::wstring data;
137         MSLLHOOKSTRUCT* st = (MSLLHOOKSTRUCT*)(lParam);
138         short delta = 0;
139         switch (wParam)
140         {
141         case WM_MOUSEWHEEL:
142             delta = GET_WHEEL_DELTA_WPARAM(st->mouseData);
143             if (delta > 0)
144             {
145                 data = L" mouse middle key forward";
146             }
147             else
148             {
149                 data = L" mouse middle key backward";
150             }
151 
152             wsprintf(szBuf, TEXT("[Mouse]pt=[%d, %d], delta=%d, thread=%u, code=%d, w=%u, l=%ld, %s\r\n"),
153                 st->pt.x, st->pt.y, delta, GetCurrentThreadId(), code, wParam, lParam, data.c_str());
154             OutputDebugString(szBuf);
155             break;
156 
157         default:
158             wsprintf(szBuf, TEXT("[Mouse]thread=%u, code=%d, w=%u, l=%ld\r\n"), GetCurrentThreadId(), code, wParam, lParam);
159             OutputDebugString(szBuf);
160             break;
161         } 
162     } 
163 
164     return CallNextHookEx(g_hookLLMouse, code, wParam, lParam);
165 }

166 /////////////////////////////////////////////////////////// 重点看这里 ///////////////////////////////////////////////////////////////////////////// 167 LRESULT CALLBACK GetMsgProc(int code, WPARAM wParam, LPARAM lParam) 168 { 169 MSG* pMsg = (MSG*)(lParam); 170 if (HC_ACTION == code) 171 { 172 HWND hWnd = pMsg->hwnd; 173 TCHAR szBuf[200] = { 0 }; 174 175 if (WM_MBUTTONDOWN == pMsg->message && IsSameProcess(g_szExeName, hWnd)) 176 { 177 g_nLastPosX = pMsg->pt.x; 178 g_nLastPosY = pMsg->pt.y; 179 wsprintf(szBuf, TEXT("[WM_MBUTTONDOWN] xy(%d, %d), exe(%s)"), g_nLastPosX, g_nLastPosY, g_szExeName); 180 OutputDebugString(szBuf); 181 } 182 else if (WM_MOUSEMOVE == pMsg->message && IsSameProcess(g_szExeName, hWnd)) 183 { 184 // 默认鼠标向上滑动 185 char flag = 1; 186 int temp = g_nLastMousePosY; 187 if (pMsg->pt.y > temp) 188 { 189 // 鼠标向下 190 flag = -1; 191 } 192 // 如果y坐标与上次相同则使用上次的方向标志 193 else if (pMsg->pt.y == temp) 194 { 195 flag = g_nLastMouseDirectionFlag; 196 } 197 g_nLastMouseDirectionFlag = flag; 198 199 // 按下了中键 200 if (pMsg->wParam & MK_MBUTTON) 201 { 202 // 转换为ctrl + movewheel 203 pMsg->message = WM_MOUSEWHEEL; 204 #if 0 205 LPARAM ptParam = pMsg->pt.y; 206 ptParam = (ptParam << (sizeof(DWORD) / 2 * 8)) | pMsg->pt.x; 207 #else 208 LPARAM ptParam = g_nLastPosY; 209 ptParam = (ptParam << (sizeof(DWORD) / 2 * 8)) | g_nLastPosX; 210 pMsg->pt.x = g_nLastPosX; 211 pMsg->pt.y = g_nLastPosY; 212 #endif 213 214 pMsg->lParam = ptParam; 215 216 DWORD dwParam = WHEEL_DELTA * flag; 217 dwParam = (dwParam << (sizeof(DWORD) / 2 * 8)) | (MK_CONTROL - 0); 218 pMsg->wParam = dwParam; 219 220 } 221 } 222 else if (WM_MOUSEWHEEL == pMsg->message && IsSameProcess(g_szExeName, hWnd)) 223 { 224 wsprintf(szBuf, TEXT("[MouseWheel] pt(%d, %d), pMsg.wParam.hi(delta)=%d, pMsg.wParam.lo(mk)=%d\r\n"), 225 pMsg->pt.x, pMsg->pt.y, GET_WHEEL_DELTA_WPARAM(pMsg->wParam), GET_KEYSTATE_WPARAM(pMsg->wParam)); 226 OutputDebugString(szBuf); 227 } 228 229 } // if (HC_ACTION == code) 230 231 g_nLastMousePosY = pMsg->pt.y; 232 233 return CallNextHookEx(g_hookGetMsg, code, wParam, lParam); 234 }

 

 

 

IsSameProcess函数实现
 1 bool IsSameProcess(const wchar_t* exe, HWND hWnd)
 2 {
 3     if (!exe || !IsWindow(hWnd))
 4     {
 5         TCHAR szBuf[80] = { 0 };
 6     //    wsprintf(szBuf, TEXT("hwnd=%0x, exe=%s, NOT!!!!"), hWnd, exe);
 7         //OutputDebugString(szBuf);
 8         return false;
 9     }
10     // 根据exe获取PID
11     DWORD dwPid = GetProcessIDByName(exe); 
12     // 根据窗口句柄获取PID
13     DWORD dwPid2 = 0;
14     GetWindowThreadProcessId(hWnd, &dwPid2);
15     TCHAR szBuf2[80] = { 0 };
16     //wsprintf(szBuf2, TEXT("hwnd=%0x, exe=%s, pid1=%u, pid2=%u"), hWnd, exe, dwPid, dwPid2);
17     //OutputDebugString(szBuf2);
18     return ((dwPid == dwPid2) && (dwPid != 0)); 
19 }

 

 
 1 DWORD GetProcessIDByName(const wchar_t* pName)
 2 {
 3     HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
 4     if (INVALID_HANDLE_VALUE == hSnapshot) {
 5         return NULL;
 6     }
 7     PROCESSENTRY32 pe = { sizeof(pe) };
 8     for (BOOL ret = Process32First(hSnapshot, &pe); ret; ret = Process32Next(hSnapshot, &pe))
 9     {
10         if (wcscmp(pe.szExeFile, pName) == 0)
11         {
12             CloseHandle(hSnapshot);
13             return pe.th32ProcessID;
14         }
15     }
16     CloseHandle(hSnapshot);
17     return 0;
18 }

 

 

 

 

 

 

 

标签:wParam,lParam,exe,pt,pMsg,HOOK,GETMESSAGE,WH,szBuf
From: https://www.cnblogs.com/cuish/p/18127771

相关文章

  • Linux系统下的HOOK
    HOOK通过在系统调用或函数调用前以替换的方式改变程序中原有的函数功能,实现更改原有函数的功能。利用LD_PRELOAD进行HOOKLinux提供了一个名为LD_PRELOAD的环境变量。这个环境变量允许用户指定一个或多个共享链接库文件的路径。当程序启动时,动态加载器会在加载C语言运行库之......
  • mysql中将where条件中过滤掉的group by分组后查询无数据的行进行补0
    背景mysql经常会用到groupBy来进行分组查询,但也经常会遇到一个问题,就是当有where条件时,被where条件过滤的数据不显示了。例如我有一组数据:我想查询创建时间大于某一范围的spu的分组下的sku的数量正常的sql查出的话,假如不存在相关记录SELECTproduct_id,count(*)countF......
  • [observe]面向Fedora Linux 40和Fedora Rawhide用户的安全警告(翻译)
    所有版本RedHatEnterpriseLinux(RHEL)均不受此漏洞影响。2024年3月30日补充:我们已确认FedoraLinux40beta版确实包含两个受影响的xz库版本——xz-libs-5.6.0-1.fc40.x86_64.rpm和xz-libs-5.6.0-2.fc40.x86_64.rpm。目前,Fedora40Linux似乎没有受到实际恶意软件攻击......
  • How to find which Azure Private DNS Zone is associated with VNet
    Herearesomecommonscenariosinquestion:AzFWgoesintofailedstateduetoaPrivateDNSzonelinkedwiththeVNETwhichcausesresolutionfailures.PrivateEndpointcreationwhereDNSzonefailstolinktothevnetasanotherzonewiththesamename......
  • jQuery.when()用法
    1、该方法在jQuery1.5开始被引入。2、用法测试a、1234567891011121314151617var url1= "/resource/ar/hometab/index_tab_games.json",    url2= "/resource/ar/hometab/index_tab_image.json",    ajax1=$.ajax(      ......
  • Python基础笔记02-while、字符串格式化、运算符、基础概念与数据类型
    前言!!!注意:本系列所写的文章全部是学习笔记,来自于观看视频的笔记记录,防止丢失。观看的视频笔记来自于:哔哩哔哩武沛齐老师的视频:2022Python的web开发(完整版)入门全套教程,零基础入门到项目实战1.条件语句补充1.1基本语句if条件: ...else:...1.2多条件if条件1:......
  • react 函数组件和hook
    函数组件1.函数组件没有生命周期2.函数组件没有this3.函数组件通过hook完成各种操作4.函数组件本身就是render函数5.props在函数第一个参数解释useState参考https://www.cnblogs.com/ssszjh/p/14581014.htmlprops参考https://www.cnblogs.com/ssszjh/p/18118746生命周期......
  • [React] Using key prop to reset component to avoid useEffect hook
    ThecomponentusinguseEffectwhichisnotnecessary:functionTopicEditor({selectedTopicId}){const[enteredNote,setEnteredNote]=useState('');constselectedTopic=DUMMY_TOPICS.find(topic=>topic.id===selectedTopicId)......
  • React中的Hooks---useEffect
    简介什么是ReactHooksReactHooks是在React16.8版本中引入的一项重大特性,旨在解决函数组件在复杂场景下的状态管理和生命周期问题。它们允许在不编写类组件的情况下使用state、生命周期方法等功能,使得函数组件更加简洁、易于理解和复用。作为ReactHooks的核心成员之一,useEf......
  • 学习Java Day3-05 (流程控制-循环结构while,do……while,for,增强for)
    循环结构while循环do…while循环for循环在Java5中引入了一种主要用于数组的增强型for循环。while循环while是最基本的循环,它的结构为:while(布尔表达式){ //循环内容}只要布尔表达式为ture,循环就会一直执行下去。我们大多数情况是会让循环停止下来的,我们需要一......