ПрограммированиеФорумОбщее

Низкоуровневая имитация нажатия клавиши

#0
15:56, 28 мая 2008

Существуют не одна и не две программы, имитирующие нажатие комбинаций и последовательностей клавиш в других программах по глобальным "горячим" клавишам. Я сам писал такую для удобства игры в несколько окон, используя обычный посыл сообщений окну, но определенные игры (и тема эта тут уже поднималась) имеют защиту от такого простого вмешательства в управление клиентом. Сообщения им просто не приходят. Занимался ли кто-нибудь решением этой проблемы (например сообщения от экранной клавиатуры Windows XP проходят отлично), низкоуровневой имитацией нажатия клавиш (в особенности для неактивного приложения) или чем-то подобным? Есть ли какие-нибудь рекомендации, работающие программы, библиотеки?

#1
16:24, 28 мая 2008

Почитай в MSDN про фунцию keybd_event

#2
17:08, 28 мая 2008

Использование функции keybd_event, прямая посылка сообщения WM_KEYDOWN окну не имеют эффекта. Об этом и речь. С использованием таких средств программа уже написана и отлично работает.

#3
17:36, 28 мая 2008

очень низкий уровень правда только для ps/2 клав, не знаю поможет или нет :)
теорию читать на wasm.ru

.386
.model flat, stdcall
option casemap:none

include \masm32\include\w2k\ntstatus.inc
include \masm32\include\w2k\ntddk.inc
include \masm32\include\w2k\ntoskrnl.inc
includelib \masm32\lib\w2k\ntoskrnl.lib
include \masm32\Macros\Strings.mac
include common.inc

.const
CCOUNTED_UNICODE_STRING "\\Device\\r0kedrv", g_usDeviceName, 4
CCOUNTED_UNICODE_STRING "\\??\\r0kedrv", g_usSymbolicLinkName, 4
;CCOUNTED_UNICODE_STRING "\\DosDevices\\r0kedrv", g_usSymbolicLinkName, 4

.code

DispatchCreateClose proc pDeviceObject:PDEVICE_OBJECT, pIrp:PIRP

  ; CreateFile was called, to get driver handle
  ; CloseHandle was called, to close driver handle
  ; In both cases we are in user process context here

  mov eax, pIrp
  assume eax:ptr _IRP
  mov [eax].IoStatus.Status, STATUS_SUCCESS
  and [eax].IoStatus.Information, 0
  assume eax:nothing

  fastcall IofCompleteRequest, pIrp, IO_NO_INCREMENT

  mov eax, STATUS_SUCCESS
  ret

DispatchCreateClose endp

KbPs2Wait proc

  ; Wait until it's okay to send a command byte to the keyboard controller port.
TestCmdPort:
  in al, 64h
  test al, 2 ; Check cntrlr input buffer full flag.
  jnz TestCmdPort
  ret

KbPs2Wait endp

KbPs2Write proc
  
  ; Save scancode
  mov dl, al
  
  ; Wait until the keyboard controller does not contain data before
  ; proceeding with shoving stuff down its throat.
WaitWhileFull:
  in al, 64h
  test al, 1
  jnz WaitWhileFull
  
  ; Tell the keyboard controller to take the next byte
  ; sent to it and return it as a scan code.
  call KbPs2Wait
  mov al, 0d2h ; Return scan code command.
  out 64h, al

  ; Send the scan code.
  call KbPs2Wait
  mov al, dl
  out 60h, al
  ret

KbPs2Write endp

DispatchControl proc uses esi edi pDeviceObject:PDEVICE_OBJECT, pIrp:PIRP

  ; DeviceIoControl was called
  ; We are in user process context here

  local status:NTSTATUS
  local dwBytesReturned:DWORD

  and dwBytesReturned, 0

  mov esi, pIrp
  assume esi:ptr _IRP

  IoGetCurrentIrpStackLocation esi
  mov edi, eax
  assume edi:ptr IO_STACK_LOCATION

  .if [edi].Parameters.DeviceIoControl.IoControlCode == IOCTL_KB_PS2_WRITE

      mov edi, [esi].AssociatedIrp.SystemBuffer
      assume edi:ptr BYTE

      xor ebx, ebx
      xor ecx, ecx
      mov cl, [edi]
      
      .while( ebx < ecx )
        inc ebx
        mov al, [edi][ebx*(sizeof BYTE)]
        call KbPs2Write
      .endw
      
      mov status, STATUS_SUCCESS
  .else
    mov status, STATUS_INVALID_DEVICE_REQUEST
  .endif

  assume edi:nothing

  push status
  pop [esi].IoStatus.Status

  push dwBytesReturned
  pop [esi].IoStatus.Information

  assume esi:nothing

  fastcall IofCompleteRequest, esi, IO_NO_INCREMENT

  mov eax, status
  ret

DispatchControl endp

DriverUnload proc pDriverObject:PDRIVER_OBJECT

  ; ControlService,,SERVICE_CONTROL_STOP was called
  ; We are in System process (pid = 8) context here

  invoke IoDeleteSymbolicLink, addr g_usSymbolicLinkName

  mov eax, pDriverObject
  invoke IoDeleteDevice, (DRIVER_OBJECT PTR [eax]).DeviceObject

  ret

DriverUnload endp

.code INIT

DriverEntry proc pDriverObject:PDRIVER_OBJECT, pusRegistryPath:PUNICODE_STRING

  ; StartService was called
  ; We are in System process (pid = 8) context here

  local status:NTSTATUS
  local pDeviceObject:PDEVICE_OBJECT

  mov status, STATUS_DEVICE_CONFIGURATION_ERROR

  invoke IoCreateDevice, pDriverObject, 0, addr g_usDeviceName, FILE_DEVICE_UNKNOWN, 0, FALSE, addr pDeviceObject
  .if eax == STATUS_SUCCESS
    invoke IoCreateSymbolicLink, addr g_usSymbolicLinkName, addr g_usDeviceName
    .if eax == STATUS_SUCCESS
      mov eax, pDriverObject
      assume eax:ptr DRIVER_OBJECT
      mov [eax].MajorFunction[IRP_MJ_CREATE*(sizeof PVOID)],      offset DispatchCreateClose
      mov [eax].MajorFunction[IRP_MJ_CLOSE*(sizeof PVOID)],      offset DispatchCreateClose
      mov [eax].MajorFunction[IRP_MJ_DEVICE_CONTROL*(sizeof PVOID)],  offset DispatchControl
      mov [eax].DriverUnload,                      offset DriverUnload
      assume eax:nothing
      mov status, STATUS_SUCCESS
    .else
      invoke IoDeleteDevice, pDeviceObject
    .endif
  .endif

  mov eax, status
  ret

DriverEntry endp

end DriverEntry

.386
.model flat, stdcall
option casemap:none

include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\user32.inc
include \masm32\include\advapi32.inc
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\user32.lib
includelib \masm32\lib\advapi32.lib
include \masm32\include\winioctl.inc
include \masm32\Macros\Strings.mac
include common.inc

.const

.data

.data?

.code

start proc uses esi edi

  local hSCManager:HANDLE
  local hService:HANDLE
  local acModulePath[MAX_PATH]:CHAR
  local _ss:SERVICE_STATUS
  local hDevice:HANDLE

  local abyScanCodes[7]:BYTE
  local dwBytesReturned:DWORD

  lea esi, abyScanCodes
  assume esi:ptr BYTE
  mov [esi][0*(sizeof BYTE)], 6
  mov [esi][1*(sizeof BYTE)], 01eh
  mov [esi][2*(sizeof BYTE)], 09eh
  mov [esi][3*(sizeof BYTE)], 01eh
  mov [esi][4*(sizeof BYTE)], 09eh
  mov [esi][5*(sizeof BYTE)], 01eh
  mov [esi][6*(sizeof BYTE)], 09eh
  assume esi:nothing

  ; Open a handle to the SC Manager database
  invoke OpenSCManager, NULL, NULL, SC_MANAGER_ALL_ACCESS
  .if eax != NULL
    mov hSCManager, eax

    push eax
    invoke GetFullPathName, $CTA0("r0kedrv.sys"), sizeof acModulePath, addr acModulePath, esp
      pop eax

    ; Install service
    invoke CreateService, hSCManager, $CTA0("r0kedrv"), $CTA0("ring0 keyboard emulator"), \
      SERVICE_START + SERVICE_STOP + DELETE, SERVICE_KERNEL_DRIVER, SERVICE_DEMAND_START, \
      SERVICE_ERROR_IGNORE, addr acModulePath, NULL, NULL, NULL, NULL, NULL

    .if eax != NULL
      mov hService, eax

      ; Driver's DriverEntry procedure will be called
      invoke StartService, hService, 0, NULL
      .if eax != 0

        ; Driver will receive I/O request packet (IRP) of type IRP_MJ_CREATE
        invoke CreateFile, $CTA0("\\\\.\\r0kedrv"), GENERIC_READ + GENERIC_WRITE, \
          0, NULL, OPEN_EXISTING, 0, NULL

        .if eax != INVALID_HANDLE_VALUE
          mov hDevice, eax

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

          ; Driver will receive IRP of type IRP_MJ_DEVICE_CONTROL
          invoke DeviceIoControl, hDevice, IOCTL_KB_PS2_WRITE, \ 
            addr abyScanCodes, sizeof abyScanCodes, \
            NULL, 0, addr dwBytesReturned, NULL

          .if ( eax == 0 )
            invoke MessageBox, NULL, $CTA0("Can't send scancodes to device."), NULL, MB_OK + MB_ICONSTOP
          ;.else
            ;invoke MessageBox, NULL, $CTA0("Success."), NULL, MB_OK
            ;invoke Sleep, 5000
          .endif

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

          ; Driver will receive IRP of type IRP_MJ_CLOSE
          invoke CloseHandle, hDevice
        .else
          invoke MessageBox, NULL, $CTA0("Device is not present."), NULL, MB_OK + MB_ICONSTOP
        .endif
        ; DriverUnload proc in our driver will be called
        invoke ControlService, hService, SERVICE_CONTROL_STOP, addr _ss
      .else
        invoke MessageBox, NULL, $CTA0("Can't start driver."), NULL, MB_OK + MB_ICONSTOP
      .endif
      invoke DeleteService, hService
      invoke CloseServiceHandle, hService
    .else
      invoke MessageBox, NULL, $CTA0("Can't register driver."), NULL, MB_OK + MB_ICONSTOP
    .endif
    invoke CloseServiceHandle, hSCManager
  .else
    invoke MessageBox, NULL, $CTA0("Can't connect to Service Control Manager."), NULL, MB_OK + MB_ICONSTOP
  .endif

  invoke ExitProcess, 0

start endp

end start
#4
17:39, 28 мая 2008

это чудо собирать с помощью masm

@echo off

set target=r0kedrv
\masm32\bin\ml /nologo /c /coff %target%.asm
\masm32\bin\link /nologo /driver /base:0x10000 /align:32 /out:%target%.sys /subsystem:native /ignore:4078 %target%.obj
del %target%.obj

set target=r0ke
\masm32\bin\ml /nologo /c /coff %target%.asm
\masm32\bin\link /nologo /subsystem:windows /ignore:4078 %target%.obj
del %target%.obj

echo.
pause
#5
19:34, 28 мая 2008

Ritchie
>но определенные игры (и тема эта тут уже поднималась) имеют защиту от такого простого вмешательства в управление клиентом. Сообщения им просто не приходят.
Да это не защита, просто эти игры не обрабатывают сообщений WM_KEYDOWN/WM_KEYUP, а пользуются DirectInput.

#6
20:26, 28 мая 2008

м.. ну я конкретно в последних хрониках Lineage пытаюсь нажимать кнопки. когда я жму ф3 на клавиатуре в окно игры приходит сообщение WM_KEYDOWN, когда посылаю его программно - не приходит, когда жму мышью ф3 на экранной клавиатуре Windows - приходит и т.п. Когда запускаю клиент игры без GameGuard (на левых серверах) работает старая версия моей программы (через сообщения).

#7
12:01, 29 мая 2008

Если не найдешь как это победить, то предлагаю посылать твоей прогой сообщения нажатий мыши на виртуальную клавиатуру.
А она уже сама передаст что надо игре :)

#8
13:35, 29 мая 2008

Возможно :о) Вопрос пока вообще довольно сложный для моей квалификации, но пытаюсь разбираться :о) Библиотеки может какие попадутся. Клавиатурой управлять - легкий способ, правда сама же она как-то работает..

Но в связи с этим возникает второй вопрос, как перенаправлять ввод в другое, неактивное, приложение?

ПрограммированиеФорумОбщее

Тема в архиве.