💾 Archived View for mirrors.apple2.org.za › active › 4am › images › games › action › Ballblazer%20(4… captured on 2024-08-18 at 17:25:10.

View Raw

More Information

⬅️ Previous capture (2023-01-29)

-=-=-=-=-=-=-

---------------Ballblazer--------------
A 4am crack                  2015-05-13
---------------------------------------

Name: Ballblazer
Genre: arcade
Year: 1985
Authors: K-BYTE
Publisher: Lucasfilm, Ltd.
Media: single-sided 5.25-inch floppy
OS: custom
Other versions: Hot Rod (file crack)

                   ~

               Chapter 0
 In Which Various Automated Tools Fail
          In Interesting Ways


COPYA
  immediate disk read error

Locksmith Fast Disk Backup
  unable to read T00,S0B; copy hangs on
  track $00

EDD 4 bit copy (no sync, no count)
  no read errors, but copy reboots
  endlessly

Copy ][+ nibble editor
  no recognizable structure in the
  missing sector

Disk Fixer
  no recognizable code beyond track $00
  (but all tracks are readable)

Why didn't COPYA work?
  intentionally bad sector (T00,S0B)

Why didn't Locksmith FDB work?
  nibble check that relies on the
  intentionally bad sector (probably)

Why didn't my EDD copy work?
  ditto

Next steps:

  1. Trace the boot
  2. Bypass the nibble check
  3. There is no step 3 (I hope)

                   ~

               Chapter 1
    In Which Automation Inevitably
     Fails And Leaves Us Confused


[S6,D1=original disk]
[S5,D1=my work disk]

]PR#5
CAPTURING BOOT0
...reboots slot 6...
...reboots slot 5...
SAVING BOOT0

]BLOAD BOOT0,A$800
]CALL -151



0801-   A2 00       LDX   #$00
0803-   BD 70 09    LDA   $0970,X

Wait, what? $0900 isn't loaded yet.
Unless...



0800- 04

Oooooh. This disk relies on a little-
used (but fully supported) feature of
the disk controller ROM that can load
more than one sector from track $00
before transferring control to $0801.

My AUTOTRACE program doesn't notice
this (note to self: fix that someday).
I'll need to write a custom boot tracer
that captures all four sectors. (They
load sequentially into $900, $A00, and
$B00.)



; by the time this is called, all four
; sectors have been loaded, so copy all
; of them into the graphics page so
; they survive a reboot
96F8-   A2 04       LDX   #$04
96FA-   A0 00       LDY   #$00
96FC-   B9 00 08    LDA   $0800,Y
96FF-   99 00 28    STA   $2800,Y
9702-   C8          INY
9703-   D0 F7       BNE   $96FC
9705-   EE FE 96    INC   $96FE
9708-   EE 01 97    INC   $9701
970B-   CA          DEX
970C-   D0 EE       BNE   $96FC

; don't actually want to run anything
; yet, just turn off the slot 6 drive
; motor and reboot to my work disk
970E-   AD E8 C0    LDA   $C0E8
9711-   4C 00 C5    JMP   $C500


...reboots slot 6...
...reboots slot 5...

]BSAVE BOOT0,A$2800,L$400

                   ~

               Chapter 2
  In Which All The Real Work Happens


]BLOAD BOOT0,A$800
]CALL -151



; move some code to $0100 -- could be
; part of the boot, but given that it's
; only $29 bytes, I'm guessing it's
; The Badlands
0801-   A2 00       LDX   #$00
0803-   BD 70 09    LDA   $0970,X
0806-   9D 00 01    STA   $0100,X
0809-   E8          INX
080A-   E0 29       CPX   #$29
080C-   D0 F5       BNE   $0803



; definitely The Badlands
0970-   A0 00       LDY   #$00
0972-   99 00 08    STA   $0800,Y
0975-   99 00 0A    STA   $0A00,Y
0978-   99 00 BE    STA   $BE00,Y
097B-   99 00 BF    STA   $BF00,Y
097E-   C8          INY
097F-   D0 F1       BNE   $0972
0981-   A9 0A       LDA   #$0A
0983-   85 F7       STA   $F7
0985-   91 F6       STA   ($F6),Y
0987-   E6 F7       INC   $F7
0989-   A5 F7       LDA   $F7
098B-   C9 BE       CMP   #$BE
098D-   D0 F6       BNE   $0985
098F-   A9 0A       LDA   #$0A
0991-   85 F7       STA   $F7
0993-   C8          INY
0994-   D0 EF       BNE   $0985
0996-   6C F4 00    JMP   ($00F4)

Continuing...



; set all kinds of vectors (reset, BRK,
; &c.) to reboot the slot we booted
080E-   A5 2B       LDA   $2B
0810-   4A          LSR
0811-   4A          LSR
0812-   4A          LSR
0813-   4A          LSR
0814-   09 C0       ORA   #$C0
0816-   85 F5       STA   $F5
0818-   A9 00       LDA   #$00
081A-   85 F4       STA   $F4
081C-   AD 83 C0    LDA   $C083
081F-   AD 83 C0    LDA   $C083
0822-   A9 00       LDA   #$00
0824-   8D F0 03    STA   $03F0
0827-   8D F2 03    STA   $03F2
082A-   8D FC 03    STA   $03FC
082D-   8D FE 03    STA   $03FE
0830-   8D FA FF    STA   $FFFA
0833-   8D FC FF    STA   $FFFC
0836-   8D FE FF    STA   $FFFE
0839-   A9 01       LDA   #$01
083B-   8D F1 03    STA   $03F1
083E-   8D F3 03    STA   $03F3
0841-   8D FD 03    STA   $03FD
0844-   8D FF 03    STA   $03FF
0847-   8D FB FF    STA   $FFFB
084A-   8D FD FF    STA   $FFFD
084D-   8D FF FF    STA   $FFFF
0850-   49 A5       EOR   #$A5
0852-   8D F4 03    STA   $03F4

; move the last two sectors we loaded
; to the top of main memory
0855-   A0 00       LDY   #$00
0857-   B9 00 0A    LDA   $0A00,Y
085A-   99 00 BE    STA   $BE00,Y
085D-   B9 00 0B    LDA   $0B00,Y
0860-   99 00 BF    STA   $BF00,Y
0863-   C8          INY
0864-   D0 F1       BNE   $0857
0866-   84 F0       STY   $F0

; get slot (x16)
0868-   A6 2B       LDX   $2B

; some sort of death counter?
086A-   A9 10       LDA   #$10
086C-   85 FF       STA   $FF
086E-   C6 FF       DEC   $FF
0870-   D0 03       BNE   $0875

; when it hits 0, jump to The Badlands
0872-   4C 00 01    JMP   $0100

; subroutine looks for next available
; address field and stores the field
; data (disk volume, track, sector) in
; zero page, exactly like DOS 3.3
0875-   20 24 09    JSR   $0924

; if that didn't work, branch back and
; try again
0878-   B0 F4       BCS   $086E

; is it sector $08?
087A-   A5 2D       LDA   $2D
087C-   C9 08       CMP   #$08

; no, branch back and try again
087E-   D0 F5       BNE   $0875

; find a specific nibble sequence
; (given at $096C)
0880-   A0 00       LDY   #$00
0882-   BD 8C C0    LDA   $C08C,X
0885-   10 FB       BPL   $0882
0887-   D9 6C 09    CMP   $096C,Y
088A-   F0 08       BEQ   $0894
088C-   C0 00       CPY   #$00
088E-   F0 F2       BEQ   $0882
0890-   A0 00       LDY   #$00
0892-   F0 F3       BEQ   $0887
0894-   C8          INY
0895-   C0 04       CPY   #$04
0897-   D0 E9       BNE   $0882

; skip one more nibble
0899-   BD 8C C0    LDA   $C08C,X
089C-   10 FB       BPL   $0899

; crazy stuff (BMI instead of BPL here)
089E-   BD 8C C0    LDA   $C08C,X
08A1-   30 FB       BMI   $089E

; one more nibble
08A3-   BD 8C C0    LDA   $C08C,X
08A6-   10 FB       BPL   $08A3

; kill some time to get out of sync
; with the "proper" start of nibbles
08A8-   9D 8D C0    STA   $C08D,X
08AB-   EA          NOP

; more crazy stuff
08AC-   BD 8C C0    LDA   $C08C,X
08AF-   30 FB       BMI   $08AC
08B1-   BD 8C C0    LDA   $C08C,X
08B4-   10 FB       BPL   $08B1
08B6-   BD 8C C0    LDA   $C08C,X
08B9-   30 FB       BMI   $08B6
08BB-   BD 8C C0    LDA   $C08C,X
08BE-   10 FB       BPL   $08BB

; calculate a running checksum of the
; desynced nibbles (in $70/$71)
08C0-   A0 0F       LDY   #$0F
08C2-   A9 00       LDA   #$00
08C4-   85 70       STA   $70
08C6-   85 71       STA   $71
08C8-   18          CLC
08C9-   BD 8C C0    LDA   $C08C,X
08CC-   10 FB       BPL   $08C9
08CE-   99 00 10    STA   $1000,Y
08D1-   45 70       EOR   $70
08D3-   85 70       STA   $70
08D5-   65 71       ADC   $71
08D7-   85 71       STA   $71
08D9-   88          DEY
08DA-   10 ED       BPL   $08C9

; use part of this checksum as an index
; into the raw nibbles we stored at
; $1000
08DC-   A5 70       LDA   $70
08DE-   29 0F       AND   #$0F
08E0-   A8          TAY

; store some magic number in page 3
08E1-   B9 00 10    LDA   $1000,Y
08E4-   8D F0 03    STA   $03F0
08E7-   C8          INY
08E8-   98          TYA
08E9-   29 0F       AND   #$0F
08EB-   A8          TAY

; store another magic number in page 3
08EC-   B9 00 10    LDA   $1000,Y
08EF-   8D F1 03    STA   $03F1

; verify other part of the checksum
08F2-   A5 71       LDA   $71
08F4-   C9 02       CMP   #$02
08F6-   F0 03       BEQ   $08FB

; if checksum verification fails, start
; over
08F8-   4C 6E 08    JMP   $086E

; execution continues here (from $08F6)
; clear all temporary storage
08FB-   A9 00       LDA   #$00
08FD-   85 70       STA   $70
08FF-   85 71       STA   $71
0901-   A0 20       LDY   #$20
0903-   99 00 10    STA   $1000,Y
0906-   88          DEY
0907-   10 FA       BPL   $0903

; this appears to be real code (legit
; part of the RWTS, read from track $00
; and moved to the top of main memory)
0909-   20 00 BF    JSR   $BF00
090C-   A5 F0       LDA   $F0
090E-   4A          LSR
090F-   85 41       STA   $41
0911-   A0 00       LDY   #$00
0913-   84 F3       STY   $F3
0915-   84 26       STY   $26
0917-   A9 60       LDA   #$60
0919-   85 27       STA   $27
091B-   A9 00       LDA   #$00
091D-   85 3D       STA   $3D
091F-   A6 2B       LDX   $2B
0921-   4C 00 BE    JMP   $BE00

If the nibble check passes, there are
two side effects, magic numbers stored
in $03F0 and $03F1. I'm guessing those
are important. A quick scan through the
RWTS confirms this.


.
. [manual scan looking for $03F0/$03F1]
.


; heart of nibble-to-byte conversion
BE79-   A0 00       LDY   #$00
BE7B-   A2 56       LDX   #$56
BE7D-   CA          DEX
BE7E-   30 FB       BMI   $BE7B
BE80-   B1 26       LDA   ($26),Y
BE82-   5E 00 03    LSR   $0300,X
BE85-   2A          ROL
BE86-   5E 00 03    LSR   $0300,X
BE89-   2A          ROL
BE8A-   38          SEC
BE8B-   ED F1 03    SBC   $03F1   <-- !
BE8E-   4D F0 03    EOR   $03F0   <-- !
BE91-   91 26       STA   ($26),Y
BE93-   C8          INY
BE94-   D0 E7       BNE   $BE7D

Wow, those magic numbers are embedded
in the RWTS itself. Every byte of every
sector is encrypted with a code that
depends on those two magic numbers we
calculated during the nibble check.
(This explains why I didn't see any
code on the rest of the disk -- it's
all encrypted!)

Let's capture those values.

                   ~

               Chapter 3
     In Which We Capture The Flag
     And Hit The Home Stretch And
     Mix Metaphors But That's OK



...
]CALL -151


; set up callback after nibble check
; succeeds
96F8-   A9 4C       LDA   #$4C
96FA-   8D FB 08    STA   $08FB
96FD-   A9 0A       LDA   #$0A
96FF-   8D FC 08    STA   $08FC
9702-   A9 97       LDA   #$97
9704-   8D FD 08    STA   $08FD

; start the boot
9707-   4C 01 08    JMP   $0801

; callback is here -- copy page 3 to
; graphics page so it survives a reboot
970A-   A0 00       LDY   #$00
970C-   B9 00 03    LDA   $0300,Y
970F-   99 00 23    STA   $2300,Y
9712-   C8          INY
9713-   D0 F7       BNE   $970C

; switch back to ROM (this was switched
; while setting reset vectors and never
; switched back)
9715-   AD 82 C0    LDA   $C082

; turn off slot 6 drive motor and
; reboot to my work disk
9718-   AD E8 C0    LDA   $C0E8
971B-   4C 00 C5    JMP   $C500


...reboots slot 6...
...reboots slot 5...

]BSAVE BOOT1 0300-03FF,A$2300,L$100
]CALL -151



23F0- D7 F9

To bypass this, I can set those values
unconditionally and jump over the
nibble check. The hostile code starts
at $086A, and the legitimate code at
$0909. Thus:

086A-   A9 D7       LDA   #$D7
086C-   8D F0 03    STA   $03F0
086F-   A9 F9       LDA   #$F9
0871-   8D F1 03    STA   $03F1
0874-   4C 09 09    JMP   $0909

T00,S00,$6A change
"A9 10 85 FF C6 FF D0 03 4C 00 01 4C"
                to
"A9 D7 8D F0 A9 F9 8D F1 03 4C 09 09"

Quod erat liberandum.

---------------------------------------
A 4am crack                     No. 311
------------------EOF------------------