Warning: WP_Syntax::substituteToken(): Argument #1 ($match) must be passed by reference, value given in
/www/wwwroot/fawdlstty.com/wp-content/plugins/wp-syntax/wp-syntax.php on line
383
Warning: WP_Syntax::substituteToken(): Argument #1 ($match) must be passed by reference, value given in
/www/wwwroot/fawdlstty.com/wp-content/plugins/wp-syntax/wp-syntax.php on line
383
模拟键盘是一个简单的话题,随便普通程序猿都能说出好多种方式。不同的方式应用于不同场合,总的来说分为三大类:
1、用户层模拟键盘
这个层来模拟是最方便的,但也是最容易无效的。总的来说有三种方式,第一种是直接往目标窗口发送按键消息;第二种是使用剪贴板复制待粘贴消息然后在目标窗口模拟Ctrl+V;第三种是用户层触发按键事件
这儿贴一个模拟输入一个字节的函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| bool input (char ch)
{
DWORD KeyScan = ::OemKeyScan (ch);
bool bShift = !!(KeyScan >> 16);
UINT uCode = KeyScan & 0xFFFF;
UINT uScanCode = ::MapVirtualKey (uCode, MAPVK_VK_TO_VSC);
UINT uShiftScanCode = ::MapVirtualKey (VK_SHIFT, MAPVK_VK_TO_VSC);
if (bShift)
{
::keybd_event (VK_SHIFT, (BYTE) uShiftScanCode, KEYEVENTF_EXTENDEDKEY, 0);
std::this_thread::sleep_for (std::chrono::milliseconds (20));
}
::keybd_event (uCode, (BYTE) uScanCode, KEYEVENTF_EXTENDEDKEY, 0);
std::this_thread::sleep_for (std::chrono::milliseconds (20));
::keybd_event (uCode, (BYTE) uScanCode, KEYEVENTF_KEYUP, 0);
std::this_thread::sleep_for (std::chrono::milliseconds (20));
if (bShift)
{
::keybd_event (VK_SHIFT, (BYTE) uShiftScanCode, KEYEVENTF_KEYUP, 0);
std::this_thread::sleep_for (std::chrono::milliseconds (20));
}
return true;
} |
bool input (char ch)
{
DWORD KeyScan = ::OemKeyScan (ch);
bool bShift = !!(KeyScan >> 16);
UINT uCode = KeyScan & 0xFFFF;
UINT uScanCode = ::MapVirtualKey (uCode, MAPVK_VK_TO_VSC);
UINT uShiftScanCode = ::MapVirtualKey (VK_SHIFT, MAPVK_VK_TO_VSC);
if (bShift)
{
::keybd_event (VK_SHIFT, (BYTE) uShiftScanCode, KEYEVENTF_EXTENDEDKEY, 0);
std::this_thread::sleep_for (std::chrono::milliseconds (20));
}
::keybd_event (uCode, (BYTE) uScanCode, KEYEVENTF_EXTENDEDKEY, 0);
std::this_thread::sleep_for (std::chrono::milliseconds (20));
::keybd_event (uCode, (BYTE) uScanCode, KEYEVENTF_KEYUP, 0);
std::this_thread::sleep_for (std::chrono::milliseconds (20));
if (bShift)
{
::keybd_event (VK_SHIFT, (BYTE) uShiftScanCode, KEYEVENTF_KEYUP, 0);
std::this_thread::sleep_for (std::chrono::milliseconds (20));
}
return true;
}
这个函数很可能在一些有简单安全措施的软件里面失效。软件屏蔽键盘模拟按键一般是使用钩子,那么可以dll注入然后反钩子,挂钩SetWindowsHookEx即可。注意反钩子必须和目标在同一个进程里,否则M$的Copy-On-Write会让反钩子失效。示例代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| BOOL func_unhook (LPVOID func_ptr, WORD param_num, DWORD ret_val)
{
// mov eax, 12345678h
// ret 0004h
BYTE bBuf [] = { '\xB8', '\x00', '\x00', '\x00', '\x00', '\xC2', '\x00', '\x00' };
*(DWORD*) (&bBuf [1]) = ret_val;
*(WORD*) (&bBuf [6]) = param_num * 4;
DWORD dw = 0, dw2 = 0;
BOOL bRet = ::VirtualProtect (func_ptr, 8, PAGE_EXECUTE_READWRITE, &dw);
if (!bRet)
return FALSE;
::memcpy (func_ptr, bBuf, 8);
if (func_ptr != ::VirtualProtect)
::VirtualProtect (func_ptr, 8, dw, &dw2);
return TRUE;
}
func_unhook (::SetWindowsHookA, 2, 1);
func_unhook (::SetWindowsHookW, 2, 1);
func_unhook (::UnhookWindowsHook, 2, 1);
func_unhook (::SetWindowsHookExA, 4, 1);
func_unhook (::SetWindowsHookExW, 4, 1);
func_unhook (::UnhookWindowsHookEx, 1, 1); |
BOOL func_unhook (LPVOID func_ptr, WORD param_num, DWORD ret_val)
{
// mov eax, 12345678h
// ret 0004h
BYTE bBuf [] = { '\xB8', '\x00', '\x00', '\x00', '\x00', '\xC2', '\x00', '\x00' };
*(DWORD*) (&bBuf [1]) = ret_val;
*(WORD*) (&bBuf [6]) = param_num * 4;
DWORD dw = 0, dw2 = 0;
BOOL bRet = ::VirtualProtect (func_ptr, 8, PAGE_EXECUTE_READWRITE, &dw);
if (!bRet)
return FALSE;
::memcpy (func_ptr, bBuf, 8);
if (func_ptr != ::VirtualProtect)
::VirtualProtect (func_ptr, 8, dw, &dw2);
return TRUE;
}
func_unhook (::SetWindowsHookA, 2, 1);
func_unhook (::SetWindowsHookW, 2, 1);
func_unhook (::UnhookWindowsHook, 2, 1);
func_unhook (::SetWindowsHookExA, 4, 1);
func_unhook (::SetWindowsHookExW, 4, 1);
func_unhook (::UnhookWindowsHookEx, 1, 1);
反钩子后模拟键盘事件差不多可以过绝大部分弱保护的安全措施了,不过这也不完全总是灵的。如果以上方法都不行,可以试试其他方案。
继续阅读C++:模拟键盘