---------------Farmfiler--------------- A 4am crack 2022-12-26 --------------------------------------- Name: Farmfiler Version: 03.11.83 Genre: productivity Year: 1983 Publisher: Farmplan Computer Systems Platform: Apple ][+ or later Media: 5.25-inch disk Sides: 4 OS: DOS 3.3 Previous cracks: none _____________________________ { } { "There are more things in } { Heaven and Earth, Horatio, } { than are dreamt of in your } { philosophy. } { } { Hamlet, I.5 } {_____________________________} ~ Chapter 0 In Which Various Automated Tools Fail In Interesting Ways COPYA immediate read error Locksmith Fast Disk Backup able to copy tracks 0-2 only EDD 4 bit copy (no sync, no count) no errors, copy works Copy ][+ nibble editor no obvious shenanigans (standard address and data prologues and epilogues, standard address field structure) --v-- TRACK: 03 START: 2965 LENGTH: 183F 2940: FF FF FF FF FF FF FF FF VIEW 2948: FF FF FF FF FF FF FF FF 2950: FF FF FF FF FF FF FF FF 2958: FF FF FF FF FF FF FF FF 2960: FF FF FF FF FF D5 AA 96 <-2965 2968: FF FE AB AE AA AA FE FA 2970: DE AA E9 99 FF FF FF FF 2978: FF FF FF FF D5 AA AD AE 2980: FE DA FF EC A6 AE E6 A7 --^-- Disk Fixer Given that DOS tracks (0-2) appear unprotected and the rest of the disk appears protected, I would expect the bootloader to jump to some custom routine after loading DOS, but it only seems to set the Applesoft RUN flag (makes all interactive commands re-RUN the current program, a weird little trick that nonetheless has nothing to do with on-disk structural protection) --v-- T00,S01 ----------- DISASSEMBLY MODE ---------- 003A:20 93 B7 JSR $B793 003D:A2 FF LDX #$FF 003F:9A TXS 0040:8E EB B7 STX $B7EB 0043:4C C8 BF JMP $BFC8 0046:20 89 FE JSR $FE89 0049:A9 80 LDA #$80 <-- 004B:65 D6 ADC $D6 004D:85 D6 STA $D6 004F:4C 84 9D JMP $9D84 --^-- Furthermore, there is nothing special at $B793, which just calls $B7B5 in a loop as usual. There is nothing special at $B7B5, which just calls $BD00 as usual. There are no unusual JMPs or JSRs at $BD00 either. The patch at $BFC8 just does the usual thing and returns to $B746. Nor are there any unusual callouts at $9D84, which exits via $A180 as usual. And there is nothing suspicious at $A180. That's a whole lot of normality for a disk that is definitely not normal. Passport Initial success in capturing the RWTS and tracing the bootloader, followed by immediate failure. --v-- Reading from S6,D1 T00,S00 Found DOS 3.3 bootloader Using disk's own RWTS T22,S0F Switching to built-in RWTS T22,S0F Fatal read error Passport does not work on this disk. --^-- I have to admit, it is surprising to see Passport fail so quickly on a disk that is, by all indications, almost entirely normal -- especially after 6+ years of development and 2999 cracks. But there are more edge cases in Heaven and Earth, &c. Why didn't COPYA work? something funky going on with tracks 3+ Why didn't Locksmith FDB work? ditto EDD worked. What does that tell us? no half or quarter tracks almost certainly no nibble check (just as-yet-unexplained structural changes) Next steps: 1. capture RWTS with AUTOTRACE 2. convert disk to standard format with Advanced Demuffin 3. patch RWTS to read standard format ~ Chapter 1 In Which We Attempt To Use The Original Disk As A Weapon Against Itself And It Does Not Go Well [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:...RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR +.5: 0123456789ABCDEF0123456789ABCDEF012 SC0:...RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR SC1:...RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR SC2:...RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR SC3:...RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR SC4:...RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR SC5:...RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR SC6:...RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR SC7:...RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR SC8:...RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR SC9:...RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR SCA:...RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR SCB:...RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR SCC:...RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR SCD:...RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR SCE:...RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR SCF:...RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR ======================================= 16SC $00,$00-$22,$0F BY1.0 S6,D1->S6,D2 --^-- This was not the rousing success I was hoping for. Somewhat stuck, I decided to compare tracks 0-2 to a standard DOS 3.3 disk, to see what I had missed. This is made only slightly more difficult by the fact that DOS 3.3 went through several minor revisions, each of which added more and more patch code to previously unused regions of track 0. But I did, at last, find this difference: --v-- BDB9- 20 00 B8 JSR $B800 BDBC- A0 30 LDY #$30 BDBE- 8C 78 05 STY $0578 BDC1- AE F8 05 LDX $05F8 BDC4- 20 00 BF JSR $BF00 <-- ! BDC7- 90 24 BCC $BDED BDC9- CE 78 05 DEC $0578 BDCC- 10 F3 BPL $BDC1 --^-- On a standard DOS 3.3 disk, the call at $BDC4 is to $B944, which finds and parses the next available address field and sets some values in zero page with the results. $BF00 isn't an entry point for anything. But on this disk, it is. --v-- ; call the usual routine to parse the ; next address field BF00- 20 44 B9 JSR $B944 ; save the result BF03- 08 PHP ; get the slot number (x16) from the ; RWTS parameter table BF04- A0 01 LDY #$01 BF06- B1 48 LDA ($48),Y ; compare to ... somewhere in zero page BF08- C5 18 CMP $18 ; if not equal (to what?) then skip ; ahead BF0A- D0 11 BNE $BF1D ; now get the drive number from the ; RWTS parameter table BF0C- C8 INY BF0D- B1 48 LDA ($48),Y ; if it's not drive 1, then skip ahead BF0F- C9 01 CMP #$01 BF11- D0 0A BNE $BF1D ; now get the track number from the ; RWTS parameter table BF13- C8 INY BF14- C8 INY BF15- B1 48 LDA ($48),Y ; if it's less than 3 then skip ahead BF17- C9 03 CMP #$03 BF19- 30 02 BMI $BF1D ; oh my God, I just figured it out BF1B- 46 2E LSR $2E BF1D- A9 80 LDA #$80 BF1F- 85 D6 STA $D6 BF21- 28 PLP BF22- 60 RTS --^-- This is actually brilliant, and it's something I've never seen before. As I mentioned, the routine at $B944 finds and parses the next available address field, and stores results in zero page. Specifically, it stores the decoded disk volume number in $2F, the track number in $2E, and the sector number in $2D. Notably, it does not do anything with those values beyond store them. (There is a checksum embedded in the address field, which it does validate.) But the rest of the RWTS logic, like "is this a sector we care about?" or "is the drive head on the right track?" resides the caller. This routine at $BF00 inserts itself between the parser at $B944 and the caller at $BDxx, and does exactly one thing: if we're reading or writing to track 3+ in a specific slot and drive, it takes the track number stored in the address field and divides it by 2. ~ Chapter 2 In Which We Retrace Our Steps This still leaves one unanswered question: what is in zero page $18, and where is it initialized? I guess that's two questions. The answer to both questions lies in the early bootloader, which I had passed over with a minimum of concern, only to find the next steps would not work. Here is track 0, sector 1, loaded into memory at $B700: --v-- T00,S01 ----------- DISASSEMBLY MODE ---------- ; blah blah blah 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 0014:A9 02 LDA #$02 0016:8D EC B7 STA $B7EC 0019:A9 04 LDA #$04 001B:8D ED B7 STA $B7ED 001E:AC E7 B7 LDY $B7E7 0021:88 DEY 0022:8C F1 B7 STY $B7F1 0025:A9 01 LDA #$01 0027:8D F4 B7 STA $B7F4 ; wait a minute, what's all this then 002A:86 18 STX $18 --^-- And there it is: zero page $18 holds the boot slot (x16). When we're reading from that slot, and drive 1, track numbers in address fields are cut in half before being interpreted. That also explains why Advanced Demuffin couldn't read the disk, even with the disk's own RWTS: we were missing a crucial initialization in zero page $18. Let's correct that. ~ Chapter 3 In Which We Attempt To Use The Original Disk As A Weapon Against Itself Again But This Time With Feeling [S6,D1=original disk] [S6,D2=blank disk] [S5,D1=my work disk] ]PR#5 ... ]BLOAD ADVANCED DEMUFFIN 1.5 ]CALL -151 ; standard IOB 1400- 4A LSR 1401- 8D 22 0F STA $0F22 1404- 8C 23 0F STY $0F23 1407- 8E 27 0F STX $0F27 140A- A9 01 LDA #$01 140C- 8D 20 0F STA $0F20 140F- 8D 2A 0F STA $0F2A ; custom for this disk: take the source ; slot and copy it into zero page $18 ; before each read 1412- AD 1F 0F LDA $0F1F 1415- 85 18 STA $18 ; now call the disk's original RWTS 1417- A9 0F LDA #$0F 1419- A0 1E LDY #$1E 141B- 4C 00 BD JMP $BD00 With this change, we should be able to read the entire disk. ; launch Advanced Demuffin *800G ["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 --^-- Glorious. We still have a disk that treats the disk in drive 1 of the boot slot as protected, but we can fix that with a one-byte patch by neutering the LSR on the track number. T00,S09,$1B: 46 -> 24 Now the custom routine at $BF00 looks like this: --v-- T00,S09 ----------- DISASSEMBLY MODE ---------- 0015:B1 48 LDA ($48),Y 0017:C9 03 CMP #$03 0019:30 02 BMI $001D 001B:24 2E BIT $2E <-- 001D:A9 80 LDA #$80 001F:85 D6 STA $D6 0021:28 PLP 0022:60 RTS --^-- BIT functions here as a no-op (well technically it will set some status flags, but we're restoring the original flags a few instructions later). We'll let it go through the motions to decide whether to treat this disk specially, then at the crucial moment, we'll do nothing special. Disks 2-4 are unprotected. Quod erat liberandum. --------------------------------------- A 4am crack No. 3000 ------------------EOF------------------