欢迎来到 黑吧安全网 聚焦网络安全前沿资讯,精华内容,交流技术心得!

Windows 键盘记录器 part1:应用层方法

来源:本站整理 作者:佚名 时间:2017-07-11 TAG: 我要投稿

正如我们所知,键盘记录器是被恶意软件广泛使用的一种技术。本文是第一部分,我将列出一些Windows应用层常见的键盘记录的方式(不是全部)。
下面是本文提到的一些方法:
1. Windows钩子(SetWindowsHookEx)
2. Windows轮询(GetAsyncKeyState、GetKeyBoardState)
3. raw input
4. direct input
0x01 SetWindowsHookEx
这是最常见的技术。使用SetWindowsHookEx向Windows的消息钩子链中注册一个预定义的函数。有非常多的消息类型,其中两种用于键盘记录:

g_hHook = SetWindowsHookEx(m_bLowLevelKeyboard == true ? WH_KEYBOARD_LL : WH_KEYBOARD, m_bLowLevelKeyboard ? LowLevelKeyboardProc : KeyboardProc, g_hModule, m_ThreadId);
在回调函数中,我们将接收KeyboardProc的wParam中的虚拟键码和LowLevelKeyboardProc的KBDLLHOOKSTRUCT.vkCode(wParam指向KBDLLHOOKSTRUCT)。
如果m_ThreadId = 0,则消息钩子是全局消息钩子。针对全局消息钩子,你必须将回调函数置于dll中,并且需要编写2个dll来分别处理x86/x64进程。
针对底层键盘钩子,SetWindowsHookEx的HMod参数可以为NULL或者本进程加载的模块(我测试了user32,ntdll)。
WH_KEYBOARD_LL不需要dll中的回调函数,并且能适应x86/x64进程。
WH_KEYBOARD需要两个版本的dll,分别处理x86/x64。但是如果使用x86版本的全局消息钩子,所有的x64线程仍被标记为“hooked”,并且系统在钩子应用上下文执行钩子。类似的,如果是x64,所有的32位的进程将使用x64钩子应用的回调函数。这就是为什么安装钩子的线程必须要有一个消息循环。
0x02 GetAsuncKeyState
使用GetAsyncKeyState查询每个键的状态是一种经典的方法。这需要一个死循环来轮询键盘状态,这将导致CPU异常。

0x03 GetKeyboardState
和GetAsyncKeyState类似,GetKeyBoardState能一次得到所有的键的状态。不同的是当键盘消息从调用进程的消息队列中移除时,GetKeyBoardState只会改变状态。这意味着它不是全局钩子,除非我们使用AttachThreadInput函数来共享键盘状态。

0x04 Raw Input
微软原始输入介绍:

因此,使用原始输入,我们必须通过RegisterRawInputDevices()函数注册一个输入设备。在那之后,在消息循环中能通过WM_INPUT得到数据。下面是注册设备并获取数据的代码:
switch (message)
{
    case WM_CREATE:
    {
        if (lParam)
        {
            CREATESTRUCT* lpCreateStruct = (CREATESTRUCT*)lParam;
            if (lpCreateStruct->lpCreateParams)
                ::SetWindowLong(hWnd, GWL_USERDATA, reinterpret_cast(lpCreateStruct->lpCreateParams));
        }
        RAWINPUTDEVICE rid;
        // register interest in raw data
        rid.dwFlags = RIDEV_NOLEGACY | RIDEV_INPUTSINK; // ignore legacy messages and receive system wide keystrokes
        rid.usUsagePage = 1;                            // raw keyboard data only
        rid.usUsage = 6;
        rid.hwndTarget = hWnd;
        RegisterRawInputDevices(&rid, 1, sizeof(rid));
        break;
    }
    case WM_INPUT:
    {
        UINT dwSize;
        if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, &dwSize, sizeof(RAWINPUTHEADER)) == -1) {
            break;
        }
        LPBYTE lpb = new BYTE[dwSize];
        if (lpb == NULL) {
            break;
        }
        if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, lpb, &dwSize, sizeof(RAWINPUTHEADER)) != dwSize) {
            delete[] lpb;
            break;

[1] [2]  下一页

【声明】:黑吧安全网(http://www.myhack58.com)登载此文出于传递更多信息之目的,并不代表本站赞同其观点和对其真实性负责,仅适于网络安全技术爱好者学习研究使用,学习中请遵循国家相关法律法规。如有问题请联系我们,联系邮箱admin@myhack58.com,我们会在最短的时间内进行处理。
  • 最新更新
    • 相关阅读
      • 本类热门
        • 最近下载