---------Teddy Bear-rels of Fun-------- A 4am crack 2015-08-23 --------------------------------------- Name: Teddy Bear-rels of Fun Genre: graphics Year: 1987 Authors: Ahead Designs Publisher: Developmental Learning Materials (DLM) Media: two single-sided 5.25-inch disks OS: Pronto-DOS Previous cracks: none _____________________________ { } { "Clear the lines of lint in } { your head, one at a time, } { and the king will be left } { standing alone, like a guy } { on a street corner." } { } { "Searching for } { Bobby Fischer" } {_____________________________} ~ Chapter 0 In Which Various Automated Tools Fail In Interesting Ways COPYA error on last pass Locksmith Fast Disk Backup can't read track $22; copy boots but shows a graphical error message "Disk Error: FILE NOT FOUND. Please press C when you have corrected the problem" and hangs EDD 4 bit copy (no sync, no count) no read errors, but copy exhibits same behavior as Locksmith Copy ][+ nibble editor there's definitely data on track $22, but it's not in any recognizable pattern --v-- COPY ][ PLUS BIT COPY PROGRAM 8.4 (C) 1982-9 CENTRAL POINT SOFTWARE, INC. --------------------------------------- TRACK: 22 START: 37F9 LENGTH: 0F0F 37D8: B7 FF BA FA BB F7 BD F7 VIEW 37E0: BE BF FB BE F6 FF FE BF 37E8: BE BB EA EA FB AA BA EA 37F0: EB EF DF BF FF F7 F6 EF 37F8: EE AB AB AB FF D5 D5 D5 <-37F9 3800: FF EA EA EA FF F5 F5 F5 3808: FF FA FA FA FF FD FD FD 3810: FF FE FE FE FF FF B6 FD 3818: B7 FF BA FA BB F7 BD F7 --------------------------------------- A TO ANALYZE DATA ESC TO QUIT ? FOR HELP SCREEN / CHANGE PARMS Q FOR NEXT TRACK SPACE TO RE-READ --^-- Disk Fixer T00 -> looks like DOS 3.3 bootloader T00-T02 -> full version of Pronto-DOS T11 -> DOS 3.3 style disk catalog T01,S07 -> startup program is "HELLO" Why didn't any of my copies work? probably a nibble check that looks for something special on track $22 Next steps: 1. Trace the startup program 2. Find and disable the nibble check 3. There is no step 3 (I hope) ~ Chapter 1 In Which Our Adventure Gets Off To An Suspiciously Smooth Start [S6,D1=non-working copy] ]PR#6 ]CATALOG PRONTO-DOS V254 *A 002 HELLO *B 029 MENU *B 003 START *B 011 L0 *B 019 L1 *B 030 L2 *B 022 L3 *B 043 L4 *B 002 [TTL 0:LOCK B 002 PRSTATE *B 016 S0 *B 013 S1 *B 012 S2 *B 012 S3 *B 012 S4 *B 011 S5 *B 008 S6 *B 012 S7 *B 006 S8 *B 019 P1 *B 026 P2 *B 032 P3 *B 019 P4 *B 017 P5 *B 037 P6 *B 018 P7 *B 007 P8 *B 035 P9 *B 025 CPRO ]LIST 10 ONERR GOTO 100 20 REM POKE 1012,0 30 HGR2 35 PRINT "MAXFILES 1" 40 PRINT "BLOAD START" 50 PRINT "BRUN MENU" 100 CALL - 1438 ]MAXFILES 1 ]BLOAD START ]CALL-151 *AA72.AA73 AA72- 00 60 *BLOAD MENU *AA72.AA73 AA72- 00 08 START is loaded at $6000, and MENU is loaded at $0800. *800L 0800- 4C 00 60 JMP $6000 Well OK then, if you insist. *6000L ; reset stack 6000- D8 CLD 6001- A2 FF LDX #$FF 6003- 9A TXS . . boring initialization and stuff . 60E6- A2 00 LDX #$00 60E8- 90 0E BCC $60F8 ... 60F8- BD E1 61 LDA $61E1,X 60FB- BC E6 61 LDY $61E6,X 60FE- AA TAX 60FF- 98 TYA 6100- 4C C0 15 JMP $15C0 $15C0 is not currently valid code, so I need to interrupt the code here and see what ends up there. The X register is $00 at $60F8. Let's see what ends up in A and Y. *61E1 61E1- 00 ; --> accumulator *61E6 61E6- 00 ; --> Y register OK, all zeroes. Let's go. *6100:4C 59 FF *6000G ...disk activity... ~ Chapter 2 In Which We Take The Long Road *15C0L 15C0- 48 PHA ; = $00 15C1- 8A TXA ; = $00 15C2- 18 CLC 15C3- 69 B0 ADC #$B0 ; = $B0 15C5- 8D E2 15 STA $15E2 15C8- A9 01 LDA #$01 15CA- 20 75 13 JSR $1375 *1375L 1375- 4C C7 13 JMP $13C7 *13C7L 13C7- 48 PHA ; = $01 ; get address of RWTS parameter table 13C8- 20 E3 03 JSR $03E3 13CB- 84 3C STY $3C 13CD- 85 3D STA $3D ; set drive 13CF- A0 02 LDY #$02 13D1- 68 PLA ; = $01 13D2- 91 3C STA ($3C),Y 13D4- 60 RTS Continuing from $15CD... 15CD- A2 E1 LDX #$E1 15CF- A0 15 LDY #$15 15D1- 20 4E 13 JSR $134E *134EL 134E- 4C 7B 13 JMP $137B *137BL ; ($3C) -> $15E1 137B- 86 3C STX $3C 137D- 84 3D STY $3D ; fill a buffer with space characters 137F- A0 1D LDY #$1D 1381- A9 A0 LDA #$A0 1383- 99 53 03 STA $0353,Y 1386- 88 DEY 1387- 10 FA BPL $1383 ; copy a string into the newly cleared ; buffer, setting high bit 1389- A0 00 LDY #$00 138B- B1 3C LDA ($3C),Y 138D- F0 08 BEQ $1397 138F- 09 80 ORA #$80 1391- 99 53 03 STA $0353,Y 1394- C8 INY 1395- D0 F4 BNE $138B 1397- 60 RTS *15E1. * CC B0 00 ; "L0" * There's a file on the disk called "L0". Maybe this is a roundabout way of loading it? A reaaaally roundabout way. Continuing from $15D4... 15D4- A2 00 LDX #$00 15D6- A0 60 LDY #$60 15D8- 20 69 13 JSR $1369 15DB- B0 EB BCS $15C8 15DD- 68 PLA 15DE- 4C 00 60 JMP $6000 Copy II Plus (CATALOG > W/FILE LENGTHS) shows the file "L0" starts at $6000. My intuition says that's what's going on here. *15DE:4C 59 FF *300:A9 00 A2 00 A0 00 4C C0 15 *300L ; reproduce entry conditions 0300- A9 00 LDA #$00 0302- A2 00 LDX #$00 0304- A0 00 LDY #$00 0306- 4C C0 15 JMP $15C0 *300G ...disk activity... *6000L 6000- 4C 89 65 JMP $6589 Bingo. That is not what was at $6000 before the last disk activity. (I later verified with a sector editor that it did indeed load this code from "L0".) *6589L . . lots more file loading (like above) . 6697- 20 DC 63 JSR $63DC 669A- F0 05 BEQ $66A1 669C- A2 06 LDX #$06 669E- 4C C0 15 JMP $15C0 66A1- 60 RTS *63DCL 63DC- 8E 07 64 STX $6407 63DF- 8C 09 64 STY $6409 63E2- A9 03 LDA #$03 63E4- 20 67 65 JSR $6567 *6567L 6567- 8D 4B 65 STA $654B 656A- 8D 5E 65 STA $655E 656D- 8D 61 65 STA $6561 6570- 60 RTS Continuing from $63E7... 63E7- 20 49 65 JSR $6549 *6549L ; I don't have this in memory anymore, ; because it was on the text page and ; that's long gone after I broke to the ; monitor. But it looks suspiciously ; like getting the address of an RWTS ; parameter table and setting some ; values, then calling the RWTS. But ; the page 3 vectors have been shifted ; by $0100 bytes, into the text page. 6549- 20 E3 04 JSR $04E3 654C- 84 FA STY $FA 654E- 85 FB STA $FB ; track $22 6550- A9 22 LDA #$22 6552- A0 04 LDY #$04 6554- 91 FA STA ($FA),Y ; RWTS command $00 (seek) 6556- A9 00 LDA #$00 6558- A0 0C LDY #$0C 655A- 91 FA STA ($FA),Y 655C- 20 E3 04 JSR $04E3 655F- 20 D9 04 JSR $04D9 ; this is a dead giveaway -- resetting ; zero page $48 after an RWTS call. 6562- A9 00 LDA #$00 6564- 85 48 STA $48 6566- 60 RTS If I'm right, we just did a drive seek to track $22. No read, just a seek. Continuing from $63EA... 63EA- A9 C0 LDA #$C0 63EC- 20 71 65 JSR $6571 *6571L 6571- A0 0B LDY #$0B 6573- BE 7D 65 LDX $657D,Y 6576- 9D 13 64 STA $6413,X 6579- 88 DEY 657A- 10 F7 BPL $6573 657C- 60 RTS 657D- [00 09 0C 39 6C 71 78 94 A9 C0 D7 EE] Weird. So we're iterating through an array of offsets, then setting the byte at each offset to the accumulator, which was set in the caller and never changes. Well, the nice thing is that it's self- contained, so I can see the code before and after. $6413 isn't actually an entry point; it seems to be in the middle of an instruction. The nearest entry point before $6413 is $640E. ($640D is an "RTS" instruction.) *640EL 640E- 18 CLC 640F- A2 60 LDX #$60 6411- BD 89 1F LDA $1F89,X 6414- A9 40 LDA #$40 6416- 85 FA STA $FA 6418- A2 60 LDX #$60 641A- BD 8E 1F LDA $1F8E,X 641D- BD 8C 1F LDA $1F8C,X 6420- 10 FB BPL $641D . . . 647D- AD E8 1F LDA $1FE8 6480- 18 CLC 6481- 60 RTS 6482- AD E8 1F LDA $1FE8 6485- 38 SEC 6486- 60 RTS Oh, that's good. Do you see it? Don't move until you see it. Here, I'll make it easier for you... BD 89 LDA $ 89,X BD 8E LDA $ 8E,X BD 8C LDA $ 8C,X 10 FB BPL . . . AD E8 LDA $ E8 18 CLC 60 RTS AD E8 LDA $ E8 38 SEC 60 RTS ~ Chapter 3 In Which You've Lost, You Just Don't Know It Yet *300:A9 C0 4C 71 65 *300L 0300- A9 C0 LDA #$C0 0302- 4C 71 65 JMP $6571 *300G *640EL ; clear carry to start 640E- 18 CLC ; turn on drive motor (hard-coded to ; slot 6, booooo) 640F- A2 60 LDX #$60 6411- BD 89 C0 LDA $C089,X A fun(*) thing to do is boot original floppies from slot 5. Lots of copy protection routines (including this one) hard-code slot 6, so you can find out when they're called because the slot 6 drive light will suddenly go on. (*) not guaranteed, actual fun may vary ; reset data latch, skip some nibbles 6414- A9 40 LDA #$40 6416- 85 FA STA $FA 6418- A2 60 LDX #$60 641A- BD 8E C0 LDA $C08E,X 641D- BD 8C C0 LDA $C08C,X 6420- 10 FB BPL $641D 6422- C6 F9 DEC $F9 6424- D0 F7 BNE $641D 6426- C6 FA DEC $FA 6428- D0 F3 BNE $641D 642A- AD 05 65 LDA $6505 642D- 8D 06 65 STA $6506 6430- 20 9F 64 JSR $649F *649FL ; initialize state 649F- A0 00 LDY #$00 64A1- 18 CLC 64A2- 2A ROL 64A3- 85 FB STA $FB ; get a nibble 64A5- AD EC C0 LDA $C0EC 64A8- 10 FB BPL $64A5 ; compute a rolling checksum 64AA- AA TAX 64AB- 45 FB EOR $FB 64AD- 2A ROL 64AE- 49 41 EOR #$41 64B0- 85 FB STA $FB 64B2- C8 INY ; Y is the Death Counter -- if it rolls ; over to 0, branch to failure path ; which sets the carry and exits 64B3- F0 CD BEQ $6482 ; loop until we find an $F7 nibble 64B5- 8A TXA 64B6- C9 F7 CMP #$F7 64B8- D0 EB BNE $64A5 ; Now we get the next nibble, but wait! ; We've spent so much time computing ; the checksum that we would ordinarily ; miss the next nibble. But on the ; original disk, there is a timing bit ; (an extra "0" bit) after the $F7, so ; that gives us just enough time to ; catch the next nibble before it goes ; whizzing by as the disk spins. 64BA- AD EC C0 LDA $C0EC 64BD- 10 FB BPL $64BA 64BF- AA TAX 64C0- 45 FB EOR $FB 64C2- 2A ROL 64C3- 49 41 EOR #$41 64C5- 85 FB STA $FB 64C7- C8 INY 64C8- 8A TXA 64C9- C9 F7 CMP #$F7 64CB- F0 ED BEQ $64BA ; must be $F6 64CD- C9 F6 CMP #$F6 64CF- D0 D4 BNE $64A5 ; get the next nibble (same deal -- ; there's a timing bit after the $F6) 64D1- AD EC C0 LDA $C0EC 64D4- 10 FB BPL $64D1 64D6- AA TAX 64D7- 45 FB EOR $FB 64D9- 2A ROL 64DA- 49 41 EOR #$41 64DC- 85 FB STA $FB 64DE- C8 INY 64DF- 8A TXA 64E0- C9 F7 CMP #$F7 64E2- F0 D6 BEQ $64BA ; must be $EF 64E4- C9 EF CMP #$EF 64E6- D0 BD BNE $64A5 ; get the next nibble (same deal -- ; there's a timing bit after the $EF) 64E8- AD EC C0 LDA $C0EC 64EB- 10 FB BPL $64E8 64ED- AA TAX 64EE- 45 FB EOR $FB 64F0- 2A ROL 64F1- 49 41 EOR #$41 64F3- 85 FB STA $FB 64F5- C8 INY 64F6- 8A TXA 64F7- C9 F7 CMP #$F7 64F9- F0 BF BEQ $64BA ; must be $EE 64FB- C9 EE CMP #$EE 64FD- D0 A6 BNE $64A5 ; get next nibble immediately 64FF- AD EC C0 LDA $C0EC 6502- 10 FB BPL $64FF 6504- 60 RTS Continuing from $6433... ; compare the nibble we just read (at ; $64FF) 6433- C9 AB CMP #$AB ; if it's not $AB, jump to failure path 6435- D0 4B BNE $6482 It goes on like this for a while, checking a whole sequence of nibbles after that, then doing it a few more times to ensure it wasn't just a fluke that it passed the first time. But my non-working copy has already failed, because it has no timing bits after the $F7, $F6, $EF, or $EE nibbles. The original disk eventually falls through to $647D, which turns off the drive, clears the carry, and exits. My non-working copy ends up at $6482, turns off the drive, sets the carry, and exits. The first instruction (at $640E) was already a "CLC", which is convenient. Let's change the second instruction to an "RTS" so this routine always signals unconditional success. [Disk Fixer] ["D"irectory Mode] [select file "L0"] [right arrow to move through file one sector at a time, 4 times] T19,S0A,$13 change "A2" to "60" Quod erat liberandum. --------------------------------------- A 4am crack No. 415 ------------------EOF------------------