Существуют не одна и не две программы, имитирующие нажатие комбинаций и последовательностей клавиш в других программах по глобальным "горячим" клавишам. Я сам писал такую для удобства игры в несколько окон, используя обычный посыл сообщений окну, но определенные игры (и тема эта тут уже поднималась) имеют защиту от такого простого вмешательства в управление клиентом. Сообщения им просто не приходят. Занимался ли кто-нибудь решением этой проблемы (например сообщения от экранной клавиатуры Windows XP проходят отлично), низкоуровневой имитацией нажатия клавиш (в особенности для неактивного приложения) или чем-то подобным? Есть ли какие-нибудь рекомендации, работающие программы, библиотеки?
Почитай в MSDN про фунцию keybd_event
Использование функции keybd_event, прямая посылка сообщения WM_KEYDOWN окну не имеют эффекта. Об этом и речь. С использованием таких средств программа уже написана и отлично работает.
очень низкий уровень правда только для 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это чудо собирать с помощью 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
Ritchie
>но определенные игры (и тема эта тут уже поднималась) имеют защиту от такого простого вмешательства в управление клиентом. Сообщения им просто не приходят.
Да это не защита, просто эти игры не обрабатывают сообщений WM_KEYDOWN/WM_KEYUP, а пользуются DirectInput.
м.. ну я конкретно в последних хрониках Lineage пытаюсь нажимать кнопки. когда я жму ф3 на клавиатуре в окно игры приходит сообщение WM_KEYDOWN, когда посылаю его программно - не приходит, когда жму мышью ф3 на экранной клавиатуре Windows - приходит и т.п. Когда запускаю клиент игры без GameGuard (на левых серверах) работает старая версия моей программы (через сообщения).
Если не найдешь как это победить, то предлагаю посылать твоей прогой сообщения нажатий мыши на виртуальную клавиатуру.
А она уже сама передаст что надо игре :)
Возможно :о) Вопрос пока вообще довольно сложный для моей квалификации, но пытаюсь разбираться :о) Библиотеки может какие попадутся. Клавиатурой управлять - легкий способ, правда сама же она как-то работает..
Но в связи с этим возникает второй вопрос, как перенаправлять ввод в другое, неактивное, приложение?
Тема в архиве.