Curso de programación de virus - Ensamblador VIII
12 - Ensamblador VIII
NASM (Linux)
Introducción al mundo de NASM
Por suerte, en aspecto vais a ver que no hay una gran diferencia entre el NASM y el TASM. Otro gallo cantaría si usáseis el GNU Assembler (más conocido como GAS), pero no hace falta repetiros la grima que me da el formato At&t de los... en fin :). En esta ocasión, al igual que antes utilizábamos el TASM y el TLINK, vamos a tener que tirar de dos ejecutables; el primero va a ser el propio ejecutable llamado nasm, el segundo es un viejo conocido, el gcc.
Compilado con NASM/GCC
Cuando utilicemos el NASM para compilar, realmente sólo van a haber dos opciones que nos resulten interesantes - aunque como siempre hay alguna más:
-o: Indica el fichero de "output", o sea, donde va a poner el resultado. Si nuestro fichero original se llama virus.asm, podría ser una buena opción escribir un "-o virus.vir" como parámetro.
-f: Indica el formato de fichero que pretendemos lograr. Lo más normal es que queramos un ejecutable tipo ELF, con lo cual nada como escribir "-f elf".
Así pues, la línea standard que usaremos con el NASM para compilar será algo como "NASM fichero.asm -o fichero.tmp -f elf".
La siguiente fase requiere tirar del GCC para acabar de compilar el fichero. La verdad es que ni me acuerdo de cual coño era el significado de las opciones (además estoy usando el Dreamweaver en Windoze así que comprenderéis que no voy a arrancar el Linux sólo para mirar las puñeteras opciones xD, RTFM sucker xDD). Importante, pues que -s significa origen (sorce) y -o destino; mi línea standard con el GCC es "gcc -Wall -g -s fichero.tmp -o fichero.exec". Una vez ejecutado esto, tendremos en fichero.exec el Elf ejecutable que estábamos buscando (facilito, ¿no?).
Un detalle, tal y como en Windous conviene usar un BAT y tal sobre todo si vamos a compilar muchas veces, pues haceros un script tonto que meta estas dos líneas para que escribiendo "sh loquesea" podáis compilar sin tener que escribir toda la burrada de atrás. Por cierto, que aquí tenemos el mismo problema que en Windows, es decir, no podemos escribir en la sección de código por defecto. Creo que está colgada de la web una utilidad que escribí llamada "dwarf" que lo que hace es coger al fichero elf y cambiar esos parámetros. Así, mi "fichero standard de compilación" es algo como esto (hago copy&paste de la que uso para mi virus Lotek):
nasm lotek.asm -o lotek.vir -f elf gcc -Wall -g -s lotek.vir -o lotek.exec dwarf lotek.exec
Particularidades en NASM
De nuevo, como creo que lo mejor es poneros un listado compilable, pues vamos con ello y cuento alguna cosita:
BITS 32
; Pues eso, 32 bits no? :)
GLOBAL main SECTION .text
; .text es la sección de código (tanto en Windows como en Linux)
main: ov eax,dword[ebx+09Ch] ; Esta linea de codigo es una estupidez pero sirve para mostrar una gran ;diferencia (de las pocas) entre TASM/NASM. En el TASM para indicar que ;queremos leer 4 bytes diríamos mov eax, dword ptr [ebx+09ch], pero aquí ;lo del ptr sobra.
mov eax,1
int 080h ; La interrupción 80h en Linux es recordemos la que vamos a utilizar casi ;exclusivamente al programar. Ojo aquí una diferencia con TASM, que son ;las formas de representar en hexadecimal: o le metemos un cero delante y una ;h al final, o hacemos como en este db que viene ahora: valores:
db 0x0A,0x04; Bien, en este db, retomando lo dicho, usamos el método alternativo, ;escribir 0x0numero para indicar que es hexadecimal. ; Encima no tenéis ni que meter el END que había en el TASM, ¿más facil chungo nop?. Bueno vale, seguro que me ;he olvidado de algo, pero no soy una máquina y esto se aprende de una sóla forma: cogéis la base que intento dar ;(suponiendo que os esté sirviendo de algo claro xD), cogéis listados en ASM para aprender a leerlos, y programáis ;a sako...
Y otra vez... ¿de dónde me lo bajo?
En caso del NASM, estamos hablando de una utilidad gratuíta. Aparte del lugar desde donde se puede llegar, muy recomendable por otros muchos aspectos que es http://linuxassembly.org, el programa puede bajarse diréctamente de http://nasm.2y.net.
Turbo Debugger (TD32)
Introducción
La primera pregunta que he de responder aquí se simple: ¿qué es un debugger?. Pues bien, un debugger es un programa con el que podemos ir ejecutando paso a paso las instrucciones de nuestro programa, de cara a depurarlas (darnos cuenta de qué es lo que falla y corregirlo).
Aunque queda muy chulo mirar el código ensamblador de tu programa y darte cuenta de qué es lo que falla y corregirlo, a veces la cosa se complica y viene bien un poco de ayuda. Con un debugger (en este caso uno sencillo como el TD), podemos ver a cada momento cual es el valor de los registros del procesador y cómo afecta la ejecución de las instrucciones a estos registros y a distintas posiciones de memoria, obteniendo bastante información que nos puede llevar a su solución.
Empezando con Turbo Debugger
Arrancarlo es tan sencillo como escribir "TD32 nombrefichero.exe" desde una ventana de Ms-Dos. Si por ejemplo como parámetro ponemos el fichero c:\windows\telnet.exe, tendremos algo como esto:

No sé si parecerá complicado o no, pero ahora os explico; como veréis, he dividido básicamente la pantalla en "Codigo", "Volcado" y "Registros". Se cambia entre estas partes de la pantalla con la tecla tabulador (si os fijáis, la parte de "código" está rodeada por un color azul, eso indica que está seleccionada):
- Código: Aquí están desensambladas una a una las instrucciones del programa. Dado que hemos ejecutado "td32 telnet.exe", estas son las primeras instrucciones del fichero telnet que tengo en el directorio del Windows. Vemos que hay tres columnas, aparte de unas cuantas filas, también.
La primera columna indica la dirección de memoria donde se encuentra la instrucción desensamblada (con ese símbolo de flecha mirando hacia la derecha en la que se va a ejecutar la próxima, en este caso la próxima es "push ebp"). La segunda columna, se trata de la codificación en hexadecimal de la instrucción desensamblada. Esto quiere decir, que push ebp se codifica como "55h", o que mov ebp,esp se codifica como "08bech".
Bueno espero que a nadie le sorprenda a estas alturas que nuestro bonito push ebp no sea más que un "055h", y que no tenga que explicar de nuevo como hice en el primer capítulo que lo que hace el procesador cuando ejecuta un programa es coger ese 55h, decir "ah, eso corresponde a un push ebp" y realizar la operación...
- Registros: Como podéis ver, está bien clarito; al lado del nombre de cada registro viene su valor. Así, en el momento en que capturé esa pantalla, EIP vale 010081c0, etc etc. Importante, los registros que vienen a la derecha, todo este tema de z=0, s=1, etc... vengaaaaa, ¿esa intuición?. Pues sí, efectivamente z=0 indica que el flag de zero en el registro de flags está a cero, así como o indica overflow, p paridad, c carry o i inhibición de interrupciones.
-Volcado: Esta parte muestra el contenido en hexadecimal (byte a byte) a partir de una dirección de memoria. En este caso es a partir de la 0000000, contando 1 por cada byte (dos cifras hexadecimales). A la derecha vemos unos cuantos símbolos raros; se trata de la representación en ASCII de los números que hay en la parte central. La ventana de volcado es bastante útil porque es independiente de la de código, y porque de paso si queremos ver un texto en la columna en la que está traducido a ASCII nos lo va a mostrar, en lugar de desensamblarlo como si fuera código.
Utilizando Turbo Debugger
Vale, hemos visto al Turbo Debugger en plan estático, pero evidentemente eso no es lo que queremos; si lo único que queremos es un listado en ensamblador de un fichero, utilizaríamos los desensambladores, que para algo están xD. Lo que queremos aquí es ejecutar paso a paso, y podemos hacerlo de las siguientes formas:
- Dar un paso: Esta órden se la podemos dar a Turbo Debugger de dos maneras, pulsando las teclas F7 o F8. La diferencia es, digamos, que con el F8 (step) vamos un poco más a lo bestia que con F7 (trace). Con F7 ejecutamos absolutamente cada instrucción paso a paso. Con F8 sin embargo damos "pequeños pasitos". Esto significa que por ejemplo, si encontramos una instrucción CALL, con F7 la siguiente instrucción que ejecutaremos será a la que llama el CALL, sin embargo con F8 ejecutaremos *todo* lo que hay dentro del CALL, pasando a la siguiente línea.
En este caso, sin embargo, al dar un paso nos vamos a encontrar con la siguiente situación lo hagamos con F7 o F8: Veamos, ¿qué ha sucedido? Pues que el "push ebp" se ha ejecutado. La flecha que está en la barra azul y la propia barra azul se han desplazado un espacio hacia abajo, señalando a la próxima instrucción que toca ejecutar. Además, vemos que en la parte dedicada a los registros, hay tres de ellos señalados en blanco. ¿Por qué es así? Bien, es una forma cómoda mediante la que Turbo Debugger nos indica que el valor de estos registros ha cambiado después de ejecutar la última instrucción. ESP ha cambiado dado que hemos empujado un valor a la pila, EIP lo ha hecho porque es el registro que señala la próxima instrucción a ejecutar.

-Ejecutar todo el programa: Para ello tenemos la opción "run". La opción "run" se activa con la tecla F9, y ejecuta el programa hasta el final a no ser que hayan... a no ser que haya lo que llamamos "breakpoints", que es una de las cosas más útiles que nos dan las utilidades de debuggeo.
-Otras opciones: Turbo Debugger tiene un montón de opciones, que no voy a dedicarme a detallar, la mejor forma de aprender a usarlas es usándolas. Sin embargo, si que voy a dedicar una sección a lo que creo que es el segundo punto importante de comprender no ya de cara a usar el TD32 sólo, sino en general para usar cualquier debugger, que son los breakpoints.
Breakpoints
El propio nombre lo dice, Breakpoint significa "punto de ruptura". Imaginad que el fallo en vuestro programa pensáis que debe estar algo así como en la línea 400, donde se producen tal y cual instrucciones. No puedes ejecutar paso a paso hasta allí porque es un maldito coñazo, ni puedes hacer un "Run" porque lo ejecuta hasta el final y es un tanto estúpido. Así pues, lo que utilizas son Breakpoints.
Un Breakpoint está ligado a un punto de la ejecución, y lo que va a hacer es que si le damos a la opción "Run", el debugger ejecute hasta el punto en el que hemos puesto nuestro Breakpoint y ahí se detenga, dejándonos que sigamos haciendo lo que hemos venido a hacer. En Turbo Debugger, la forma más sencilla de poner un breakpoint es movernos en la ventana de código hasta la instrucción en la que queramos ponerlo y darle a F2:
.jpg)
Como vemos, el lugar donde hemos puesto el breakpoint (bueno, entretanto he movido con las flechas la barrita azul para ponerla donde ahora está la roja y la he vuelto a poner en la instrucción que toca ejecutar), es donde se encuentra la barra roja. De esta forma tan gráfica, vemos que hay un Breakpoint ahí. Ahora, si le damos sencillamente a F3 (ejecutar programa), este se ejecutará hasta llegar al breakpoint, momento en que se parará y podremos volver a meter mano por donde queramos.
Y en fin, con esto y un bizcocho mañana a las ocho.
De donde bajarlo
Turbo Debugger, aunque se puede encontrar como programa aparte, viene con el TASM normalmente, con lo que si tenéis uno tenéis el otro y fuera problemas ;-).
ALD (Linux)
Introducción
Bueno, dado el hecho de que antes ya he explicado un poco como funciona un debugger y esas cosas, esta sección va a ser deliberadamente bastante corta; básicamente, voy a explicar cómo hacer lo mismo que antes con un debugger bastante sencillito para Linux y que para hacer cuatro cosas no está nada mal. Además tiene una ventaja y es que se parece bastante al Debug de Ms-Dos, con lo que los que lo hayan usado tardarán pocos minutos en sacarle buen partido. Vamos pues con ALD, o Assembly Language Debugger:
Bases de funcionamiento
ALD es una aplicación que funciona en modo texto, por lo que no vamos a tener ni ventanitas con menús ni demás pijaditas que venían por ejemplo con el Turbo Debugger. Esto, no implica que vaya a ser complejo en su funcionamiento. Al contrario, es muy rápido aprender las cuatro cosas que aprendimos con TD y seguir averiguando por nuestra cuenta el resto de opciones que posee.
El ALD puede arrancarse después de instalarlo (sencillito, configure/make/make install y no suele dar problemas) simplemente tecleando su nombre o indicando como parámetro un fichero. Al ejecutarlo nos dirá la versión y tal, y si metimos un nombre de fichero como parámetro (ald /bin/ls p.ej), nos confirmará que se trata de un fichero de formato ELF (ejecutable) y tal.
Veremos que estamos en una línea de comandos, el programa nos pide que introduzcamos palabras para decirle qué hacer a continuación. Listo algunas interesantes:
-help:Una de las más importantes :-). Nos sacará un listado con todos los comandos que podemos utilizar, e igualmente podremos escribir "help comando" para obtener más detalles de una en particular.
-d (desensamblar): Puesto sin parámetros va a desensamblar unas cuantas líneas a partir de la línea de código que toca ejecutar ahora. Con parámetro podremos hacerlo sobre una dirección de memoria que deseemos.
-e (examine): Esto mostraría un trozo en hexadecimal/ASCII tal y como hacía la ventana inferior del Turbo Debugger, sobre la zona que indiquemos como parámetro.
- reg (register): Muestra el contenido de los registros (como la ventana derecha del Turbo Debugger)
-s (step) y n (next): Se trata de los dos equivalentes a step y trace que vimos en Turbo Debugger. Ejecutaremos con ellos paso a paso las instrucciones del fichero desensamblado, y a cada paso (cada vez que introduzcamos el comando), se nos mostrará la próxima instrucción a ejecutar y el contenido de los registros.
-load : Si hemos cargado el ALD sin poner como parámetro un fichero, con la órden load podemos cargarlo ahora (o elegir otro).
-r (restart): Recomienza el programa desde el principio.
-break/tbreak: Pone en la dirección que indiquemos un breakpoint (no volveré a explicar qué son). La diferencia entre ambos comandos es que break pone un breakpoint permanente (aunque luego podemos quitarlo), y tbreak un breakpoint temporal, que sólo funcionará una vez. Además, podremos usar algunos comandos más para gestionar los breakpoints (por ejemplo, lbreak lista los breakpoints actuales).
En fin, ya dije que esta sección dedicada al ALD iba a ser deliberadamente breve; no es pereza, sencillamente los conceptos básicos como qué es un breakpoint o para qué sirve un debugger ya los expliqué en la sección anterior. Turbo Debugger y ALD sirven para lo mismo y tienen la misma base de funcionamiento, sencillamente el entorno en que funcionan es diferente y es este el que he intentado introducir un poco.
Donde conseguirlo
La última versión del ALD se encuentra en http://ellipse.mcs.drexel.edu/ald.html (sólo para plataformas Intel x86)
Otras utilidades
Hay unas cuantas utilidades más perfectas para el escritor de virus y que han sin duda de formar parte de su kit de supervivencia, aunque se vayan incorporando poco a poco con el tiempo. De nuevo os recuerdo que aunque haya elegido el ALD como debugger para Linux y el TD32 como debugger para Windows, no son los mejores en absoluto (aunque cumplen su función), sino simplemente los más sencillos de manejar. La idea es que en un par de minutos estéis manejando cualquiera de los dos, pero si os metéis en serio, es recomendable echarle un poco de tiempo y aprender a manejar el GDB para Linux (que viene en toda distribución), y el Softice para Windows, que podéis buscar a través de la página de Darknode en la misma sección que el TASM.
Un apartado que no voy a desarrollar es el de los desensambladores. Tenemos el IDA disassembler, que viene con un editor de texto integrado para comentar el código desensamblado y unas cuantas pijaditas más (es muy bueno, en serio).
|
Opiniona sobre 'Curso de programación de virus - Ensamblador VIII' (122)
Opina sobre este curso gratis |


