💾 Archived View for mirrors.apple2.org.za › active › 4am › images › games › action › conan › Conan%2… captured on 2024-08-19 at 03:41:34.

View Raw

More Information

-=-=-=-=-=-=-

-----------------Conan-----------------
A 4am crack                  2014-05-21
---------------------------------------

Conan is a 1984 arcade game distributed
by DataSoft, Inc. It comes on a single
double-sided 5.25" floppy disk and uses
both sides.

COPYA fails immediately with a disk
read error. EDD 4 bit copy appears to
copy both sides of the disk, and in
fact the copy of side A boots to the
animated title screen. But after it
asks to flip the disk, it accesses the
disk and reboots.

In my experience, programs do not
spontaneously reboot unless someone
tells them to.

My trusty Copy ][+ sector editor can't
read a single sector of side A. But
wait! Once I go into the Sector Editor
Patcher (press "P") and select "DOS 3.3
PATCHED" to ignore epilogue bytes and
checksums, it can read all of track 0.
But nothing else.

Furthermore, the Copy ][+ nibble editor
can't make heads or tails of anything
above track 0. By which I mean,
pressing "A" to analyze the track just
jumps to a random position within the
raw nibble stream. Pressing <space> to
re-read the track, then searching for
"D5 AA 96" (the standard address
prologue) unsurprisingly finds no
matches. But searching for "D5 AA AD"
(the standard data prologue) does find
several matches.

   COPY ][ PLUS BIT COPY PROGRAM 8.4
(C) 1982-9 CENTRAL POINT SOFTWARE, INC.
---------------------------------------

TRACK: 01  START: 1800  LENGTH: 3DFF

18B0: FF FF FF FF FF FF FF FF   VIEW
18B8: FF CC 96 AA FF FE AA AB
                  ^^^^^^^^^^^
 first half of address field?

18C0: AF AA FA FF DE AA FF FF
      ^^^^^^^^^^^
      second half of address field?

18C8: FF FF FF FF FF FF FF FF
18D0: D5 AA AD FF EA 9F DB B6  <-18D0
      ^^^^^^^^
      possible data prologue here

18D8: CE AF F2 D9 97 FE B9 FC
18E0: DE FF AE FB DF B7 DF D3
18E8: A6 9B B4 FB D6 CB F2 EA
18F0: CE 96 DA EB 9E DD F5 BE

That looks like a normal 16-sector disk
with a standard data prologue ("D5 AA
AD") but a custom address prologue
(possibly "CC 96 AA"). Searching for
"CC 96 AA" finds several more matches
on track 1, as well as tracks 2 and 3.
That's certainly promising, and it
suggests that the only reason I
couldn't read the disk with a sector
editor is that it uses a custom address
prologue. (I'm not sure why that
particular address prologue confuses
the nibble editor's auto-analyze
routine, but it does.)

Let's see if my AUTOTRACE program can
capture the RWTS. If not, I'll need to
find another tool to read the disk and
convert it to a standard format.

[S6D1=original disk, side A]
[S5D1=my work disk]

]PR#5
...
CAPTURING BOOT0
...reboots slot 6...
...reboots slot 5...
SAVING BOOT0

Well that's not encouraging. Let's see
what we have.

]CALL -151
*800<2800.28FFM
*801L
.
. nothing unusual, until...
.
084A-   4C B4 08    JMP   $08B4

Normally this would be JMP ($08FD) or
JMP $B700, which is where the boot1
code starts. But it's doing something,
...extra... before continuing to the
next boot stage. I don't like extra.
Extra is bad.

*8B4L

; OK, normal RWTS initialization code
08B4-   8E E9 B7    STX   $B7E9
08B7-   8A          TXA
08B8-   4A          LSR
08B9-   4A          LSR
08BA-   4A          LSR
08BB-   4A          LSR
08BC-   AA          TAX
08BD-   A9 00       LDA   #$00
08BF-   9D F8 04    STA   $04F8,X
08C2-   9D 78 04    STA   $0478,X

; hmm
08C5-   20 CB B6    JSR   $B6CB

; OK, jump to boot1 code
08C8-   4C 00 B7    JMP   $B700

What's at $B6CB? Well, this sector
(T00,S00) is loaded twice: once by the
disk controller ROM routine at $0800,
then again by itself, as part of the
boot1 code, at $B600. So I can find out
what's at $B6CB by looking at $08CB.

*8CBL

; read a sector
08CB-   A9 B7       LDA   #$B7
08CD-   A0 E8       LDY   #$E8
08CF-   20 B5 B7    JSR   $B7B5

; loop to read more sectors
08D2-   AC ED B7    LDY   $B7ED
08D5-   88          DEY
08D6-   10 05       BPL   $08DD
08D8-   A0 0F       LDY   #$0F
08DA-   CE EC B7    DEC   $B7EC
08DD-   CE F1 B7    DEC   $B7F1
08E0-   8C ED B7    STY   $B7ED
08E3-   CE E1 B7    DEC   $B7E1
08E6-   D0 E3       BNE   $08CB

; hmm
08E8-   A9 CC       LDA   #$CC
08EA-   8D 55 B9    STA   $B955
08ED-   4C CC BF    JMP   $BFCC

The code at $08CB..$08E7 seems to be a
harmless multi-sector read loop, using
a bog standard RWTS (parameter table at
$B7E8, entry point at $B7B5). But the
code at $08E8 is suspicious. It's
directly modifying bits of the RWTS,
and I want to know what and why.

The boot0 code is close enough to the
normal DOS 3.3 code that my AUTOTRACE
program should be able to capture the
boot1 code, and hopefully the RWTS as
well.

*BRUN AUTOTRACE1
...reboots slot 6...
...reboots slot 5...
SAVING BOOT1
SAVING RWTS

Yay! Now let's see what evil lurks at
$BFCC. I can't move the code there (my
work disk runs Diversi-DOS, which
occupies $BF00..$BFFF), so we'll just
have to load it in a safe place and do
some address arithmetic and hope things
don't get too complicated.

]BLOAD BOOT1,A$2600
]CALL -151

*2FCCL

2FCC-   A9 96       LDA   #$96
2FCE-   8D 5F B9    STA   $B95F
2FD1-   A9 AA       LDA   #$AA
2FD3-   8D 6A B9    STA   $B96A
2FD6-   60          RTS

Well that wasn't complicated at all.
But what is it doing? Disassembling the
code near those addresses provides the
answer: it's twiddling the RWTS again.
Both this routine and the code at $08E8
work together to change the address
prologue sequence that the RWTS expects
to find on disk reads, from "D5 AA 96"
to "CC 96 AA".  Which, incidentally, is
just what I saw in the nibble editor:
an address prologue of "CC 96 AA" on
every track above track 0.

I have a plan. The original disk has
two RWTS routines. The first one will
read track 0. Then the boot code
modifies the RWTS so it will read track
1 and up.

So, I need two RWTS files.

]BLOAD RWTS,A$2800

]CALL -151

*2955:CC

*295F:96

*296A:AA

*BSAVE RWTS TRACK 1+,A$2800,L$800

[S6D1=my work disk]

*BRUN ADVANCED DEMUFFIN 1.1,S6,D1

--> LOAD NEW RWTS MODULE
    At $B8, load "RWTS"
    from D1

[S6D1=original disk side A]
[S6D2=blank disk]

--> FORMAT TARGET DISK

...grind grind grind...

--> CONVERT DISK

"CHANGE DEFAULT VALUES?" Y

This RWTS file can only read track 0,
so let's only read track 0.

ADVANCED DEMUFFIN 1.1  - COPYRIGHT 1983
WRITTEN BY THE STACK -CORRUPT COMPUTING
=======================================

INPUT ALL VALUES IN HEX

SECTORS PER TRACK? (13/16) 16

START TRACK: $00
START SECTOR: $00

END TRACK: $00
            ^^-- important

END SECTOR: $0F

INCREMENT: 1

MAX # OF RETRIES: 0

COPY FROM DRIVE 1
TO DRIVE: 2
=======================================
16 SC $00,$00 TO $00,$0F BY $01 TO DRV2


And go...


ADVANCED DEMUFFIN 1.1  - COPYRIGHT 1983
WRITTEN BY THE STACK -CORRUPT COMPUTING
=======PRESS ANY KEY TO CONTINUE=======
TRK:.
+.5:
    0123456789ABCDEF0123456789ABCDEF012
SC0:.
SC1:.
SC2:.
SC3:.
SC4:.
SC5:.
SC6:.
SC7:.
SC8:.
SC9:.
SCA:.
SCB:.
SCC:.
SCD:.
SCE:.
SCF:.
=======================================
16 SC $00,$00 TO $00,$0F BY $01 TO DRV2


Now I need to use the other RWTS file
to read the rest of the disk.

[S6D1=my work disk]

--> LOAD NEW RWTS MODULE
    At $B8, load "RWTS TRACK 1+"
    from D1

[S6D1=original disk side A]
[S6D2=disk with track 0 on it]

--> CONVERT DISK

"CHANGE DEFAULT VALUES?" Y

This RWTS file can only read tracks 1
and 1, so let's make sure we only read
tracks 1 and up.

ADVANCED DEMUFFIN 1.1  - COPYRIGHT 1983
WRITTEN BY THE STACK -CORRUPT COMPUTING
=======================================

INPUT ALL VALUES IN HEX

SECTORS PER TRACK? (13/16) 16

START TRACK: $01
              ^^-- important

START SECTOR: $00

END TRACK: $22
END SECTOR: $0F

INCREMENT: 1

MAX # OF RETRIES: 0

COPY FROM DRIVE 1
TO DRIVE: 2
=======================================
16 SC $01,$00 TO $22,$0F BY $01 TO DRV2


And go...


ADVANCED DEMUFFIN 1.1  - COPYRIGHT 1983
WRITTEN BY THE STACK -CORRUPT COMPUTING
=======PRESS ANY KEY TO CONTINUE=======
TRK: ..................................
+.5:
    0123456789ABCDEF0123456789ABCDEF012
SC0: ..................................
SC1: ..................................
SC2: ..................................
SC3: ..................................
SC4: ..................................
SC5: ..................................
SC6: ..................................
SC7: ..................................
SC8: ..................................
SC9: ..................................
SCA: ..................................
SCB: ..................................
SCC: ..................................
SCD: ..................................
SCE: ..................................
SCF: ..................................
=======================================
16 SC $01,$00 TO $22,$0F BY $01 TO DRV2


As it turns out, side B of the original
disk uses the same "CC 96 AA" address
prologue throughout the disk. That
means I can reuse this "RWTS TRACK 1+"
file to copy all of side B.

[S6D1=original side side B]
[S6D2=blank disk]

--> FORMAT TARGET DISK

...grind grind grind...

--> CONVERT DISK

"CHANGE DEFAULT VALUES?" N

ADVANCED DEMUFFIN 1.1  - COPYRIGHT 1983
WRITTEN BY THE STACK -CORRUPT COMPUTING
=======PRESS ANY KEY TO CONTINUE=======
TRK:R..................................
+.5:
    0123456789ABCDEF0123456789ABCDEF012
SC0:R..................................
SC1:...................................
SC2:...................................
SC3:...................................
SC4:...................................
SC5:...................................
SC6:...................................
SC7:...................................
SC8:...................................
SC9:...................................
SCA:...................................
SCB:...................................
SCC:...................................
SCD:...................................
SCE:...................................
SCF:...................................
=======================================
16 SC $00,$00 TO $22,$0F BY $01 TO DRV2


Well, *almost* all of side B. It seems
that T00,S00 uses a standard address
prologue (and epilogue!) in order to
load just enough code to tell you that
you booted the wrong side of the disk.


SECTOR EDITOR                   DRIVE 1

00- 01 2C 10 C0 A2 00 8E F2  .,.@"..r
08- 03 A9 C6 8D F3 03 49 A5  .)F.s.I%
10- 8D F4 03 20 58 FC 2C 51  .t. X|,Q
18- C0 2C 54 C0 A9 00 20 95  @,T@). .
20- FE A2 0A 20 66 FC CA D0  ~". f|JP
28- FA A2 00 BD 58 08 20 ED  z".=X. m
30- FD E8 C9 AE D0 F5 20 62  }hI.Pu b
38- FC 20 66 FC A2 00 BD 77  | f|".=w
40- 08 20 ED FD E8 C9 AE D0  . m}hI.P
48- F5 2C 10 C0 AD 00 C0 10  u,.@-.@.
50- FB C9 8D D0 F7 4C 00 C6  {I.PwL.F
58- A0 A0 A0 A0 A0 A0 A0 A0
60- D0 CC C5 C1 D3 C5 A0 C2  PLEASE B
68- CF CF D4 A0 CF D4 C8 C5  OOT OTHE
70- D2 A0 D3 C9 C4 C5 AE A0  R SIDE.
78- A0 A0 A0 A0 A0 C6 CC C9       FLI
80- D0 A0 C4 C9 D3 CB A0 C1  P DISK A


TRACK $00  SECTOR $00   DOS 3.3


I used the sector editor to manually
copy T00,S00 to my copy of side B, and
that was that.

Now I have both sides in a standard
disk format, but I still have (at
least) two problems:

1. My copy can't read itself, since the
   RWTS still expects most of side A
   and all of side B to have that non-
   standard address prologue.

2. Even after I fix that, it will still
   reboot after flipping the disk and
   trying to play the game.

One thing at a time. I know where the
boot code modifies the RWTS, so I can
just tell it not to do that.

Side A,T00,S00,$E8 change "A9" to "60"

Now side A boots successfully, loads
the title screen and plays the music.
The little guy on the horse gallops
across the screen, then it asks for
side B. Then it reboots.

                    ~

 AN INTERLUDE ABOUT DIFFERENT FORMS OF
   COPY PROTECTION ON APPLE II DISKS

I've only been studying Apple II copy
protection for a short time, so this is
not meant to be the final word on the
subject. But so far, I've come across
three basic forms of copy protection.

The first kind relies on storing data
on the disk in weird ways. Conan has
some of that; most of side A and all of
side B use a non-standard address
prologue so that naive disk copiers
can't find the start of each sector.

(Other possibilities: storing data
between tracks; splitting up data into
chunks other than 16 sectors per track;
metadata that lies, such as sectors on
track 1 claiming to be on track 0;
physically positioning data on the disk
in such a way that the RWTS can make
assumptions about how to find data on
each track without seeking; using a
custom nibble table so that third-party
tools can't decode the nibbles into
sector data; and on and on. Anything
you can imagine, it's been done.)

The second kind of protection is to
require the user to provide something
that is only available outside the disk
itself, either a hardware dongle (very
rare in the Apple II era) or something
physical that was shipped with the
original disk. Decoder rings, lookup
tables, "third word on page 17 of the
manual," that kind of thing. (Remember,
in the 1980s, games were shipped on
disks and documentation was printed on
paper. Photocopying an entire software
manual wasn't impossible, but it had a
non-zero cost in time and money.) Conan
doesn't do any of that.

The third kind of protection relies on
actual code on the disk that looks at
some feature of the disk itself and
makes a decision about whether it's an
original or a copy. The most popular
form of this is the so-called "nibble
check," which looks at the raw data
between sectors that is more difficult
to reproduce faithfully with automated
(non-program-specific) disk copiers.
If the code decides that the disk is a
copy, it can do whatever it wants --
anything from "immediately reboot" to
"subtly corrupt the game to make it
unwinnable." Most disks choose to wipe
memory and reboot, and that appears to
be what Conan is doing.

Of course, nobody tells you which
combination of protection techniques
are in use by any particular disk when
you buy it. It could be none; it could
be all three. That's part of the fun.
Every new disk is a blank slate.

As a tangent to this tangent, you
should absolutely read Andy McFadden's
article, "Early Copy Protection on the
Apple II," which covers some of the
crazy things that Apple II developers
in the late 1970s used to prevent users
from copying programs distributed on
cassette tape. It's currently available
at <http://www.fadden.com/techmisc/
cassette-protect.htm>

                    ~

Since Conan is rebooting, and programs
don't just do that without a good
reason, I'm guessing there is a nibble
check somewhere. One thing that all
nibble checks have in common is they
need to turn on the drive motor by
accessing a specific address in the
$C0xx range. For slot 6, it's $C0E9,
but to allow disks to boot from any
slot, developers usually use code like
this:

  LDX <slot number x 16>
  LDA $C089,X

There's nothing that says where the
slot number has to be, although the
disk controller ROM routine uses zero
page $2B and lots of disks just reuse
that. There's also nothing that says
you have to use the X-register as the
index, or that you must use the
accumulator as the load register. But
most RWTS code does, out of convention
I suppose (or possibly fear of messing
up such low-level code in subtle ways).

Also, since developers don't actually
want people finding their protection-
related code, they may try to encrypt
it or obfuscate it to prevent people
from finding it. But eventually, the
code must exist and the code must run,
and it must run on my machine, and I
have the final say on what my machine
does or does not do.

But sometimes you get lucky.

Using my trusty Copy ][+ sector editor,
I searched side B (of my copy) for the
hex sequence "BD 89 C0". No matches.
But then I did the same search on side
A (because maybe the protection code is
loaded earlier but called later), and I
found a match on T00,S01, starting at
byte $42. The subroutine appears to
start at byte $31. (Before that is an
unconditional jump.)

Keep in mind that the sector editor
disassembly listing always shows the
code starting at $0E00, but that's not
necessarily where it's loaded by the
original disk. So the listing is a
weird mix of relative branches (listed
in the $0E00 range) and absolute
addresses (listed as-is). It appears
this code is loaded at $B700, which
is where T00,S01 is usually loaded.


SECTOR EDITOR                   DRIVE 1

; Read a sector, probably to position
; the drive head over the area of the
; special nibble sequence.
0E31-   A9 00       LDA   #$00
0E33-   8D F4 B7    STA   $B7F4
0E36-   A9 10       LDA   #$10
0E38-   8D EC B7    STA   $B7EC
0E3B-   A9 B7       LDA   #$B7
0E3D-   A0 E8       LDY   #$E8
0E3F-   20 B5 B7    JSR   $B7B5

; Turn on the drive motor manually
; and initialize some counters.
0E42-   BD 89 C0    LDA   $C089,X
0E45-   A9 01       LDA   #$01
0E47-   8D F4 B7    STA   $B7F4
0E4A-   A0 FF       LDY   #$FF
0E4C-   8C AB B7    STY   $B7AB
0E4F-   EE AB B7    INC   $B7AB
0E52-   A9 00       LDA   #$00
0E54-   85 3E       STA   $3E
0E56-   85 3F       STA   $3F
0E58-   85 48       STA   $48
0E5A-   A0 04       LDY   #$04
0E5C-   C6 3E       DEC   $3E
0E5E-   D0 04       BNE   $0E64
0E60-   C6 3F       DEC   $3F

; If the counter in zero page $3F hits
; zero, the nibble check has failed.
0E62-   F0 30       BEQ   $0E94

; Look for a specific nibble pattern.
0E64-   BD 8C C0    LDA   $C08C,X
0E67-   EA          NOP
0E68-   10 FA       BPL   $0E64
0E6A-   D9 A5 B7    CMP   $B7A5,Y
0E6D-   D0 EB       BNE   $0E5A
0E6F-   88          DEY
0E70-   10 F2       BPL   $0E64
0E72-   A0 08       LDY   #$08
0E74-   BD 8C C0    LDA   $C08C,X
0E77-   85 2C       STA   $2C
0E79-   10 F9       BPL   $0E74
0E7B-   88          DEY
0E7C-   D0 F6       BNE   $0E74
0E7E-   20 AC B7    JSR   $B7AC
0E81-   AC AB B7    LDY   $B7AB
0E84-   D0 05       BNE   $0E8B
0E86-   8D AA B7    STA   $B7AA
0E89-   F0 C4       BEQ   $0E4F
0E8B-   CD AA B7    CMP   $B7AA

; This appears to be the only place
; that branches forward, so I'm going
; to guess that this is the success
; condition.
0E8E-   D0 0F       BNE   $0E9F
0E90-   C0 18       CPY   #$18
0E92-   D0 BB       BNE   $0E4F

; The Badlands, from which there is no
; return. Some more manual inspection
; confirms that $BFC8 finishes setting
; the stack to "return" to $BEAF, which
; copies a small amount of code to $200
; and jumps to it in order to wipe all
; memory and reboot. NO SOUP FOR YOU!
0E94-   BA          TSX
0E95-   A9 BE       LDA   #$BE
0E97-   9D 02 01    STA   $0102,X
0E9A-   A9 AE       LDA   #$AE
0E9C-   4C C8 BF    JMP   $BFC8

; This is the success path. It turns
; off the drive motor and jumps to
; $A000.
0E9F-   BC 88 C0    LDY   $C088,X
0EA2-   4C 00 A0    JMP   $A000

So, if the nibble check succeeds, it
ends up at byte $9F, which turns off
the disk motor and jumps to $A000 to go
do something else. (I'm not sure what
that is, and I hope it doesn't matter.)
If it fails, it ends up at byte $94,
which wipes memory and reboots.

Therefore, I need to change the code at
byte $31 to jump unconditionally to
$A000.

Side A,T00,S01,$31 change "A9 00 8D"
                       to "4C 00 A0"

Success! Where the game previously
rebooted, it now continues on side B to
show Conan jumping over the moat and
running into the castle.  Pressing any
key or button starts the game.

Quod erat liberandum.

                    ~

        POSTSCRIPT: CHEAT CODES

I have *not* enabled the following
cheats, but I have verified that they
work. You can use one or both.

Unlimited lives:
Side A,T05,S09,$F5 change "CE" to "AD"

Unlimited axes:
Side A,T05,S0B,$4E change "CE" to "AD"

---------------------------------------
A 4am crack                      No. 42
-------------------EOF-----------------