首页
社区
课程
招聘
解决WPF使用HVNC的问题-HwndHook
发表于: 5天前 1197

解决WPF使用HVNC的问题-HwndHook

5天前
1197

这里就不多介绍HVNC了 因为单纯在之前的文章已经介绍过了
https://bbs.kanxue.com/thread-264956.htm

那么我们都知道 CPP中HVNC有现成的代码(github TinyNuke)可以直接抄
https://github.com/Sts0mrg0/TinyNuke

C#中也有比如
https://github.com/moom825/xeno-rat
核心代码在

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
public class CustomPictureBox : PictureBox
   {
       Node client;
       public CustomPictureBox(Node _client)
       {
           client = _client;
       }
 
       [DllImport("user32.dll")]
       public static extern short GetKeyState(int nVirtKey);
 
       // Constants for clipboard data formats
       public const uint CF_TEXT = 1;          // Text format
       public const uint CF_BITMAP = 2;        // Bitmap format
       public const uint CF_UNICODETEXT = 13;   // Unicode text format
       public const uint CF_HDROP = 15;         // File format
 
       // Import the necessary WinAPI functions
       [DllImport("user32.dll")]
       [return: MarshalAs(UnmanagedType.Bool)]
       public static extern bool IsClipboardFormatAvailable(uint format);
 
       [DllImport("user32.dll")]
       [return: MarshalAs(UnmanagedType.Bool)]
       public static extern bool OpenClipboard(IntPtr hWndNewOwner);
 
       [DllImport("user32.dll")]
       public static extern IntPtr GetClipboardData(uint uFormat);
 
 
       [DllImport("user32.dll")]
       [return: MarshalAs(UnmanagedType.Bool)]
       public static extern bool CloseClipboard();
 
       [DllImport("user32.dll")]
       static extern short VkKeyScan(char c);
 
       [DllImport("user32.dll")]
       public static extern int ToAscii(uint uVirtKey, uint uScanCode, byte[] lpKeyState, out uint lpChar, uint uFlags);
       public Point TranslateCoordinates(Point originalCoords, Size originalScreenSize, PictureBox targetControl)
       {
           // Calculate the scaling factors
           float scaleX = (float)targetControl.Image.Width / originalScreenSize.Width;
           float scaleY = (float)targetControl.Image.Height / originalScreenSize.Height;
 
           // Apply the scaling factors
           int scaledX = (int)(originalCoords.X * scaleX);
           int scaledY = (int)(originalCoords.Y * scaleY);
 
           // Get the unzoomed and offset-adjusted coordinates
           Point translatedCoords = UnzoomedAndAdjusted(targetControl, new Point(scaledX, scaledY));
 
           return translatedCoords;
       }
       public static void GetClipboardFormat()
       {
           if (!OpenClipboard(IntPtr.Zero))
               return;
 
 
           if (IsClipboardFormatAvailable(CF_TEXT))
           {
               IntPtr hGlobal = GetClipboardData(CF_TEXT);
               string clipboardText = Marshal.PtrToStringUni(hGlobal);
               Marshal.FreeHGlobal(hGlobal);
           }
           else if (IsClipboardFormatAvailable(CF_BITMAP))
           {
               IntPtr hBitmap = GetClipboardData(CF_BITMAP);
               System.Drawing.Bitmap clipboardBitmap = System.Drawing.Bitmap.FromHbitmap(hBitmap);
               Marshal.FreeHGlobal(hBitmap);
           }
           else if (IsClipboardFormatAvailable(CF_UNICODETEXT))
           {
               IntPtr hGlobal = GetClipboardData(CF_UNICODETEXT);
               string clipboardText = Marshal.PtrToStringUni(hGlobal);
               Marshal.FreeHGlobal(hGlobal);
           }
 
           CloseClipboard();
       }
       private Point UnzoomedAndAdjusted(PictureBox pictureBox, Point scaledPoint)
       {
           // Calculate the zoom factor
           float zoomFactor = Math.Min(
               (float)pictureBox.ClientSize.Width / pictureBox.Image.Width,
               (float)pictureBox.ClientSize.Height / pictureBox.Image.Height);
 
           // Get the displayed rectangle of the image
           Rectangle displayedRect = GetImageDisplayRectangle(pictureBox);
 
           // Offset and unzoom the coordinates
           int translatedX = (int)((scaledPoint.X - displayedRect.X) / zoomFactor);
           int translatedY = (int)((scaledPoint.Y - displayedRect.Y) / zoomFactor);
 
           return new Point(translatedX, translatedY);
       }
 
       private Rectangle GetImageDisplayRectangle(PictureBox pictureBox)
       {
           if (pictureBox.SizeMode == PictureBoxSizeMode.Normal)
           {
               return new Rectangle(0, 0, pictureBox.Image.Width, pictureBox.Image.Height);
           }
           else if (pictureBox.SizeMode == PictureBoxSizeMode.StretchImage)
           {
               return pictureBox.ClientRectangle;
           }
           else
           {
               float zoomFactor = Math.Min(
                   (float)pictureBox.ClientSize.Width / pictureBox.Image.Width,
                   (float)pictureBox.ClientSize.Height / pictureBox.Image.Height);
 
               int imageWidth = (int)(pictureBox.Image.Width * zoomFactor);
               int imageHeight = (int)(pictureBox.Image.Height * zoomFactor);
 
               int imageX = (pictureBox.ClientSize.Width - imageWidth) / 2;
               int imageY = (pictureBox.ClientSize.Height - imageHeight) / 2;
 
               return new Rectangle(imageX, imageY, imageWidth, imageHeight);
           }
       }
       public static char GetModifiedKey(char c)
       {
           short vkKeyScanResult = VkKeyScan(c);
 
           // a result of -1 indicates no key translates to input character
           if (vkKeyScanResult == -1)
               return c;
 
           // vkKeyScanResult & 0xff is the base key, without any modifiers
           uint code = (uint)vkKeyScanResult & 0xff;
           // set shift key pressed
           byte[] b = new byte[256];
           b[0x10] = 0x80;
 
           uint r;
           // return value of 1 expected (1 character copied to r)
           if (1 != ToAscii(code, code, b, out r, 0))
               return c;
 
           return (char)r;
       }
 
       public static bool IsAlphaNumeric(char c)
       {
           return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '1' && c <= '9');
       }
       public void TriggerWndProc(ref Message m)
       {
           WndProc(ref m);
       }
 
       protected override void WndProc(ref Message m)
       {
           if (this.Image == null)
           {
               base.WndProc(ref m);
               return;
           }
           byte[] payload;
           switch (m.Msg)
           {
               case 0x0201: // WM_LBUTTONDOWN
               case 0x0202: // WM_LBUTTONUP
               case 0x0204: // WM_RBUTTONDOWN
               case 0x0205: // WM_RBUTTONUP
               case 0x0207: // WM_MBUTTONDOWN
               case 0x0208: // WM_MBUTTONUP
               case 0x0203: // WM_LBUTTONDBLCLK
               case 0x0206: // WM_RBUTTONDBLCLK
               case 0x0209: // WM_MBUTTONDBLCLK
               case 0x0200: // WM_MOUSEMOVE
               case 0x020A: // WM_MOUSEWHEEL
                   int x = (int)(m.LParam.ToInt32() & 0xFFFF);
                   int y = (int)((m.LParam.ToInt32() >> 16) & 0xFFFF);
                   Point newpoint = TranslateCoordinates(new Point(x, y), this.Image.Size, this);
                   x = newpoint.X;
                   y = newpoint.Y;
 
                   m.LParam = (IntPtr)((y << 16) | (x & 0xFFFF));
 
                   uint msg = (uint)m.Msg;
                   IntPtr wParam = m.WParam;
                   IntPtr lParam = m.LParam;
                   int Imsg = (int)msg;
                   int IwParam = (int)wParam;
                   int IlParam = (int)lParam;
                   Task.Run(async () =>
                   {
                       payload = client.sock.Concat(new byte[] { 3 }, client.sock.IntToBytes(Imsg));
                       payload = client.sock.Concat(payload, client.sock.IntToBytes(IwParam));
                       payload = client.sock.Concat(payload, client.sock.IntToBytes(IlParam));
                       await client.SendAsync(payload);
                   }).Wait();
                   break;
 
               case 0x0302: //WM_PASTE
                   break;
 
               case 0x0100: //WM_KEYDOWN
               case 0x0101: // WM_KEYUP
                   msg = (uint)m.Msg;
                   wParam = m.WParam;
                   lParam = m.LParam;
 
                   // Check if the Shift or Caps Lock key is pressed
                   bool isShiftPressed = (GetKeyState((int)Keys.ShiftKey) & 0x8000) != 0;
                   bool isCapsLockOn = Control.IsKeyLocked(Keys.CapsLock);
                   if (isShiftPressed || isCapsLockOn)
                   {
                       // Modify the wParam to include the SHIFT or CAPSLOCK flag
                       const int VK_SHIFT = 0x10;
                       const int VK_CAPITAL = 0x14;
 
                       if (wParam.ToInt32() == VK_SHIFT || wParam.ToInt32() == VK_CAPITAL)
                       {
                           // Skip processing SHIFT or CAPSLOCK key release
                           break;
                       }
 
                       if (isShiftPressed)
                       {
                           msg = 0x0102;
                           uint scanCode = (uint)((lParam.ToInt32() >> 16) & 0xFF);
                           byte[] keyboardState = new byte[256];
                           ToAscii((uint)wParam.ToInt32(), scanCode, keyboardState, out uint charCode, 0);
                           wParam = (IntPtr)Convert.ToInt32(GetModifiedKey((char)charCode));
                       }
 
                       if (isCapsLockOn)
                       {
                           uint scanCode = (uint)((lParam.ToInt32() >> 16) & 0xFF);
                           byte[] keyboardState = new byte[256];
                           ToAscii((uint)wParam.ToInt32(), scanCode, keyboardState, out uint charCode, 0);
                           if (IsAlphaNumeric((char)charCode))
                           {
                               msg = 0x0102;
                           }
                       }
                   }
                   Imsg = (int)msg;
                   IwParam = (int)wParam;
                   IlParam = (int)lParam;
                   Task.Run(async () =>
                   {
                       payload = client.sock.Concat(new byte[] { 3 }, client.sock.IntToBytes(Imsg));
                       payload = client.sock.Concat(payload, client.sock.IntToBytes(IwParam));
                       payload = client.sock.Concat(payload, client.sock.IntToBytes(IlParam));
                       await client.SendAsync(payload);
                   }).Wait();
                   break;
           }
           base.WndProc(ref m);
       }
 
   }

他创建了一个控件来解决这个问题
其实WPF中也可以用类似的创建控件的方法来解决
但是我们这里给出第二个方案

HwndHook

什么是HwndHook
HwndHook 是一个委托(Delegate),在 WPF (Windows Presentation Foundation) 中用于处理 Win32 消息。它定义了 HwndSourceHook 方法的签名,该方法可以被用来拦截和处理发送到 WPF 窗口的消息。HwndHook 委托允许开发者在 WPF 应用程序中自定义和扩展对 Win32 消息的处理

在 WPF 中,HwndSource 类代表一个 Win32 窗口句柄(HWND),并且提供了一个方法 AddHook,允许你将一个 HwndSourceHook 委托(即 HwndHook)添加到窗口消息队列中。这个委托会在每个 Win32 消息被分派到相应的 WPF 窗口之前被调用,从而允许开发者在消息到达目标窗口之前对其进行拦截和处理

结构如下

1
2
3
4
5
IntPtr HwndSourceHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled);
//hwnd 参数是窗口句柄
//msg 参数是消息的标识符
//wParam 和 lParam 参数是与消息相关的参数
//handled 参数是一个引用参数,用于指示消息是否已经被处理。如果设置为 true,则消息将不再被分派到窗口过程(WndProc)

HwndHook有什么用

拦截消息:在消息到达目标控件之前拦截它。
自定义消息处理:根据需要自定义消息的处理逻辑。
修改消息参数:在消息被处理之前修改 wParam 和 lParam 参数。
阻止消息传播:通过设置 handled 参数为 true 来阻止消息继续传播到默认的窗口过程。

为什么使用HwndHook
在C# WPF中,使用HwndHook(通过HwndSource.AddHook方法)来将消息转化为Window消息,是因为WPF框架本身并不直接处理Win32消息,而是通过一个叫做HwndSource的类来与Win32 API进行交互。
HwndSource提供了一个桥梁,使得WPF应用程序能够接收和处理Win32消息
另外他还可以免除自写控件的痛苦等等 总而言之就是节省了时间

如何使用HwndHook
这里只说对单个控件的HwndHook 因为全窗口的HwndHook网上教程还是比较多的
首先定义HwndSource

1
2
3
using System.Windows.Interop;//要引入这个
 
private HwndSource _hwndSource;

接下来写HOOK主体

1
2
3
4
private IntPtr HwndSourceHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled){
//..........................................   
 return IntPtr.Zero;
}

接下来就是在Loaded写加载HwndHook的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
private void InitializeHwndSource()
{
    try
    {
        // 获取名为 CapturedImage 的控件的 HwndSource
        PresentationSource presentationSource = PresentationSource.FromVisual(CapturedImage);
        _hwndSource = (HwndSource)presentationSource;
        if (_hwndSource != null)
        {
            _hwndSource.AddHook(HwndSourceHook);
        }
        else
        {
            throw new InvalidOperationException("Unable to find HwndSource for CapturedImage.");
            //System.Windows.Forms.MessageBox.Show("Unable to find HwndSource for CapturedImage.");
        }
    }
    catch (Exception ex)
    {
     //处理异常                 
    }
}

这里有个小坑需要注意一下
一般来说 有人喜欢在InitializeComponent();后直接就下HwndHook
这是不可行的
因为如果这样PresentationSource.FromVisual找不到对象
所以你要先等他Loaded
也就是

1
this.Loaded += (s, e) => InitializeHwndSource();//加载事件

最后回归主题 不需要多麻烦 直接抄袭别人的代码就行了 甚至不用更改多少

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
private IntPtr HwndSourceHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
    try
    {
        switch (msg)
        {
            case 0x0201: // WM_LBUTTONDOWN
            case 0x0202: // WM_LBUTTONUP
            case 0x0204: // WM_RBUTTONDOWN
            case 0x0205: // WM_RBUTTONUP
            case 0x0207: // WM_MBUTTONDOWN
            case 0x0208: // WM_MBUTTONUP
            case 0x0203: // WM_LBUTTONDBLCLK
            case 0x0206: // WM_RBUTTONDBLCLK
            case 0x0209: // WM_MBUTTONDBLCLK
            case 0x0200: // WM_MOUSEMOVE
            case 0x020A: // WM_MOUSEWHEEL
                if (EnabledMouse)
                {
                    int x = (int)(lParam.ToInt32() & 0xFFFF); // 提取 x 坐标
                    int y = (int)((lParam.ToInt32() >> 16) & 0xFFFF); // 提取 y 坐标
 
                    // 将屏幕坐标转换为相对于 CapturedImage 控件的坐标
                    Point p = new Point(
                        x * DeskTopSize.Width / CapturedImage.ActualWidth,
                        y * DeskTopSize.Height / CapturedImage.ActualHeight
                    );
                    lParam = (IntPtr)(((int)p.Y << 16) | ((int)p.X & 0xFFFF));
                    int Imsg = (int)msg;
                    int IwParam = (int)wParam;
                    int IlParam = (int)lParam;
 
                }
                break;
 
            case 0x0302: //WM_PASTE
                break;
 
            case 0x0100: //WM_KEYDOWN
            case 0x0101: // WM_KEYUP
                if (EnabledKeyBoard)
                {
                    // Check if the Shift or Caps Lock key is pressed
                    bool isShiftPressed = (GetKeyState((int)System.Windows.Forms.Keys.ShiftKey) & 0x8000) != 0;
                    bool isCapsLockOn = System.Windows.Forms.Control.IsKeyLocked(System.Windows.Forms.Keys.CapsLock);
                    if (isShiftPressed || isCapsLockOn)
                    {
                        // Modify the wParam to include the SHIFT or CAPSLOCK flag
                        const int VK_SHIFT = 0x10;
                        const int VK_CAPITAL = 0x14;
 
                        if (wParam.ToInt32() == VK_SHIFT || wParam.ToInt32() == VK_CAPITAL)
                        {
                            // Skip processing SHIFT or CAPSLOCK key release
                            break;
                        }
 
                        if (isShiftPressed)
                        {
                            msg = 0x0102;
                            uint scanCode = (uint)((lParam.ToInt32() >> 16) & 0xFF);
                            byte[] keyboardState = new byte[256];
                            ToAscii((uint)wParam.ToInt32(), scanCode, keyboardState, out uint charCode, 0);
                            wParam = (IntPtr)Convert.ToInt32(GetModifiedKey((char)charCode));
                        }
 
                        if (isCapsLockOn)
                        {
                            uint scanCode = (uint)((lParam.ToInt32() >> 16) & 0xFF);
                            byte[] keyboardState = new byte[256];
                            ToAscii((uint)wParam.ToInt32(), scanCode, keyboardState, out uint charCode, 0);
                            if (IsAlphaNumeric((char)charCode))
                            {
                                msg = 0x0102;
                            }
                        }
                    }
                    int Imsg = (int)msg;
                    int IwParam = (int)wParam;
                    int IlParam = (int)lParam;
   
                }
                break;
        }
    }
    catch(Exception e)
    {
    }
    return IntPtr.Zero;
}

本文仅供技术学习交流


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费
支持
分享
最新回复 (1)
雪    币: 9195
活跃值: (6410)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
2
.net yyds
5天前
0
游客
登录 | 注册 方可回帖
返回

账号登录
验证码登录

忘记密码?
没有账号?立即免费注册
//