----------Create with Garfield--------- -------------Deluxe Edition------------ A 4am crack 2015-08-23 --------------------------------------- Name: Create with Garfield: Deluxe Edition Genre: graphics Year: 1987 Authors: Ahead Designs Publisher: Developmental Learning Materials (DLM) Media: two single-sided 5.25-inch discs OS: Pronto-DOS Previous cracks: Bachelor Beer / The Foreign Boys Similar cracks: Teddy Bear-rels of Fun (no. 415) ~ 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 003 START *B 029 MENU *B 021 L0 *B 021 L1 *B 030 L2 *B 022 L3 *B 043 L4 B 002 PRSTATE *B 002 [TTL 0:LOCK *B 012 S0 *B 014 S1 *B 007 S2 *B 013 S3 *B 011 S4 *B 013 S5 *B 014 S6 *B 014 S7 *B 015 S8 *B 006 S9 *B 037 P1 *B 016 P2 *B 013 P3 *B 019 P4 *B 013 P5 *B 014 P6 *B 015 P7 *B 018 P8 *B 000 P9 *B 007 PA *B 035 PB *B 001 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? 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 E9 6E JMP $6EE9 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".) *6EE9L . . lots more file loading (like above) . 700E- 20 3C 6D JSR $6D3C 7011- F0 05 BEQ $7018 7013- A2 06 LDX #$06 7015- 4C C0 15 JMP $15C0 7018- 60 RTS *6D3CL 6D3C- 8E 67 6D STX $6D67 6D3F- 8C 69 6D STY $6D69 6D42- A9 03 LDA #$03 6D44- 20 C7 6E JSR $6EC7 *6EC7L 6EC7- 8D AB 6E STA $6EAB 6ECA- 8D BE 6E STA $6EBE 6ECD- 8D C1 6E STA $6EC1 6ED0- 60 RTS Continuing from $6D47... 6D47- 20 A9 6E JSR $6EA9 *6EA9L ; 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. 6EA9- 20 E3 04 JSR $04E3 6EAC- 84 FA STY $FA 6EAE- 85 FB STA $FB ; track $22 6EB0- A9 22 LDA #$22 6EB2- A0 04 LDY #$04 6EB4- 91 FA STA ($FA),Y ; RWTS command $00 (seek) 6EB6- A9 00 LDA #$00 6EB8- A0 0C LDY #$0C 6EBA- 91 FA STA ($FA),Y 6EBC- 20 E3 04 JSR $04E3 6EBF- 20 D9 04 JSR $04D9 ; this is a dead giveaway -- resetting ; zero page $48 after an RWTS call. 6EC2- A9 00 LDA #$00 6EC4- 85 48 STA $48 6EC6- 60 RTS If I'm right, we just did a drive seek to track $22. No read, just a seek. Continuing from $6D4A... 6D4A- A9 C0 LDA #$C0 6D4C- 20 D1 6E JSR $6ED1 *6ED1L 6ED1- A0 0B LDY #$0B 6ED3- BE DD 6E LDX $6EDD,Y 6ED6- 9D 73 6D STA $6D73,X 6ED9- 88 DEY 6EDA- 10 F7 BPL $6ED3 6EDC- 60 RTS 6EDD- [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. $6D73 isn't actually an entry point; it seems to be in the middle of an instruction. The nearest entry point before $6D73 is $6D6E. ($6D6D is an "RTS" instruction.) *6D6EL 6D6E- 18 CLC 6D6F- A2 60 LDX #$60 6D71- BD 89 1F LDA $1F89,X 6D74- A9 40 LDA #$40 6D76- 85 FA STA $FA 6D78- A2 60 LDX #$60 6D7A- BD 8E 1F LDA $1F8E,X 6D7D- BD 8C 1F LDA $1F8C,X 6D80- 10 FB BPL $6D7D . . . 6DDD- AD E8 1F LDA $1FE8 6DE0- 18 CLC 6DE1- 60 RTS 6DE2- AD E8 1F LDA $1FE8 6DE5- 38 SEC 6DE6- 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 We See It *300:A9 C0 4C D1 6E *300L 0300- A9 C0 LDA #$C0 0302- 4C D1 6E JMP $6ED1 *300G *6D6EL ; clear carry to start 6D6E- 18 CLC ; turn on slot 6 drive motor (hard- ; coded, boo) 6D6F- A2 60 LDX #$60 6D71- 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 6D74- A9 40 LDA #$40 6D76- 85 FA STA $FA ; reset data latch, skip some nibbles 6D78- A2 60 LDX #$60 6D7A- BD 8E C0 LDA $C08E,X 6D7D- BD 8C C0 LDA $C08C,X 6D80- 10 FB BPL $6D7D 6D82- C6 F9 DEC $F9 6D84- D0 F7 BNE $6D7D 6D86- C6 FA DEC $FA 6D88- D0 F3 BNE $6D7D 6D8A- AD 65 6E LDA $6E65 6D8D- 8D 66 6E STA $6E66 6D90- 20 FF 6D JSR $6DFF *6DFFL ; initialize state 6DFF- A0 00 LDY #$00 6E01- 18 CLC 6E02- 2A ROL 6E03- 85 FB STA $FB ; get a nibble 6E05- AD EC C0 LDA $C0EC 6E08- 10 FB BPL $6E05 ; compute a rolling checksum 6E0A- AA TAX 6E0B- 45 FB EOR $FB 6E0D- 2A ROL 6E0E- 49 41 EOR #$41 6E10- 85 FB STA $FB ; Y is the Death Counter -- if it rolls ; over to 0, branch to failure path ; which sets the carry and exits 6E12- C8 INY 6E13- F0 CD BEQ $6DE2 ; loop until we find an $F7 nibble 6E15- 8A TXA 6E16- C9 F7 CMP #$F7 6E18- D0 EB BNE $6E05 ; 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. 6E1A- AD EC C0 LDA $C0EC 6E1D- 10 FB BPL $6E1A 6E1F- AA TAX 6E20- 45 FB EOR $FB 6E22- 2A ROL 6E23- 49 41 EOR #$41 6E25- 85 FB STA $FB 6E27- C8 INY 6E28- 8A TXA 6E29- C9 F7 CMP #$F7 6E2B- F0 ED BEQ $6E1A ; must be $F6 6E2D- C9 F6 CMP #$F6 6E2F- D0 D4 BNE $6E05 ; get the next nibble (same deal -- ; there's a timing bit after the $F6) 6E31- AD EC C0 LDA $C0EC 6E34- 10 FB BPL $6E31 6E36- AA TAX 6E37- 45 FB EOR $FB 6E39- 2A ROL 6E3A- 49 41 EOR #$41 6E3C- 85 FB STA $FB 6E3E- C8 INY 6E3F- 8A TXA 6E40- C9 F7 CMP #$F7 6E42- F0 D6 BEQ $6E1A ; must be $EF 6E44- C9 EF CMP #$EF 6E46- D0 BD BNE $6E05 ; get the next nibble (same deal -- ; there's a timing bit after the $EF) 6E48- AD EC C0 LDA $C0EC 6E4B- 10 FB BPL $6E48 6E4D- AA TAX 6E4E- 45 FB EOR $FB 6E50- 2A ROL 6E51- 49 41 EOR #$41 6E53- 85 FB STA $FB 6E55- C8 INY 6E56- 8A TXA 6E57- C9 F7 CMP #$F7 6E59- F0 BF BEQ $6E1A ; must be $EE 6E5B- C9 EE CMP #$EE 6E5D- D0 A6 BNE $6E05 ; get next nibble immediately 6E5F- AD EC C0 LDA $C0EC 6E62- 10 FB BPL $6E5F 6E64- 60 RTS Continuing from $6D93... ; compare the nibble we just read (at ; $6E5F) 6D93- C9 AB CMP #$AB ; if it's not $AB, jump to failure path 6D95- D0 4B BNE $6DE2 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 $6DDD, which turns off the drive, clears the carry, and exits. My non-working copy ends up at $6DE2, turns off the drive, sets the carry, and exits. The first instruction (at $6D6E) 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] T15,S0F,$73 change "A2" to "60" Quod erat liberandum. --------------------------------------- A 4am crack No. 416 ------------------EOF------------------