jueves, 11 de abril de 2013

WinDbg Breakpoint on key pressed

Imaginad por un momento que os surge la necesidad de detener la ejecución de un programa cuando se pulsa una determinada tecla o combinación de estas, en fin, algo normal que a cualquier persona se le puede pasar por la imaginación, ¿o no?. Bromas aparte, para conseguirlo podemos utilizar infinidad de técnicas y en esta ocasión voy a comentaros la última que he empleado yo en uno de los trabajos que estoy realizando actualmente.

Esta técnica consiste en colocar un punto de ruptura en la llamada a la API TranslateAccelerator. Ahora bien, debemos tener en cuenta que si colocamos un breakpoint de ejecución sin más, el debugger estará constantemente saltando y no podremos conseguir nuestro objetivo o, cuando menos, será muy desesperante.

Para evitar tal situación, lo que tenemos que hacer es utilizar un breakpoint condicional para indicarle al debugger que se detenga solamente cuando se cumple una determinada condición o condiciones y por consiguiente que continúe con la ejecución normal del programa en el resto de los casos.

A modo de ejemplo y haciendo uso de uno de los depuradores que más me gusta (sí el depurador de Microsoft), vamos a detener la ejecución del programa cuando se pulse la tecla INTRO. Para esto, es preciso conocer tanto el prototipo de la API TranslateAccelerator que podéis ver a continuación:

int WINAPI TranslateAccelerator(
  _In_  HWND hWnd,
  _In_  HACCEL hAccTable,
  _In_  LPMSG lpMsg
);


como las estructuras de datos involucradas, ya que, si os fijáis el tercer parámetro de la API es un puntero a la estructura MSG. En este caso la principal estructura que debemos conocer es MSG, ya que necesitaremos hacer buen uso de algunos de sus miembros:

typedef struct tagMSG {
  HWND   hwnd;
  UINT   message;
  WPARAM wParam;
  LPARAM lParam;
  DWORD  time;
  POINT  pt;
} MSG, *PMSG, *LPMSG;


Llegados a este punto, ya conocemos bien a nuestro objetivo y cómo podemos "jugar" con él. Ahora tan solo falta saber cómo le decimos al depurador que utilice toda esta información. Aunque puede hacerse de varias formas, la técnica que expongo aquí es mediante el uso del evaluador de expresiones al estilo C++ que incorpora el debugger.

Si observamos la estructura MSG, enseguida nos damos cuenta de que hay varias cosas muy útiles para conseguir nuestro objetivo. Dado que queremos detener la ejecución del programa al pulsar una tecla, será preciso acceder al miembro 'message' de MSG.

En concreto el mensaje que vamos a capturar será WM_KEYDOWN cuyo valor constante equivalente en hexadecimal es el 0x0100. Además, como no queremos que se detenga la ejecución al pulsar cualquier tecla, sino solamente la tecla ENTER, también tendremos que utilizar el miembro 'wParam' que nos indicará cual es la tecla (VK_KEY) que se ha pulsado. En este caso para la tecla ENTER el valor que deberá contener 'wParam' será 0x0D (ya sabéis el número de la suerte).

Bueno, pues ahora sí que tenemos a nuestro alcance todo lo que necesitamos, así que, manos a la obra. Vamos a indicarle al debugger cual será nuestro maravilloso breakpoint:

bp 003814ac "j @@c++((((ole32!MSG *)@eax)->message == 0x0100) && (((ole32!MSG *)@eax)->wParam == 0x0D)) ''; 'gc'"

Como digo, hay muchas maneras de conseguir esto mismo, pero para mi caso concreto, esta era la más efectiva. La expresión habla más o menos por si sola, no obstante si tenéis alguna duda, sentiros libres de comentarlo.

¡Suerte y feliz debugging!

2 comentarios:

  1. 003814ac es el offset del textbox/objeto, el handler de la ventana el entry point de la aplicación, o...? Gracias.

    ResponderEliminar
  2. Este comentario ha sido eliminado por el autor.

    ResponderEliminar