PC Floppy Copy Protection: Electronic Arts Interlock

This is Part 3 of a series on PC floppy copy protection methods. 

Again, I need to thank NewRisingSun and OBattler for their invaluable contributions.

You can read the previous installments here:


Before Electronic Arts (EA) was the publishing juggernaut that it is today, it was just one of dozens of software publishers putting out titles for various home computers, including the IBM PC. EA was founded in 1982 by Trip Hawkins, who would go on to create the ultimately unsuccessful 3DO game console. In the mid-1980's, EA was perhaps most famous for their paint program, Deluxe Paint, which became a popular graphics tool for the whole computer gaming industry.

Unlike the companies we have covered to date, EA is mostly widely known for their games, not their copy protection schemes. EA is famous enough that a long segue into their corporate history isn't really necessary - you can just read the Wikipedia entry

EA wasn't selling their copy protection technology, so there are no flashy advertisements extolling its virtues or many articles discussing it at all.  All that is left to talk about is the protection itself.

The Interlock Copy Protection

From 1984 to 1987 EA published several titles for the IBM PC that had a unique form of copy-protection, called Interlock. These titles appeared on both 5.25" and 3.5" double-density media.  Many of these titles were 'booters'. A booter was a game that did not load from DOS - instead you inserted the disk at power-on, and booted from the disk directly.  Often booter titles lacked a readable FAT filesystem and would give garbled directory listings if viewed from DOS, or produce "General Error Reading Drive A:".  

By lacking a filesystem, such titles couldn't be copied to a hard drive. This was a basic form of copy-protection in itself, but these games were inevitably cracked and distributed either as standalone COM files or a COM plus overlay file. You may have played several booter titles without knowing it - popular games like Digger were originally distributed as booters but are probably far more widely known in their form as cracked DOS conversions.

I'm aware of the following titles utilizing EA Interlock protection:
The disks for these titles contained a special protection track.  By itself, that wouldn't be terribly remarkable. What is interesting is the extremes to which this protection goes. A normal track may on a PC floppy may contain 8 or 9 sectors. The protection track for an EA Interlock-protected title contains an astonishing 96 sector IDs. 

You may wonder where the name Interlock comes from, considering EA was not discussing this protection anywhere.  It isn't a name that was ever advertised. Instead we gather the clue from the special duplication mark sector written to some of these disks, in non-standard FM encoding:

508-040L3       ELECTRONIC ARTS IBM INTERLOCK.

It's not clear whether the Interlock protection was developed in-house by EA, or was a turnkey solution from a commercial disk duplicator. According to this interview with former Disclone employee Scott Cronce, in 1984 EA was using XEMAG for disk duplication services but changed duplicators to Disclone in fall of 1985.  The Interlock protection scheme is seen in titles published from 1984 to 1987, which spans the transition, and it doesn't resemble XEMAG's "XELOK" protection scheme, so perhaps this was EA's invention after all. If any readers have further insight into the origins of Interlock, please let me know!

Let's take a look at the disk surface of Side #0 of the 1984 mystery-adventure title, Murder on the Zinderneuf:



As you can probably tell, the protection track is Track #15, which is lit up like a Christmas tree with colored metadata.  Notice anything else odd?  There are rather suspiciously small gaps between each sector - because there are 10 sectors per track on the "normal" tracks on this side! The sector IDs on these tracks are not consecutive either, appearing in the order 6,1,7,2,8,3,9,4,10, and 5. This is a basic sector interleaving scheme, since removing the gaps between sectors made the timing much tighter.

Let's zoom in and render just the metadata for clarity:


Purple lines here indicate address marks. The first purple lines at the top of the image are index address marks (IAM) - these are not always present on a disk, but are normal to see. The blue areas are simply the region between an ID address mark (IDAM) and either a data address mark (DAM) or deleted data address mark (DDAM).  Light blue indicates a valid IDAM CRC. Dark blue indicates a bad IDAM CRC. 

Green regions indicate sector data with a valid CRC. Orange regions indicate sector data with a bad CRC. Red regions indicate "deleted" sector data with a bad CRC.  These sectors are all overlapped with one another, of course.  Despite the overlap, every fifth sector manages to have a valid data CRC.

Deleted Data

We haven't encountered "deleted" data before in this blog series.  Sector data came in two flavors, normal sector data indicated by a normal Data Address Mark (DAM), and deleted sector data, indicated by a Deleted Data Address Mark (DDAM).  

A normal Data Address Mark is the 4-byte sequence, A1 A1 A1 FB.  However, a slightly different sequence, A1 A1 A1 F8, would indicate a "Deleted" Data Address Mark. In either case, sector data follows, and a CRC that validates the data.  It may seem odd to have "deleted" data in a sector - why not just remove the sector, or wipe it to all 0's?  

It has been a common practice in many areas of computing to delete data by simply toggling some sort of flag rather than deliberately zeroing out all the data.  For example, marking a record in a database with a "deleted" flag while retaining the contents of the record is not only faster, but allows for un-deletion and historical auditing.  In many filesystems, entire files are deleted in this manner - allowing their recovery using an undelete utility assuming the disk space formerly occupied by the file hasn't been overwritten. 

The "deleted data" feature of the floppy disk format wasn't commonly used by DOS or any PC software. It is sort of a vestigial relic carried forward by the floppy format's roots in older systems from IBM, such as the System/34.  The PC floppy format was even called "System 34" format until ISO standardization as ISO 7487.  

The NEC µ765 floppy controller used in the original IBM PC effectively standardized separate commands for handling deleted data on a disk. Along with the normal Read Data and Write Data commands, there are corresponding Read Deleted Data and Write Deleted Data commands. 

A flag provided to the floppy controller, called the "skip" flag, determines whether deleted sectors are skipped during normal Read Data commands, or whether normal sectors are skipped during Read Deleted Data commands. Without the skip flag, encountering an unexpected sector data type will terminate the read operation.

The EA Interlock copy protection reads various sectors on the protection track and carefully analyzes the responses from the disk controller.  If a sector is supposed to be skipped, it needs to be, if a sector had a good or bad CRC, the various status flags need to reflect such. If a sector has a bad IDAM CRC, no data should be read, and the CHS value in the response bytes should not be advanced.

In essence, it requires that the protection track be duplicated exactly - something that would have been very difficult to do.  Some copiers had figured out a few tricks for reproducing sectors with bad CRC's, such as resetting the floppy controller during a write operation with very careful timing.  The sheer number of sectors on the Interlock protection track likely aimed to make the timing required to duplicate it implausibly difficult.

CHECKEA.COM

A name we have heard a few times in this blog series is NewRisingSun, an expert on PC copy protection methods. NewRisingSun produced a utility that reads every sector on the Interlock protection track and dumps the response codes returned by the floppy disk controller.  Along with the provided logs, this utility gives emulator authors an easy way to check whether their FDC emulation will pass muster.  I fixed a few bugs in my floppy controller emulation, until eventually MartyPC produced an identical log to a real PC.

Time to Debug...

I was hoping Interlock would just work after fixing all my floppy controller response codes to match the CHECKEA logs. But alas, booting Interlock protected titles in MartyPC still only produced soft-locks or error messages. I spent considerably more time analyzing why than I have on the previous protections we have covered, partially due to the sheer complexity of this protection.

Before we dive into analysis of the protection, we need to bear in mind that there were several versions of Interlock produced. Titles released in 1984 seem to use the first version of Interlock, which I will call Interlock v1.0.  That scheme will be analyzed in the next section. From 1985 onward, it appears that correct handling of bad IDAM CRCs is not required - perhaps EA encountered a hardware compatibility issue? In any case, this makes subsequent versions of Interlock protection actually weaker.  Versions of Interlock were used for both booter and DOS titles - some of the more advanced anti-cracking techniques Interlock 1.0 uses couldn't be used in a DOS environment, so keep that in mind also when such techniques are mentioned.

The Interlock v1.0 Bootloader

When the BIOS boots a diskette, it loads Cylinder 0, Head 0, Sector 1 at address 0000:7C00 and passes execution to it. This single 512-byte sector must contain the code to start whatever program is contained on the diskette.  This is usually done by loading additional sectors into memory and jumping to them.

Overriding EOT

Before the bootloader for Zinderneuf does so, it does something interesting. It loads the address of the Disk Parameter Table (DPT) from interrupt vector 1Eh.  It then copies the 11-byte table over the next few entries of the IVT (a bit of a gross hack), patches the 'End-of-Track' (EOT) table parameter at offset +4, then points the original 1Eh vector at this new, patched table.

Why do this? The EOT parameter is used by the BIOS to pass a terminating sector ID to the disk controller when issuing Read Data commands.   Normally, a Read Data operation will terminate if either a sector ID matching EOT is reached, or DMA terminal count is reached, whichever happens first.  Leaving EOT at its default value could cause this protection some real problems with its ridiculous number of sectors, so Interlock sets EOT to 0.  This has the effect of disabling the EOT parameter. No sector should have an ID of 0, which means the only thing that will terminate a Read Data operation should be the DMA terminal count. This also means your emulator must handle an EOT value of 0 properly - don't just abort on EOT of 0!

Loading the Logo

The bootloader then sets BIOS video mode 4 (CGA 320x200, 4 colors) and then loads the EA logo into video memory directly from the disk.  Despite the screen being mostly black with the logo centered in the middle, simplicity was apparently the primary concern here, so a full 16k block is copied to represent the entire screen. This graphic spans the first 4 tracks. On real hardware, the graphic fades in with a bit of an interlaced effect. This is due to the way CGA divides even and odd scanlines into two banks of memory. On an emulator without drive timings, this may be too fast to see.

The Interlock v1.0 logo screen

If you don't have an adequate copy of the disk, this is all you will ever see - you may see the graphic vanish and then redraw, as failing the check basically reboots from floppy again. If you let it sit like that, you'd wear out your floppy drive as it continued to attempt to pass the protection check forever.

This screen is where MartyPC was getting stuck.

Decrypting the Second-Stage Loader

The next part of the loader reads two sectors, 9 and 10, from 4 tracks, 16, 17, 18, 19, for a total of 4k of data and writes it sequentially to 0000:F000, filling the rest of that segment. Part of this is our second-stage bootloader code. There's other stuff mixed in here including what seems to be dialogue from the game, and FE00-FFFF (the last sector read) contains only 0xF6 bytes. 

If we try to disassemble code at 0000:F000 now, it's clearly nonsense. This is because the code is XOR-encrypted.

To decrypt the code, 7 sectors are read from the protection track #15 into FE00 and then used to XOR-decrypt the memory at F000-FDFF, one sector at a time, reusing the FE00 buffer for each sector. The code makes an attempt at obfuscating this process by storing the sector counter in a previous opcode byte and doing some math on it rather than using a variable or a table of sector numbers.  The counter ticks down from 7 to 1, and the sector number to be read is calculated as counter * 2 + 1.   So we read sectors 15,13,11,9,7,5 and 3.  Coincidentally, all these sectors have valid data CRC's despite being overlapped. 

7 sectors is only 3584 bytes, which gives us decrypted second-stage bootloader code from 0000:F000 to 0000:FE00, which we then execute.

The Second-Stage Loader

Anti-Cracking Countermeasures

The second stage loader puts a few modest obstacles in the way of a potential cracker.  First, it reads the first 29 entries of the Interrupt Vector Table and writes a single byte, 0xCF, at the first byte each entry points to.   0xCF happens to be the IRET instruction. The effect of this is to disable any interrupt hooks a cracker may have installed to snoop on what the protection was doing.  Any interrupt handler installed in ROM would of course not be affected by this write.

seg000:F089 B9 1D 00                          mov     cx, 1Dh         ; loop counter 29
seg000:F08C BB 00 00                          mov     bx, 0           ; ptr to addr 0
seg000:F08F B8 CF 00                          mov     ax, 0CFh        ; IRET opcode
seg000:F092
seg000:F092                   disable_hook:
seg000:F092 C4 3F                             les     di, [bx]        ; load IVT entry pointer
seg000:F094 AB                                stosw
seg000:F095 83 C3 04                          add     bx, 4           ; inc offset to next ivt entry
seg000:F098 E2 F8                             loop    disable_hook
The hook-disabling code

In case you're curious which ISRs this affects, here's a list:

  00h  Divide by Zero Error
  01h  Single Step
  02h  Non-maskable Interrupt (NMI)
  03h  Breakpoint
  04h  Arithmetic Overflow Error
  05h  Print Screen and BOUND Range Exceeded
  06h  Illegal Instruction Error
  07h  Processor Extension Not Available
  08h  Timer Tick
  09h  Keyboard I/O
  0Bh  Secondary Communications Port (COM2:)
  0Ch  Primary Communications Port (COM1:)
  0Eh  Disk Drive I/O
  11h  Get Equipment Configuration
  12h  Get Conventional Memory Size
  18h  ROM BASIC
  19h  Reboot System
  1Bh  CTRL+BREAK Handler
  1Ch  User Timer Service
  1Dh  Pointer to Video Parameter Table
  1Eh  Pointer to Disk Drive Parameter Table
  1Fh  Pointer to Graphics Character Pattern Table

Note that at 1Dh we have pointers to parameter tables instead of code routines, so the protection stops writing IRETs at 1Ch

You might wonder how you could have installed a handler even if you had wanted to, considering that the game is a booter and can't be loaded from DOS.  It is actually possible to "boot" from a disk from a running DOS environment, so this addressed that scenario.

After disabling any interrupt hooks, the protection code installs its own handler for interrupt 3h, the breakpoint handler used by many debuggers. It will then perform a XOR checksum on the protection code (itself), within the handler to interrupt 3h. If you had somehow managed to reinsert your breakpoint interrupt at this point, the code will call your debugger instead of calculating the XOR, without which the protection check will later fail.

The code then clears memory all the way up to B0000, which is the start of the MDA video memory address space. If you are running from DOS, this will be a bit of a problem as well. Curiously, it also clears the region C0000-F0000; which is reserved for expansion ROMs.  Again, an actual ROM here would be unaffected. If you have any idea why clearing this range would be done, please leave a comment.

The Main Protection Check

Next, 10 tracks (4-13) are read from the disk, 10 sectors (5120 bytes) at a time, and stored in consecutive memory from 0600h - CE00h. This is the main program code, and again it appears to be XOR encrypted.

Now we start accessing the protection track again. To start, a random, even-numbered sector < 48 is chosen and read.  To pick a 'random' sector, the drive motor shutoff counter in the BIOS Data Area (0040:0040) is used for a seed.   After the read, the ST2 status byte from the floppy controller is checked to verify the controller reported a bad data CRC.  All even-numbered sectors from 2-48 have bad data CRCs, so picking a random one is fine.

If we don't detect a bad CRC we will fail the protection check.

If we pass this initial read, then the protection check enters the main phase. Starting with Sector 11 on the protection track, sectors are now read from the disk and the response bytes from the FDC are checked.  Data read from each sector is XOR'd in a complex scheme that produces the next sector number to read. 

This is where I admittedly lost the plot. There are at least 8 functions that do some sort of XOR operation with different parameters, and they're called in a chain as the sectors are read.  A complete description of the flow through all of them would be exhausting - and more importantly, somewhat boring to read.

Various XOR routines

One interesting note is that some of the XOR routines use a section of the BIOS Data area as scratch space - this is at 0040:00F0, which is referenced as the "Intra-Applications Communications Area," whatever that is.  Was using the BDA for storage instead of some variable within the bootloader an attempt at further obfuscation?

The correct sequence of sectors Interlock v1.0 should read is 11, 29, 23, 95, 66, 87, 88, 27, 74, 70, 17, 3 and 65.  After reading sector 65, the main program code is decrypted and executed.

In any case, what was going wrong in MartyPC became clear after comparing logs graciously provided by OBattler of the 86Box emulator project. MartyPC was reading the wrong sector after 66. Instead of sector 87,  we were reading sector 31. This implies something was going wrong reading sector 66.

What's special about sector 66? It's the first sector read in this sequence that has a bad IDAM CRC, and that was exactly the clue I needed.

Handling Bad IDAMs

When the FDC is instructed to read a sector, if the IDAM it finds for that sector (containing the matching Sector ID) has a bad CRC, then the corresponding data for that sector should not be read, and the response bytes reporting the new values for current cylinder, head and sector should not be advanced.  I was handling the "don't read any data" part correctly, and was not advancing CHS - but what I was failing to do was stop DMA from running. I was effectively still transferring 512 bytes!  This caused the XOR decoding to fail, and so an incorrect next sector ID was calculated. 

This also caused the terminating condition for the protection check to never be reached (the terminating sector ID seems to also be XOR-calculated). The check would continue to read sectors until eventually the destination buffer address - incremented each time in the loop - reached a value that caused the BIOS to detect an overflow condition.  Subsequent calls to interrupt 13h would then fail, but Interlock never stops retrying, leading to a soft-lock.

Success At Last

Once we appropriately bypass the DMA operation when reading a bad IDAM CRC, the game finally loads, in glorious composite color:

Murder on the Zinderneuf (1984)

Let's check the later versions of this protection as well. Here is 1987's Marble Madness, which lacks the EA logo splash screen, but will give you an error message if the protection fails. It has been reported that this title, along with ArcticFox, can have issues under emulation (and rarely even on actual hardware.) It seems to boot reliably in MartyPC, which is nice to see.

Marble Madness (1987)

The DOS-based title World Tour Golf  also loads. The last hurdle to pass would be ArcticFox, which will return you to the menu on a failed protection check. If we actually get in game, then I think we can consider Interlock conquered.

ArcticFox (1987)

Support for EA Interlock protected titles will be included with the upcoming 0.3.0 release of MartyPC.

Thanks for reading!















Comments

  1. Clearing the expansion ROM area would make it more difficult to use hardware debuggers. Some Periscope boards inserted code there before a soft reboot.

    ReplyDelete
    Replies
    1. Thanks for the tip. I suspected it might be an attack on some kind of hardware, but I wasn't sure what would have anything writable there.

      Delete

Post a Comment

Popular posts from this blog

PC Floppy Copy Protection: Softguard Superlok

Bus Sniffing the IBM 5150: Part 1

PC Floppy Copy Protection: Formaster Copy-Lock