现在的位置: 首页 > 综合 > 正文

用底层键盘钩子改变键盘布局

2013年09月20日 ⁄ 综合 ⁄ 共 5608字 ⁄ 字号 评论关闭

from http://zerray.com/

 

看win32汇编看到钩子部分,突发奇想,打算写一个改变键盘布局的整人程序

查了查资料,发现底层键盘钩子(WH_KEYBOARD_LL)可以实现。首先是安装和卸载钩子:

InstallHook proc hIns: DWORD
    invoke SetWindowsHookEx, WH_KEYBOARD_LL, addr KeyProc, hIns, NULL
    mov hHook, eax
    ret
InstallHook endp

UninstallHook proc
    invoke UnhookWindowsHookEx, hHook
    ret
UninstallHook endp

这些与别的钩子一样,在回调函数 KeyProc 中 nCode 值为 HC_ACTION,wParam 为按键消息(WM_KEYDOWN, WM_KEYUP, WM_SYSKEYDOWN, WM_SYSKEYUP),lParam 为指向 KBDLLHOOKSTRUCT 结构的指针。

首先,我想捕获按键x,为了知道是否捕获到了,我让程序在有x按下时弹出一个对话框。

KeyProc proc nCode: DWORD, wParam: WPARAM, lParam: LPARAM
    .IF nCode == HC_ACTION
 .IF (wParam == WM_KEYDOWN)
            mov edx, lParam
            assume edx: PTR KBDLLHOOKSTRUCT
            .IF ([edx].vkCode == VK_X)
                invoke MessageBox, NULL, addr szMsg, addr szTit, MB_OK
            .ENDIF
        .ENDIF
    .ENDIF
    invoke CallNextHookEx, hHook, nCode, wParam, lParam
    ret
KeyProc endp

程序很简单,当有x按下时弹出一个对话框,按一下试试,OK!看来我们已经捕获到按键了。

那现在要怎样才能把x按键改成其它的按键呢?先试试其他程序是不是在我们的钩子函数之后处理按键消息:把 KeyProc 中的 invoke 一句改成 ret。运行程序,按x,呵呵,x键被屏蔽了,看来钩子函数处理按键消息的优先级是很高的。那把这个消息改成别的按键传下去就能起到改变按键的作用了吧?试试看喽!

KeyProc proc nCode: DWORD, wParam: WPARAM, lParam: LPARAM
    .IF nCode == HC_ACTION
 .IF (wParam == WM_KEYDOWN)
            mov edx, lParam
            assume edx: PTR KBDLLHOOKSTRUCT
            .IF ([edx].vkCode == VK_X)
                mov [edx].vkCode, VK_Y
            .ENDIF
        .ENDIF
    .ENDIF
    invoke CallNextHookEx, hHook, nCode, wParam, lParam
    ret
KeyProc endp

我把改 vkCode 改成了 VK_Y,现在其它程序得到的消息应该是按键y了吧?运行,按x,奇怪,还是x??为什么啊?难道 scanCode 也要改??查了一下 VK_Y 的 scanCode ,也改了,还是不行。难道我们的程序没有修改那段内存的权限?提升程序的权限?太麻烦了,还是想想别的办法吧!既然现在能够屏蔽掉一个按键了,那我们不是可以再用 keybd_event 模拟按键吗?对,就这么干!

我们现在屏蔽x和y按键,然后在x按键时模拟y按键,在y按键时模拟x按键,这样就调换了x和y的键盘位置。

KeyProc proc nCode: DWORD, wParam: WPARAM, lParam: LPARAM
    .IF nCode == HC_ACTION
        .IF (wParam == WM_KEYDOWN)
            mov edx, lParam
            assume edx: PTR KBDLLHOOKSTRUCT
            .IF ([edx].vkCode == VK_X)
                invoke keybd_event, VK_Y, 0, 0, 0
                ret
            .ELSEIF ([edx].vkCode == VK_Y)
                invoke keybd_event, VK_X, 0, 0, 0
                ret
            .ENDIF
        .ENDIF
    .ENDIF
    invoke CallNextHookEx, hHook, nCode, wParam, lParam
    ret
KeyProc endp

这回应该没问题了吧?运行一下试试!哦,NO,又出问题了,按下x或y后,x和y不停的交替出现,消息循环了?是啊,连用 keybd_event 模拟的按键都被底层键盘钩子捕获了!怎么办呢?其实多判断一下 scanCode 就好了,模拟的按键和实际的按键 scanCode 是不同的。好了,现在问题完全解决了!在加上判断 WM_SYSKEYDOWN 的判断,这样就换的更彻底了。下面是完整的程序:

.386
.model flat, stdcall
option casemap: none

include /masm32/include/windows.inc
include /masm32/include/kernel32.inc
include /masm32/include/user32.inc
includelib /masm32/lib/kernel32.lib
includelib /masm32/lib/user32.lib

WinMain proto :DWORD, :DWORD, :DWORD, :DWORD
InstallHook proto :DWORD
UninstallHook proto

HotKeyID1 equ 1
HotKeyID2 equ 2

KBDLLHOOKSTRUCT STRUCT
    vkCode DWORD ?
    scanCode DWORD ?
    flags DWORD ?
    time DWORD ?
    dwExtraInfo ULONG ?
KBDLLHOOKSTRUCT ENDS

.const
    ClassName db 'HookxClass', 0
    AppName db 'Hookx', 0
    Running db 'i am running...', 0

.data?
    inst dd ?
    hHook dd ?
    cmd dd ?

.code

start:
    invoke GetModuleHandle, NULL
    mov inst, eax
    invoke GetCommandLine
    mov cmd, eax
    invoke WinMain, inst, NULL, cmd, SW_HIDE ;整人的程序,当然不能把窗口显示出来了!
    invoke ExitProcess,eax

WinMain proc hInst:HINSTANCE, hPrevInst:HINSTANCE, CmdLine:LPSTR, CmdShow:DWORD
    LOCAL wc:WNDCLASSEX
    LOCAL msg:MSG
    LOCAL hwnd:HWND

    mov wc.cbSize, SIZEOF WNDCLASSEX
    mov wc.style, CS_HREDRAW or CS_VREDRAW
    mov wc.lpfnWndProc, OFFSET WndProc
    mov wc.cbClsExtra, NULL
    mov wc.cbWndExtra, NULL
    push hInst
    pop wc.hInstance
    mov wc.hbrBackground, COLOR_WINDOW + 1
    mov wc.lpszMenuName, NULL
    mov wc.lpszClassName, OFFSET ClassName
    invoke LoadIcon, NULL, IDI_APPLICATION
    mov wc.hIcon, eax
    mov wc.hIconSm, eax
    invoke LoadCursor, NULL, IDC_ARROW
    mov wc.hCursor, eax
    invoke RegisterClassEx, addr wc
    invoke CreateWindowEx, NULL,/
           addr ClassName,/
           addr AppName,/
           WS_OVERLAPPEDWINDOW,/
           CW_USEDEFAULT,/
           CW_USEDEFAULT,/
           CW_USEDEFAULT,/
           CW_USEDEFAULT,/
           NULL,/
           NULL,/
           hInst,/
           NULL
    mov hwnd, eax
    invoke ShowWindow, hwnd, CmdShow
    invoke UpdateWindow, hwnd

    .WHILE TRUE
        invoke GetMessage, addr msg, NULL, 0, 0
        .BREAK .IF (!eax)
        invoke TranslateMessage, addr msg
        invoke DispatchMessage, addr msg
    .ENDW
    mov eax, msg.wParam
    ret
WinMain endp

WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
    .IF uMsg == WM_CREATE
        invoke InstallHook, inst
        invoke RegisterHotKey, hWnd, HotKeyID1, MOD_ALT, VK_8 ;注册两个快捷键用来查看程序时候在运行和关闭程序
        invoke RegisterHotKey, hWnd, HotKeyID2, MOD_ALT, VK_9
    .ELSEIF uMsg == WM_HOTKEY
        .IF wParam == HotKeyID1
            invoke MessageBox, NULL, addr Running, addr AppName, MB_OK
        .ELSEIF wParam == HotKeyID2
            invoke UninstallHook
            invoke PostMessage, hWnd, WM_DESTROY, NULL, NULL
        .ENDIF
    .ELSEIF uMsg == WM_DESTROY
        invoke UnregisterHotKey, hWnd, HotKeyID2
        invoke UnregisterHotKey, hWnd, HotKeyID1
        invoke UninstallHook
        invoke PostQuitMessage, NULL
    .ELSE
        invoke DefWindowProc, hWnd, uMsg, wParam, lParam
        ret
    .ENDIF
    xor eax, eax
    ret
WndProc endp

KeyProc proc nCode: DWORD, wParam: WPARAM, lParam: LPARAM
    .IF nCode == HC_ACTION
        .IF (wParam == WM_KEYDOWN) || (wParam == WM_SYSKEYDOWN)
            mov edx, lParam
            assume edx: PTR KBDLLHOOKSTRUCT
            .IF ([edx].vkCode == VK_X) && ([edx].scanCode != 0)
                invoke keybd_event, VK_Y, 0, 0, 0
                ret
            .ELSEIF ([edx].vkCode == VK_Y) && ([edx].scanCode != 0)
                invoke keybd_event, VK_X, 0, 0, 0
                ret
            .ENDIF
        .ENDIF
    .ENDIF
    invoke CallNextHookEx, hHook, nCode, wParam, lParam
    ret
KeyProc endp

InstallHook proc hIns: DWORD
    invoke SetWindowsHookEx, WH_KEYBOARD_LL, addr KeyProc, hIns, NULL
    mov hHook, eax
    ret
InstallHook endp

UninstallHook proc
    invoke UnhookWindowsHookEx, hHook
    ret
UninstallHook endp

end start

现在我可以把这个程序偷偷的放到同学的机器上去运行了
,咦?我的键盘怎么了?

抱歉!评论已关闭.