MS-DOS Copy Protection

I'm no fan of the IBM PC or of MS-DOS, really I'm not.

However, copy protection fascinates me, and of course the IBM PC wasn't immune to this.

Disk copy protection has a major Achilles heel. The BIOS needs to be able to read the first sector of the first track. From here on in you can obfuscate as much as you want to, but a determined hacker will always be able to trace the boot, whether it's a PC or an Apple or anything else.

MS-DOS 1.0 supported 8 sectors per track, 40 tracks, single-sided disks. MS-DOS 1.1 added double-sided disk support, and MS-DOS 2.0 changed to 9 sectors per track to give the traditional 360 kilobyte floppy (720 sectors of 512 bytes, 360 (40 x 9) sectors per side). If you're morbidly curios, MS-DOS also supports 77 track 8" floppies, using the DEC standard, not the IBM standard... I wouldn't mind getting hold of a NEC APC -- MS-DOS 2.11 on 8" floppies, with a uPD7220 graphics controller.


I blogged about the IBM PC Infocom copy protection, where tracks 1 to 3 are configured with 4 x 1024-byte sectors. I also mentioned that ZORKTOOLS will rewrite the 4×1024 byte sectors to 8×512 byte sectors and patch the bootloader to match.

In my stash I have a copy of Infocom's Planetfall. The bootsector had me puzzled, until I realised that it had already been patched by ZORKTOOLS or similar.

If you want to investigate a boot (or other) sector, you can use MS-DOS debug to read sectors and write them to a file.

C>debug
-l 100 1 0 1 (Load at address 0x100, from drive 1 (my 5 1/2& floppy is B), sector 0, 1 sector)
-n dos32.bin (Name of file to save)
rcx          (BX:CX holds the length to save)
:200
w 100        (Write starting at address 0x100)

If you want to know what a typical DOS bootsector looks like, look here. My Planetfall bootsector is not even close:

00007C00 FA               cli
00007C01 EB19             jmp   000007C1Ch     Patched (by ZorkTools) to jump over the code that points to the new parameter block

00007C03 8ED8             mov   ds,ax          Redundant code
00007C05 BB7800           mov   bx,0078h
00007C08 B97900           mov   cx,0079h
00007C0B BAC007           mov   dx,07C0h
00007C0E 8B37             mov   si,[bx]
00007C10 8B7F02           mov   di,[bx+02h]
00007C13 890F             mov   [bx],cx
00007C15 895702           mov   [bx+02h],dx
00007C18 8CC8             mov   ax,cs
00007C1A 8ED8             mov   ds,ax

00007C1C BA0000           mov   dx,0000h       DX = 0x0000
00007C1F 8ED2             mov   ss,dx          SS = 0x0000
00007C21 BB007C           mov   bx,7C00h       BX = 0x7C00
00007C24 8BE3             mov   sp,bx          SP = 0x7C00 So the stack grows down from 0x07C00
00007C26 FB               sti                  Enable interrupts
00007C27 B86000           mov   ax,0060h       
00007C2A 8ED8             mov   ds,ax          DS = 0x0060
00007C2C 8EC0             mov   es,ax          ES = 0x0060, this is used in the next block of code
00007C2E 2BC0             sub   ax,ax          AX = 0, AH = 0, Reset disk drive
00007C30 2BD2             sub   dx,dx          DL = 0, first floppy
00007C32 CD13             int   13h            Reset disk drive
00007C34 BA0300           mov   dx,0003h
00007C37 2BDB             sub   bx,bx          BX = 0x0000, ES:BX = 0x00600, Buffer address (IBM PC reserved memory spans 0x00000 to 0x0005FF)
00007C39 B501             mov   ch,01h         CH = 0x01, Cylinder 1

LOOP1:
00007C3B 52               push  dx
00007C3C B101             mov   cl,01h         CL = 0x01, Sector 1
00007C3E 51               push  cx
00007C3F 2BD2             sub   dx,dx          DH = 0x00, Head 0. DL = 0x00, first floppy
00007C41 B80802           mov   ax,0208h       AH = 0x02, Read Sectors from Drive. AL = 0x08, eight sectors (of 512 bytes each), 0x00600 to 0x015FF
00007C44 CD13             int   13h            Read Sectors
00007C46 721C             jb    000007C64h     On error (CF=1)
00007C48 59               pop   cx
00007C49 FEC5             inc   ch             Next cylinder
00007C4B 81C30010         add   bx,word 1000h  BX = 0x1000, 0x2000
00007C4F 5A               pop   dx
00007C50 4A               dec   dx
00007C51 75E8             jnz   000007C3Bh     If DX not zero, goto LOOP1:


So here we have sectors 1-8 of cylinder 1 at 0x00600 to 0x015FF
                sectors 1-8 of cylinder 2 at 0x01600 to 0x025FF
                sectors 1-8 of cylinder 3 at 0x02600 to 0x035FF


00007C53 EB0A             jmp   000007C5Fh     GO:   Patched to jump over code that restores the parameter block

00007C55 8ED8             mov   ds,ax          Redundant code
00007C57 BB7800           mov   bx,0078h
00007C5A 8937             mov   [bx],si
00007C5C 897F02           mov   [bx+02h],di

GO:
00007C5F 06               push  es             ES is still 0x0060
00007C60 2BC0             sub   ax,ax          AX = 0x0000
00007C62 50               push  ax
00007C63 CB               retf                 Return Far, pop CS and the Instruction Pointer (IP) = 0x00600

ERROR:
00007C64 2BDB             sub   bx,bx
00007C66 B049             mov   al,49h         Character "I"
00007C68 B40E             mov   ah,0Eh         Teletype output
00007C6A CD10             int   10h
00007C6C B04C             mov   al,4Ch         Character "L"
00007C6E B40E             mov   ah,0Eh
00007C70 CD10             int   10h
00007C72 B04C             mov   al,4Ch         Character "L"
00007C74 B40E             mov   ah,0Eh
00007C76 CD10             int   10h
00007C78 F4               hlt                  And die.
00007C79 CF 02 25 03 04 2A FF 50 F6 19 04      1024-sector parameter block


[Image] Hit Count
hits since 2015-10-20.

Back (This page last modified 2022-12-07)