Microzine19

From DaphneWiki

(Difference between revisions)
Jump to: navigation, search
(Created page with "== 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 #...")
(The Copy Protection Routine)
Line 84: Line 84:
  00/9076: a9 56          LDA    #$56
  00/9076: a9 56          LDA    #$56
  00/9078: 85 fd          STA    $fd
  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
  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/907c: c6 fc          DEC    $fc
  00/907e: d0 04          BNE    $9084
  00/907e: d0 04          BNE    $9084
Line 92: Line 98:
  00/9087: 10 fb          BPL    $9084
  00/9087: 10 fb          BPL    $9084
  00/9089: c0 fb          CPY    #$fb
  00/9089: c0 fb          CPY    #$fb
 +
 +
It starts off looking for a specific nibble, #$fb.
 +
  00/908b: d0 ed          BNE    $907a
  00/908b: d0 ed          BNE    $907a
  00/908d: f0 00          BEQ    $908f
  00/908d: f0 00          BEQ    $908f
Line 100: Line 109:
  00/9096: 2a            ROL
  00/9096: 2a            ROL
  00/9097: b0 0b          BCS    $90a4
  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/9099: bc 8c c0      LDY    $c08c,X
  00/909c: 10 fb          BPL    $9099
  00/909c: 10 fb          BPL    $9099
Line 105: Line 117:
  00/90a0: d0 d8          BNE    $907a
  00/90a0: d0 d8          BNE    $907a
  00/90a2: f0 eb          BEQ    $908f
  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/90a4: bc 8c c0      LDY    $c08c,X
  00/90a7: 10 fb          BPL    $90a4
  00/90a7: 10 fb          BPL    $90a4
Line 110: Line 125:
  00/90ab: c9 0a          CMP    #$0a
  00/90ab: c9 0a          CMP    #$0a
  00/90ad: d0 cb          BNE    $907a
  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/90af: bd 8c c0      LDA    $c08c,X
  00/90b2: 10 fb          BPL    $90af
  00/90b2: 10 fb          BPL    $90af
Line 117: Line 135:
  00/90b8: 49 ff          EOR    #$ff
  00/90b8: 49 ff          EOR    #$ff
  00/90ba: d0 04          BNE    $90c0
  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/90bc: dd 88 c0      CMP    $c088,X
  00/90bf: 60            RTS
  00/90bf: 60            RTS
 +
 +
$90bc is the place we go if the copy protection routine has succeeded.
 +
  00/90c0: a8            TAY
  00/90c0: a8            TAY
  00/90c1: dd 88 c0      CMP    $c088,X
  00/90c1: dd 88 c0      CMP    $c088,X
Line 128: Line 152:
  00/90cc: d0 f8          BNE    $90c6
  00/90cc: d0 f8          BNE    $90c6
  00/90ce: 60            RTS
  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 ===
=== Things I learned afterwards ===

Revision as of 00:41, 11 February 2011

Contents

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,

  1. Boot a DOS 3.3 system master disk
  2. type the following at the BASIC prompt:
    1. "call -151"
    2. "b942: 18 60"
    3. "q"
    4. "RUN COPYA"
  3. 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?

The Crack

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.

Unfinished Business

This crack is incomplete because attempting to boot the copied disk crashes at some point in the boot process. I suspect that this is because the sector signatures have been altered on the copy protected disk. Maybe someone else can figure it out. :)

Personal tools