---------------Crime Wave-------------- A 4am crack 2018-02-15 -------------------. updated 2018-02-17 |___________________ Name: Crime Wave Genre: arcade Year: 1983 Credits: Scott Schram Publisher: Penguin Software Platform: Apple ][+ or later Media: single-sided 5.25-inch floppy OS: custom Similar cracks: #1676 Thunder Bombs ~ Chapter 0 In Which Various Automated Tools Fail In Interesting Ways COPYA immediate disk read error, but it gets a participation certificate that spells out "You Tried" Locksmith Fast Disk Backup unable to read any track EDD 4 bit copy to .nib file on CFFA3000 read errors on T12+ copy fails to boot, hangs with the drive motor off Copy ][+ nibble editor tracks $12-$22 appear unformatted Lower tracks have modified epilogues ("DA AA EB" instead of "DE AA EB") Odd tracks also have custom prologue ("D4 AA 96" instead of "D5 AA 96") no sign of anything beyond track $11 no half or quarter tracks Disk Fixer "O" for INPUT/OUTPUT CONTROL, set the epilogues to "DA AA EB" track 0 is readable boot sector is custom no sign of disk catalog on any track no sign of DOS or ProDOS or any OS whatsoever Why didn't COPYA work? modified prologues/epilogues on every track Why didn't Locksmith FDB work? ditto Why didn't my .nib image work? This is a bit of a mystery. The alternating D4/D5 prologues shouldn't pose any problem for a nibble copier, so I'm assuming there is some sort of nibble check early in the boot. Next steps: 1. Run Passport to convert the disk to a standard format 2. Patch the bootloader to read the newly standardized disk 3. Find and disable the nibble check 4. Declare victory (*) ~ Chapter 1 In Which Our Automated Tools Take Us Just About As Far As They Can The 2017-12-27 development version of Passport successfully auto-converted the disk to a standard format. The built-in RWTS was designed to read this kind of alternating D4/D5 prologue (it was very common), and the adaptive RWTS correctly determined the first epilogue nibble (DA) and enforced it on the rest of the sectors. All in all, I have high confidence in the integrity of this conversion. More information and source code is available at https://archive.org/details/Passport4am Here is the Passport transcript: --v-- READING FROM S6,D1 USING BUILT-IN RWTS T22 IS UNFORMATTED WRITING TO RAM DISK T21 IS UNFORMATTED T20 IS UNFORMATTED T1F IS UNFORMATTED T1E IS UNFORMATTED T1D IS UNFORMATTED T1C IS UNFORMATTED T1B IS UNFORMATTED T1A IS UNFORMATTED T19 IS UNFORMATTED T18 IS UNFORMATTED T17 IS UNFORMATTED T16 IS UNFORMATTED T15 IS UNFORMATTED T14 IS UNFORMATTED T13 IS UNFORMATTED T12 IS UNFORMATTED WRITING TO S5,D2 THE DISK WAS COPIED SUCCESSFULLY, BUT PASSPORT DID NOT APPLY ANY PATCHES. POSSIBLE REASONS: - THE SOURCE DISK IS NOT COPY PROTECTED. - THE TARGET DISK WORKS WITHOUT PATCHES. - THE DISK USES AN UNKNOWN PROTECTION, AND PASSPORT CAN NOT HELP ANY FURTHER. --^-- The copy that Passport produces can not read itself, which is not entirely unexpected. It is most likely enforcing the custom "DA AA EB" epilogue, which is now the standard "DE AA EB". A quick sector search for "BD 8C C0" (the standard opcode to read the data latch from a drive indexed by the X register) found a DOS-shaped RWTS on track $06, which is interesting but not the code I was looking for. (My failed copy never gets off track 0.) So, putting a pin in the fact that there is a secondary RWTS that may be used later once the game is loaded, I set off to trace the boot. ~ Chapter 2 Boot Trace and Chill [S6,D1=original disk] [S5,D1=my work disk] ]PR#5 ... ]CALL -151 *9600 DE Then comes the data field parsing: 259A- A6 30 LDX $30 259C- A0 18 LDY #$18 259E- 88 DEY 259F- 30 F7 BMI $2598 ; match $D5 $AA $AD as normal 25A1- BD 8C C0 LDA $C08C,X 25A4- 10 FB BPL $25A1 25A6- C9 D5 CMP #$D5 25A8- D0 F4 BNE $259E 25AA- EA NOP 25AB- BD 8C C0 LDA $C08C,X 25AE- 10 FB BPL $25AB 25B0- C9 AA CMP #$AA 25B2- D0 F2 BNE $25A6 25B4- A0 56 LDY #$56 25B6- BD 8C C0 LDA $C08C,X 25B9- 10 FB BPL $25B6 25BB- 49 AD EOR #$AD 25BD- D0 E7 BNE $25A6 ; ??? 25BF- 08 PHP 25C0- 20 96 05 JSR $0596 25C3- 28 PLP *2596L 2596- 18 CLC 2597- 60 RTS Nothing. We're calling a subroutine to do absolutely nothing except clear the carry. And since we saved and restored the status flags, we're not even doing that. Onward. *25C4L 25C4- 88 DEY 25C5- 84 0B STY $0B 25C7- BC 8C C0 LDY $C08C,X 25CA- 10 FB BPL $25C7 25CC- 59 D6 02 EOR $02D6,Y 25CF- A4 0B LDY $0B 25D1- 99 00 03 STA $0300,Y 25D4- D0 EE BNE $25C4 25D6- 84 0B STY $0B 25D8- BC 8C C0 LDY $C08C,X 25DB- 10 FB BPL $25D8 25DD- 59 D6 02 EOR $02D6,Y 25E0- A4 0B LDY $0B 25E2- 99 00 02 STA $0200,Y 25E5- C8 INY 25E6- D0 EE BNE $25D6 25E8- BC 8C C0 LDY $C08C,X 25EB- 10 FB BPL $25E8 25ED- 59 D6 02 EOR $02D6,Y 25F0- D0 A6 BNE $2598 ; also extremely weird 25F2- A1 00 LDA ($00,X) ; match all three epilogue nibbles 25F4- BD 8C C0 LDA $C08C,X 25F7- 10 FB BPL $25F4 25F9- C9 DA CMP #$DA <-- ! 25FB- D0 9B BNE $2598 25FD- EA NOP 25FE- BD 8C C0 LDA $C08C,X 2601- 10 FB BPL $25FE 2603- C9 AA CMP #$AA 2605- D0 91 BNE $2598 2607- A4 2F LDY $2F 2609- BD 8C C0 LDA $C08C,X 260C- 10 FB BPL $2609 260E- C9 EB CMP #$EB 2610- D0 86 BNE $2598 ; finish decoding disk nibbles into ; bytes in memory 2612- A2 56 LDX #$56 2614- CA DEX 2615- 30 FB BMI $2612 2617- B9 00 02 LDA $0200,Y 261A- 5E 00 03 LSR $0300,X 261D- 2A ROL 261E- 5E 00 03 LSR $0300,X 2621- 2A ROL 2622- 91 04 STA ($04),Y 2624- C8 INY 2625- D0 ED BNE $2614 2627- 18 CLC 2628- 60 RTS OK, the most obvious patch is the data epilogue, which is now $DE $AA $EB. T00,S0E,$FA: DA -> DE Unfortunately, my copy still grinds on boot. Which means I'm still missing something. It's not the address prologue matching; the LSR/EOR code to match $D4 or $D5 is a common trick. It still matches $D5 on any track, so it shouldn't require any change. After staring at this code for longer than I would like to admit, it finally dawned on me what this weird code immediately after the data prologue is for: ; cycles counts in margin, because that ; is the most important part 25B6- BD 8C C0 LDA $C08C,X ; 4 25B9- 10 FB BPL $25B6 ; 2 25BB- 49 AD EOR #$AD ; 2 25BD- D0 E7 BNE $25A6 ; 2 25BF- 08 PHP ; 3 25C0- 20 96 05 JSR $0596 ; 6 2596- 18 CLC ; 2 2597- 60 RTS ; 6 25C3- 28 PLP ; 4 25C4- 88 DEY ; 2 25C5- 84 0B STY $0B ; 3 25C7- BC 8C C0 LDY $C08C,X ;*3 25CA- 10 FB BPL $25C7 4+2+2+2+3+6+2+6+4+2+3+3 = 39, which is too long. Each bit on disk takes 4 CPU cycles to come around as the disk is spinning. That means we need to read an 8-bit nibble every 32 cycles. The data latch will hold the last value for 4 more cycles -- to compensate for the fact that the read is actually done on the third cycle of the 4-cycle LDA/LDY instruction that hits the data latch soft switch -- so we can spend an absolute maximum of 36 cycles fetching any one nibble. We've intentionally wasted enough time that we'll miss the first bit of the first nibble of the data field. Unless... If the $AD, the third data prologue nibble, has a timing bit after it, then the data latch would hold that value for 4 more cycles, and we would just barely have enough time to catch the first bit of the next nibble. Turning back to the Copy II Plus nibble editor, I can see it on the original disk (originally shown in inverse, which I converted to "+"): --v-- COPY ][ PLUS BIT COPY PROGRAM 8.4 (C) 1982-9 CENTRAL POINT SOFTWARE, INC. --------------------------------------- TRACK: 01 START: 2FA8 LENGTH: 186A 2F88: E9 EB 97 BD 95 B5 9C DF VIEW 2F90: B4 97 94 D4 94 BD 9F D6 2F98: DC FE F4 95 DD 97 F4 97 2FA0: 94 BD 9D D5 FE+FF+FF+FF+ 2FA8: D4 AA 96 AA AA AA AB AA ^^^^^^^^ address prologue 2FB0: AA AA AB DA AA EB 99 FF+ ^^^^^^^^ address epilogue 2FB8: EB E9+FF+F9 FE+FF+FF+D5 ^^ 2FC0: AA AD+EC EC D7 D9 AB 9A ^^^^^ data prologue with timing bit after the third nibble 2FC8: E6 D9 96 9A DE FD B5 9A --^-- At $05BF, I can put a "BEQ" to branch over the JSR, which will save enough time to make this code work on my normalized disk with no extra timing bits. T00,S0E,$BF: 0820 -> F003 ]PR#6 ...still grinds... I. Am. Still. Missing. Something. ~ Chapter 6 And One More Thing After staring at this bootloader for an embarrassing amount of time, looking at the original disk in both a nibble and a sector editor, I found another difference: the disk volume number. The original disk has a disk volume number of 0 -- which, by the way, is impossible to create with standard tools. But as we've already seen, this disk is anything but standard. (My copy has a disk volume 254, the default.) But who cares? On an unprotected disk, or even a protected disk with a modified copy of DOS 3.3, the RWTS can check and intentionally reject a disk with the wrong disk volume number. This bootloader does not contain that check. Except... At $0607, the custom RWTS is finishing up matching the data field epilogue and getting ready to finish decoding the disk nibbles in the data field and verifying the checksum before returning a final boolean in the carry flag -- yes, this sector was read successfully, or no, it wasn't. ``'-.,_,.-'``'-.,_,.='``'-.,_,.-'``'-., ``'-.,_,.-'``'-.,_,.='``'-.,_,.-'``'-., `` ., `` 2607- A4 2F LDY $2F ., `` 2609- BD 8C C0 LDA $C08C,X ., `` 260C- 10 FB BPL $2609 ., `` 260E- C9 EB CMP #$EB ., `` 2610- D0 86 BNE $2598 ., `` ., ``'-.,_,.-'``'-.,_,.='``'-.,_,.-'``'-., ``'-.,_,.-'``'-.,_,.='``'-.,_,.-'``'-., Look how we're initializing the Y register: with zero page $2F. What's in $2F? Why, we just set that, while we were parsing the address field. It's the disk volume number. The disk volume number has nothing to do with this part of the RWTS. The Y register is used to look up into the nibble translation table. In a standard DOS 3.3 RWTS, that register is always initialized to 0. So this is a very, very, very sneaky way of ensuring that the disk volume number is 0. Which, of course, mine isn't. The patch is to make the code do what DOS 3.3 does: always initialize the Y register to 0 going into the final nibble decoding. T00,S0D,$07: A42F -> A000 ]PR#6 ...works... Whew. Except... ~ Chapter 7 And One More Thing, Again The game boots. The game loads. The game plays. Then you get a high score, you enter your initials, and... the game reboots. Because there is AN ENTIRELY OTHER RWTS on the disk for the exclusive purpose of writing out high scores. It's stored in a weird order on track $06, but it looks like it gets loaded into $B800 in memory. (I actually found this RWTS by accident when I was looking for the bootloader. That's called 4shadowing!) T06,S00,$9E: DA -> DE T06,S0E,$35: DA -> DE T06,S0E,$91: DA -> DE ]PR#6 ...works, and it is glorious... Quod erat liberand I can not even with this but would you believe there is still one more thing... ~ Epilogue Disk Volume Number, How Do I Hate Thee? Let Me Count The Ways Due to insufficient testing(*), my initial crack was incomplete. (*) didn't actually play the game The secondary RWTS on track 6 is not what you would call "friendly." It is DOS-shaped, but stripped down to remove unnecessary functionality like the format command. Also this: --v-- T06,S0A (loaded at $BD00) ----------- DISASSEMBLY MODE ---------- ; save everything 000A:84 3D STY $3D 000C:08 PHP 000D:D8 CLD 000E:78 SEI ; turn on drive motor, reset data latch 000F:A6 30 LDX $30 0011:BD 8E C0 LDA $C08E,X 0014:BD 8C C0 LDA $C08C,X 0017:BD 89 C0 LDA $C089,X 001A:A9 03 LDA #$03 001C:85 3F STA $3F 001E:A9 00 LDA #$00 0020:85 3E STA $3E 0022:A4 3D LDY $3D 0024:D0 0C BNE $0032 ; wait wait wait 0026:20 A8 FC JSR $FCA8 0029:20 A8 FC JSR $FCA8 002C:20 A8 FC JSR $FCA8 002F:20 00 B8 JSR $B800 0032:20 A8 FC JSR $FCA8 0035:20 A8 FC JSR $FCA8 ; Death Counter 0038:A9 50 LDA #$50 003A:85 3C STA $3C 003C:C6 3C DEC $3C 003E:D0 03 BNE $0043 0040:4C D9 03 JMP $03D9 ; get next address field 0043:A6 30 LDX $30 0045:20 44 B9 JSR $B944 0048:B0 F2 BCS $003C ; check disk volume number (AGAIN!) 004A:A5 2F LDA $2F 004C:05 2E ORA $2E ; if it's not 0, try again (but ; eventually give up) 004E:D0 EC BNE $003C Thus. Since my copy is no longer disk volume 000, this check fails and the game reboots. Solution: set the disk volume number (in zero page $2F) to 0 instead of checking it. T06,S0A,$4A: -> A9 00 85 2F ]PR#6 ...works, and it is glorious... Apropos of nothing except it was on the same sector as the last patch, this copy protection has its own copyright message, which seems like the sort of thing you would enjoy reading if you're the sort of person who read this far. --v-- -------------- DISK EDIT --------------- TRACK $06/SECTOR $0A/VOLUME $FE/BYTE$00 --------------------------------------- $70: C0 28 60 20 44 69 73 6B @(` Disk $78: 20 49 2F 4F 20 64 65 73 I/O des $80: 69 67 6E 65 64 20 62 79 igned by $88: 20 48 6F 6C 6C 65 20 41 Holle A $90: 73 73 6F 63 69 61 74 65 ssociate $98: 73 20 66 6F 72 20 50 65 s for Pe $A0: 6E 67 75 69 6E 20 53 6F nguin So $A8: 66 74 77 61 72 65 2E 20 ftware. $B0: 28 43 29 20 43 6F 70 79 (C) Copy $B8: 72 69 67 68 74 20 31 39 right 19 $C0: 38 33 20 62 79 20 44 61 83 by Da $C8: 76 20 48 6F 6C 6C 65 2E v Holle. $D0: 20 41 6C 6C 20 72 69 67 All rig $D8: 68 74 73 20 72 65 73 65 hts rese $E0: 72 76 65 64 2E 20 00 00 rved. .. --^-- Quod erat liberandum. ~ Changelog 2018-02-17 - disabled final volume check (thanks to @racerupnik for the bug report) - added disk I/O copyright message 2018-02-15 - initial release --------------------------------------- A 4am crack No. 1697 ------------------EOF------------------