Curso de programación de virus - Ensamblador V: Emsamblador avanzado

9 - Ensamblador V: Emsamblador avanzado

[editar]
Curso gratis creado por Wintermute.
22 de Febrero de 2006
En este capítulo vamos a presentar algunos conceptos avanzados en la programación en lenguaje ensamblador, como la relación con la API. También, listaré toda una serie de instrucciones que me parecen importantes a la hora de programar, y que aún no han sido mencionadas. Todo, irá acompañado de ejemplos de código, que a estas alturas ya deberíamos de saber manejar un poco.

Así como los apartados 5.1, 5.2 y 5.3 son bastante importantes, a quienes se vean abrumados por todo esto ya les digo que pueden saltarse tranquilamente el apartado 5.4 (dedicado al coprocesador) . Se van a perder poco si se los saltan en el sentido de que si les resulta ya agotador todo lo aprendido hasta el momento, esto puede que les despiste del verdadero objetivo en el sentido de que no es necesario entenderlo para escribir virus ni para aprender ensamblador; se trata de un poquito de "cultura general" sobre cómo funcionan las cosas internamente (en cualquier caso he intentado dar una visión poco profunda en ese apartado precisamente para no marear a nadie).

Interrupciones y API

Introducción

La API es, como decíamos en el segundo capítulo de este tutorial, la herramienta por la cual nos comunicamos con el sistema operativo y las funciones que este tiene para hacernos la vida más fácil. Una operación puede ser abrir un fichero, escribir sobre él, cambiar el directorio actual o escribir en la pantalla.

Hay dos métodos que vamos a ver en que se usa API del sistema operativo; por interrupciones pasando los parámetros en registros como hace Ms-Dos, por llamadas a subrutina como hace Win32, y un híbrido con llamadas a interrupción y paso de parámetros en pila, el sistema operativo Linux.

Interrupciones en Ms-Dos

Vale, lo primero que hay que tener en la cabeza es que en Ms-Dos *todo* se hace a través de interrupciones; y que distintas interrupciones llaman a servicios orientados hacia algo distinto.

¿Qué es una interrupción software?. Se trata de un tipo muy especial de llamada a una función del sistema operativo (o de otros programas residentes, el sistema es bastante flexible). La instrucción para hacerlo es INT, y viene acompañada siempre de un número del 0 al 255 (decimal), es decir, del 00h al 0FFh en hexadecimal.

¿Dónde se va la ejecución cuando escribimos por ejemplo "INT 21h"? Bien, en Ms-Dos, en la posición de memoria 0000:0000 (en Ms-Dos usamos un direccionamiento de 16 bits pero paso de explicarlo porque a estas alturas es un tanto ridículo jugar con el Ms-Dos) hay una "Tabla de Vectores de Interrupción" o IVT. Esta IVT, contiene 256 valores que apuntan a distintas direcciones de memoria, a las que va a saltar la ejecución cuando se haga una INT.

Entonces, si escribimos algo como "INT 21h", lo que va a hacer es leer en la posición de memoria 0000 + (21*4), el valor que hay, para luego pasar (como si fuera un CALL, empujando en la pila la dirección de retorno) a ejecutar en esa posición de memoria. En realidad, la única diferencia con un CALL es que no le indicamos la dirección a la que saltar (el procesador la lee de la tabla de interrupciones), y que además de empujar el valor de la dirección de retorno, se empuja también el registro de FLAGS.

Por ello, cuando se acaba de ejecutar el servicio solicitado de la interrupción, esta rutina no acaba en un RET, sino en lo que antes habíamos mencionado, en un IRET; la función de esta instrucción es sencilla: saca la dirección de retorno y los flags, en lugar de tan sólo la dirección de retorno.

Como ejemplo práctico, el tipo de función en Ms-Dos dentro de una interrupción suele indicarse en EAX, y los parámetros en el resto de registros (EBX, ECX y EDX normalmente). Por ejemplo, cuando queramos abrir un fichero como sólo lectura tenemos que hacer lo siguiente:

mov ax, 3D02h ; el 3D indica "abrir fichero", y el 02h indica "en lectura y escritura" mov dx, offset Fichero ; Apuntamos al nombre del fichero int 21h ; Ahora, se abrirá el fichero (paso de explicar todavia qué es un handler xD) Fichero: db 'fichero.txt',0

Bueno, nos hemos encontrado (qué remedio) una cosa nueva de la que no habíamos hablado antes... esto de "db" significa "data byte", vamos, que estamos indicando datos "a pelo", en este caso el nombre de un fichero. Y sí, hay una coma, indicando que después de esos datos "a pelo" se ponga un byte con valor cero (para delimitar el fin del nombre del fichero). DX va a apuntar a ese nombre de fichero y AX indica la función... y voilá, fichero abierto.

Destacar otra cosa: existen dos instrucciones que sirven para activar o inhabilitar las interrupciones (ojo, que inhabilitar no las deshace por completo, pero sí impide la mayor parte; es útil por ejemplo al cambiar los valores de SS/SP para que no nos pete en la cara). CLI (CLear Interrupts) inhabilita las interrupciones, y STI (SeT Interrupts) las activa.

Otra cosa: se puede ver que no estoy usando registros extendidos, que uso AX y DX en vez de EAX y EDX... en fin, recordad hace cuánto que existe el Ms-Dos y así respondéis a la pregunta :-)

Y en fin, que esto es el sistema de interrupciones en Ms-Dos, que me niego a volver a tocar porque es perder el tiempo: a quien le interese que escriba en un buscador algo así como "Ralf Brown Interrupt List", que es una lista salvaje que tiene todas las funciones habidas y por haber para interrupciones de Ms-Dos. Las más importantes están dentro de la INT 21h, que controla cosas como el acceso a ficheros (creacion, lectura/escritura, borrado...) y directorios.

La Int80h y Linux

En Linux pasa tres cuartas de lo mismo, pero todas las funciones del sistema están reunidas bajo una sóla interrupción, la 80h. Vamos a tener 256 posibilidades, que se indican en AL (bueno, podemos hacer un MOV EAX, igualmente).

Hay algunas diferencias básicas con el sistema de Ms-Dos. La primera es más "teórica" y hace referencia a la seguridad. Cuando estamos ejecutando normalmente, el procesador tiene privilegio de "usuario". Cuando llamamos a la INT80h, pasamos a estado de supervisor y el control de todo lo toma el kernel. Al terminar de ejecutarse la interrupción, el procesador vuelve a estar en sistema usuario (y por supuesto con nivel de usuario el proceso no puede tocar la tabla de interrupciones). Con Ms-Dos digamos que siempre estamos en supervisor, podemos cambiar los valores que nos salga de la tabla de interrupciones y hasta escribir sobre el kernel... pero vamos, lo importante, que con este sistema, en Linux está todo "cerrado", no hay fisuras (excepto posibles "bugs", que son corregidos, a diferencia de Windows).

Respecto al paso de parámetros, se utilizan por órden EBX, ECX, etc (y si la función de interrupción requiere muchos parámetros y no caben en los registros, lo que se hace es almacenar en EBX un puntero a los parámetros.

mov eax, 05h ; Función OpenDir (para abrir un directorio para leer sus contenidos) lea ebx, [diractual] ; LEA funciona como un "MOV EBX, offset diractual"; es más cómodo. xor ecx, ecx int 080h diractual: db '.',0 ; Queremos que habra el directorio actual, o sea, el '.'

Para más información, recomiendo echar un vistazo a www.linuxassembly.org, de donde tiene que colgar algún link hacia listas de funciones. Y también cuidado porque aunque en Linux parece que todo está documentado hay mucho que o no lo está o incluso está mal (si alguien se ha tenido que mirar la estructura DIRENT sabrá de qué le hablo, es más difícil hacer un FindFirst/FindNext en ASM en Linux que infectar un ELF xD)

DLL's y llamadas en Windows

Bajo Win32 (95/98/Me/NT/etc) no vamos a utilizar interrupciones por norma general. Resulta que la mayor parte de funciones del sistema están en una sóla librería, "KERNEL32.DLL", que suelen importar todos los programas.

DLL significa Librería Dinámica, y no solo tiene porque tener funciones "básicas" (por ejemplo, en Wininet.DLL hay toda una serie de funciones de alto nivel como enviar/coger fichero por FTP). Lo que sucede es que cuando un programa quiere hacer algo así (pongamos estas funciones de FTP) tiene dos posibilidades: uno, las incorpora a su código, y dos, las coge de una librería dinámica. ¿Cuál es la ventaja de esto? Bien, cuando usamos una librería dinámica no tenemos que tener diez copias de esa rutina en cada uno de los programas compilados; al estar en la DLL, el primer programa que la necesite la pide, la DLL se carga en memoria, y se usa una sóla copia en memoria para todos los programas que pidan servicios de ella.

En palabras sencillas; tenemos la función MessageBox, por ejemplo, que abre una ventana en pantalla mostrando un mensaje y con algún botón del tipo OK, Cancelar y tal. ¿Qué es más eficiente, tener una librería que sea consultada por cada programa, o tener una copia en cada uno?. Si cada programa ocupa 100Kb de media y la librería 10Kb, al arrancar 10 veces el programa si tuviéramos el MessageBox en DLLs, el espacio en memoria sería de 1010Kb (y en disco, igual). En caso de que no usáramos DLLs y la función MessageBox estuviera en cada programa, tendríamos 1100Kb de memoria ocupada (y de disco). Por cierto, que el Linux también usa librerías dinámicas, sólo que para programar en ASM sobre él normalmente nos va a sobrar con lo que tengamos en la Int80h.

Volviendo al tema, la forma de llamar a una función de la API en Win32 es como lo que comentábamos de paso de parámetros al final del apartado dedicado a subrutinas. Todos los valores que han de pasársele a la función se empujan a la pila, y luego se hace un CALL a la dirección de la rutina. El aspecto de una llamada a la API de Win32 (exáctamente a MessageBox), es así:

push MB_ICONEXCLAMATION ; El tipo de ventana a mostrar push offset Azathoth ; Esto, el título de la ventana que va a aparecer. push offset WriteOurText ; Y esto el texto de dentro de la ventana. push NULL call MessageBoxA ; Llamamos tras empujar los parámetros, y luego seguimos ejecutando

|| WriteOurText: texto. || db || 'H0 H0 H0 NOW I HAVE A MACHINE GUN',0 || ; El 0 delimita el final del ||
|| Azathoth: || || db || 'Hiz :P',0 || ||
|| || || || || ||

La mayoría de las funciones que se van a utilizar están en KERNEL32.DLL. No obstantes hay otras, como USER.DLL, bastante importantes. Podemos ver si un ejecutable las importa si están en su "tabla de importaciones" dentro del fichero (es decir, que está indicado que se usen funciones de ellas). Una de las cosas más interesantes sobre este sistema, será que podemos cargar DLLs (con la API LoadLibrary) aún cuando ya se haya cargado el programa, y proveernos de servicios que nos interesen.

Una lista bastante interesante de funciones de la API de Windows está en el típico CD del SDK de Windows; puede encontrarse también por la Red, se llama win32.hlp y ocupa más de 20Mb (descomprimido).

Representación de datos, etiquetas y comentarios

Buena parte de lo que voy a explicar ahora ha aparecido irremediablemente en ejemplos de código anteriores; no obstante, creo que ya es hora de "formalizarlo" y detallarlo un poco. Así pues, hablemos de estos formalismos utilizados en lenguaje ensamblador (tomaré como base los del Tasm, algunos de los cuales lamentablemente no son aplicables al Nasm de Linux):

Datos

La forma más básica de representar datos "raw", o sea, "a pelo", es usar DB, DW o DD. Como se puede uno imaginar, B significa byte, W word y D Dword (es decir, 8, 16 y 32 bits). Cuando queramos usar una cadena de texto - que encerraremos entre comillas simples -, usaremos DB. Así, son válidas expresiones como las siguientes:

db 00h, 7Fh, 0FFh, 0BAh dw 5151h dd 18E7A819h db 'Esto también es una cadena de datos' db 'Y así también',0 db ?,?,? ; así también... esto indica que son 3 bytes cuyo valor nos es indiferente.

Hay una segunda forma de representar datos que se utiliza cuando necesitamos poner una cantidad grande de ellos sin describir cada uno. Por ejemplo, pongamos que necesito un espacio vacío de 200h bytes cuyo contenido quiero que sea "0". En lugar de escribirlos a pelo, hacemos algo como esto:

db 200h dup (0h)

Etiquetas

Ya hemos visto el modo más sencillo de poner una etiqueta; usar un nombre (ojo, que hay que estar pendiente con mayúsculas/minúsculas porque para ensambladores como Tasm, "Datos" no es lo mismo que "dAtos" o que "datos"), seguido de un símbolo de ":". Cualquier referencia a esa etiqueta (como por ejemplo, MOV EAX,[Datos]), la utiliza para señalar el lugar donde ha de actuar.

Pero hay más formas de hacer referencias de este tipo; podemos marcar con etiqueta un byte escribiendo el nombre y a continuación "label byte" (etiquetar byte). Un ejemplo (y de paso muestro algo más sobre lo que se puede hacer) sería esto:

virus_init label byte <código> <código> virus_end label byte virus_length equ virus_end - virus_init

Parece que siempre que meto un ejemplo saco algo nuevo de lo que antes no había hablado... pero bueno, creo que se entiende; marcamos con label byte inicio y fin del virus, y hacemos que el valor virus_length equivalga gracias al uso de "equ", a la diferencia entre ambos (es decir, que si el código encerrado entre ambas etiquetas ocupa 300 bytes, si hacemos un "MOV EAX, virus_length" en nuestro código, EAX pasará a valer 300).

Comentarios

Conocemos en este punto de sobra la forma standard de incluir comentarios al código, esto es, utilizando el punto y coma. Todo lo que quede a la derecha del punto y coma será ignorado por el programa ensamblador, con lo que lo utilizaremos como comentarios al código.

Hay otro método interesante presente en el ensamblador Tasm, que señala el inicio de un comentario por la existencia de la cadena "Comment %". Todo lo que vaya después de esto será ignorado por el ensamblador hasta que encuentre otro "%", que marcará el final:

Comment % Esto es un comentario para Tasm y puedes escribir lo que quieras entre los porcentajes. %
[editar]

110 opiniones

q buena

pues tios les digo q lo aprendan por que por ejemplo si hacen un viruz pero lo hacen con lo que es ensamblador pues asi el tio formatee su pc seguira infectado ademas es recomendado si piensan en conocimiento i en aprender mas de esto


salu2
Duda.

Sacado de: "2 - estructura de computadores"
"dado que los ejemplos nunca sobran, veamos una instrucción como cmp ac, 12. Una vez llegue tras la fase de fetch a la unidad de control, de nuevo se utilizará la alu; en esta ocasión se la indicará mediante señales de control que realice la operación de resta (sub), metiendo por un lado el 12 y por otro el registro ac"

¿por qué hará la operación sub? ¿no sería menos costoso hacer la operacion xor?
¿si el resultado de aplicar dos oprendos a xor da 0 implica que son el mismo número, no?

un saludo!.
Falta pewrsec.

Probe ensamblar el codigo del primer ejemplo pero no encuentra las referencias a las funciones de las apis no existe mas la web donde estan los recursos http://www.oninet.es/usuarios/darknode

me parece muy bueno el curso. Es imposible encontrar algo de ensamlador de 32 bits en español. Entiendo ingles pero ya me produce nauseas tener que leer en ese idioma todo la informacion sobre programacion. Lo unico que le falta es que el autor le de una actualizacion (revision).
Buenisimo.

En realidad me encanto el curso,a demas entendible. Y eso que apenas yo estoy aprendiendo sobre estos temas, pero vaya esta muy claro. Muchas gracias por su ayuda.
Virus.

Pues aqui hay un virus:
abres block de notas y pones lo siguiente:
@echo off
echo... :adcc
msg * adcc=windows_live_447@hotmail.com
goto adcc
se guarda en. Bat
ej: hola. Bat.
1 2 3 4 5 6 7 ... 22 | siguiente >

Cursos gratis relacionados con 'Curso de programación de virus'

La meta de este curso es el aprendizaje de métodos en programación, tanto en teoría... Más »
Completo curso acerca de los virus informáticos, historia, clasificación, protección...
Completo curso de lenguaje ensamblador.
En este glosario, lo primero que se ha de definir es la palabra HACKER ya... Más »
Curso de introducción al Comercio Electrónico.

Autor y licencia de 'Curso de programación de virus'


Curso gratis de Wintermute. Extraido de: CopyLeft
Este contenido ha sido recopilado por el equipo de Wikilearning. Todo el contenido recopilado se ha obtenido respetando y comunicando en nuestro site la licencia de cada fuente.
Wikilearning tiene permiso expreso por escrito de los autores para publicar los contenidos que ha extraído de otras webs, incluyendo su uso comercial.