Microzine #19 (Malice in Wonderland)
Starting with Microzine #19, the copy protection scheme has changed. As far as I can tell, they used the same protection scheme for #7-9, 12, 14, 18 and probably all of them in between, but I can't verify.
Recovering the data
The disk has non-standard formatting but can still be read properly as long as a copy program ignores non-fatal errors. To copy the disk,
- Boot a DOS 3.3 system master disk
- type the following at the BASIC prompt:
- "call -151"
- "b942: 18 60"
- "RUN COPYA"
- copy the microzine disk to a duplicate
The Copy Protection Routine
I located this copy protection routine by using KEGS' debugger and tracing the program flow using a lot of breakpoints until I found the point of failure. It took me a couple of hours.
Techniques I used to help were to examine the stack register so I could quickly return from functions to see if they mattered or not. This is equivalent to doing Shift-F11 in visual studio's debugger or "finish" in gdb (for higher level languages). Unfortunately, KEGS doesn't have a "finish" function at the time of this writing that I know of, but it would sure be handy.
I also set a breakpoint on $c0ec because that is the raw nibble read routine and I strongly suspected it would be called. However, in this case, doing this only lead me into the depths of DOS 3.3 and not to the actual copy protection routine. It's just something to keep in mind that you can do when using emulators to debug.
Eventually, after iterating through this breakpoint/stack examination process, I finally stumbled across this code:
00/a38e: 20 5d a3 JSR $a35d 00/a391: 20 51 a8 JSR $a851 00/a394: 6c 72 aa JMP ($aa72)
I had been expecting to find an indirect JMP at some point that took program flow out of DOS and into the copy protection routine (which I was assuming was NOT in DOS). Setting a breakpoint at $a394, and checking the address pointed to by $aa72, I found:
aa72: 00 90 ..
Now _this_ looked like what I was after: a JMP into a separate part of RAM, $9000 in this case. I eagerly disassembled $9000 and was not disappointed. It was rife with direct accesses to $c0ec which is the raw disk nibble read location. This looked exactly like what I was looking for. And as it turned out, it was.
Here is the relevant code:
00/9000: 20 e3 03 JSR $03e3 00/9003: 85 fb STA $fb 00/9005: 84 fa STY $fa 00/9007: a9 c5 LDA #$c5 00/9009: 48 PHA 00/900a: a9 00 LDA #$00 00/900c: 85 fc STA $fc 00/900e: a2 03 LDX #$03 00/9010: bc 49 90 LDY $9049,X 00/9013: 91 fa STA ($fa),Y 00/9015: ca DEX 00/9016: 10 f8 BPL $9010 00/9018: 8a TXA 00/9019: 48 PHA 00/901a: 20 3c 90 JSR $903c 00/901d: a0 01 LDY #$01 00/901f: b1 fa LDA ($fa),Y 00/9021: aa TAX 00/9022: 20 73 90 JSR $9073 00/9025: 68 PLA 00/9026: 68 PLA 00/9027: a0 00 LDY #$00 00/9029: 84 fe STY $fe 00/902b: b9 4d 90 LDA $904d,Y 00/902e: 09 80 ORA #$80 00/9030: 20 39 90 JSR $9039 00/9033: a4 fe LDY $fe 00/9035: c8 INY 00/9036: 4c 29 90 JMP $9029 00/9039: 6c 36 00 JMP ($0036) 00/903c: 20 e3 03 JSR $03e3 00/903f: 20 d9 03 JSR $03d9 00/9042: a9 00 LDA #$00 00/9044: 85 48 STA $48 00/9046: b0 78 BCS $90c0 00/9048: 60 RTS
Calling $3e3 prepares to go a RWTS call (disk I/O call) using DOS. $3d9 actually makes the call. So most likely, (I haven't verified) is the code calls $903C which then seeks to the track with the nibble signature to be checked against. Once the seek is complete, $9073 is called, which contains the actual nibble check. According to KEGS, track 0 is the track that has the nibble signature on it (based on setting breakpoint at $9073 and looking at KEGS' stats).
As you read this listing, keep in mind that $90c0 is the point of failure, and $90bc is where the code goes if the copy protection routine passed.
00/9073: bd 89 c0 LDA $c089,X 00/9076: a9 56 LDA #$56 00/9078: 85 fd STA $fd
$907a is the "try again" spot. If the copy protection fails, it will branch back to $907a to try again. Eventually, after a number of attempts, it will assume the disk is a copy and branch to $90c0.
00/907a: a9 08 LDA #$08
This accumulator value is significant and is part of the nibble count (see $9096). I just haven't figured it out completely yet.
00/907c: c6 fc DEC $fc 00/907e: d0 04 BNE $9084 00/9080: c6 fd DEC $fd 00/9082: f0 3c BEQ $90c0 00/9084: bc 8c c0 LDY $c08c,X 00/9087: 10 fb BPL $9084 00/9089: c0 fb CPY #$fb
It starts off looking for a specific nibble, #$fb.
00/908b: d0 ed BNE $907a 00/908d: f0 00 BEQ $908f 00/908f: ea NOP 00/9090: ea NOP 00/9091: bc 8c c0 LDY $c08c,X 00/9094: c0 08 CPY #$08 00/9096: 2a ROL 00/9097: b0 0b BCS $90a4
I am not fully sure what's going on here. Normally when loading from $c08c, you have to wait (using BPL) for new data to be ready but that doesn't happen here. The CPY #$08 seems to exist only to set or clear the carry flag. If Y >= #$8, the carry flag will be set. ROL is then called which rotates the accumulator left and puts the carry flag's value in at the bit 0 position. So if A was 8, and the carry was set, a ROL would make A equal to #$11 (if I've done my math right). It almost seems like this routine is trying to read in bits off the disk rather than bytes.
00/9099: bc 8c c0 LDY $c08c,X 00/909c: 10 fb BPL $9099 00/909e: c0 ff CPY #$ff 00/90a0: d0 d8 BNE $907a 00/90a2: f0 eb BEQ $908f
It is expecting to find an #$ff at some point soon after the #$fb. If it doesn't find it, it restarts the whole check.
00/90a4: bc 8c c0 LDY $c08c,X 00/90a7: 10 fb BPL $90a4 00/90a9: 84 fc STY $fc 00/90ab: c9 0a CMP #$0a 00/90ad: d0 cb BNE $907a
$90ab wants A to be #$A which is the result of the obscure check at $908F. If it's not, it restarts the whole check.
00/90af: bd 8c c0 LDA $c08c,X 00/90b2: 10 fb BPL $90af 00/90b4: 38 SEC 00/90b5: 2a ROL 00/90b6: 25 fc AND $fc 00/90b8: 49 ff EOR #$ff 00/90ba: d0 04 BNE $90c0
At this point, it expects the next nibble to be a specific value, but I'm not sure what it is. Since $90b8 does an XOR with #$ff and expects the result to be 0, you can assume that the value stored at $fc that it ANDs against must be #$FF. Furthermore, the value of A after the ROL instruction looks like it must be #$FF too. Unless I'm missing something. :)
00/90bc: dd 88 c0 CMP $c088,X 00/90bf: 60 RTS
$90bc is the place we go if the copy protection routine has succeeded.
00/90c0: a8 TAY 00/90c1: dd 88 c0 CMP $c088,X 00/90c4: 68 PLA 00/90c5: 68 PLA 00/90c6: 99 00 90 STA $9000,Y 00/90c9: c8 INY 00/90ca: c0 8b CPY #$8b 00/90cc: d0 f8 BNE $90c6 00/90ce: 60 RTS
$90c0 is where we go if the copy protection routine has failed. As you can see, it's not much different from $90bc except it attempts to obscure/erase the program at $9000 and it also pops the stack twice, which puts the system in an unstable state for when the RTS is called. This is how it "crashes".
Things I learned afterwards
I was curious about the indirect JMP at $a394 that lead us to $9000 so I looked in "Beneath Apple DOS" and on page 8-11 it says that $A38E-$a396 is the code for the BRUN command. Ah ha, now it's making sense. So whatever is at $9000 is actually a file that is being BRUN. With a sector editor, I scanned for 20 73 90 68 68 (see $9022) and found it on track $d, sector $4. I then used Copy ][+ to show a file map to see which file corresponded to that location. It turned out to be a file called W.SPC which apparently does nothing but check the copy protection and then run HELLO if all is well. An astute reader may now note that if we simply change DOS/BASIC to run HELLO directly instead of W.SPC, then we would effectively bypass the protection, but that wouldn't be nearly as educational, now would it?
We have a number of options to tackle this. One of them is to make sure if we ever go to $90c0 that we simply branch/jump to $90bc. Another option is to disable the call at $9022 so that the nibble check is never run. I opted for the second one because it represented the most minimal change.
With a sector editor
Search for "20 73 90 68 68". I found mine on track $d, sector $4, byte $26. Simply change the $20 to an $AD (changes JSR $9073 to LDA $9073) and the copy protection will be bypassed.
This crack should work when run on a real 5.25" floppy disk/drive but won't port over to emulator .DSK (or .PO) images because the DOS 3.3 volume has been changed (along with possibly other changes to the headers). So to use in an emulator, these images must be in .NIB format to preserve the sector header information.