💾 Archived View for mirrors.apple2.org.za › active › 4am › images › games › action › arkanoid › Arka… captured on 2024-08-19 at 03:41:15.
View Raw
More Information
-=-=-=-=-=-=-
----------------Arkanoid---------------
A 4am crack 2015-03-03
---------------------------------------
Name: Arkanoid
Genre: arcade
Year: 1988
Publisher: Taito America Corp.
Media: single-sided 5.25-inch floppy
OS: custom
Other versions: an uncredited crack on
Asimov
_________________________
{ }
{ "Brave Clarice. You }
{ will let me know when }
{ those lambs stop }
{ screaming, won't you?" }
{ }
{ Silence of the Lambs }
{_________________________}
~
Chapter 0
In Which Various Automated Tools Fail
In Interesting Ways
COPYA
no read errors, but copy squeals and
crashes
Locksmith Fast Disk Backup
ditto
EDD 4 bit copy (no sync, no count)
ditto
Disk Fixer
T00 -> custom bootloader
T11 -> DOS 3.3 disk catalog with one
file ("HELLO") which is blank
no other signs of DOS, so I assume
this is just a diversion
Why didn't any of my copies work?
Probably a nibble check during boot.
It's definitely intentional code
detecting that the disk is a copy.
(Seriously, it *squeals*.)
Next steps:
1. Boot trace
2. Find and disable the nibble check
3. Record the squeal for posterity
~
Chapter 1
In Which We Wake Up In The Dark
[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
; create denibblization table (copied
; verbatim from drive controller ROM
; routine at $C602..$C620)
0801- A0 00 LDY #$00
0803- A2 03 LDX #$03
0805- 86 3C STX $3C
0807- 8A TXA
0808- 0A ASL
0809- 24 3C BIT $3C
080B- F0 10 BEQ $081D
080D- 05 3C ORA $3C
080F- 49 FF EOR #$FF
0811- 29 7E AND #$7E
0813- B0 08 BCS $081D
0815- 4A LSR
0816- D0 FB BNE $0813
0818- 98 TYA
0819- 9D 80 09 STA $0980,X
081C- C8 INY
081D- E8 INX
081E- 10 E5 BPL $0805
; probably an address ($BC00)
0820- A2 00 LDX #$00
0822- 86 80 STX $80
0824- A2 BC LDX #$BC
0826- 86 81 STX $81
; maybe a sector count?
0828- A2 05 LDX #$05
082A- 8E FB 08 STX $08FB
; dunno
082D- A2 D8 LDX #$D8
082F- 86 47 STX $47
; assume slot 6 and get drive ready
0831- A2 60 LDX #$60
0833- BD 8E C0 LDA $C08E,X
0836- BD 8A C0 LDA $C08A,X
; find and parse next address field
0839- 20 56 08 JSR $0856
; is it the sector we wanted?
083C- AC FB 08 LDY $08FB
083F- B9 EB 08 LDA $08EB,Y
0842- C5 2D CMP $2D
; nope
0844- D0 F3 BNE $0839
; match data field prologue
0846- 20 8F 08 JSR $088F
; read sector data, store it in ($80)
0849- 20 D3 08 JSR $08D3
; decrement page
084C- C6 81 DEC $81
; decrement sector count
084E- CE FB 08 DEC $08FB
0851- D0 DA BNE $082D
; jump to boot1
0853- 4C 00 BC JMP $BC00
That is probably the least obfuscated
custom bootloader I've ever seen.
I can interrupt it at $0853 and capture
the boot1 code in $B800..$BCFF.
; set up callback at $0853 instead of
; continuing to $BC00
96F8- A9 05 LDA #$05
96FA- 8D 54 08 STA $0854
96FD- A9 97 LDA #$97
96FF- 8D 55 08 STA $0855
; start the boot
9702- 4C 01 08 JMP $0801
; callback is here -- relocate boot1 to
; graphics page so it survives a reboot
9705- A2 05 LDX #$05
9707- A0 00 LDY #$00
9709- B9 00 B8 LDA $B800,Y
970C- 99 00 28 STA $2800,Y
970F- C8 INY
9710- D0 F7 BNE $9709
9712- EE 0B 97 INC $970B
9715- EE 0E 97 INC $970E
9718- CA DEX
9719- D0 EE BNE $9709
; turn off slot 6 drive motor
971B- AD E8 C0 LDA $C0E8
; reboot to my work disk
971E- 4C 00 C5 JMP $C500
- BSAVE TRACE1,A$9600,L$121
- 9600G
...reboots slot 6...
...reboots slot 5...
]BSAVE BOOT1 B800-BCFF,A$2800,L$500
~
Chapter 2
In Which We Open The Gate
]CALL -151
; 80STOREOFF
BC00- 8D 00 C0 STA $C000
; read/write language card, RAM bank 1
BC03- AD 8B C0 LDA $C08B
BC06- AD 8B C0 LDA $C08B
; clear hi-res screen 1
BC09- A9 00 LDA #$00
BC0B- A8 TAY
BC0C- 85 00 STA $00
BC0E- A2 20 LDX #$20
BC10- 86 01 STX $01
BC12- 91 00 STA ($00),Y
BC14- C8 INY
BC15- D0 FB BNE $BC12
BC17- E6 01 INC $01
BC19- CA DEX
BC1A- D0 F6 BNE $BC12
; and show it
BC1C- AD 57 C0 LDA $C057
BC1F- AD 52 C0 LDA $C052
BC22- AD 54 C0 LDA $C054
BC25- AD 50 C0 LDA $C050
; loop
BC28- A9 00 LDA #$00
BC2A- 85 82 STA $82
BC2C- A2 00 LDX #$00
BC2E- 8E 9C BC STX $BC9C
BC31- 20 50 BC JSR $BC50
BC34- EE 9C BC INC $BC9C
BC37- AE 9C BC LDX $BC9C
BC3A- E0 04 CPX #$04
BC3C- D0 09 BNE $BC47
; when X hits 4, switch to RAM bank 2
BC3E- AD 83 C0 LDA $C083
BC41- AD 83 C0 LDA $C083
BC44- 4C 2E BC JMP $BC2E
; when X hits 9, we're done
BC47- E0 09 CPX #$09
BC49- D0 E3 BNE $BC2E
; jump to game code
BC4B- A2 60 LDX #$60
BC4D- 4C 00 40 JMP $4000
The main action happens in $BC50, which
is a multi-sector read loop driven by
four data tables.
; starting phase (track x2)
BC50- BD 78 BC LDA $BC78,X
BC53- A8 TAY
; starting memory page
BC54- BD 8A BC LDA $BC8A,X
BC57- 85 81 STA $81
; sector count
BC59- BD 93 BC LDA $BC93,X
BC5C- 8D 9D BC STA $BC9D
; starting sector
BC5F- BD 81 BC LDA $BC81,X
BC62- AA TAX
; read a sector
BC63- 20 00 B8 JSR $B800
; decrement sector count
BC66- CE 9D BC DEC $BC9D
BC69- F0 0C BEQ $BC77
; decrement memory page
BC6B- C6 81 DEC $81
; decrement sector
BC6D- CA DEX
BC6E- 10 F3 BPL $BC63
; decrement track
BC70- A2 0F LDX #$0F
BC72- 88 DEY
BC73- 88 DEY
BC74- 4C 63 BC JMP $BC63
BC77- 60 RTS
Here's the raw data:
BC78-14 12 12 04 04 02 00 00 40 ;phase
BC81-0D 0F 07 02 04 0F 0F 0B 03 ;sector
BC8A-ED BF B7 DE DD 1F 0C 06 43 ;page
BC93-0E 02 58 03 02 10 04 05 04 ;count
And here's how it translates into code:
1. read $0E sectors into $E000..$EDFF
starting from T0A,S0D
2. read $02 sectors into $BE00..$BFFF
starting from T09,S0F
3. read $58 sectors into $6000..$B7FF
starting from T09,S07
4. read $03 sectors into $DC00..$DEFF
starting from T02,S02
(main loop switches to RAM bank 2)
5. read $02 sectors into $DC00..$DDFF
starting from T02,S04
6. read $10 sectors into $1000..$1FFF
starting from T01,S0F
7. read $04 sectors into $0900..$0CFF
starting from T00,S0F
8. read $05 sectors into $0200..$06FF
starting from T00,S0B
9. read $04 sectors into $4000..$43FF
starting from T20,S03
My non-working copy gets this far.
Still no evidence of copy protection.
~
Chapter 3
In Which We Ask,
Of Each Particular Thing,
What Is It In Itself?
I need to interrupt the boot at $BC4D,
before it jumps to the game code at
$4000. This will be a little tricky
since my normal boot tracer (starting
at $9600) will be overwritten by the
loader. It looks like it doesn't touch
the stack page though, so I'll relocate
my callback there.
; set up callback #1
96F8- A9 05 LDA #$05
96FA- 8D 54 08 STA $0854
96FD- A9 97 LDA #$97
96FF- 8D 55 08 STA $0855
; start the boot
9702- 4C 01 08 JMP $0801
; callback #1 is here -- set up
; callback #2 at $0100
9705- A9 00 LDA #$00
9707- 8D 4E BC STA $BC4E
970A- A9 01 LDA #$01
970C- 8D 4F BC STA $BC4F
; copy callback #2 code to $0100
970F- A2 10 LDX #$10
9711- BD 1D 97 LDA $971D,X
9714- 9D 00 01 STA $0100,X
9717- CA DEX
9718- 10 F7 BPL $9711
; continue the boot
971A- 4C 00 BC JMP $BC00
; callback #2 is here (running at $100)
; turn off language card
971D- AD 82 C0 LDA $C082
; turn off slot 6 drive motor
9720- AD E8 C0 LDA $C0E8
; jump to monitor
9723- 4C 59 FF JMP $FF59
- BSAVE TRACE2,A$9600,L$126
- 9600G
...reboots slot 6...
<beep>
; slow down IIgs to 1 Mhz (does nothing
; on older machines)
4000- AD 36 C0 LDA $C036
4003- 29 7F AND #$7F
4005- 8D 36 C0 STA $C036
; here we go
4008- A9 00 LDA #$00
400A- 8D F7 40 STA $40F7
400D- 20 90 40 JSR $4090
; turn on slot 6 drive motor manually
; (always suspicious)
4090- A2 60 LDX #$60
4092- BD 8E C0 LDA $C08E,X
4095- BD 8A C0 LDA $C08A,X
; position drive head on track $21
4098- A9 42 LDA #$42
409A- 85 31 STA $31
409C- A2 60 LDX #$60
409E- 20 40 B8 JSR $B840
; zero some memory
40A1- A9 00 LDA #$00
40A3- 8D F9 40 STA $40F9
40A6- 8D F8 40 STA $40F8
40A9- A0 0F LDY #$0F
40AB- 99 FA 40 STA $40FA,Y
40AE- 88 DEY
40AF- 10 FA BPL $40AB
40B1- 20 B5 40 JSR $40B5
40B4- 60 RTS
; just a memory move
40B5- 20 0B 41 JSR $410B
40B8- EE F8 40 INC $40F8
40BB- A9 19 LDA #$19
40BD- 8D 7B 41 STA $417B
40C0- 8D 89 41 STA $4189
40C3- 20 19 41 JSR $4119
4119- A2 60 LDX #$60
411B- DD 8E C0 CMP $C08E,X
; wait loop
411E- A9 C0 LDA #$C0
4120- 8D 9F 41 STA $419F
4123- 8D A0 41 STA $41A0
4126- EE 9F 41 INC $419F
4129- D0 08 BNE $4133
412B- EE A0 41 INC $41A0
412E- D0 03 BNE $4133
4130- 4C 97 41 JMP $4197
; look for address prologue
4133- BD 8C C0 LDA $C08C,X
4136- 10 FB BPL $4133
4138- C9 D5 CMP #$D5
413A- D0 EA BNE $4126
413C- BD 8C C0 LDA $C08C,X
413F- 10 FB BPL $413C
4141- C9 AA CMP #$AA
4143- D0 E1 BNE $4126
4145- BD 8C C0 LDA $C08C,X
4148- 10 FB BPL $4145
414A- C9 96 CMP #$96
414C- D0 D8 BNE $4126
414E- 20 B0 41 JSR $41B0
4151- 20 B0 41 JSR $41B0
; get 4-4 encoded value from address
; field
41B0- BD 8C C0 LDA $C08C,X
41B3- 10 FB BPL $41B0
41B5- 38 SEC
41B6- 2A ROL
41B7- 8D C0 41 STA $41C0
41BA- BD 8C C0 LDA $C08C,X
41BD- 10 FB BPL $41BA
41BF- 29 00 AND #$00
41C1- 8D C0 41 STA $41C0
41C4- 60 RTS
; keep trying until we find track $21
4154- C9 21 CMP #$21
4156- 8D F7 40 STA $40F7
4159- D0 CB BNE $4126
; keep trying until we find sector $0F
415B- 20 B0 41 JSR $41B0
415E- C9 0F CMP #$0F
4160- D0 C4 BNE $4126
; look for a nibble sequence "D5 AA"
; which is the first 2/3 of the data
; prologue, but I'm betting there's
; another one in between the address
; and data fields at the end of T21
4162- BD 8C C0 LDA $C08C,X
4165- 10 FB BPL $4162
4167- C9 D5 CMP #$D5
4169- D0 F7 BNE $4162
416B- BD 8C C0 LDA $C08C,X
416E- 10 FB BPL $416B
4170- C9 AA CMP #$AA
4172- D0 F3 BNE $4167
; now do some math on the nibbles that
; follow
4174- A0 00 LDY #$00
4176- BD 8C C0 LDA $C08C,X
4179- 10 FB BPL $4176
417B- 19 C5 41 ORA $41C5,Y
417E- 99 C5 41 STA $41C5,Y
4181- C8 INY
4182- D0 F2 BNE $4176
4184- BD 8C C0 LDA $C08C,X
4187- 10 FB BPL $4184
4189- 19 C5 42 ORA $42C5,Y
418C- 99 C5 42 STA $42C5,Y
418F- C8 INY
4190- D0 F2 BNE $4184
; turn off drive motor
4192- 20 AA 41 JSR $41AA
4195- 18 CLC
4196- 60 RTS
4197- 20 0B 41 JSR $410B
419A- 20 AA 41 JSR $41AA
419D- 38 SEC
419E- 60 RTS
Execution continues at $40C6:
; if nibble check failed to find
; T21,S0F then just bail
40C6- B0 2D BCS $40F5
; more checks on the nibbles we read
; after T21,S0F
40C8- AD 20 43 LDA $4320
40CB- C9 99 CMP #$99
40CD- D0 26 BNE $40F5
40CF- A9 59 LDA #$59
40D1- 8D 7B 41 STA $417B
40D4- 8D 89 41 STA $4189
40D7- 20 19 41 JSR $4119
40DA- A2 00 LDX #$00
40DC- A0 0F LDY #$0F
40DE- B9 20 43 LDA $4320,Y
40E1- F0 01 BEQ $40E4
40E3- E8 INX
40E4- 88 DEY
40E5- 10 F7 BPL $40DE
40E7- FE FA 40 INC $40FA,X
40EA- E0 03 CPX #$03
40EC- 90 07 BCC $40F5
; if everything checks out, execution
; falls through to here
; $40F8 was initialized with 0 and only
; incremented once, so this decrements
; it back to 0
40EE- CE F8 40 DEC $40F8
40F1- EE F9 40 INC $40F9
40F4- 60 RTS
40F5- 60 RTS
Execution continues at $4010:
; check counter set by nibble check
4010- AD F8 40 LDA $40F8
; non-zero is bad, jump to The Badlands
4013- D0 03 BNE $4018
; success path falls through to here --
; jump to actual game code
4015- 4C 00 02 JMP $0200
; The Badlands
; switch to text screen (full of
; executable code at this point)
4018- AD 51 C0 LDA $C051
; $40F7 contains the track, and this
; prints it to the upper-left corner
; of the text screen (for debugging?)
401B- AD F7 40 LDA $40F7
401E- 4A LSR
401F- 4A LSR
4020- 4A LSR
4021- 4A LSR
4022- AA TAX
4023- BD 80 40 LDA $4080,X
4026- 8D 00 04 STA $0400
4029- AD F7 40 LDA $40F7
402C- 29 0F AND #$0F
402E- AA TAX
402F- BD 80 40 LDA $4080,X
4032- 8D 01 04 STA $0401
; and a space -- this is all very
; intentional and deliberate and weird
4035- A9 A0 LDA #$A0
4037- 8D 02 04 STA $0402
; now squeal
403A- 20 65 40 JSR $4065
; now crash
403D- 00 BRK
; The Squealing routine
4065- A2 00 LDX #$00
4067- A0 00 LDY #$00
4069- EE 30 C0 INC $C030
406C- A9 FF LDA #$FF
406E- A9 FF LDA #$FF
4070- A9 FF LDA #$FF
4072- A9 FF LDA #$FF
4074- CE 30 C0 DEC $C030
4077- E8 INX
4078- D0 EF BNE $4069
407A- C8 INY
407B- C0 FF CPY #$FF
407D- 90 EA BCC $4069
407F- 60 RTS
~
Chapter 4
In Which We Take One Byte
And Run Away As Fast As We Can
It looks like I can just disable the
branch at $4013. Changing the "BNE"
to a "BIT" will give me a one byte
patch:
T20,S00,$13 change "D0" to "24"
Quod erat liberandum.
---------------------------------------
A 4am crack No. 237
------------------EOF------------------