-----------The Perfect Score----------- A 4am crack 2015-02-09 --------------------------------------- Name: The Perfect Score: Computer Preparation for the SAT Genre: educational Year: 1984 Credits: Developed by: Burt Munk and Company Author: Deborah Gold Educational Consultants: Dr. Robert S. Boone, Dr. John W. Amberg Applications Consultant: Dr. Bruce Kallick Publisher: Mindscape, Inc. Media: six double-sided 5.25-inch disks OS: custom / ProntoDOS Other versions: none (preserved here for the first time) The disks are labeled "A" through "F". Each is double-sided. All 12 sides are bootable and independent of each other. "A" side 1 - Antonyms I "A" side 2 - Antonyms II "B" side 1 - Analogies I "B" side 2 - Analogies II "C" side 1 - Sentence Completion I "C" side 2 - Sentence Completion II "D" side 1 - Reading Comprehension I "D" side 2 - Reading Comprehension II "E" side 1 - Mathematics I "E" side 2 - Mathematics II "F" side 1 - Practice SAT "F" side 2 - Timed Test of Standard Written English I'll start with disk "A" side 1. ~ Chapter 0 In Which Various Automated Tools Fail In Interesting Ways COPYA immediate disk read error Locksmith Fast Disk Backup can't read any track EDD 4 bit copy (no sync, no count) no read errors, but copy fills screen with garbage and reboots Copy ][+ nibble editor modified epilogue bytes "FF FF FF" (address and data) Disk Fixer ["O" -> "Input/Output Control"] set Address Epilogue to "FF FF FF" set Data Epilogue to "FF FF FF" Success! All tracks readable! T00 -> sectors are in a weird order, but I think it's a variant of the DOS 3.3 bootloader and RWTS No sign of a disk catalog on T11 or any other track Why didn't COPYA work? modified epilogue bytes Why didn't Locksmith FDB work? modified epilogue bytes Why didn't my EDD copy work? probably a nibble check during boot Next steps: 1. capture RWTS with AUTOTRACE 2. convert disk to standard format with Advanced Demuffin 3. patch RWTS to read standard format (if necessary) 4. find nibble check and bypass it ~ Chapter 1 In Which We Attempt To Use The Original Disk As A Weapon Against Itself [S6,D1=original disk] [S6,D2=blank disk] [S5,D1=my work disk] ]PR#5 CAPTURING BOOT0 ...reboots slot 6... ...reboots slot 5... SAVING BOOT0 CAPTURING BOOT1 ...reboots slot 6... ...reboots slot 5... SAVING BOOT1 SAVING RWTS ]BRUN ADVANCED DEMUFFIN 1.5 ["5" to switch to slot 5] ["R" to load a new RWTS module] --> At $B8, load "RWTS" from drive 1 ["6" to switch to slot 6] ["C" to convert disk] --v-- ADVANCED DEMUFFIN 1.5 (C) 1983, 2014 ORIGINAL BY THE STACK UPDATES BY 4AM =======PRESS ANY KEY TO CONTINUE======= TRK:................................... +.5: 0123456789ABCDEF0123456789ABCDEF012 SC0:................................... SC1:................................... SC2:................................... SC3:................................... SC4:................................... SC5:................................... SC6:................................... SC7:................................... SC8:................................... SC9:................................... SCA:................................... SCB:................................... SCC:................................... SCD:................................... SCE:................................... SCF:................................... ======================================= 16SC $00,$00-$22,$0F BY1.0 S6,D1->S6,D2 --^-- Reading the original disk takes longer than I would expect, but it does work. I've done this a lot. There's a certain rhythm to it. This disk has a different rhythm. ~ Chapter 2 In Which The Original Disk Strikes Back [S6,D1=demuffin'd copy] [S5,D1=my work disk] ]PR#6 ...crashes... Turning to my trusty Disk Fixer sector editor, I see that the sectors on T00 are no longer in a "weird order" like the original disk. T00,S01 is custom, but it appears to be loaded at $B700. T00,S02-S09 are the RWTS. This is just like a DOS 3.3 disk and quite unlike the original disk. Suddenly it clicks: this disk's logical sectors are mapped in a different order than usual. Here's T00,S00, split for clarity: --v-- ------------- DISK EDIT --------------- TRACK $00/SECTOR $00/VOLUME $FE/BYTE$00 --------------------------------------- $00:>01X-~ $18: 08 6D FF 08 8D FE 08 AE H-.H.~H. $20: FF 08 30 15 BD 4D 08 85 .H0U=MH. $28: 3D CE FF 08 AD FE 08 85 =N.H-~H. $30: 27 CE FE 08 A6 2B 6C 3E 'N~H&+,> $38: 00 EE FE 08 EE FE 08 20 @n~Hn~H $40: 89 FE 20 93 FE 20 2F FB .~ .~ /{ $48: A6 2B 6C FD 08 &+,}H --^-- The first $4C bytes are standard. In fact, they are byte-for-byte identical to DOS 3.3. But look at the table that starts at $084D: --v-- $4D: 00 05 0A @EJ $50: 0F 04 09 0E 03 08 0D 02 ODINCHMB $58: 07 0C 01 06 0B GLAFK --^-- This is the table that the boot0 code uses to map the logical sector it wants (loaded into the X register from $08FF) to the physical sector that is stored on disk (stored in zero page $3D and referenced by the disk controller ROM routine). Here's the same table from a standard DOS 3.3 disk: --v-- $48: 00 0D 0B @MK $50: 09 07 05 03 01 0E 0C 0A IGECANLJ $58: 08 06 04 02 0F HFDBO --^-- (Technically this copy of the sector table is only used by boot0. The RWTS has its own copy, starting at $BFB8. Sure enough, that table is in the same non-standard order.) Now think about how Advanced Demuffin works. It uses the original disk's RWTS to read one sector at a time. "Hey," it says, "let's read track $0, sector $F." "Sure thing," replies the RWTS, and it proceeds to do exactly that. Then, using a completely separate RWTS (built into Advanced Demuffin, starting at $1500), it says "hey, let's write these 256 bytes of data to track $0, sector $F on a regular disk." In other words, Advanced Demuffin normalized the sector order for me, but of course it didn't patch the RWTS or the boot0 code because it doesn't do that. T00,S00,$4D change 00 05 0A 0F 04 09 0E 03 08 0D 02 07 0C 01 06 0B to 00 0D 0B 09 07 05 03 01 0E 0C 0A 08 06 04 02 0F ]PR#6 ...crashes at $BB10... BB10- A=01 X=06 Y=0D P=38 S=ED *B700L B700- 20 4C B7 JSR $B74C <-- ! B703- 8E F7 B7 STX $B7F7 B706- A9 01 LDA #$01 B708- 8D F8 B7 STA $B7F8 B70B- 8D EA B7 STA $B7EA B70E- AD E0 B7 LDA $B7E0 B711- 8D E1 B7 STA $B7E1 B714- A9 00 LDA #$00 B716- 8D EC B7 STA $B7EC B719- AD E2 B7 LDA $B7E2 B71C- 8D ED B7 STA $B7ED B71F- AD E3 B7 LDA $B7E3 B722- 8D F1 B7 STA $B7F1 B725- A9 01 LDA #$01 B727- 8D F4 B7 STA $B7F4 B72A- 8A TXA B72B- 4A LSR B72C- 4A LSR B72D- 4A LSR B72E- 4A LSR B72E- 4A LSR B72F- AA TAX B730- A9 00 LDA #$00 B732- 9D F8 04 STA $04F8,X B735- 9D 78 04 STA $0478,X B738- 20 00 BB JSR $BB00 <-- ! B73B- A2 FF LDX #$FF B73D- 9A TXS B73E- 8E EB B7 STX $B7EB B741- 4C C8 BF JMP $BFC8 B744- 20 89 FE JSR $FE89 B747- 4C 00 08 JMP $0800 <-- ! Well, at least the boot1 code is being loaded into the right place in memory. And, since I'm here, I should note that there's a very suspicious looking "JSR $BB00" at $B738 and a non-standard "JMP $0800" at $B747. But first, I need to trace through $B74C. *B74CL ; this is the instruction that I ; expected at $B700 (instead of calling ; this subroutine) B74C- 8E E9 B7 STX $B7E9 ; save $BB00 page B74F- A0 00 LDY #$00 B751- B9 00 BB LDA $BB00,Y B754- 99 00 90 STA $9000,Y B757- 88 DEY B758- D0 F7 BNE $B751 ; convert slot x16 ($60) to slot ($06) B75A- 20 8E BE JSR $BE8E ; init current track (prevents disk ; grinding) B75D- A9 00 LDA #$00 B75F- 99 78 04 STA $0478,Y ; call RWTS -- but no setup, so the ; parameters must be hard-coded on disk B762- A9 B7 LDA #$B7 B764- A0 E8 LDY #$E8 B766- 20 B5 B7 JSR $B7B5 ; and exit via the code we just read B769- 6C F0 B7 JMP ($B7F0) *B7E8.B7FF B7E8- 01 60 01 00 00 0C FB B7 ^^ ^^ track/sector B7F0- 00 02 00 01 01 41 00 60 ^^^^^ address B7F8- 01 00 00 00 01 EF D8 00 Let's capture it. *C500G ... ]CALL -151 *9600 ($48) 222B- A9 CC LDA #$CC 222D- 85 48 STA $48 222F- A9 02 LDA #$02 2231- 85 49 STA $49 ; loop to search for specific sector 2233- A9 80 LDA #$80 2235- 85 2B STA $2B 2237- C6 2B DEC $2B 2239- F0 5D BEQ $2298 ; find next address field 223B- 20 44 B9 JSR $B944 223E- B0 58 BCS $2298 ; bad ; sector number from address field 2240- A5 2D LDA $2D 2242- C9 02 CMP #$02 ; loop until we find sector $02 2244- D0 F1 BNE $2237 ; 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 slower than normal. So ; the timing bits need to be in the ; right places too, or else this code ; will read the wrong nibble values ; while it's out of sync. 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.) 2246- A0 00 LDY #$00 2248- BD 8C C0 LDA $C08C,X 224B- 10 FB BPL $2248 224D- 88 DEY 224E- F0 48 BEQ $2298 ; bad 2250- C9 D5 CMP #$D5 2252- D0 F4 BNE $2248 2254- A0 00 LDY #$00 2256- BD 8C C0 LDA $C08C,X 2259- 10 FB BPL $2256 225B- 88 DEY 225C- F0 3A BEQ $2298 ; bad 225E- C9 E7 CMP #$E7 2260- D0 F4 BNE $2256 2262- BD 8C C0 LDA $C08C,X 2265- 10 FB BPL $2262 2267- C9 E7 CMP #$E7 2269- D0 2D BNE $2298 ; bad 226B- BD 8C C0 LDA $C08C,X 226E- 10 FB BPL $226B 2270- C9 E7 CMP #$E7 2272- D0 24 BNE $2298 ; bad 2274- BD 8D C0 LDA $C08D,X ; kill some time to get out of sync ; with the "proper" start of nibbles) 2277- A0 10 LDY #$10 2279- 24 06 BIT $06 ; now start looking for nibbles that ; don't really exist (except they do, ; because we're out of sync and reading ; timing bits as data) 227B- BD 8C C0 LDA $C08C,X 227E- 10 FB BPL $227B 2280- 88 DEY 2281- F0 15 BEQ $2298 ; bad 2283- C9 EE CMP #$EE 2285- D0 F4 BNE $227B ; check for nibble sequence stored ; in reverse order at $2CC 2287- A0 07 LDY #$07 2289- BD 8C C0 LDA $C08C,X 228C- 10 FB BPL $2289 228E- D1 48 CMP ($48),Y 2290- D0 06 BNE $2298 ; bad 2292- 88 DEY 2293- 10 F4 BPL $2289 ; if we made it this far, the nibble ; check passed 2295- 4C D4 02 JMP $02D4 ; bad path -- decrement Death Counter ; and eventually give up 2298- C6 2A DEC $2A 229A- D0 97 BNE $2233 ; The Badlands (wipe memory and reboot) 229C- A2 22 LDX #$22 229E- BD A9 02 LDA $02A9,X 22A1- 95 00 STA $00,X 22A3- CA DEX 22A4- 10 F8 BPL $229E 22A6- 4C 00 00 JMP $0000 22A9- A0 01 LDY #$01 22AB- 84 4F STY $4F 22AD- 88 DEY 22AE- 84 4E STY $4E 22B0- 98 TYA 22B1- 91 4E STA ($4E),Y 22B3- 88 DEY 22B4- D0 FB BNE $22B1 22B6- E6 4F INC $4F 22B8- F0 0C BEQ $22C6 22BA- A5 4F LDA $4F 22BC- C9 C0 CMP #$C0 22BE- D0 F1 BNE $22B1 22C0- A9 D0 LDA #$D0 22C2- 85 4F STA $4F 22C4- D0 EB BNE $22B1 22C6- AD 81 C0 LDA $C081 22C9- 6C FC FF JMP ($FFFC) ; table of expected nibbles 22CC- [FC EE EE FC E7 EE FC E7] ; success path continues here -- ; restore code at $BB00, restore X, and ; exit gracefully 22D4- A0 00 LDY #$00 22D6- B9 00 90 LDA $9000,Y 22D9- 99 00 BB STA $BB00,Y 22DC- 88 DEY 22DD- D0 F7 BNE $22D6 22DF- AE E9 B7 LDX $B7E9 22E2- 60 RTS This nibble check has no side effects. It doesn't decrypt the next stage of the boot. It doesn't even clear the carry. I can just skip it. Popping the stack, the subroutine at $B74C is also unnecessary. All it does is save $BB00..$BBFF, read the sector that contains the (bypass-able) copy protection routine, call it, restore $BB00, and exit. Oh, and it sets $B7E9, which is the instruction that should have been at $B700 in the first place. T00,S01,$00 change "20 4C B7" to "8E E9 B7" ]PR#6 ...crashes at $6042... Grr. Either I missed something, or there is more copy protection. Or both. ~ Chapter 4 In Which I Discover Where It All Went Wrong The next significant code chunk is at $BB00 (called from $B738). ]PR#5 ... ]BLOAD BOOT1,A$2600 ]CALL -151 *FE89G FE93G ; disconnect DOS *B600<2600.2FFFM ; move RWTS into place *BB00L BB00- 86 1B STX $1B ; neat trick to get the high byte of ; the return address from the stack BB02- BA TSX BB03- BD 02 01 LDA $0102,X ; $B7 --> zero page $03 BB06- 85 03 STA $03 BB08- 18 CLC BB09- 69 04 ADC #$04 ; $BB --> zero page $05 BB0B- 85 05 STA $05 BB0D- A0 00 LDY #$00 BB0F- 84 02 STY $02 BB11- A0 40 LDY #$40 BB13- 84 04 STY $04 BB15- A0 39 LDY #$39 BB17- A9 93 LDA #$93 ; $93 --> $B739 BB19- 91 02 STA ($02),Y BB1B- C8 INY BB1C- A5 03 LDA $03 ; $B7 --> $B73A BB1E- 91 02 STA ($02),Y ; $BB40..$BBBF --> $0800..$087F BB20- A0 7F LDY #$7F BB22- B1 04 LDA ($04),Y BB24- 99 00 08 STA $0800,Y BB27- 88 DEY BB28- 10 F8 BPL $BB22 ; restore X register and jump to ; relocated code BB2A- A6 1B LDX $1B BB2C- 4C 00 08 JMP $0800 I need to know what ends up at $0800. *BB2C:4C 59 FF ; break into monitor *B738G *800L 0800- A9 00 LDA #$00 0802- A0 EC LDY #$EC ; ($02) points to $B700 at this point, ; so this looks like it's setting up ; the RWTS parameter table again. ; $B7EC holds the track number (0). 0804- 91 02 STA ($02),Y 0806- C8 INY ; $B7ED holds the sector number (0). 0807- 91 02 STA ($02),Y 0809- A9 09 LDA #$09 080B- A0 F1 LDY #$F1 ; $B7F1 holds the destination address, ; so it looks like we're going to read ; a bunch of sectors from track 0 ; into $0900 and up. 080D- 91 02 STA ($02),Y 080F- A9 08 LDA #$08 0811- A0 E1 LDY #$E1 ; $B7E1 holds the number of sectors ; to read (8). 0813- 91 02 STA ($02),Y ; set up a JMP instruction at $01 (WTF) 0815- A9 4C LDA #$4C 0817- 85 01 STA $01 0819- A9 93 LDA #$93 081B- 85 02 STA $02 ; call $B793 to read multiple sectors 081D- 20 01 00 JSR $0001 Despite the odd indirection, this part isn't suspicious at all. It's using the standard multi-read routine at $B793 to read the rest of track 0 into memory. But look what comes next: 0820- A0 00 LDY #$00 0822- 84 02 STY $02 0824- A5 1E LDA $1E <-- ! 0826- 85 08 STA $08 0828- 0A ASL 0829- 90 01 BCC $082C 082B- C8 INY 082C- 48 PHA 082D- 68 PLA 082E- D0 F8 BNE $0828 0830- C0 02 CPY #$02 0832- 69 FF ADC #$FF 0834- 85 1F STA $1F Wait a minute. The standard DOS 3.3 RWTS doesn't use zero page $1E, so at this point it should be uninitialized and essentially random. But this loop seems to be doing some bit math on it (specifically, counting how many bits are zero and storing that in the Y register), so either something is setting $1E or I'm hallucinating. Or possibly both. Disk Fixer --> [F]ind --> [H]ex --> "1E" ...lots of matches, including T00,S0C,$04... Oh look, there it is, in the nibble check I just bypassed. --v-- T00,S0C ---------- DISASSEMBLY MODE ----------- 0000:A0 28 LDY #$28 0002:84 1E STY $1E <-- ! 0004:A0 00 LDY #$00 0006:F0 0B BEQ $0013 0008:88 DEY 0009:D0 F7 BNE $0002 000B:EA NOP 000C:EA NOP 000D:EA NOP 000E:EA NOP 000F:EA NOP 0010:EA NOP 0011:EA NOP 0012:EA NOP 0013:20 8E BE JSR $BE8E 0016:A9 00 LDA #$00 0018:99 78 04 STA $0478,Y 001B:8D 78 04 STA $0478 001E:A9 0A LDA #$0A 0020:85 2A STA $2A 0022:AE E9 B7 LDX $B7E9 0025:BD 89 C0 LDA $C089,X 0028:BD 8E C0 LDA $C08E,X --^-- I was wrong: the nibble check did have one important side effect. The first thing it did was set zero page $1E to $28, then never used it again. I didn't even notice it, because nibble checks use zero page for pointers and counters all the time, then the next phase of the boot ignores all that and just re- initializes whatever it needs to use. But this one was important, and since I bypassed the entire routine, it was never set properly. T00,S05,$64 change "A5 1E" to "A9 28" ]PR#6 ...works... All sides use identical protection, except disk "F", side 2. Wait, what? ~ Chapter 5 In Which Various Automated Tools Fail In Interesting Ways, Again [S6,D1=disk "F", side 2] 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 copy fills screen with garbage and reboots Copy ][+ nibble editor all tracks use standard prologues (address: D5 AA 96, data: D5 AA AD) but modified epilogues (address: FF FF FF, data: FF FF FF) Disk Fixer ["O" -> "Input/Output Control"] set Address Epilogue to "FF FF FF" set Data Epilogue to "FF FF FF" Success! All tracks readable... until I get to track 12 T00 -> looks like a DOS 3.3 RWTS T11 -> DOS 3.3 disk catalog T01,S07 -> startup program is "TSWE" (probably a Pronto-DOS variant) Copy ][+ nibble editor again T12-T22 appear unformatted Why didn't COPYA work? modified epilogue bytes (every track) Why didn't Locksmith FDB work? modified epilogue bytes (every track) Why didn't my EDD copy work? probably a nibble check during boot Next steps: 1. capture RWTS with AUTOTRACE 2. convert disk to standard format with Advanced Demuffin 3. patch RWTS to read standard format (if necessary) 4. find nibble check and bypass it ~ Chapter 6 In Which We Attempt To Use The Original Disk As A Weapon Against Itself, Again [S6,D1=original disk "F", side 2] [S6,D2=blank disk] [S5,D1=my work disk #2] ]PR#5 CAPTURING BOOT0 ...reboots slot 6... ...reboots slot 5... SAVING BOOT0 /!\ BOOT0 JUMPS TO $08C0 CAPTURING BOOT1 ...reboots slot 6... ...reboots slot 5... SAVING BOOT1 SAVING RWTS /!\ NIBBLE CHECK AT $BB00 ]BRUN ADVANCED DEMUFFIN 1.5 ["5" to switch to slot 5] ["R" to load a new RWTS module] --> At $B8, load "RWTS" from drive 1 ["6" to switch to slot 6] ["C" to convert disk] ["Y" to change options] [Start = T00,S00] [End = T11,S0F] --v-- ADVANCED DEMUFFIN 1.5 (C) 1983, 2014 ORIGINAL BY THE STACK UPDATES BY 4AM =======PRESS ANY KEY TO CONTINUE======= TRK:.................. +.5: 0123456789ABCDEF0123456789ABCDEF012 SC0:.................. SC1:.................. SC2:.................. SC3:.................. SC4:.................. SC5:.................. SC6:.................. SC7:.................. SC8:.................. SC9:.................. SCA:.................. SCB:.................. SCC:.................. SCD:.................. SCE:.................. SCF:.................. ======================================= 16SC $00,$00-$11,$0F BY1.0 S6,D1->S6,D2 --^-- ]PR#5 ]CATALOG,S6,D2 C1983 DSR^C#254 152 FREE A 006 TSWE B 019 DISPLAY.O B 002 INPUT.O B 060 TOTAL ]RUN TSWE ...works... I'm still curious about those high- numbered tracks. Copy ][+ 5.5 --> TRACK/SECTOR MAP --v-- TRACK 1 2 0123456789ABCDEF0123456789ABCDEF012 S0 ...DD D B .................. E1 ..DDD D B .................. C2 ..DDD D B .................. T3 ..DDD D B .................. O4 ..DDD D B .................. R5 ..DDD D B .................. 6 ..DDD D B .................. 7 ..DDD D B .................. 8 ..DDD D B .................. 9 ..DDD D B .................. A ..DDD D BA.................. B ..DDD D BA.................. C ..DDD D BA.................. D ..BDD D BA.................. E ..BDD DCBA.................. F ..BDD DCBA.................. --^-- Confirmed: tracks $12-$22 are marked as "used" in the disk catalog, but no file actually uses them. ]PR#6 ...fills screen with garbage, reboots endlessly... Let's go find that nibble check. ~ Chapter 7 In Which We Run Into An Old Friend And Our Adventure Draws To A Close [S5,D1=my work disk #2] ]PR#5 ]BLOAD BOOT0,A$800 ]CALL -151 *801L . . all normal until... . 084A- 4C C0 08 JMP $08C0 *8C0L 08C0- 8E E9 B7 STX $B7E9 08C3- 6C FD 08 JMP ($08FD) *BLOAD BOOT1,A$2600 *FE89G FE93G ; disconnect DOS *B600<2600.2FFFM ; move RWTS into place *B700L B700- 20 00 BB JSR $BB00 *BB00L BB00- A0 00 LDY #$00 BB02- B9 00 BB LDA $BB00,Y BB05- 99 00 02 STA $0200,Y BB08- 88 DEY BB09- D0 F7 BNE $2B02 BB0B- 60 RTS *20C