------Multiplication and Division------ A 4am crack 2014-09-29 --------------------------------------- "Multiplication and Division" is a 1987 educational game distributed by Silver Burdett Company. COPYA fails miserably and immediately. EDD 4 bit copy gives no read errors, but the copy it creates only gets as far as loading DOS and displaying the BASIC prompt before crashing into the monitor at address $910A. Turning to my trusty Disk Fixer sector editor, I press "O" to get to the Input/Output Control and set "CHECKSUM ENABLED" to "NO". This will ignore the checksum bytes and epilogue sequences. i.e. As long as the address and data prologue are standard ("D5 AA 96" and "D5 AA AD", respectively), this will allow me to read each sector. And lo and behold, it works! I can read the data from every sector on every track. Track $11 does appear to contain a disk catalog, which strongly suggests this program is file-based. Based on my limited experience cracking other disks, I would guess that this disk has - Standard prologue bytes before the address and data fields [otherwise a sector editor would give read errors, even when ignoring checksums] - Non-standard epilogue bytes after the address and data fields [otherwise COPYA would work] - Some secondary protection [otherwise the bit copy created with EDD 4 would work] Given the (relatively) weak structural protection, I used to turn to the DOS 3.3 master disk, patch the RWTS to ignore checksums and epilogue bytes (changing $B942 from "SEC" to "CLC"), and run COPYA. Then, one fine day, and completely by accident, I came across an original disk with a bad sector. I suppose this shouldn't surprise me. These floppies are decades old by now; it's amazing any of them work at all. The point is, I shouldn't be using tools that ignore potentially serious read errors. There are other tools, like Super Demuffin, that can convert a disk like this (with non-standard epilogue bytes) into a standard format. It requires figuring out what the actual epilogue bytes are, but it has the advantage of surfacing a read error if the original disk actually has a read error. So... no more COPYA+B942:18 patch. From now on, it's Super Demuffin or Advanced Demuffin to convert disks to a standard format. Just by looking at the first few sectors, it appears that this disk uses a DOS 3.3-derived RWTS, which means that my AUTOTRACE program should be able to extract the RWTS from the original disk. [S6,D1=original 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 Well, that worked flawlessly. My work disk first runs AUTOTRACE0, which sets up a minimal boot trace to capture the first sector of track 0 and saves it to the file "BOOT0" (on my work disk, not the original disk). If the first sector looks reasonably normal, it runs AUTOTRACE1, which sets up a more involved boot trace to capture the rest of track 0 and save it to the file "BOOT1". If *that* looks reasonably normal, it saves the RWTS to a file, unimaginatively named "RWTS", which can be loaded with Advanced Demuffin to copy each track of the original disk to a duplicate with standard address/data prologue/epilogue sequences. Now I can use Advanced Demuffin to convert the disk to a standard format. [S6,D1=original disk] [S6,D2=blank disk] [S5,D1=my work disk] ]PR#5 ... ]BRUN ADVANCED DEMUFFIN 1.5 [press "5" to switch to slot 5] [press "R" to load a new RWTS module] --> At $B8, load "RWTS" from drive 1 [press "6" to switch to slot 6] [press "C" to convert disk] This disk is 16 sectors, and the default options (copy the entire disk, all tracks, all sectors) don't need to be changed unless something goes horribly wrong. --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 --^-- The disk's own RWTS gave no read errors on any track. This is the power and the genius of Advanced Demuffin. Every disk must be able to read itself. So, let it read itself, then capture the data and write it out in a standard format. There are two problems with this copy: 1. Depending on how the original RWTS was written, a demuffin'd disk may not be able to read itself. Some developers just patch the RWTS to ignore epilogue bytes, while others patch the RWTS to look for specific non-standard epilogue bytes. Demuffin'd disks in the latter category will grind immediately on boot, since as soon as the RWTS is loaded, all further disk reads will look for the original (non-standard) epilogue bytes and not find them. 2. Even if it can read itself, it won't run. The copies I tried to make -- even the bit copies -- just crashed. The original disk boots fine, which means there is some code being executed during boot to check if the disk is original. (Hint: it's not.) This disk appears to be in the "strict RWTS" category. My demuffin'd copy just grinds on boot, which tells me that the RWTS is looking for specific (non- standard) epilogue bytes and failing to find them. So I need to patch the RWTS to undo the modifications and look for the standard epilogue bytes instead. For future reference (mostly mine), here's a nice chart of the memory locations for all the prologues and epilogues in a DOS 3.3-shaped RWTS. If the RWTS stores $B700 in T00,S01 (most do), then $B8xx will be in T00,S02; $B9xx in T00,S03; and so on. 0x | read | write ---------------+-------+------- D5 | $B955 | $BC7A prologue AA | $B95F | $BC7F / 96 | $B96A | $BC84 ADDRESS -------+-------+------- \ DE | $B991 | $BCAE epilogue AA | $B99B | $BCB3 EB | | $BCB8 ---------------+-------+------- D5 | $B8E7 | $B853 prologue AA | $B8F1 | $B858 / AD | $B8FC | $B85D DATA ----------+-------+------- \ DE | $B935 | $B89E epilogue AA | $B93F | $B8A3 EB | | $B8A8 ---------------+-------+------- Here are the patches to normalize the RWTS. (Note to self: make an RWTS comparison tool.) T00,S03,$91 change "AB" to "DE" T00,S03,$9B change "FF" to "AA" Now my demuffin'd copy boots, loads DOS, displays a prompt, runs a boot program, and... crashes in exactly the same way that my failed bit copy did. As expected, I can now CATALOG all the files on this disk: ]PR#5 ... ]CATALOG,S6,D1 C1983 DSR^C#254 008 FREE A 008 HELLO B 006 DBS16 03/12/86 B 004 BS.NUM 03/14/86 B 005 BS.LARGE.CHAR 09/16/85 B 006 BS.SMALL.CHAR 09/16/85 B 002 BS.OBJ 02/21/86 B 006 BS.OWL 02/05/86 B 002 DBFLIP 03/19/86 B 004 MUS.MOV 11/19/85 B 004 DB.INPUT 03/12/86 B 006 PAC.SB 09/10/85 B 003 DB.COMMA 03/14/86 A 023 MENUS 04/26/86 A 011 ROUTINES 03/14/86 A 015 P.ROUTINES 03/14/86 A 010 G.ROUTINES 11/19/85 A 012 INSTRUCTION 03/14/86 A 010 I.PRACTICE 03/14/86 A 025 I.GAME 04/26/86 A 017 M.L.BASFACT 03/31/86 A 029 M.L.DIGITS 04/28/86 A 029 M.L.ESTIMATE 04/02/86 A 019 M.P.BASFACT 03/14/86 A 027 M.P.DIGITS 03/31/86 A 044 M.G.DIGITS 04/01/86 A 014 D.L.BASFACT 03/14/86 A 036 D.L.DIGITS 03/31/86 A 014 D.P.BASFACT 03/14/86 A 033 D.P.DIGITS 03/28/86 A 051 D.G.DIGITS 05/09/86 B 006 MEM.SWITCH 03/12/86 B 004 ESI.STARTUP B 003 XMGPRT Ooh, interesting. This disk is using a modified DOS that stores modification dates in the end of each file. The down side is that I can't run the program from my work disk, because it doesn't know to ignore the last 8 bytes of each filename. According to Copy ][+, the boot program on this disk is a binary file called "XMGPRT" that is loaded at $9000. ("CHANGE BOOT PROGRAM" will show the current boot program, even if you don't want to change it. "CATALOG DISK" -> "W/ FILE LENGTHS" will show the start address of each file.) ]PR#5 ... ]BLOAD XMGPRT,S6,D1 ]CALL -151 *9000L ; get address of RWTS parameter table 9000- 20 E3 03 JSR $03E3 9003- 85 FB STA $FB 9005- 84 FA STY $FA ; hmm 9007- A9 C5 LDA #$C5 9009- 48 PHA ; copy RWTS parameters 900A- A9 00 LDA #$00 900C- 85 FC STA $FC 900E- A2 03 LDX #$03 9010- BC 4C 90 LDY $904C,X 9013- 91 FA STA ($FA),Y 9015- CA DEX 9016- 10 F8 BPL $9010 9018- 8A TXA ; hmm 9019- 48 PHA The X register will be $FF after the loop, so this means we've pushed $C5FF to the stack. At this point, a bare RTS will "return" to $C5FF+1, i.e. reboot. 901A- 20 3C 90 JSR $903C *903CL ; call the RWTS (most likely just to ; move the drive head to the proper ; position for an impending nibble ; check) 903C- 20 E3 03 JSR $03E3 903F- 20 D9 03 JSR $03D9 9042- A9 00 LDA #$00 9044- 85 48 STA $48 ; if that fails, off to The Badlands 9046- B0 65 BCS $90AD 9048- 60 RTS Caller was $901A, so resuming at $901D: *901DL 901D- A0 01 LDY #$01 901F- B1 FA LDA ($FA),Y 9021- AA TAX 9022- 20 6B 90 JSR $906B *906BL ; turning on the drive motor manually ; is always suspicious 906B- BD 89 C0 LDA $C089,X 906E- A9 56 LDA #$56 9070- 85 FD STA $FD 9072- A9 08 LDA #$08 9074- C6 FC DEC $FC 9076- D0 04 BNE $907C 9078- C6 FD DEC $FD 907A- F0 7E BEQ $90FA ; look for a nibble 907C- BC 8C C0 LDY $C08C,X 907F- 10 FB BPL $907C 9081- C0 FB CPY #$FB 9083- D0 ED BNE $9072 ; This looks like a pointless branch, ; but remember that all this low-level ; disk access is time-sensitive down to ; the CPU cycle. This branch isn't for ; branching. It's here to kill time ; while the disk keeps spinning. 9085- F0 00 BEQ $9087 9087- EA NOP 9088- EA NOP ; Do some fancy stuff looking at the ; individual timing bits between the ; nibbles. Note the lack of a BPL loop, ; so this will just get whatever is in ; the read latch, even if it hasn't ; read all 8 bits yet. 9089- BC 8C C0 LDY $C08C,X 908C- C0 08 CPY #$08 908E- 2A ROL 908F- B0 0B BCS $909C 9091- BC 8C C0 LDY $C08C,X 9094- 10 FB BPL $9091 ; look for a self-sync byte ($FF) 9096- C0 FF CPY #$FF 9098- D0 D8 BNE $9072 909A- F0 EB BEQ $9087 ; now do some bit math on the nibbles ; following the self-sync bytes 909C- BC 8C C0 LDY $C08C,X 909F- 10 FB BPL $909C 90A1- 84 FC STY $FC 90A3- C9 0A CMP #$0A 90A5- D0 CB BNE $9072 90A7- BD 8C C0 LDA $C08C,X 90AA- 10 FB BPL $90A7 90AC- 38 SEC 90AD- 2A ROL 90AE- 25 FC AND $FC 90B0- 49 FF EOR #$FF 90B2- D0 46 BNE $90FA ; success path falls through to here -- ; turn off the drive motor and ; (eventually) return to the caller 90B4- DD 88 C0 CMP $C088,X . . . ; failure path (a.k.a. "The Badlands", ; from which there is no return) 90FA- A8 TAY 90FB- DD 88 C0 CMP $C088,X ; manually pop the return address of ; the immediate caller (which leaves ; the manually pushed $C5FF address on ; the top of the stack to "return" to) 90FE- 68 PLA 90FF- 68 PLA ; destroy all trace of this program in ; memory 9100- 99 00 90 STA $9000,Y 9103- C8 INY 9104- C0 00 CPY #$00 9106- D0 F8 BNE $9100 9108- 00 BRK 9109- 00 BRK Literally, that's it. I'm pretty sure they meant to have an "RTS" here (which would reboot), but they had an off-by-1 bug in saving this file and the "RTS" instruction went missing. Meanwhile, the success path returns to the real caller and continues execution at $9025: *9025L ; remove the dummy address that we ; manually pushed to the stack earlier ; (which would reboot if "returned" to) 9025- 68 PLA 9026- 68 PLA ; umm, I'm no expert, but this looks ; like an infinite loop 9027- A0 00 LDY #$00 9029- 84 FE STY $FE 902B- B9 50 90 LDA $9050,Y 902E- 09 80 ORA #$80 9030- 20 39 90 JSR $9039 9033- A4 FE LDY $FE 9035- C8 INY 9036- 4C 29 90 JMP $9029 9039- 6C 36 00 JMP ($0036) Zero page $36 is the DOS output vector, so this prints stuff. In an infinite loop. And this is the *success* path. Never a dull moment in the land of Apple II copy protection. Whatever it's printing, it starts at $9050. This is what's at $9050: *9050.9070 9050- 04 42 52 55 4E 20 45 53 9058- 49 2E 53 54 41 52 54 55 9060- 50 20 20 20 20 20 20 20 9068- 20 0D 00 BD 89 C0 A9 56 9070- 85 * *FC58G N 400<9050.906AM DBRUN ESI.STARTUP M@ Aha! It's not an infinite loop after all. Well, it is, technically, but it won't actually run forever. It's not easy to show in plain text, but the initial "D" and the final "M@" are in inverse. So that string starts with Ctrl-D and ends with Ctrl-M and a null byte. Since DOS is already loaded, printing this through the DOS vector will execute that command, as if you typed it from a prompt yourself. Since that program never returns to the caller, it will break out of the seemingly "infinite" loop. Since there are no side effects to the copy protection routine, I can change this file to simply skip over it and go straight to running the real startup program. *9000:4C 27 90 *BSAVE XMGPRT,A$9000,L$0108 Quod erat liberandum. --------------------------------------- A 4am crack No. 151 ------------------EOF------------------