💾 Archived View for mirrors.apple2.org.za › active › 4am › images › games › action › Rad%20Warrior%2… captured on 2023-05-24 at 23:04:18.
⬅️ Previous capture (2023-01-29)
-=-=-=-=-=-=-
--------------Rad Warrior-------------- A 4am crack 2014-05-23 --------------------------------------- "Rad Warrior" is a 1987 arcade game by Palace Software; programmed by Stanley Schembri; plot and graphics by Daniel Malone; Apple translation by Craig Seastrom; distributed by Epyx, Inc. as part of their "MAXX OUT!" series. [The copy protection is identical to "The Movie Monster Game," also distributed by Epyx. This write-up is therefore quite similar to that one, with a few corrections.] Trying to copy the original disk with COPYA fails immediately with a disk read error. EDD 4 bit copy gives no read errors, but the copy it produces just reboots endlessly. Firing up my trusty Copy ][+ sector editor, I press "P" to get to the Sector Editor Patcher and selected "DOS 3.3 Patched." This option ignores the epilogue bytes after the address field and data field (normally "DE AA EB"). It doesn't work on every disk; lots of disks use non- standard prologue bytes as well, or don't use 16 sectors, or any one of a hundred different ways to deviate from the norm within the tolerance of the Disk II hardware. But it works with this one! Ignoring epilogue bytes, the sector editor can whiz through the entire disk and read all the data from every sector on every track. That means that the easiest way to copy this disk is to use COPYA (from the DOS 3.3 master disk), with one small modification: after booting, I'll go into the monitor and patch the DOS 3.3 RWTS to ignore epilogue bytes. This is similar to what the Copy ][+ Sector Editor Patcher does, and in most cases the difference is immaterial. [S6,D1=DOS 3.3 master disk] ]PR#6 ... ]CALL -151 *B942:18 <-- CLC to indicate no error reading epilogue bytes (was SEC, signifying an error) *3D0G ]RUN COPYA [S6,D1=original disk] [S6,D2=blank disk] ...read read read... ...grind grind grind... ...write write write... Unfortunately, the copy does not work; it just reboots endlessly. There must be some additional code designed to detect that the disk is not original. To find out where this code is, I'll need to trace the boot process until I find the offending code, and remove it or bypass it somehow. [S6,D1=original disk] [S5,D1=my work disk] ]PR#5 CAPTURING BOOT0 ...reboots slot 6... ...reboots slot 5... SAVING BOOT0 OK, Now I have the very early boot code (colloquially called "boot0") which is loaded by the disk controller ROM from track 0, sector 0. This code's job is to load the next section of code, commonly called "boot1" (usually by re-using part of the disk controller ROM routine at $C600). Then "boot1" either loads the rest of the disk operating system, or (in many commercial games) simply loads the game code directly off the disk and jumps to it. There are variations (of course), but that's the basic idea. Booting a disk is a cumulative process; each step builds on the previous steps. My work disk automatically runs a program called AUTOTRACE1, which interrupts the boot process immediately after the disk controller ROM routine loads the code from track 0, sector 0. It relocates this code (up to 256 bytes worth) to address $2800, then reboots and saves it to a file called BOOT0. I've seen a lot of boot0 code from different copy-protected (and not copy-protected) disks, so I've kind of developed a sense of what a "normal" boot looks like. I can look at almost-but-not-quite normal boot0 code and hone in pretty quickly on the part that's unusual. Maybe it calls a subroutine that isn't normally called, or it does things in a strange order, or it jumps to an unusual place after loading the boot1 code. ]CALL -151 *800<2800.28FFM *801L ... Everything here looks pretty normal (i.e. just like an unprotected DOS 3.3 disk), until it goes to jump to the boot1 code. Usually that happens with an indirect JMP ($08FD), which, in a normal boot0, will end up continuing execution at $B700 which is stored in track 0, sector 1. But in this case, I see: 084A- 4C 00 BB JMP $BB00 Highly suspect. I definitely want to see what evil lurks at $BB00. That area of memory is normally reserved for the denibblizing process when reading data from a sector. It's scratch space, essentially. It's overwritten every time the disk reads itself (after boot1 is loaded). But $BB00 isn't loaded yet, because I interrupted the boot process before it could be loaded. So now I need to trace the boot again, but a little bit further -- far enough for boot0 to load boot1 (including the suspicious code at $BB00), but no further. My work disk has another program, unimaginatively named AUTOTRACE1, which does just that. It loads track 0, sector 0, then patches the boot0 code at $084A to call back to a routine under my control (instead of jumping to the original disk's boot1 code). ]BRUN AUTOTRACE1 CAPTURING BOOT1 ...reboots slot 6... ...reboots slot 5... SAVING BOOT1 Let's see what we have. ]CALL -151 *B600<2000.29FFM *BB00L ; initialize some zero page addresses BB00- A9 00 LDA #$00 BB02- A2 F0 LDX #$F0 BB04- 9A TXS BB05- 95 00 STA $00,X BB07- E8 INX BB08- D0 FB BNE $BB05 BB0A- A9 0A LDA #$0A BB0C- 85 FC STA $FC ; Turn on the disk motor. Zero page $2B ; contains the slot number x 16. This ; is the standard way to access low- ; level disk commands. BB0E- A6 2B LDX $2B BB10- BD 89 C0 LDA $C089,X BB13- BD 8E C0 LDA $C08E,X ; a counter of some kind BB16- A9 80 LDA #$80 BB18- 85 FD STA $FD BB1A- C6 FD DEC $FD ; $BB98 is what I call "The Badlands" ; i.e. the code from which there is ; no return. Skipping ahead, I can see ; that $BB98 is one branch away from ; tweaking the reset vector and ; rebooting the computer. Which is, ; you know, not what we want. BB1C- F0 7A BEQ $BB98 ; $BBA5 looks for the standard address ; prologue, as a setup for the real ; test which comes next. BB1E- 20 A5 BB JSR $BBA5 ; If, for some reason, that doesn't ; work, off to The Badlands with you. BB21- B0 75 BCS $BB98 ; Search for a specific sequence of ; nibbles in the "dead zone" between ; the address field and data field. ; This area is normally not important, ; so COPYA didn't copy it precisely ; because normal disks don't care. ; (Actually, it's even more evil than ; that, because the original disk is ; written with timing bits in specific ; non-standard places between the ; nibbles in the dead zone. This code ; not only requires the right nibbles ; in the right order, it reads them ; just slightly faster than normal. So ; the timing bits need to be in the ; right places too, or the disk will ; get out of sync and read the wrong ; nibble values. This will trip up even ; the best bit copiers. And you can ; forget about making a disk image for ; emulators -- those don't store timing ; bits at all.) BB23- A5 F9 LDA $F9 BB25- C9 08 CMP #$08 BB27- D0 F1 BNE $BB1A BB29- A0 00 LDY #$00 BB2B- BD 8C C0 LDA $C08C,X BB2E- 10 FB BPL $BB2B BB30- 88 DEY BB31- F0 65 BEQ $BB98 BB33- C9 D5 CMP #$D5 BB35- D0 F4 BNE $BB2B BB37- A0 00 LDY #$00 BB39- BD 8C C0 LDA $C08C,X BB3C- 10 FB BPL $BB39 BB3E- 88 DEY BB3F- F0 57 BEQ $BB98 BB41- C9 E7 CMP #$E7 BB43- D0 F4 BNE $BB39 BB45- BD 8C C0 LDA $C08C,X BB48- 10 FB BPL $BB45 BB4A- C9 E7 CMP #$E7 BB4C- D0 4A BNE $BB98 BB4E- BD 8C C0 LDA $C08C,X BB51- 10 FB BPL $BB4E BB53- C9 E7 CMP #$E7 BB55- D0 41 BNE $BB98 BB57- BD 8D C0 LDA $C08D,X BB5A- A0 10 LDY #$10 BB5C- 24 80 BIT $80 BB5E- BD 8C C0 LDA $C08C,X BB61- 10 FB BPL $BB5E BB63- 88 DEY BB64- F0 32 BEQ $BB98 BB66- C9 EE CMP #$EE BB68- D0 F4 BNE $BB5E BB6A- EA NOP BB6B- EA NOP BB6C- A0 07 LDY #$07 BB6E- BD 8C C0 LDA $C08C,X BB71- 10 FB BPL $BB6E BB73- 99 F0 00 STA $00F0,Y BB76- EA NOP BB77- 88 DEY BB78- 10 F4 BPL $BB6E ; Wait, it gets better -- this disk ; actually uses the raw nibble data it ; stores in this "dead zone" AS THE ; DECRYPTION KEY FOR THE REST OF BOOT1 BB7A- A2 03 LDX #$03 BB7C- A9 00 LDA #$00 BB7E- A8 TAY BB7F- 85 F8 STA $F8 BB81- A9 B7 LDA #$B7 BB83- 85 F9 STA $F9 BB85- B5 F0 LDA $F0,X BB87- 51 F8 EOR ($F8),Y BB89- 91 F8 STA ($F8),Y BB8B- 88 DEY BB8C- D0 F7 BNE $BB85 BB8E- E6 F9 INC $F9 BB90- CA DEX BB91- 10 F2 BPL $BB85 ; now that the boot1 code is decrypted, ; jump to it as normal BB93- A6 2B LDX $2B BB95- 6C FD 08 JMP ($08FD) ; The Badlands BB98- C6 FC DEC $FC BB9A- F0 03 BEQ $BB9F BB9C- 4C 16 BB JMP $BB16 BB9F- EE F4 03 INC $03F4 BBA2- 6C FC FF JMP ($FFFC) ; subroutine (called from $BB1E) to ; find the next address prologue and ; skip over the address field to ; position the drive head to read the ; special nibbles in the dead zone BBA5- A0 FD LDY #$FD BBA7- 84 F0 STY $F0 BBA9- C8 INY BBAA- D0 04 BNE $BBB0 BBAC- E6 F0 INC $F0 BBAE- F0 3D BEQ $BBED BBB0- BD 8C C0 LDA $C08C,X BBB3- 10 FB BPL $BBB0 BBB5- C9 D5 CMP #$D5 BBB7- D0 F0 BNE $BBA9 BBB9- EA NOP BBBA- BD 8C C0 LDA $C08C,X BBBD- 10 FB BPL $BBBA BBBF- C9 AA CMP #$AA BBC1- D0 F2 BNE $BBB5 BBC3- A0 03 LDY #$03 BBC5- BD 8C C0 LDA $C08C,X BBC8- 10 FB BPL $BBC5 BBCA- C9 96 CMP #$96 BBCC- D0 E7 BNE $BBB5 BBCE- A9 00 LDA #$00 BBD0- 85 F1 STA $F1 BBD2- BD 8C C0 LDA $C08C,X BBD5- 10 FB BPL $BBD2 BBD7- 2A ROL BBD8- 85 F0 STA $F0 BBDA- BD 8C C0 LDA $C08C,X BBDD- 10 FB BPL $BBDA BBDF- 25 F0 AND $F0 BBE1- 99 F8 00 STA $00F8,Y BBE4- 45 F1 EOR $F1 BBE6- 88 DEY BBE7- 10 E7 BPL $BBD0 BBE9- A8 TAY BBEA- EA NOP BBEB- 18 CLC BBEC- 60 RTS BBED- 38 SEC BBEE- 60 RTS Because the next stage of the boot is encrypted, I can't simply bypass this copy protection routine. I need to let it run at least once, from the original disk, so that it can decrypt the rest of the code. The decryption happens at $BB7A..$BB92, then at $BB95 it does an indirect jump to ($08FD) to continue execution at the start of boot1 ($B700, just like a normal DOS 3.3 disk). TRACE1A will let this copy protection run (off the original disk), then interrupt the boot process immediately after the decryption loop, by setting $8FD/$8FE to point to a routine under my control. *9600<C600.C6FFM ; set up callback #1 (after boot0 but ; before copy protection runs) 96F8- A9 4C LDA #$4C 96FA- 8D 4A 08 STA $084A 96FD- A9 0A LDA #$0A 96FF- 8D 4B 08 STA $084B 9702- A9 97 LDA #$97 9704- 8D 4C 08 STA $084C ; start the boot 9707- 4C 01 08 JMP $0801 ; callback #1 starts here ; set up callback #2 (after copy ; protection runs but before it jumps ; to the decrypted boot1 code) 970A- A9 17 LDA #$17 970C- 8D FD 08 STA $08FD 970F- A9 97 LDA #$97 9711- 8D FE 08 STA $08FE ; continue the boot (run the copy ; protection) 9714- 4C 00 BB JMP $BB00 ; callback #2 starts here ; turn off the disk motor and capture ; the entire boot1 code, which has now ; been decrypted 9717- AD E8 C0 LDA $C0E8 971A- A2 0A LDX #$0A 971C- B9 00 B6 LDA $B600,Y 971F- 99 00 26 STA $2600,Y 9722- C8 INY 9723- D0 F7 BNE $971C 9725- EE 1E 97 INC $971E 9728- EE 21 97 INC $9721 972B- CA DEX 972C- D0 EE BNE $971C ; reboot to my work sidk 972E- 4C 00 C5 JMP $C500 *BSAVE TRACE1A,A$9600,L$131 *9600G ...reboots slot 6... ...reboots slot 5... ]BSAVE BOOT1 DECRYPTED,A$2600,L$A00 Now I have a copy of the decrypted boot1 code. If I write this decrypted code to track 0 of my (currently non- working) copy, I can safely bypass the copy protection routine completely. It's no longer needed to decrypt boot1 code, and its only other purpose is to prevent my copy from working. Which is, you know, bad for me. [S6,D1=non-working copy] [S5,D1=my work disk] ]CALL -151 ; patch to skip nibble check *264A:6C FD 08 This is a short program to write the decrypted boot1 code (currently in memory at $2600..$2FFF) to track 0, overwriting the original sectors 0-9. It uses a custom RWTS parameter table that assumes that the target disk is in slot 6, drive 1. There is no error checking, so don't f--- this up. It reuses the multi-sector read/write routine at $B793, which uses $B7E1 as a sector counter and operates on sectors and memory pages in reverse order. So the RWTS parameter table here is set up to write $2F00..$2FFF to T00,S09, then decrement once and write $2E00..$2EFF to T00,S08, and so on until the sector counter decrements to 0. 08C0- A0 E0 LDY #$E0 08C2- B9 00 08 LDA $0800,Y 08C5- 99 00 B7 STA $B700,Y 08C8- C8 INY 08C9- D0 F7 BNE $08C2 08CB- 4C 93 B7 JMP $B793 08E0- 17 0A 0A 1B E8 B7 00 B4 ^^ ++-- write 10 sectors (0-9) 08E8- 01 60 01 00 00 09 FB 08 ^^ ^^ start on --++ ++-- start on track 0 sector 9 08F0- 00 2F 00 00 02 00 FE 60 ^^^^^ +++++-- start at address $2F00 08F8- 01 00 00 00 01 EF D8 00 *BSAVE WRITE BOOT1 DECRYPTED,A$8C0,L$40 *8C0G *C600G Quod erat liberandum. --------------------------------------- A 4am crack No. 49 ------------------EOF------------------