Página 1 de 1

Detectar envío simulado de tecla

Publicado: 19 Ago 2010, 20:29
por Jonny
Hola

¿Sabeis como puedo detectar la pulsación de una tecla simulada (enviada con la función Send())?

La verdad, es que no se si tanto si se le envía a la aplicación con Send() como si se pulsa realmente será lo mismo...

Lo que quiero es, saber cuando la aplicación recibe un mensaje conforme se ha pulsado una tecla, para luego con la api GetKeyState o la función IsPressed de autoit, comprobar si se ha pulsado realmente, o fue una simulación de esta, para si es el segundo caso, no hacer nada, mientras que si es el primero realizar la acción que corresponda.

Imagino, que tendré que hacerlo con la función GuiRegisterMsg(), pero no entiendo muy bien los parámetros de la función a la que llama.

En la ayuda dice, que el tercero y el cuarto, son el primer y segundo parámetros del mensaje
¿qué quiere decir esto?
Y ¿Como los obtengo como string? Porque en hexadecimal de poco me sirven :), siempre obtengo lo mismo, algo como esto:
- 0x00000000
- 0x00000000
Correspondientes al tercer y cuarto parámetro de la función que invoca GuiRegisterMsg(), respectivamente.

Lo que quiero es, hacer una especie de anti boot, para que no pueda usarse el programa con software de automatización...

¡Acias!

Salu2!

Re: Detectar envío simulado de tecla

Publicado: 20 Ago 2010, 08:53
por Ximorro
Pues como no mires por la webcam si está pulsando el teclado... :smt005 :smt005

Me parece que será prácticamente indistinguible, digo "prácticamente" porque no sé si algún método verá la diferencia, pero lo normal es que no. Digamos que estoy seguro al 98%.

Send() funciona enviando mensajes, que es como los programas detectan esos cambios, que yo sepa es indistinguible saber si el mensaje lo provoca el driver del teclado o un programa "tramposo"...

Que sepas que IsPressed y GetKeyState son básicamente lo mismo, en realidad IsPressed usa la versión asíncrona, GetAsyncKeyState, pero es prácticamente lo mismo.

Vaya, he hecho algunas pruebas y con estas funciones no veo diferencia entre apretar yo o mandar un Send...

Y lo mismo con los mensajes, aunque no es fácil capturar mensajes como WM_KEYDOWN en AutoIt, en una entrada reciente lo discutíamos y no sabemos si es un bug o una limitación de diseño de AutoIt, pero sólo se pueden capturar mensajes de teclas en tu GUI... ¡si no tiene controles!, lo que lo hace bastante inútil.

No sé qué mensaje estás capturando, es imposible saber qué representan los parámetros si no dices qué mensaje es. Cada mensaje es un mundo, tienes que mirar la MSDN a ver qué manda o recibe en los mismos.
Y probablemente no tiene sentido pasar a String esos valores, prácticamente todos los mensajes que me he encontrado trabajan con valores numéricos, si lo pasas a String, pues es como pasar el número 234 a la cadena "234", eso no ayuda mucho...
Es más, esos dos parámetros SIEMPRE son enteros, otra cosa es que realmente sean por ejemplo un puntero a una estructura más compleja, que podría ser un String, pero no me suena haber visto algo así...

Un tema interesante, si averiguas cómo distinguir las pulsaciones virtuales no te olvides de postearlo.
Por cierto, creo que si fuera fácil los juegos que prohíben bots no permitirían teclas virtuales.

Hombre, hay otras soluciones más de "ingeniería social", si ves que las teclas presionadas siempre duran presionadas el mismo tiempo (por ejemplo 5ms, como pasará en AutoIT con el valor por defecto de SendKeyDownDelay) y que al enviar varias teclas seguidas entre tecla y tecla hay siempre la misma espera (otros 5ms en AutoIT por SendKeyDelay) entonces evidentemente las teclas las está enviando un programa, un humano no puede ser tan regular.
Esa es una solución interesante, naturalmente si consiguen averiguar que haces eso se puede modificar el programa para que simule un tecleo humano, pero a la inmensa mayoría de los programas los cazas. :smt003

Re: Detectar envío simulado de tecla

Publicado: 20 Ago 2010, 17:49
por Jonny
Hola

Vaya, para empezar, algo hacía mal... Intentaba capturar $WM_USER y tengo que capturar $WM_KEYDOWN en GuiRegisterMsg().

He estado mirando el apéndice de mensajes de windows en la ayuda de autoit, pero no hay explicación de qué es cada uno, aunque más o menos se entiende que son...

Por lo que he podido leer hasta ahora, esta forma de detectar si la pulsación es virtual o física, podría ser efectiva:
[ul] http://winapi.conclase.net/curso/?cap=034 [/ul]

Por lo que pone ahí, parece que podría comprobarse si se trata de una pulsación física, en el parámetro lParam, pues entre otras cosas, da información sobre el código de escaneo, que es relativo a la capa física del teclado.
No lo pone en esa página, pero imagino, que si no se ha pulsado la tecla físicamente, valdrá 0 o algún valor de error..., y con eso podría hacerse la distinción entre tecla simulada y pulsada.
Pero si como dices y parece que es, no funciona, se va a complicar bastante el tema.

He hecho algo como esto:

Código: Seleccionar todo

;Código original de Chefito:
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <SendMessage.au3>

Local $hGUI, $nButton2, $GUIMsg

$hGUI = GUICreate("MI GUI", 300, 200)

$nButton2 = GUICtrlCreateButton("SENDMESSAGE", 90, 110, 120, 30)

GUIRegisterMsg($WM_KEYDOWN, "WM_KEYDOWN")

GUISetState()

While 1
$GUIMsg = GUIGetMsg()
Switch $GUIMsg
Case $GUI_EVENT_CLOSE
ExitLoop
Case $nButton2
;_SendMessage($hGUI, $WM_USER, -1, 0)
Send("{enter}")

EndSwitch
WEnd

Func WM_KEYDOWN($hWnd, $Msg, $wParam, $lParam)
ConsoleWrite($wParam ","& $lparam & @cr)
Return $GUI_RUNDEFMSG
EndFunc
que debería recibir la tecla enter al pulsar sobre el botón, y nada, ¡ni se inmuta!.
Si quitáramos

Código: Seleccionar todo

Send("{enter}")
para que esperara que se pulsara cualquier tecla, tampoco funcionaría al pulsar una tecla cualquiera.

Efectivamente, tienes razón; al quitar el botón de la gui, el mismo código (anulando el case que comprueba si fue pulsado y el envío de la tecla enter), funciona perfecto.
Al pulsar la barra espaciadora obtengo:
$wParam=0x00000020
y
$lParam=0x00390001
Lo qué, como bien dice la página que puse más arriba, $wParam devuelve el código virtual de la tecla pulsada (el espacio tiene asignado el 20) como podemos ver en lo que he obtenido al pulsarlo.
Faltaría pues, entender $lParam, que va a ser más difícil.
Según la web que puse arriba, puede hacerse una estructura para obtener más fácilmente los datos:
Podemos crear un campo de bits para tratar estos datos más fácilmente:

Código: Seleccionar todo

typedef union {
   struct {
      unsigned int repeticion:16;
      unsigned int scan:8;
      unsigned int extendida:1;
      unsigned int reservado:4;
      unsigned int contexto:1;
      unsigned int previo:1;
      unsigned int transicion:1;
   };
   unsigned int lParam;
} keyData;
De C ando pegadísimo, pero supongo que esto es una estructura que podría hacerse en autoit con DllStructCreate()...
De todas formas, no se como seguir a partir de ahí, una vez creada la estructura, habría que ver el ejemplo de esa página.

Puf, que lástima que ahora no funcione la página inglesa de AutoIt, dicen que están haciendo backup y mantenimiento y no se puede acceder al foro, para buscar algo de todo esto.

Para solucionar el bug (creo que lo es) de que WM_KEYDOWN no funciona habiendo controles en la gui se me ocurre una cosilla, que voy a probar y os cuento ¡a ver si puedo daros una sorpresa! :).

--------

Se trataba de crear una ventana padre oculta sin controles, que recibiera los mensajes del teclado y pasárselos a una ventana hija que tuviera los controles, pero no puede funcionar, porque el mensaje se envía a la ventana activa...

Quizá, con una ventana padre transparente o algo así, pero la chapuza es descomunal :).

Salu2!

Re: Detectar envío simulado de tecla

Publicado: 20 Ago 2010, 19:15
por Jonny
Aquí os dejo una página (¡para los más atrevidos!), donde explican lo más oscuro del teclado, jeje.

[ul]http://www.zator.com/Hardware/H7_1_1.htm[/ul]

Explican lo que decía en el post anterior, de los códigos de escaneo (scan codes). Yo desde luego, me he quedado igual, pensando en como implementar todo eso en AutoIt, pero quizá alguna de las máquinas de AutoIt de por aquí se les ocurra algo...
Mientras yo, sigo ideando como hacerlo..

--------

He pensado en probar con la api GetMessage, para recibir directamente el mensaje del teclado, por si así funcionara WM_KEYDOWN.
Mirando esta url:

http://winapi.conclase.net/curso/?winfu ... age#inicio

He hecho esto:

Código: Seleccionar todo

$GetMsg=DllCall("User32", "bool", "GetMessage", "ptr", DllStructGetPtr(DllStructCreate("HWND hwnd;UINT message;WPARAM wParam;LPARAM lParam;DWORD time;POINT pt")), "hwnd", $hGUI, "int", 0, "int", 0)
y algo no tiene que estar bien (sospecho que la estructura, que la he copiado de la misma URL que he puesto más arriba, en el link "MSG" del primer parámetro), porque al ejecutar el código, el autoit peta con lo de los informes de errores. Eso, o no soporta esta api, porque lo demás de DllCall() diría que está bien.

--------

Bueno, bueno... Con el fin de semana por medio (que bueno es descansar), me he puesto hace sólo un poco a seguir mirando esto y he visto por qué petaba el autoit al llamar a "GetMessage".

En la página que puse arriba, no especificaba como había que pasar los parámetros (por valor o por referencia). Mirando por casualidad el Api Viewer, he visto que el primer parámetro (el buffer donde se almacenarán los parámetros que se reciban) hay que pasarlo por referencia, era lo único que hacía mal:

Código: Seleccionar todo

Global $BuffMessages=DllStructCreate("HWND hwnd;UINT message;WPARAM wParam;LPARAM lParam;DWORD time;POINT pt")
Global $GetMsg

$GetMsg=DllCall("User32", "long", "GetMessage", "ptr*", DllStructGetPtr($BuffMessages), "hwnd", $hGUI, "int", 0, "int", 0)

Msgbox(0, "", DllStructGetData($BuffMessages, "wParam"))
(No pongo todo el código de la gui para no hacer el post más largo todavía, y porque ya está más arriba)

¡Yea! ¡que arte con las apis! :)
si me dicen hace seis meses que las iba a empezar a entender...

Imagino que habré hecho bien lo de leer el parámetro "wParam" de la estructura, y que la estructura esté bien.

Errores no da el AutoIt, aunque al leer "wParam" como en el código anterior, siempre obtengo 0, aún pulsando teclas. Así, que habrá que trabajar más esto, a ver que le pasa, pero parece que es la solución para recibir mensajes del teclado en una gui con controles ¿no?

Salu2!

Re: Detectar envío simulado de tecla

Publicado: 23 Ago 2010, 09:56
por Ximorro
Me parece que va a ser un tema complicado lo de capturar WM_KEYDOWN, lo hemos comentado en un par de sitios, ahora no encuentro el otro pero aquí sale el tema:
http://www.emesn.com/autoitforum/viewto ... =12&t=2258

Lo de tener un GUI oculto sospecho que no va a servir porque el mensaje se manda a la ventana activa, si tengo activa la ventana visible, la oculta no capturará el teclado...

Creo que tienes un poco de lío con lo de "teclas virtuales", no se refiere a teclas pulsadas virtualmente, si no que se dan códigos virtuales a las teclas físicas del teclado, o sea, que se les asigna numeritos para trabajar con ellos... Con WM_KEYDOWN recibirás estos códigos virtuales, ya sea porque viene del teclado o porque se ha hecho un Send, así que me parece que lo de winapi.conclase.net, aunque es muy interesante, no servirá para distinguir teclas pulsadas por software.
Y por cierto, con DllStruct me temo que no se pueden definir campos de bits o uniones de C. Para empezar no se pueden definir uniones, los datos de una DllStruct no están superpuestos si no necesariamente uno después de otro, pero es que tampoco vas a poder acceder así a los bits o grupos arbitrarios de los mismos, pues como se puede ver en la ayuda de DllStructCreate , el tamaño mínimo que se puede definir es de un byte, o sea, ocho bits...

Supongo que si hay solución a lo de separar teclas físicas de los keycodes y los mensajes que te los dan, será acceder directamente al hardware. Quizás eso era más fácil en la vieja época de los "compatible IBM", tal como te explica en el artículo de zator.com, no sé cómo estará ahora el tema. Por un lado las controladoras pueden haber cambiado, por otro es fácil que el Windows te impida tocar directamente el hardware... Pero creo que es la manera de atacar el problema, no se me ocurre otra si quieres ver si se presiona el teclado realmente, tendrás que preguntárselo a él, su controladora, su driver directo...

Buen intento con GetMessage, pero al fin y al cabo tiene el mismo problema que leer el mensaje con GUIRegisterMsg, en GUIs con controles no funciona... :smt012

Re: Detectar envío simulado de tecla

Publicado: 23 Ago 2010, 16:21
por Jonny
Hola
Me parece que va a ser un tema complicado lo de capturar WM_KEYDOWN, lo hemos comentado en un par de sitios, ahora no encuentro el otro pero aquí sale el tema:
...
Lo vi el viernes, cuando comentabas que se había discutido este tema recientemente, me puse a buscar WM_KEYDOWN y vi el post y saqué en claro, que con hooks podría hacerse algo, pero eso todavía me viene grande
:)
Lo de tener un GUI oculto sospecho que no va a servir porque el mensaje se manda a la ventana activa, si tengo activa la ventana visible, la oculta no capturará el teclado...
Por eso desheché la idea enseguida :). Quería jugar con transparencias de ventanas etc, pero supongo que eso iba a dar muchos dolores de cabeza y no llevaría a ningún sitio.

¿Windows envía los mensajes siempre únicamente a las ventanas activas?

Porque buscando y buscando que si WM_KEYDOWN, recibir mensajes de Windows ... etc etc, algo vi de capturar los mensajes que Windows le envía a otras aplicaciones, aunque no miré muy a fondo el tema.

Si Windows enviara mensajes a ventanas que no estuvieran activas y estos pudieran capturarse... Quizá por ahí pudiera hacerse algo...
Creo que tienes un poco de lío con lo de "teclas virtuales", no se refiere a teclas pulsadas virtualmente, si no que se dan códigos virtuales a las teclas físicas del teclado, o sea, que se les asigna numeritos para trabajar con ellos... Con WM_KEYDOWN recibirás estos códigos virtuales, ya sea porque viene del teclado o porque se ha hecho un Send, así que me parece que lo de winapi.conclase.net, aunque es muy interesante, no servirá para distinguir teclas pulsadas por software.
Sí, sí, lo entendí exactamente así, quizá me expresara mal en algún momento, pero entendí que el código virtual es una traducción que hace Windows, para que pueda tratarse el teclado sin preocuparse de marca o modelo, ya que parece que cada fabricante por ejemplo asigna un scan code distinto a las teclas.
Y por cierto, con DllStruct me temo que no se pueden definir campos de bits o uniones de C. Para empezar no se pueden definir uniones, los datos de una DllStruct no están superpuestos si no necesariamente uno después de otro, pero es que tampoco vas a poder acceder así a los bits o grupos arbitrarios de los mismos, pues como se puede ver en la ayuda de DllStructCreate , el tamaño mínimo que se puede definir es de un byte, o sea, ocho bits...
Puf, pues ahí me pillas, estructuras, uniones... Son de esas cosas que me han hechado atrás cuando he querido aprender C/C++. Nunca lo entendí.
Me limité a copiar eso que parecía una estructura de C, en DllStructCreate().
Supongo que si hay solución a lo de separar teclas físicas de los keycodes y los mensajes que te los dan, será acceder directamente al hardware. Quizás eso era más fácil en la vieja época de los "compatible IBM", tal como te explica en el artículo de zator.com, no sé cómo estará ahora el tema. Por un lado las controladoras pueden haber cambiado, por otro es fácil que el Windows te impida tocar directamente el hardware... Pero creo que es la manera de atacar el problema, no se me ocurre otra si quieres ver si se presiona el teclado realmente, tendrás que preguntárselo a él, su controladora, su driver directo...
Yo creo también que consultar los controladores del teclado o acceder a este (que viene a ser lo mismo) va a ser la solución. La verdad es que si no hay otra forma como parece que no hay, no se ni por donde empezar con eso, es demasiada tela, pero sería lo más efectivo.
Buen intento con GetMessage, pero al fin y al cabo tiene el mismo problema que leer el mensaje con GUIRegisterMsg, en GUIs con controles no funciona...
Vaya, pues pensaba que algo más que GuiRegisterMsg() hacía.

y digo yo: ¿No habrá ninguna dll por ahí, algún objeto WMI.... para acceder al teclado, o a sus controladores?.
Supongo que eso lo haría mucho más fácil...

Salu2!

Re: Detectar envío simulado de tecla

Publicado: 24 Ago 2010, 08:38
por Ximorro
WM_KEYDOWN y compañía se envía sólo a la ventana con el foco de teclado, tal como pone en la MSDN. No sé si se podrá hacer un receptor global de mensajes, ¡si se puede estará realmente ocupado! A lo mejor detectando los WM_KEYDOWN antes de que llegen a la ventana receptora se pueden capturar antes de que AutoIT haga lo que hace para perderlos, pero esto no resuelve el problema de ver si se están presionando teclas por hardware. La cuestión es saber si hay algún tipo de mensaje que te distinga ese tipo de keypress, y sospecho que no porque los mensajes no están pensados para notificar detalles hardware...

Así que tendrás que estudiar lo de la controladora de teclado...

O como dije al principio, siempre puedes pinchar la webcam y ver si el usuario está manejando el teclado, ja, ja.

Re: Detectar envío simulado de tecla

Publicado: 24 Ago 2010, 17:25
por Jonny
ja ja; Sí, desde luego, como efectivo lo que más, lo de pinchar la cam :).
Porque lo de meterme con el controlador del teclado ... no se yo, me da escalofríos pensarlo :).

Casi voy pensando una forma más basada en ingeniería social, como decías al principio del post. Lo de la regularidad de las pulsaciones sería una buena forma; hacer que para que por ejemplo un botón realice la acción que le corresponda haya que pulsar x veces sobre él (cantidad aleatoria entre 1 y 3 por ejemplo) que cambie cada x segundos, es otra cosa que se me está ocurriendo...

Vi también, que en una página explicaban que hay algún juego online que pone como un captcha en el cursor que has de resolver para verificar que eres un jugador real (otra opción)...

Salu2!