-----Uncle Clyde's Consonant Slides---- ----------Beginning Consonants--------- A 4am crack 2021-02-10 --------------------------------------- Name: Uncle Clyde's Consonant Slides: Beginning Consonants Genre: educational Year: 1993 Publisher: Micrograms Platform: Apple //e or later (128K) Media: 5.25-inch disk Sides: 1 OS: custom Previous cracks: none Similar cracks: #797 Marty's Family Reader #2301 Hugo Hound's Vowel Sounds ~ Chapter 0 In Which Various Automated Tools Fail In Interesting Ways COPYA immediate disk read error Locksmith Fast Disk Backup can't read track $00, sector $0C copy loads title screen then breaks to text page with "]ERROR D51" EDD 4 bit copy (no sync, no count) works Copy ][+ nibble editor T00,S0C exists (I searched for the raw nibble sequence "AA AB AE", which matches the second half of the track ("AA AA" -> $00) and the sector ("AB AE" -> $06 = logical sector $0C) in the address field Disk Fixer setting "CHECKSUM ENABLED" to "NO" allows me to read T00,S0C Why didn't COPYA work? intentionally corrupted sector on T00 Why didn't Locksmith FDB work? probably a run-time check to ensure that sector on T00 is corrupted, which it isn't, on my copy, because Locksmith Fast Disk Backup will just write out a standard sector of zeroes instead of reproducing the corruption EDD worked. What does that tell us? Probably just a bad block check: unreadable sector = original, readable sector = unauthorized copy Next steps: 1. Use a sector editor to search for obvious signs of sector reads 2. If that fails, trace the boot 3. I don't know, go feed the ducks or something (*) (*) not bread though, it's bad for them ~ Chapter 1 Unexpected Operating System Found In Bagging Area The disk appears to boot directly to the program, without loading any known operating system first. But while I was poking around the corrupted track 0, I noticed a normal ProDOS catalog. And in fact, I can boot from my ProDOS hard drive and catalog this disk! --v-- ]PR#7 ... ]CAT,S6,D1 /BOOT NAME TYPE BLOCKS MODIFIED PRODOS SYS 7 CLYDETITLESCR2 BIN 33 LOWDOS BIN 6 FONT1.DHR BIN 7 UTL BIN 9 MAIN BIN 23 FONT2.DHR BIN 8 MAINWIN BIN 24 DATA TXT 5 16-JUN-92 SLIDES1A BIN 25 SLIDES1B BIN 25 SLIDES2A BIN 23 SLIDES2B BIN 24 SLIDES3A BIN 23 SLIDES3B BIN 22 PRINTUTL BIN 6 DISK BIN 3 BLOCKS FREE: 1 BLOCKS USED: 279 --^-- Anyway, might prove useful, especially being able to cross-reference sectors to files and finding out where they're loaded in memory. Onward! My non-working copy prints an error message. Let's see if we can find it. Turning to my trusty Disk Fixer sector editor, I search for the ASCII string "ERROR" and find it in T08,S04! Copy II Plus recognizes this disk as ProDOS, and the "disk map" says that T08,S04 is part of file "MAIN". --v-- DISK MAP SLOT 6 DRIVE 1 /BOOT/MAIN TRACK 1 2 0123456789ABCDEF0123456789ABCDEF012 S0 .........***....................... EE .........***....................... CD .........***....................... TC .........***....................... OB .........***....................... RA .........***....................... 9 .........***....................... 8 .........***....................... 7 .........**........................ 6 .........**........................ 5 ........***........................ 4 ........***........................ 3 ........***........................ 2 ........***........................ 1 ........***........................ F ........***........................ --^-- Booting my ProDOS hard drive, I can BLOAD that file into memory and start tracing. According to the full CATALOG command (not shown), the file "MAIN" is loaded at address $6000. ]PR#7 ... ]PREFIX /BOOT ]BLOAD MAIN ]CALL -151 *6000L 6000- 4C CE 60 JMP $60CE *60CEL ; read/write RAM bank 1 and set mainmem ; (as opposed to auxmem) 60CE- AD 8B C0 LDA $C08B 60D1- AD 8B C0 LDA $C08B 60D4- 8D 08 C0 STA $C008 ; could be anything, but given the ; current program counter, I'm guessing ; this is the address $6126, which is ; nearby 60D7- A9 26 LDA #$26 60D9- 8D 07 08 STA $0807 60DC- A9 61 LDA #$61 60DE- 8D 08 08 STA $0808 ; don't know 60E1- A9 01 LDA #$01 60E3- 8D 72 0A STA $0A72 ; don't know 60E6- 20 03 08 JSR $0803 ; set mainmem and read/write RAM bank 2 60E9- 8D 08 C0 STA $C008 60EC- AD 83 C0 LDA $C083 60EF- AD 83 C0 LDA $C083 ; don't know (RAM is banked in, so this ; could be anything) 60F2- 20 0F E0 JSR $E00F ; 40 column mode 60F5- 8D 0C C0 STA $C00C ; text mode 60F8- 2C 51 C0 BIT $C051 ; don't know 60FB- 20 15 E0 JSR $E015 ; copy string to text page 60FE- A0 0A LDY #$0A 6100- B9 1B 61 LDA $611B,Y 6103- 99 D0 07 STA $07D0,Y 6106- 88 DEY 6107- 10 F7 BPL $6100 6109- A9 C8 LDA #$C8 610B- 85 45 STA $45 610D- A9 5F LDA #$5F 610F- 4D DA 07 EOR $07DA 6112- 8D DA 07 STA $07DA 6115- 20 18 E0 JSR $E018 6118- 4C 0D 61 JMP $610D 611B- "]ERROR D51" 6125- FF ??? Oh, this is the Badlands! That's the error message I found earlier in the sector editor. I don't know what's going on, but we don't want to be here. Which tells me we are definitely on the right track. Let's back up and figure out how the original disk avoids ending up here. ~ Chapter 2 When They Go Low, We Go High At $60E1 we're setting some parameters and calling a routine at $0803. What's at $0803? According to the ProDOS metadata, "LOWDOS" is loaded at $0800. Let's see what the heck "LOWDOS" is. *BLOAD LOWDOS *803L 0803- 4C 5A 0A JMP $0A5A *A5AL 0A5A- A2 08 LDX #$08 0A5C- A9 00 LDA #$00 0A5E- 9D 27 08 STA $0827,X 0A61- CA DEX 0A62- 10 FA BPL $0A5E ; turn on slot 6 drive motor 0A64- 2C E9 C0 BIT $C0E9 ; initialize... something 0A67- A9 01 LDA #$01 0A69- 8D 31 08 STA $0831 0A6C- A9 00 LDA #$00 0A6E- 8D 32 08 STA $0832 0A71- A9 02 LDA #$02 0A73- 8D 43 08 STA $0843 0A76- A9 00 LDA #$00 0A78- 8D 44 08 STA $0844 0A7B- A9 0D LDA #$0D 0A7D- A0 00 LDY #$00 0A7F- 20 45 08 JSR $0845 *845L 0845- 8D AF 09 STA $09AF 0848- 8C AE 09 STY $09AE ; memory fiddling (not shown) 084B- 20 B4 09 JSR $09B4 ; do something 084E- AE 41 08 LDX $0841 0851- BD 28 10 LDA $1028,X 0854- 20 6E 08 JSR $086E (1) ; increment something 0857- EE AF 09 INC $09AF ; and do the same thing again, but ; differently 085A- AE 41 08 LDX $0841 085D- BD 30 10 LDA $1030,X 0860- 20 6E 08 JSR $086E (2) ; and we're done 0863- A9 00 LDA #$00 0865- 8D AE 09 STA $09AE 0868- A9 0D LDA #$0D 086A- 8D AF 09 STA $09AF 086D- 60 RTS Here's the tables at $1028 and $1030: *1028. 1028- 00 04 08 0C 01 05 09 0D 1030- 02 06 0A 0E 03 07 0B 0F OK, I'm beginning to see what's going on here. This routine looks like it's loading a ProDOS "block" -- two consecutive sectors on disk, where by "consecutive," I mean "consecutive in the ProDOS skewing order." $0841 holds the index into the 8-item arrays at $1028 and $1030, which map logical to physical sectors. If I'm right, that means that $086E is the main entry point to read a sector from disk. *86EL 086E- 85 EC STA $EC ; reset data latch 0870- AD EE C0 LDA $C0EE 0873- A9 03 LDA #$03 0875- 8D 30 08 STA $0830 0878- 20 8D 08 JSR $088D *88DL ; set up death counter 088D- A9 00 LDA #$00 088F- 8D 8C 08 STA $088C 0892- CE 8C 08 DEC $088C 0895- D0 03 BNE $089A ; if death counter hits 0, JSR(?!) here ; (more on this later) 0897- 20 81 09 JSR $0981 ; another death counter 089A- A9 00 LDA #$00 089C- 85 FC STA $FC 089E- 88 DEY 089F- D0 07 BNE $08A8 08A1- C6 FC DEC $FC 08A3- D0 03 BNE $08A8 ; and again, if that death counter hits ; 0, JSR to the same place as $0887 08A5- 20 81 09 JSR $0981 I'm beginning to suspect that $0981 doesn't ever return, but we'll get to that in a minute. ; find address prologue (D5 AA 96) 08A8- AD EC C0 LDA $C0EC 08AB- 10 FB BPL $08A8 08AD- C9 D5 CMP #$D5 08AF- D0 ED BNE $089E 08B1- AD EC C0 LDA $C0EC 08B4- 10 FB BPL $08B1 08B6- C9 AA CMP #$AA 08B8- D0 EE BNE $08A8 08BA- AD EC C0 LDA $C0EC 08BD- 10 FB BPL $08BA 08BF- C9 96 CMP #$96 08C1- D0 E5 BNE $08A8 ; parse address field, store in $083D+ 08C3- A0 03 LDY #$03 08C5- A9 00 LDA #$00 08C7- 8D 3C 08 STA $083C 08CA- AD EC C0 LDA $C0EC 08CD- 10 FB BPL $08CA 08CF- 2A ROL 08D0- 85 F9 STA $F9 08D2- AD EC C0 LDA $C0EC 08D5- 10 FB BPL $08D2 08D7- 25 F9 AND $F9 08D9- 99 3D 08 STA $083D,Y 08DC- 4D 3C 08 EOR $083C 08DF- 88 DEY 08E0- 10 E5 BPL $08C7 08E2- A8 TAY 08E3- D0 AD BNE $0892 08E5- AD 3F 08 LDA $083F 08E8- C5 EB CMP $EB ; success path branches (if address ; field checksum verifies) 08EA- F0 0B BEQ $08F7 ; failure path -- recalibrate the drive ; and try again to find the right track ; (not shown) 08EC- 0A ASL 08ED- 85 ED STA $ED 08EF- A5 EB LDA $EB 08F1- 20 E8 09 JSR $09E8 08F4- 4C 92 08 JMP $0892 ; execution continues here (from $08EA) ; check if we got the sector we wanted, ; otherwise branch back and try again 08F7- AD 3E 08 LDA $083E 08FA- C5 EC CMP $EC 08FC- D0 94 BNE $0892 08FE- 60 RTS Continuing from $087B... ; read data field (prologue, data, and ; epilogue -- not shown, but it sets ; the carry on failure and clears it on ; success, like DOS 3.3) 087B- 20 FF 08 JSR $08FF ; branch forward on success 087E- 90 08 BCC $0888 ; decrement death counter and try again 0880- CE 30 08 DEC $0830 0883- D0 F3 BNE $0878 ; once again, we end up calling $0981 ; after the death counter hits 0 0885- 20 81 09 JSR $0981 ; execution continues here (from $087E) ; this routine finishes the nibble-to- ; byte conversion of the raw nibbles ; that were read earlier in $08FF ; (not shown) 0888- 20 9B 09 JSR $099B 088B- 60 RTS So... we're reading sectors, more or less the same way that DOS 3.3 reads sectors. The strangest part is that any fatal error ends up JSR'ing to $0981. What's at $0981? *981L ; turn off slot 6 drive motor 0981- 2C E8 C0 BIT $C0E8 ; reset stack pointer (so I was right, ; this routine never returns to the ; caller) 0984- A2 FF LDX #$FF 0986- 9A TXS ; jump to "fatal error" vector 0987- 4C 06 08 JMP $0806 But wait! We set that vector before calling LOWDOS -- all the way back at $60D9: 60D7- A9 26 LDA #$26 60D9- 8D 07 08 STA $0807 60DC- A9 61 LDA #$61 60DE- 8D 08 08 STA $0808 Immediately after setting that fatal error vector, we called LOWDOS to read an unreadable block that spans T00,S0C: 60E6- 20 03 08 JSR $0803 And that's the key to this protection scheme: the "success" path routes through the fatal error vector at $0806 and continues to the start of the game at $6126. If LOWDOS doesn't encounter an error, it returns to... what? Well, the "JSR $0803" at $60E6 eventually returns gracefully, and we continue to the next instruction, which as we saw before was The Badlands. Which is exactly the behavior I saw on my non-working copy. ~ Chapter 3 One Byte(*) To Rule Them All And In The Darkness Patch Them (*) not guaranteed, actual count may vary The protection routine has no side effects. We can bypass the entire thing by jumping to the "error" handler directly from $6000, instead of jumping to the protection routine. T08,S05,$01: CE 60 -> 26 61 ]PR#6 ...works, and it is glorious... Quod erat liberandum. --------------------------------------- A 4am crack No. 2316 ------------------EOF------------------