lunes, junio 04, 2007

Cómo funciona el motor de compatibilidad de aplicaciones de Windows XP/Vista

En un hipotético mundo perfecto, todas las aplicaciones funcionarían correctamente, no seguirían métodos indocumentados y los fabricantes se encargarían de adaptarlas adecuadamente a una nueva versión del sistema operativo en el que se ejecutan. Lamentablemente, en el mundo real, esto no es en absoluto así y los problemas de compatibilidad son muchos y variados. En algunos casos, Microsoft simplemente decide aplicar un shim general, o bien específico para esa aplicación, con el objetivo de que funcione correctamente. Pero, ¿qué es un shim?

Se trata de un "arreglo" incluido en el sistema operativo que permite que ciertas aplicaciones antiguas o mal diseñadas puedan funcionar correctamente. Windows NT fue diseñado con la compatibilidad en mente, y las sucesivas versiones así lo demuestran. Por ejemplo, Windows XP y Vista incorporan una base de datos de shims en el directorio \WINDOWS\AppPatch. En este directorio hay bases de datos de shims (.sdb) y DLLs que contienen las funciones que "arreglan" ciertas cosas que suele hacer mal el software existente. El fichero AcGenral.dll contiene shims inespecíficos, generales; AcSpecfc.dll contiene shims aplicables sólo a cierto software mal diseñado y AcLua.dll contiene shims relacionados con lo que hoy día es UAC (User Account Control). Vista no tiene esta DLL porque ha sido sustituida por el mecanismo de virtualización. Las bases de datos (.sdb) contienen shims aplicables en general (Sysmain.sdb), sólo a instaladores MSI (Msimain.sdb), o incluso a controladores de hardware (Drvmain.sdb). Una vez hemos analizado el cometido de esa carpeta, veremos a continuación cómo funciona el motor de shims (Shim engine):

Todo comienza en CreateProcess

La función encargada de crear procesos en Windows XP/Vista reside en Kernel32.dll y se llama CreateProcess. Ésta lleva a cabo muchas operaciones antes de "dar a luz" al proceso. Entre estas operaciones está el comprobar si la aplicación es una aplicación que Microsoft sabe que no funciona o que necesita de algún "arreglillo" para hacerlo correctamente. La DLL Apphelp.dll del directorio \system32 se encarga de consultar las bases de datos antes mencionadas y así decidir si se debe aplicar un shim o no.

En el caso de que la aplicación necesite de un shim, durante la ejecución del proceso se cargarán un par de módulos, entre ellos el motor shim (Shimeng.dll) encargado entre otras cosas de enlazar con la función residente en alguna de las DLLs antes mencionadas del directorio \AppPatch y que contiene el código del "arreglo".

Podemos "forzar" un shim persistente. Modo de compatibilidad

En Windows XP y Windows Vista, al desplegar las propiedades de un acceso directo se presenta una pestaña denominada Compatibilidad. Si el ejecutable no está protegido por Windows (WFP) y no procede de una unidad de red, se nos permitirá configurarlo de modo que funcione, por ejemplo, "forzando" una cierta resolución de pantalla, o como si estuviera ejecutándose en un sistema operativo anterior. Esta información se almacena en la clave de Registro HKCU\Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers y es leída por Apphelp.dll antes de crear el proceso del ejecutable.

¿Pero qué hace que la aplicación se crea que está funcionando en un sistema Windows anterior?

Realmente lo que ocurre es que las llamadas a las funciones GetVersion, GetVersionEx (ANSI y Unicode) son interceptadas por una función hook (gancho) del shim, que "miente" y devuelve información referente a un sistema anterior. Veremos lo que ocurre exactamente empleando un depurador:



Una API un poco rara...

En la imagen puede ver como intento abrir una instancia del programa WinRAR, que expresamente he configurado con el modo compatibilidad con Windows 95. Una función de WinRAR al parecer quiere saber en qué sistema operativo está funcionando. Aquí entra en acción una cierta función de AcLayers.dll (del directorio \WINDOWS\AppPatch), "APIHook_GetVersionExA". El nombre lo dice todo, :-P, pero veamos qué hace exactamente:

Se realiza una llamada a las funciones "reales" GetVersionExA y GetVersionExW. Poniendo un breakpoint en el retorno de la función, examinamos la estructura OSVERSIONINFOEX que devuelve como parámetro. Para ello, anotamos el contenido del registro ESI y examinamos esa porción de memoria (con el comando dd). Esta imagen ejemplifica esto:


Accediendo al tipo estructurado OSVERSIONINFOEX


Probablemente se sienta abrumado por esos números hexadecimales, es algo normal, hay que echar antes un vistazo a cómo es el tipo estructurado OSVERSIONINFOEX. El comando dt del depurador puede hacer esto mismo (dt OSVERSIONINFOEX), aunque también puede consultar MSDN. Con la información en la mano, podemos ver que, la segunda columna hace referencia al tamaño de la información de versión (dwOSVersionInfoSize), la tercera, ese 5 hexadecimal, indica cuál es la versión principal (dwMajorVersion), el 1 siguiente, la versión secundaria (dwMinorVersion), y el 0xa28 el número de build. Introduciendo el comando ?a28 obtenemos la representación decimal de este valor: 2600. ¡Ajá! 5.1.2600, o lo que es lo mismo, Windows XP, el sistema operativo donde está funcionando "realmente" la aplicación. :-)

Veamos ahora qué ocurre cuando se retorna el control a la función del shim y, en último momento, a la función de WinRAR que está interesada en obtener información del sistema operativo donde está funcionando. Repetimos el mismo procedimiento:



Bueno, WinRAR, estás funcionando en... Windows 95

¡Ops! parece ser que la información que se devuelve ha cambiado "por arte de magia". Ahora, como ve, hace referencia a un tal Windows 4.0.950, que precisamente es Windows 95. Lo que ha ocurrido es que el shim que apliqué explícitamente a WinRAR le ha "mentido" vilmente. Si esta aplicación hiciera una comprobación de versión y ni siquiera se ejecutara en el caso de que no conociera la versión del sistema operativo, con el shim que he aplicado probablemente no habría problema alguno.

Así es como funciona a grandes rasgos el motor de compatibilidad de aplicaciones de Windows XP/Vista. Gracias a él nuestra experiencia con Windows es mucho más agradable y menos frustrante: actúa en segundo plano, no nos enteramos apenas de lo que ocurre "por lo bajo" y nos permite hacer funcionar cierto software no muy bien diseñado.

3 comentarios:

pablo dijo...

hola. tenes idea si el motor de compatibilidad de aplicaciones de Windows XP depende de algun servicio de windows. mi problema puntualmente es que me desaparecio la solapa de compatibilidad en las propiedades de cualquier ejecutable, es por eso que trato de aberiguar si algun servicio esta fallando. desde ya gracias por el post, me ayudo a comprender como funciona la compatibilidad

yo dijo...

Mirá, yo tenía el mismo problema y me rompi la cabeza un buen rato tratando de encontrar las entradas de registro que se podían haber modificado. Al final buscando encontré que no tenía el archivo apphelp.dll en la carpeta %SystemRoot%\system32. En mi caso lo que hice fue reemplazarlo por el que viene viene con el SP3 y funcionó. Sino tendrías que tratar de bajarlo de algún lado. Es importante que te fijes la versión

TAP4 dijo...
Este comentario ha sido eliminado por el autor.