lunes, 19 de abril de 2010

Paso de mensajes entre procesos en windows

En la plataforma Windows tanto para la comunicación entre procesos como para el manejo de eventos se usa un sistema de mensajes.

Una de las características del sistema de mensajes de Windows es no disponer de autentificación, se puede aprovechar este hecho para simular eventos en los programas que se están ejecutando y de esta forma controlarlos desde el API de mensajes.

Se puede por tanto manejar un programa de forma automática desde otro, con las posibilidades que ello ofrece. Orientándolo a la ingeniería inversa se puede hacer fuzzing a programas que tan solo admiten entradas desde la interfaz gráfica.

Supongamos que tenemos un programa cuya ventana principal se llama "Form1" y un botón con el texto "Boton" en el cual al hacer click en un botón muestra un mensaje.

...
private void button_Click(object sender, EventArgs e){
MessageBox.Show("Wow!", "Clicked", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
...

Podemos simular que hacemos click en ese botón si desde otro programa hacemos lo siguiente.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace WFuzzing{
public partial class Form2 : Form{
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr FindWindow(string strClassName, string strWindowName);

[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int SendMessage(int hWnd, uint Msg, long wParam, long lParam);

[DllImport("user32.dll", CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool EnumChildWindows(IntPtr hwndParent, EnumWindowsProc lpEnumFunc, IntPtr lParam);
public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr parameter);

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);

public const int WM_LBUTTONDOWN = 0x0201;
public const int WM_LBUTTONUP = 0x0202;

public Form2(){
InitializeComponent();
}

private void fuzzbutton_Click(object sender, EventArgs e){
IntPtr wHandle = FindWindow(null, "Form1");
List<IntPtr> result = new List<IntPtr>();
GCHandle listHandle = GCHandle.Alloc(result);
EnumWindowsProc childProc = new EnumWindowsProc(EnumWindow);
EnumChildWindows(wHandle, childProc, GCHandle.ToIntPtr(listHandle));
}

private static bool EnumWindow(IntPtr handle, IntPtr pointer){
StringBuilder text = new StringBuilder(256); ;
GetWindowText(handle, text, text.Capacity);

if(text.ToString() == "Boton"){
long lngResult = SendMessage(handle.ToInt32(), WM_LBUTTONDOWN, 0, 0);
long lngResult2 = SendMessage(handle.ToInt32(), WM_LBUTTONUP, 0, 0);
}
return true;
}

}
}

Se le mandan dos mensajes, el primero el de hacer click con el ratón y el segúndo el de levantar el dedo del ratón.

Al ejecutar ese código el primer programa debería mostrar el mismo mensaje que si hacemos click en el botón.

De forma similar, se puede automatizar la búsqueda de fallos en programas de los cuales no disponemos del código fuente, tan solo manipulando la interfaz, rellenando campos de texto, activando y desactivando controles, todo de forma automática.

0 comentarios: