---------America Coast to Coast-------- A 4am crack 2017-05-07 -------------------. updated 2017-05-10 |___________________ Name: America Coast to Coast Genre: educational Year: 1984 Publisher: Mindscape Platform: Apple //e or later Media: single-sided 5.25-inch floppy OS: Pronto-DOS Previous cracks: none (of this version) Similar cracks: #1142 Zoo Collector #1141 Zoo Builder #1140 Zoo Goer #812 A Brand New View (very different loading environment, but the underlying protection check is the same) #811 Rosie the Counting Rabbit #202 Squeegee Learns About Drugs ~ Chapter 0 In Which Various Automated Tools Fail In Interesting Ways COPYA immediate disk read error Locksmith Fast Disk Backup unable to read any track EDD 4 bit copy (no sync, no count) no errors, but the copy swings to a high track and hangs with the drive motor on Copy ][+ nibble editor nothing suspicious Disk Fixer track 0 looks like Pronto-DOS loader, but it jumps to $B3C3 after loading the RWTS (instead of $9D84) Why didn't any of my copies work? I don't know. Not a lot to go on yet. Presumably if there was some structural protection I would have noticed read errors. Hanging with the drive motor on could mean anything. Next steps: 1. Search for common markers of a runtime protection check 2. If that fails, trace the boot 3. If that fails, I dunno, go feed the ducks or something? ~ Chapter 1 In Which The Ducks Will Have To Wait Turning to my trusty Disk Fixer sector editor, I search for "BD 89 C0", a common instruction to turn on the disk drive motor. It finds several matches, but nothing that looks like the start of a protection check. So we get to start at the beginning. T00,S00 is a standard DOS 3.3-style bootloader. It loads most of the rest of track 0 into $B600+ and jumps to $B700, stored on T00,S01, to load the rest of the disk. --v-- T00,S01 ----------- DISASSEMBLY MODE ---------- 0000:8E E9 B7 STX $B7E9 0003:8E F7 B7 STX $B7F7 0006:A9 01 LDA #$01 0008:8D F8 B7 STA $B7F8 000B:8D EA B7 STA $B7EA 000E:AD E0 B7 LDA $B7E0 0011:8D E1 B7 STA $B7E1 ; start reading DOS on T02,S00 -- looks ; like ProntoDOS 0014:A9 02 LDA #$02 0016:8D EC B7 STA $B7EC 0019:A9 00 LDA #$00 001B:8D ED B7 STA $B7ED ; $B7E7 is $B4, so the first sector is ; read into $B300 001E:AC E7 B7 LDY $B7E7 0021:88 DEY 0022:8C F1 B7 STY $B7F1 ; RWTS read command 0025:A9 01 LDA #$01 0027:8D F4 B7 STA $B7F4 ; set up globals (normal) 002A:8A TXA 002B:4A LSR 002C:4A LSR 002D:4A LSR 002E:4A LSR 002F:AA TAX 0030:A9 00 LDA #$00 0032:9D F8 04 STA $04F8,X 0035:9D 78 04 STA $0478,X ; read DOS into memory (normal) 0038:20 93 B7 JSR $B793 ; reset stack (normal) 003B:A2 FF LDX #$FF 003D:9A TXS 003E:8E EB B7 STX $B7EB ; machine initialization (not shown, ; but this is definitely ProntoDOS -- ; other DOS variants put this code ; somewhere else) 0041:20 69 BA JSR $BA69 0044:20 89 FE JSR $FE89 ; and continue in a strange spot 0047:4C C3 B3 JMP $B3C3 --^-- Standard unprotected ProntoDOS would jump to $9D84 at this point, but this disk has other ideas. $B300 was on T02,S00, so let's jump over there and continue disassembly. --v-- T02,S00 ----------- DISASSEMBLY MODE ---------- ; set up another RWTS read: track $11 00C3:A9 11 LDA #$11 00C5:8D EC B7 STA $B7EC ; sector 1 00C8:A9 01 LDA #$01 00CA:8D ED B7 STA $B7ED ; into $B400 00CD:A9 B4 LDA #$B4 00CF:8D F1 B7 STA $B7F1 00D2:A9 00 LDA #$00 00D4:8D F3 B7 STA $B7F3 00D7:8D EB B7 STA $B7EB ; and go 00DA:A9 B7 LDA #$B7 00DC:A0 E8 LDY #$E8 00DE:20 B5 B7 JSR $B7B5 ; and decrypt it (!) 00E1:A0 00 LDY #$00 00E3:B9 12 B4 LDA $B412,Y 00E6:49 4C EOR #$4C 00E8:99 12 B4 STA $B412,Y 00EB:C8 INY 00EC:C0 FF CPY #$FF 00EE:D0 F3 BNE $00E3 00F0:EA NOP 00F1:EA NOP 00F2:EA NOP 00F3:EA NOP 00F4:EA NOP ; and continue in decrypted code 00F5:4C 12 B4 JMP $B412 --^-- Here's T11,S01: --v-- -------------- DISK EDIT -------------- TRACK $11/SECTOR $01/VOLUME $FE/BYTE$12 --------------------------------------- $00: A0 00 B9 12 20 49 4C 99 @9R IL. $08: 12 20 C8 C0 FF D0 F3 60 R H@.Ps $10: EA EA>E5<6F C1 A0 FB E5 jje/A {e $18: 4C C9 48 C1 B8 FB C1 A7 LIHA8{A' $20: FB 6C A7 F8 6C BE F8 E5 {,'x,>xe $28: 4C C9 04 F1 C5 8C F1 C2 LIDqE.qB $30: 8C 6C 08 F5 E9 61 89 48 .,Hui!.H $38: 9C BB F1 C2 8C F1 C0 8C .;qB.q@. $40: 5C B7 85 B3 9C B8 EC 4C \7.3.8lL $48: 84 F1 C2 8C F1 C0 8C 5C .qB.q@.\ $50: B7 85 99 9C BF 8C 4B 9C 7...?.K. $58: F5 AA 48 E5 5C 89 48 9C u*He\.H. $60: 9C E5 4C C9 48 6C 08 F5 .eLIH,Hu $68: E9 61 89 48 9C BB F1 C0 i!.H.;q@ $70: 8C 5C B7 85 E6 9C BB F1 .\7.f.;q $78: C0 8C 5C B7 85 A7 9C A2 @.\7.'." --------------------------------------- BUFFER 0/SLOT 6/DRIVE 1/MASK OFF/NORMAL --------------------------------------- COMMAND : --^-- Not much to see, since everything after byte $12 is encrypted (in the 80s kind of way where it's just XOR'd with a constant byte, but still). Amusingly, there is unencrypted code at byte $00 which decrypts the rest of the sector in the same way as the bootloader. It assumes the code is stored at $2000 instead of $B400. Maybe debugging code left by the original developer? At this point, you might think I would need to switch to boot tracing the old fashioned way so I could see this decrypted code. But Disk Fixer is very powerful! It has a "MASK" mode that allows me to XOR every byte of a sector with a constant byte -- exactly as the bootloader is doing after it reads this sector into memory. Pressing "X" in Disk Fixer lets me set the mask: --v-- MASK WITH ADD $00/EOR $4C/AND $FF/OR$00 ^^^ +--- change this --^-- Then pressing applies the mask to the current sector: --v-- -------------- DISK EDIT -------------- TRACK $11/SECTOR $01/VOLUME $FE/BYTE$12 --------------------------------------- $00: EC 4C F5 5E 6C 05 00 D5 lLu^,E@U $08: 5E 6C 84 8C B3 9C BF 2C ^,..3.?, $10: A6 A6>A9<23 8D EC B7 A9 &&)#.l7) $18: 00 85 04 8D F4 B7 8D EB @.D.t7.k $20: B7 20 EB B4 20 F2 B4 A9 7 k4 r4) $28: 00 85 48 BD 89 C0 BD 8E @.H=.@=. $30: C0 20 44 B9 A5 2D C5 04 @ D9%-ED $38: D0 F7 BD 8E C0 BD 8C C0 Pw=.@=.@ $40: 10 FB C9 FF D0 F4 A0 00 P{I.Pt @ $48: C8 BD 8E C0 BD 8C C0 10 H=.@=.@P $50: FB C9 D5 D0 F3 C0 07 D0 {IUPs@GP $58: B9 E6 04 A9 10 C5 04 D0 9fD)PEDP $60: D0 A9 00 85 04 20 44 B9 P)@.D D9 $68: A5 2D C5 04 D0 F7 BD 8C %-EDPw=. $70: C0 10 FB C9 AA D0 F7 BD @P{I*Pw= $78: 8C C0 10 FB C9 EB D0 EE .@P{IkPn --------------------------------------- BUFFER 0/SLOT 6/DRIVE 1/MASK ON /NORMAL --------------------------------------- COMMAND : --^-- And now we can list the encrypted routine at byte $12. ~ Chapter 2 The Belly Of The Beast The protection check starts at byte $12 (loaded into memory at $B412): --v-- T09,S01 ----------- DISASSEMBLY MODE ---------- ; Hmm, seeking to track $23 perhaps? ; That would explain why even my EDD ; bit copy failed -- I didn't copy ; track $23! 0012:A9 23 LDA #$23 0014:8D EC B7 STA $B7EC ; 0 = RWTS seek command 0017:A9 00 LDA #$00 0019:85 04 STA $04 001B:8D F4 B7 STA $B7F4 ; 0 also = disk volume wildcard 001E:8D EB B7 STA $B7EB ; get address of RWTS parameter table ; and call RWTS entry point to seek ; (not shown) 0021:20 EB B4 JSR $B4EB 0024:20 F2 B4 JSR $B4F2 0027:A9 00 LDA #$00 0029:85 48 STA $48 ; Oh look! Here's that "BD 89 C0" ; instruction I was looking for earlier ; but couldn't find (because it was ; encrypted) 002B:BD 89 C0 LDA $C089,X 002E:BD 8E C0 LDA $C08E,X ; look for the next available address ; field 0031:20 44 B9 JSR $B944 ; compare the sector we found against ; the counter we initialized in zp$04 ; earlier 0034:A5 2D LDA $2D 0036:C5 04 CMP $04 ; loop until we find the address field ; for track $23, sector $00 0038:D0 F7 BNE $0031 ; skip to sync byte (#$FF) 003A:BD 8E C0 LDA $C08E,X 003D:BD 8C C0 LDA $C08C,X 0040:10 FB BPL $003D 0042:C9 FF CMP #$FF 0044:D0 F4 BNE $003A ; skip an exact number of nibbles and ; look for #$D5 0046:A0 00 LDY #$00 0048:C8 INY 0049:BD 8E C0 LDA $C08E,X 004C:BD 8C C0 LDA $C08C,X 004F:10 FB BPL $004C 0051:C9 D5 CMP #$D5 0053:D0 F3 BNE $0048 ; if not found in correct location, try ; again from the top (Y register is the ; nibble counter) 0055:C0 07 CPY #$07 0057:D0 B9 BNE $0012 ; do this for all sectors 0059:E6 04 INC $04 005B:A9 10 LDA #$10 005D:C5 04 CMP $04 005F:D0 D0 BNE $0031 ; start over on sector 0 0061:A9 00 LDA #$00 0063:85 04 STA $04 0065:20 44 B9 JSR $B944 0068:A5 2D LDA $2D 006A:C5 04 CMP $04 006C:D0 F7 BNE $0065 ; skip to address epilogue 006E:BD 8C C0 LDA $C08C,X 0071:10 FB BPL $006E 0073:C9 AA CMP #$AA 0075:D0 F7 BNE $006E 0077:BD 8C C0 LDA $C08C,X 007A:10 FB BPL $0077 007C:C9 EB CMP #$EB 007E:D0 EE BNE $006E ; skip to sync byte (#$FF) 0080:BD 8C C0 LDA $C08C,X 0083:10 FB BPL $0080 0085:C9 FF CMP #$FF 0087:D0 F7 BNE $0080 ; skip an exact number of nibbles and ; look for #$D5 0089:A0 00 LDY #$00 008B:C8 INY 008C:BD 8E C0 LDA $C08E,X 008F:BD 8C C0 LDA $C08C,X 0092:10 FB BPL $008F 0094:C9 D5 CMP #$D5 0096:D0 F3 BNE $008B ; if not found in correct location, try ; again from the top (Y register is the ; nibble counter again) 0098:C0 10 CPY #$10 009A:D0 C5 BNE $0061 ; do this for several other sectors 009C:E6 04 INC $04 009E:A9 0A LDA #$0A 00A0:20 A8 FC JSR $FCA8 00A3:A9 04 LDA #$04 00A5:C5 04 CMP $04 00A7:D0 04 BNE $00AD 00A9:E6 04 INC $04 00AB:D0 B8 BNE $0065 00AD:A9 0F LDA #$0F 00AF:C5 04 CMP $04 00B1:D0 BB BNE $006E 00B3:20 44 B9 JSR $B944 00B6:A5 2D LDA $2D 00B8:C9 0F CMP #$0F 00BA:D0 F7 BNE $00B3 ; read data field (not shown) 00BC:20 DC B8 JSR $B8DC ; look for #$A5 nibble in a specific ; place 00BF:A0 00 LDY #$00 00C1:C8 INY 00C2:BD 8E C0 LDA $C08E,X 00C5:BD 8C C0 LDA $C08C,X 00C8:10 FB BPL $00C5 00CA:C9 A5 CMP #$A5 00CC:D0 F3 BNE $00C1 ; if not found in correct location, try ; again from the top (Y register is the ; nibble counter again) 00CE:C0 2B CPY #$2B 00D0:D0 E1 BNE $00B3 ; look for #$D5 nibble in a specific ; place 00D2:A0 00 LDY #$00 00D4:C8 INY 00D5:BD 8E C0 LDA $C08E,X 00D8:BD 8C C0 LDA $C08C,X 00DB:10 FB BPL $00D8 00DD:C9 D5 CMP #$D5 00DF:D0 F3 BNE $00D4 ; if not found in correct location, try ; again from the top (Y register is the ; nibble counter again) 00E1:C0 5D CPY #$5D 00E3:D0 CE BNE $00B3 ; success path falls through to here -- ; turn off the drive motor and jump ; forward 00E5:BD 88 C0 LDA $C088,X 00E8:4C F5 B4 JMP $B4F5 ; set RUN flag to disable user control ; in case they manage to get to a BASIC ; prompt 00F5:A9 FF LDA #$FF 00F7:85 D6 STA $D6 ; continue with standard boot sequence 00F9:4C 84 9D JMP $9D84 --^-- My copy never makes it this far. It's stuck in an infinite loop, trying to find a precisely organized track $23 (and failing). It looks like I should be able to skip over the protection check by jumping straight to $9D84 instead of $B412. This is the 4th disk I've found (from the 2nd publisher) that shares this protection, so I've added support for it to the next version of Passport. Here is the transcript: --v-- READING FROM S6,D1 T00,S00 FOUND PRONTO-DOS BOOTLOADER USING DISK'S OWN RWTS WRITING TO S5,D2 T02,S00 DISK CALLS A PROTECTION CHECK AT $B412 BEFORE INITIALIZING DOS. T02,S00,$F6: 12B4 -> 849D CRACK COMPLETE. --^-- More information and source code is available at https://archive.org/details/Passport4am Quod erat liberandum. ~ Changelog 2017-05-10 - fixed descriptions of automated tools 2017-05-07 - initial release --------------------------------------- A 4am crack No. 1194 ------------------EOF------------------