|
El primero de los pasos de cara a los últimos chequeos
y a la posterior infección del fichero es más o menos
la piedra angular de toda rutina de infección nativa de
Win32 que se precie: el mapeo de ficheros en memoria.
Esta técnica, también descubierta por el autor Jacky
Qwerty, y empleada desde entonces en la inmensa mayoría
de los virus de Win32, viene a ser una simplificación
en cuanto a manejo de ficheros con respecto al obsoleto
sistema de apertura empleado en los virus de DOS y en
los ejemplares más primitivos de Win32.
La principal ventaja para los escritores de virus radica
en el hecho de que, por medio del mapeo de ficheros, no
es necesario emplear funciones de lectura, ni de
escritura, ni de movimiento de la posición del puntero
dentro del fichero abierto. Es mucho más fácil que eso.
Al mapear un fichero en memoria se obtiene una dirección
en EAX a partir de la cual Win32 ha colocado una imagen
íntegra del fichero que hemos especificado, y basta con
referenciar cualquier desplazamiento de dicha imagen
por medio de su dirección real para poder trabajar en
él, sin necesidad de punteros. Asimismo, una vez que se
desmapea el fichero, cualquier cambio efectuado en su
código durante el mapeo queda automáticamente guardado,
sin necesidad de recurrir a la función de escritura. Y
no sólo se trata de ventajas a la hora de programar,
sino también a la hora de correr lo que se ha programado,
ya que la rapidez de ejecución de un código en el que
se emplea mapeo de memoria es muy superior a la de uno
en el que se utilicen las tradicionales y obsoletas
funciones de lectura, escritura y movimiento de puntero,
y esto es algo que juega a favor de los virus, que en
todo momento buscan la velocidad y la optimización, con
el fin de pasar inadvertidos.
El mapeo de ficheros se lleva a cabo por medio de las
APIs CreateFileMapping y MapViewOfFile, las cuales
necesitan un handle del fichero a procesar, obtenido
a partir de la manida CreateFile, empleada la mayor
parte de las veces, a pesar de lo que pueda aparentar
su engañoso nombre, para abrir ficheros, en lugar de
para crearlos. Una vez que Girigat ha mapeado a una
posible víctima en memoria, procede a comprobar, por
orden, la presencia de la marca MZ (característica
intrínseca de todo fichero EXE), la de la marca de
infección del virus (un ";)", siguiendo con la línea
del autor), situada en el offset 12h de la cabecera
MZ del EXE, la existencia de una cabecera PE en la
dirección especificada por el puntero situado en el
desplazamiento 3ch (característica intrínseca de toda
aplicación nativa de Win32, es decir, EXEs de formato
PE con cualquier extensión), y, por último, comprueba
por medio de la sección Characteristics de la cabecera
PE que no se trata de una DLL renombrada. Si estos
chequeos no presentan ningún problema, el virus guarda
la fecha (día y mes) de la infección y cede el control
a lo que es la rutina de infección de PE en sí.
El método empleado por Girigat para infectar PEs es el
puesto de moda por, quién si no, Jacky Qwerty, que ha
creado toda una escuela de programación en lo que a
Win32 se refiere. Hoy en día se pueden distinguir, a
grandes rasgos, tres tipos de infección PE: la creación
de una nueva sección (el método más antiguo, empleado
por Quantum en su virus Bizatch), la adhesión del
cuerpo del virus al código de la última sección del
ejecutable (la técnica inventada por Jacky Qwerty), y,
por último, la puesta en práctica por parte de un
pequeño grupo de virus neocompanion, entre los cuales
quizás el más importante sea el Mylene de SSR, que
funcionan de manera independiente, instalándose en la
memoria del sistema como una aplicación legítima, aunque
su código en realidad es volcado por medio de aplicaciones
que fueron infectadas con anterioridad. El más extendido
de los citados es el segundo, ya que es más difícil de
detectar y, sin duda, pasa más desapercibido a los ojos
de aquellos usuarios de nivel medio que ocasionalmente
decidan inspeccionar por medio de un editor hexadecimal
la cabecera de las aplicaciones PE en las que sospechen
que se pueda esconder un virus.
La anexión de código vírico a la última sección no es
un proceso complicado, ni mucho menos: basta con obtener
el número de secciones de código, por medio del campo
NumberOfSections, y hacer unas sencillas operaciones
aritméticas para calcular, conociendo la longitud estándar
de cada sección (28h bytes), cuál es el punto de entrada
de la última sección de la aplicación PE que se pretende
infectar. Girigat lo tiene aún más fácil, debido a un
original y eficiente truco de programación: mientras lo
normal durante el proceso de API hooking es emplear
las funciones GetCurrentProcess y WriteProcessMemory
para parchear la IT del host, debido a la posibilidad
de causar un error de protección general en caso de
pretender escribir un código en ejecución directamente.
El motivo se esconde tras los atributos que posee cada
sección, que son capaces de restringir, entre otras
cosas, los permisos de escritura. Girigat mata en este
punto dos pájaros de un tiro, ya que procede a añadir
atributos de escritura a todas las secciones de las
aplicaciones que infecta. De esta forma no se tiene
que preocupar de emplear WriteProcessMemory en su
rutina de residencia por proceso, y, como recompensa,
además tiene más fácil la localización del punto de
entrada de la última sección, ya que, con simplemente
restar el tamaño de ésta (28h) tras efectuar el bucle
de parcheo, el trabajo está hecho. A partir de aquí, el
virus ha de obtener el tamaño de la sección, el valor
de alineamiento del ejecutable, y el punto de entrada
de la aplicación, perteneciendo estos dos últimos
campos a la cabecera PE, mientras que el primero está
ubicado en la última sección. Un punto importante para
cualquier virus es no confundir SizeOfRawData, que es
el tamaño real del código de una sección, con el valor
especificado en VirtualSize, que es el tamaño mínimo
de la sección una vez adaptado a 32 bits por medio del
alineamiento por defecto de la aplicación a la que
pertenece. Manejando estos valores, Girigat calcula el
final de la última sección (añadiendo SizeOfRawData
al punto de entrada de la sección), fija esa dirección
como punto de entrada de la aplicación (en el offset
28h de la cabecera PE), anexa su código a partir de
ese punto de la última sección, y modifica los valores
correspondientes al SizeOfRawData y al VirtualSize
(por medio del FileAlignment), así como SizeOfImage,
perteneciente a la cabecera PE. Este último campo es, en
la mayoría de los virus, objeto de olvido, y la
consecuencia más inmediata es la posibilidad de causar
errores de protección general en algunas aplicaciones
al trabajar bajo WindowsNT. Durante el proceso, y de
manera desornenada con el fin de optimizar bytes, Girigat
obtiene las RVAs necesarias para su posterior proceso de
inicialización, tal y como describimos en el primer
capítulo de este análisis.
Es importante fijarse en este tipo de detalles dentro de
la "oscuridad" intrínseca de la mayoría de las rutinas
de infección, que suelen seguir unas pautas determinadas
y ofrecen pocas posibilidades de innovación a los autores
de virus, que en algunos casos se limitan a copiar los
procedimientos de infección de otros virus, como ocurría
con otro tipo de rutinas estándar en el DOS, como, por
ejemplo, las de ocultación o stealth.
|