| Actualización 09/03/2007 |
| Importante Luego de escribir esta corrección hace unos días atrás, continué navegando y noté un problema con ella. Al parecer la solución no era tan simple como pensé y describí en aquel momento. Los elementos ActiveX en ciertos escenarios no activaban completamente la interactividad con el usuario nunca y dependiendo según estaba declarado el objeto esto podía ser bastante limitante. Por ejemplo, un objeto Flash con la propiedad WMODE configurada como TRANSPARENT (modo transparente del objeto) podía provocar este comportamiento frente al clic secundario: ![]() Lo extraño era que el objeto seguía funcionando perfectamente pero no respondía a ninguna acción del usuario, como los clics sobre el mismo. Esto puede no tener importancia en los recuadros de publicidad, pero sin duda que muchas veces es útil tener interactividad con algunos objetos. Es por ese motivo que me puse a revisar a fondo la librería y elaboré otra solución, para corregir este mismo documento. |
| Nota |
| Las correcciones de los problemas que se enumeran a continuación no pueden ser publicadas en forma de un archivo modificado o instalador, pues así se violaría el punto cuarto "LIMITACIONES EN MATERIA DE INGENIERÍA INVERSA, DESCOMPILACIÓN Y DESENSAMBLAJE." y el punto seis "SEPARACIÓN DE COMPONENTES." del acuerdo de licencia del sistema operativo Windows, aceptado al momento de la instalación (la extensión de la licencia de Windows para Internet Explorer 7 se detalla en el archivo "C:\windows\system32\ie7Eula.rtf"). Sin embargo, mi idea es mostrar las formas en que podrían ser reparadas, en forma de conocimiento computacional general. |
1. Introducción
Hace bastante tiempo que los usuarios de Internet Explorer comenzamos a darnos cuenta de lo incómodo que podía ser tener que hacer un clic en todos los objetos dinámicos de las páginas web. Aunque muchos no se den cuenta, probablemente realizan un millar de inútiles clics sobre recuadros similares al siguiente:

Para los que tienen el navegador en inglés el mensaje es "Click to activate and use this control", pero el fastidio es similar.
Ajeno a los problemas legales de Microsoft con eso de la activación de objetos ActiveX, el resto de navegadores que no utilice internamente el motor de IE goza de la comodidad tradicional de no tener que activar los controles. Otro problema que genera la necesidad de esa "activación" es que provoca un funcionamiento a veces inestable en Internet Explorer, por ejemplo cuando tenemos varias páginas en distintas pestañas y el marco de "Haga clic para..." se queda parpadeante mientras tratamos de cambiar entre pestañas provocando cierta inestabilidad al navegar.
Personalmente debo reconocer que antes de Internet Explorer 7 utilizaba bastante Mozilla Firefox e incluso una versión portable de Opera. Actualmente y principalmente por una tonta comodidad volví a utilizar IE como navegador principal y a decir verdad estoy increíblemente satisfecho con la última versión de este navegador, exceptuando claro, el pequeño detalle ya comentado.
2. ¿Por siempre?
Al parecer Microsoft no tiene intenciones (o no puede) solucionar los problemas legales (caso EOLAS) a corto plazo y el mayor ejemplo es la implementación de la activación en su navegador, desde finales de la versión 6 incrustado en una actualización de seguridad, hasta la nueva versión 7, permanentemente incrustado en las librerías base del navegador.
| La ayuda del navegador del gigante de Redmond dice: |
| ¿Por qué es necesario activar los controles en las páginas web que visito? Microsoft ha realizado cambios en la forma en que Internet Explorer trata algunos de los contenidos de las páginas web. Cuando Internet Explorer encuentra una página con controles ActiveX, puede que le pida que apruebe (active) dichos controles para que puedan utilizarse. Cuando se desplaza el mouse sobre el control, aparece una nota en la que se le pide al usuario que haga clic en ella para activar el control. En ocasiones, el control mostrará un cuadro de diálogo antes de que aparezca la página. En cuanto haga clic en Aceptar, la página se cargará normalmente. Cuando haya activado el control, éste funcionará según lo esperado. Tendrá que activar el control cada vez que actualice o visite ese sitio web. |
3. Las soluciones o ¿las soluciones?
Muchas veces me impacta el rumbo que toman las cosas. Por ejemplo con el correo basura o SPAM en donde se colocan increíbles esfuerzos en crear filtros bayesianos, captchas y tecnologías totalmente defensivas, frente a educar bien a los usuarios o atacar fuertemente a los generadores de esta basura.
En el caso del "clic para activar" es algo similar, aunque desconozco los términos exactos del acuerdo, creo que lo más sano hubiera sido que Microsoft negociase en pos de una solución real. Más aún los miles de documentos en la red apuntan directamente en aplicar soluciones editando los sitios y el código de las páginas (en mi sitio los componentes son activados automáticamente por un Javascript) y entre estas soluciones "de desarrollo" figuran:
A) El sitio "oficial" de la solución:
Documenta la forma de activar los controles utilizando código activo al desarrollar un sitio.
http://msdn.microsoft.com/
B) La solución más popular:
Es una "nueva" forma de incrustar los objetos de flash utilizando un javascript que incrusta el código de flash luego de cargada la página.
El problema principal de esta solución es que en mi caso esta página que están leyendo se genera con un conjunto de reglas que traducen el documento a HTML, en donde las etiquetas tienen representación posterior en formato HTML, pero SWFobject requiere un identificador único para cada objeto flash, por lo que tendría que reescribir mi intérprete de documentos para ir generando estos identificadores para cada objeto flash de cada página (y no usar una especie de plantilla reemplazada por coincidencias basadas en expresiones regulares, como pasa actualmente)
http://blog.deconcept.com/swfobject
C) A4 Flash Easy Coder:
Similar al caso anterior, pero ahora un programa reescribe las páginas añadiendo el código javascript y modificando las llamadas desde la página estática que contiene los objetos que necesitan la activación.
http://www.a4flash.com/a4flasheasycoder/
D) La solución más genérica
Esta solución no obliga al desarrollador a modificar la forma de añadir los objetos flash, simplemente se debe añadir un código javascript que se invoca al final de la carga de la página (un método idéntico al utilizado en este sitio).
El gran problema de esta solución es que para una página compleja y con varios objetos flash, el usuario ve por unos instantes los objetos sin activación, además en situaciones así puede paralizar la página por un momento al comienzo.
http://hoeben.net/aldo/
4. La solución real
Luego de revisar esas soluciones anteriores el lector puede darse cuenta de lamentablemente está totalmente impotente frente a las millones de páginas que no son actualizadas periódicamente o aquellas en donde el webmaster ni siquiera piensa en implementar una solución adecuada para activar los objetos de su sitio.
Lo cierto es que con esas soluciones un navegante tiene la única posibilidad de hacer clic sobre los recuadros activando los controles, pues esas soluciones son para aplicar de forma exclusiva al momento de desarrollar sitio, no navegarlos.
La solución que encontré va mucho más allá y se encargará de activar internamente esos objetos sin que nunca más veamos ni el más mínimo indicio de esa activación.
5. Manos a la obra, buscando el mensaje
En estas misiones "search and destroy", el primer paso es buscar la cadena "Haga clic aquí para activar y usar este control", "Click to activate and use this control" o según corresponda.
Para esto me volví un fanático de la comodidad de la búsqueda de recursos de Restorator de la empresa Bome:

Los que comenzaron a leer mis artículos a finales del año 2005 notarán que este buscador simplifica la tarea de buscar los recursos usando la lógica y revisando las dependencias usando la consola de comandos (tasklist /m) y otras artimañas.
Luego de unos minutos encontramos al contenedor de la frase:

¿Extraño o no?, ¿Cuál será el motivo de la extensión del archivo mshtml.dll.mui?, ¿Qué significa mui?
Alguien que haya tratado de instalar un paquete de idiomas para Windows debe saber la respuesta o tener algún indicio de ella:
| MUI es el acrónimo de Multilingual User Interface, es decir Interfaz de Usuario Multilenguaje (IUM). |
A que ya adivinaron en donde continúa el trabajo.
6. Dentro de Microsoft HTML Viewer
En el punto anterior encontramos un recurso localizado dentro de mshtml.dll.mui. Si ese archivo es la localización, entonces el código estará consecuentemente dentro de la librería del visor de HTML de Microsoft, Microsoft HTML Viewer o para los amigos, simplemente mshtml.dll.
El cómo se cargan los recursos del archivo MUI acá no tiene mucha importancia, pero lo interesante es la referencia al recurso visto anteriormente:
| Código: |
2352, "Haga clic aquí para activar y usar este control" |
En consecuencia buscaremos ese PUSH HEX(2352) o literalmente PUSH 930. Cerrando cualquier ventana del navegador Internet Explorer, respaldando la librería y borrando los respaldos que Windows mantiene de forma automática de los archivos protegidos del sistema, es la manera ideal de comenzar a trabajar.
Para buscar PUSH 930, abrimos el archivo en OllyDbg:

Vamos a la coincidencia literal del comando y llegamos a la siguiente zona:

Vean bien las excelentes pistas que nos proporciona el desensamblador, claramente esa rutina de seis líneas que termina en LoadStringW carga el recurso que buscamos previamente. Hasta la documentación de la API de Windows corrobora la teoría:

Con la diferencia de que LoadStringW es la versión UNICODE de ese comando documentado (LoadString).
7. Modificando esa activación
Ya que encontramos el sector en donde se añade el "tooltip" con el texto sobre la necesidad de dar clic para activar la intuición dice rápidamente que subamos:

Un poco más arriba está señalado el comienzo de la subrutina. Esta rutina es claramente la que administra la activación de los objetos (esta activación no es sólo lo de "clic para activar" pues se necesita un mensaje de inicio para esos objetos, con o sin el infame clic del usuario).
Aclarando, ¿Qué es lo que realmente queremos lograr?
Esta rutina que acabamos de aislar hace lo siguiente:
-Prepara la activación necesaria de los elementos dinámicos (ActiveX) como las animaciones flash de las páginas, activación que les da el punto de partida para comenzar a funcionar.
-Crea y administra el recuadro y el mensaje que nos pide hacer un clic para activar el control.
-Cuando el usuario hace clic, finalmente realiza la activación que había preparado anteriormente y dejó en suspensión mientras esperaba nuestra "aprobación" con ese clic.
¿No sería ideal quitar el segundo paso y activar directamente los objetos dinámicos como en antaño?
Siguiendo un poco la ejecución de la rutina me doy cuenta que hay sólo dos saltos condicionales que suben y todos los demás bajan, lo que inevitablemente provocará la salida o retorno de ese fragmento de código:

Revisen atentamente la imagen anterior, vean que en base es un SWITCH y ¿alguien notó los CASE de ese SWITCH?
| Sobre la estructura SWITCH/CASE |
| Para entender bien la explicación, se debe entender esta estructura. La estructura SWITCH/CASE permite representar gran cantidad de condiciones como pasaría con una lista de IF/ELSE y en realidad OllyDbg sólo interpreta una lista de saltos condicionales en donde se van realizando cambios leves al código entre estos saltos como una estructura SWITCH/CASE. Como referencia se tener en cuenta que la estructura IF/ELSE de la izquierda es equivalente a la estructura SWITCH/CASE de la derecha: ![]() |
En esta rutina, la ejecución comienza en la parte superior y termina con el retorno al final, por lo que es muy importante encontrar los saltos que puedan generar ciclos.
Entonces si la rutina hace todo lo que dije anteriormente y adicionalmente sólo hay 2 saltos condicionales que suben, claramente esos dos saltos son los únicos que pueden producir un tiempo de espera (del clic) que se interponen naturalmente a la activación.
Cuando me refiero a los saltos que suben o bajan lo que quiero decir es si la ejecución continua en un código sobre o bajo ellos, según las direcciones de memoria. Aunque cuando un salto es condicional hay posibilidades de que ese flujo de ejecución de bifurque.
Como ya entienden lo que hace la estructura SWITCH/CASE, vamos a buscar el primer SWITCH de la rutina:

Considerando que sobre el SWITCH hay dos saltos, utilizaré uno de ellos para intentar omitir las instrucciones indeseadas. Revisando el salto condicional que está justo sobre el SWITCH, descubrí que aquella sólo redirige a un CASE dentro del mismo SWITCH. Sin embargo el salto de más arriba nos lleva mucho más abajo, recordemos que no debemos omitir toda la rutina, pues hay que activar el objeto de todas formas (omitir todo hubiera sido mucho más simple añadiendo un RETURN al comienzo).
Cambiaré el salto condicional por uno incondicional:

Automáticamente OllyDbg añade la instrucción NOP abajo del salto condicional para no alterar la estructura original de la librería. Copiamos estas modificaciones al ejecutable:

y finalmente guardamos la librería reemplazando la original y omitiendo, como siempre, el siguiente mensaje de Windows:

8. Probando los cambios
El navegador inicia correctamente, pero la librería se comporta de manera extraña. No muestra el marco ni el cursor de la "mano" para activar los objetos dinámicos, pero de todas formas requiere que hagamos clic sobre el objeto para activarlos y el mensaje sobre "Haga clic aquí para activar y usar este control" también aparece:

Esto simplemente refleja un pequeño avance en la búsqueda de una solución efectiva. Cerremos el navegador y volvamos a trabajar sobre la librería.
9. La estocada final
| Importante |
| Esta sección del procedimiento fue actualizada el 09/03/2007 |
Lo que haremos ahora es simular un clic del usuario cuando la rutina intente cargar el "tooltip" que señala lo relacionado con "Haga clic aquí para activar y usar este control":

En la imagen anterior se puede ver el "PUSH 930" al fondo y arriba, resaltado, las líneas que añadí. Automáticamente OllyDbg añade la instrucción NOP abajo del salto condicional para no alterar la estructura original de la librería. Pero ¿donde salta?

Salta exactamente al CASE 201 de nuestro SWITCH, en la dirección 7EB0D5B8. Así simulamos el primer evento del clic del ratón, marcado como WM_LBUTTONDOWN, en nuestro lenguaje es como "el botón izquierdo del botón se presionó".
¡Pero atención!, el evento es "se presionó" pero NUNCA señala que "se soltó", es decir si un usuario presiona un botón con el ratón y luego se arrepiente de realizar esa acción, fácilmente puede mover el puntero y soltar fuera el botón para no efectuar ninguna acción (prueben haciendo clic en el botón para actualizar esta página en su navegador).
Entonces lo que haré es dejar que el flujo simule que también soltamos el botón, luego de presionarlo, saltando a WM_LBUTTONUP:

Para los más incrédulos, les mostraré que esa dirección del salto corresponde al evento que dije:

Pero, ¿por qué cambiamos el primer salto en el punto anterior para eliminar el recuadro de la activación si finalmente simularíamos el clic?
Pues porque cuando se hace un debug (seguimiento de la ejecución) de la librería, uno puede darse cuenta de las diversas llamadas que recibe la rutina que modificamos. Un ejemplo de problema que puede conllevar no eliminar el recuadro que señala sobre la activación es que al actualizar la página, el puntero del ratón esté sobre un objeto que necesite activación y la librería intentaría dibujar el recuadro antes de activar forzadamente el objeto.
Claro que esto puede solucionarse haciendo más saltos forzados a la sección que simula el clic, pero eso ya sería parte de otra posible solución.
Finalmente y como previamente lo hicimos, volvemos a guardar los cambios y a reemplazar la librería.
10. Resumen
Para comprender mejor lo que acabamos de hacer, veamos los cambios hechos a la librería MSHTML.dll, especialmente los segmentos de código y sus modificaciones (incluso se pueden probar los efectos de estos cambios independientemente):
A) Quitar el recuadro de activación de objetos

| Código: |
Original: |
B) Luego de presionar el ratón, el código asume que se soltó
Este cambio es en referencia al paso entre los eventos señalados como WM_LBUTTONDOWN y WM_LBUTTONUP, sin requerir una segunda acción del usuario. Es decir, el usuario puede activar un control al presionar sobre el mismo, sin tener que soltar el botón.
| Código: |
Original: |
C) Simular la presión del botón
Aquí simulamos un WM_LBUTTONDOWN, lo que posteriormente lleva a WM_LBUTTONUP como vimos con la modificación anterior. El salto se produce antes de poder cargar el mensaje sobre la activación del control, PUSH HEX(2352), que equivale a la secuencia 6830090000 en donde 68 es el PUSH y el resto 00000930 (Little Endian).
Lo curioso es que la carga del mensaje sobre la activación se realiza al momento de que movemos el puntero sobre el objeto, es decir, podemos resumir como que la activación ahora es automática cuando movemos el ratón y no cuando hacemos clic sobre cada elemento.
| Código: |
Original: |
| Actualización 17/08/2007 |
| Nueva versión Hace unos días apareció una nueva versión del archivo "mshtml.dll", gracias a una actualización de Internet Explorer. Para la versión 7.0.6000.16481 y para la 7.0.6000.16527 del archivo "mshtml.dll", los cambios son los siguientes: 89BDA0FDFFFF0F87040300000F848902000083F9 8D440628830808E90907000033C040E907070000 0F84EB000000FF75148D8 Por los siguientes fragmentos, en el mismo orden: 89BDA0FDFFFFE905030000900F848902000083F9 8D440628830808EB3290909033C040E907070000 E999FEFFFF90FF75148D8 |
11. La solución ideal, lista
Gratamente la necesidad del clic desaparece y todos los objetos dinámicos como las animaciones flash aparecen correctamente.
Puedo decir casi con seguridad de que, hasta el momento de esta publicación, mi solución es única en el mundo.
12. Tarea para la casa
Nunca mi idea es no entregar todo en bandeja, sino dejar gran parte del trabajo y posibilidades de investigación al lector (sería bastante fácil hacer un parche que aplique todos estos cambios) por lo que si se fijan en el punto cinco de este documento, hay varios mensajes que necesitan un análisis:

Por ejemplo el siguiente:
| Código: |
2356, "Haga clic aquí para ejecutar un control ActiveX en esta página web" |
Ese mensaje se muestra en un "msgbox" cuando la página requiere ejecutar un ActiveX al comienzo. A diferencia del que reparé en este artículo, este mensaje aparece en muy pocas ocasiones y personalmente lo he visto unas dos veces en mi vida. Sin embargo también puede ser "anulado" y de una forma bastante simple.
| Pistas |
| -Buscar PUSH 934 para el mensaje de referente a la carga de ActiveX al comienzo -Notar que el mensaje está escrito en inglés también dentro de la librería |
13. Conclusiones
Bueno, muy poco tengo que añadir como conclusión aparte de recordarles que siempre mantengan una copia de seguridad de los archivos que modifiquen y que ojalá quede claro que en temas de software casi todo lo molesto o repetitivo tiene solución.



