Programación en Lenguaje Ensamblador

-El Verdadero Lenguaje de las Máquinas-

¡Quiero Ver Mas Allá de lo Evidente!

–Crackeando tutorial de Iczelion con OllyDbg–

En esta nota procesaremos el primero de los tutoriales de Iczelion, aunque en realidad vamos a comenzar por el segundo porque la primera parte de lo único que habla es de como funciona el MASM. Y como hay mas gente que habla inglés de la que sabe ensamblador, mejor pasamos a los códigos.

Para esta parte, la mayoría de ustedes ya leyeron la parte teórica, asi que no voy a volver a explicar cosas como para que sirve el .386 (no es la entrada al Modo Protegido) ni lo del modelo flat y stdcall ni los includes ni lo de optioncasemap. Además de que nada de esto nos va a servir. La parte de la sección de datos que tiene 2 cadenas ascii no es la gran cosa y si han seguido este blog desde el principio ya han de saber lo que es una cadena ASCII-Z. Lo único que nos interesa son estas 2 lineas:

invoke MessageBox, NULL,addr MsgBoxText, addr MsgCaption, MB_OK

invoke ExitProcess,NULL

¿Parece sencillo? ¡No lo es! Lo mismo dijeron muchos que intentaron descifrar los tutoriales de Iczelion y ahora viven de hacer querys de SQL. Ese invoke no es una operación de CPU sino una horrible macro, y el primer renglón en realidad se “desenrolla” en 5 instrucciones que en su totalidad miden 18 bytes. Sin contar que a su vez tiene dos llamadas a otra macro anidada llamada ‘addr’ Esta última puede convertirse en 3 o 4 instrucciones mas cada vez que aparece dependiendo de la complejidad de los operandos.

El segundo invoke se desenrolla en solo 2 instrucciones que en total miden solo 7 bytes.

Bueno, los 2 invokes es lo último que ven muchos programadores antes de frustrarse e irse de DBA’s. Pero nosotros tenemos el depurador OllyDbg y al comando de:

OllyDbg, ¡Quiero ver mas allá de lo Evidente!

Obtenemos esta vista del demo de Iczelion en lenguaje máquina:


debug

	00401000 >/$ 6A 00          PUSH 0                                   ; /Style = MB_OK|MB_APPLMODAL
	00401002  |. 68 00304000    PUSH msgbox.00403000                     ; |Title = "Iczelion's tutorial no.2"
	00401007  |. 68 19304000    PUSH msgbox.00403019                     ; |Text = "Win32 Assembly is Great!"
	0040100C  |. 6A 00          PUSH 0                                   ; |hOwner = NULL
	0040100E  |. E8 0D000000    CALL            ; \MessageBoxA
	00401013  |. 6A 00          PUSH 0                                   ; /ExitCode = 0
	00401015  \. E8 00000000    CALL          ; \ExitProcess
	0040101A   .-FF25 00204000  JMP DWORD PTR DS:[<&KERNEL32.ExitProcess>;  kernel32.ExitProcess
	00401020   $-FF25 08204000  JMP DWORD PTR DS:[<&USER32.MessageBoxA>] ;  USER32.MessageBoxA

Como pueden ver, y si no pueden mas abajo lo reescribí en texto para que sea mas sencillo de lamear, las macros se desenrollaron y se convirtieron en instrucciones de ensamblador, las constantes se convirtieron en simples números y además podemos ver las posiciones de memoria del programa.

Para los que ven por primera vez algo como esto, la primera columna indica las posiciones dentro de la memoria virtual, la segunda son los OpCodes en Lenguaje Máquina y la columna que parece código es en realidad código desensamblado. Hasta donde puede, OllyDbg intenta averiguar lo que el programa está haciendo y escribe comentarios en el extremo derecho del código. En este caso, supo que estábamos llamando a MessageBox y Exit Process e incluso identificó plenamente los parámetros. Ahora veamos paso por paso lo que el programa está haciendo:

6A 00 PUSH 0

Aquí están empujando un cero de 32 bits al stack. Esta es la constante MB_OK que le dice a MessageBox el estilo de caja.

68 00304000 PUSH msgbox.00403000

En esta linea están empujando al stack la posición de memoria de la cadena ASCII-Z que dice “Iczelion’s tutorial no.2”. Esta posición es 403000. Pero en el source se usa la etiqueta MsgCaption. La macro addr en este caso no hizo nada porque 403000 es una posición de memoria absoluta. Cuando se usan direcciones relativas como esp + 123h o ebx + edi, addr convierte estas posiciones relativas en absolutas con el uso de la instrucción LEA (esta será motivo de otra entrada)


debug

En esta captura de pantalla se ve el segmento de memoria en ascii y hexadecimal. Si conocen de memoria el código ASCII pueden leer las cadenas directamente. Noten el cero binario con el que terminan y la posición de memoria a la que hacen referencia las funciones que usan texto.

00401007 |. 68 19304000 PUSH msgbox.00403019

Esta linea hace lo mismo que la anterior pero manda al stack la posición de la cadena ascii que dice: “Win32 Assembly is Great!”

0040100C |. 6A 00 PUSH 0 ; |hOwner = NULL

En esta linea se empuja otro cero al stack que le dice a MessageBox que la ventana no tiene padres, pues hOwner indica el handler del Pwner Owner o dueño de la ventana. Este cero indica que aparece en el centro del escritorio. Pero eso ya es cosa de la API de Windows


0040100E |. E8 0D000000 CALL <JMP.&USER32.MessageBoxA> ; \MessageBoxA

Aquí se llama a MessageBoxA. Nótese la A mayúscula que va al final. Para Windows hay diferencias entre funciones ascii y unicode. En este caso llamamos a MessageBox versión ASCII. Otra de las cosas importantes que nos ocultan las macros.

00401013 |. 6A 00 PUSH 0 ; /ExitCode = 0

Esta ya es parte del segundo Invoke, aquí se empuja un cero al stack que es el único parámetro de la función ExitProcess que termina el programa. La constante NULL se transforma en cero, parece que NULL es mas sencillo de leer en un código que el propio cero.

0040101A .-FF25 00204000 JMP DWORD PTR DS:[<&KERNEL32.ExitProcess>; kernel32.ExitProcess

Finalmente se llama a la función API de windows ExitProcess y se termina el programa. La linea que sigue pertenece un sistema de llamado de funciones conocido como JUMP TABLE o trampolín. Que permite que un programa pueda llamar funciones externas y cambiantes sin necesidad de reprogramación. En esta tabla se colocan los verdaderos llamados a función y cuando hacemos un CALL para ejecutar una función API lo que en realidad hacemos es saltar a esta tabla. Este es un mecanismo interesante pero no hay urgencia de verlo porque FASM no lo usa. De hecho, este programa hace exactamente lo mismo que el PEDEMO.ASM que viene en los ejemplos del fasm y en este mismo blog en la nota llamada “¡Al fin un código!”

Hasta ahora, apenas hemos entendido lo que realmente ocurre tras esos inocentes invokes, y ya tienen una idea de lo peligrosas que pueden ser las macros para los principiantes, es mejor que las guarden para cuando tengan artritis y no puedan teclear sus programas los suficientemente rápido. O por lo menos hasta que realmente sepan lo que están haciendo y lo confirmen con el depurador de códigos en la primera oportunidad.

Para recapitular, hoy aprendimos todo esto:

1.- Se necesita un depurador para entender los tutoriales de Iczelion.

2.- Una macro puede desenrollarse en muchísimas instrucciones de Ensamblador

3.- Los argumentos de una función se introducen por el STACK en orden inverso a como quedan dentro del stack frame.

4.- Las constantes como MB_OK, hOwner, etc. Se convierten en simples números enteros casi siempre de 32 bits

5.- La macro ADDR convierte una referencia a memoria de tipo relativa a una posición de memoria absoluta usando la instrucción LEA.

6.- En este caso se usa una JUMP TABLE para hacer las llamadas a la API de Windows, pero FASM no trabaja asi.

Por ahora me regreso a donde estaba, estoy desarrollando una trampa para lamers muy divertida, aunque va a estar lista para la siguiente temporada de caza y esta es hasta el final del otoño. Por ahora traten de leer y analizar los tutoriales de Iczelion ustedes solos a ver que mas se encuentran, recuerden que hay un link a la web de Iczelion a la derecha de este blog.

Junio 9, 2009 Publicado por asm86 | Uncategorized | , , , , , | 15 comentarios