Dvd-Auth-Win32.C
blogan | RecentChanges | Preferences

This is a program that authenticate DVD discs in win32, released under GNU GPL 
2. 

Besides the code bloew, you need css-auth.c and css-auth.h. Because the 
publising those files are illegal in here the United States, please get them 
somwhere else. 

/* Dodo.c */

/*
 * Copyright (C) Takuya Murata 2003
 * Released under the GPL version 2 or later.
 */

/* Reference

	http://groups.google.com/groups?q=dvd+challenge+key+authentication&hl=en&lr=&ie=UTF-8&oe=UTF-8&safe=off&selm=199809121906.PAA07510%40ztransform.velsoft.com&rnum=1
	http://groups.google.com/groups?q=dvd+challenge+key+authentication&hl=en&lr=&ie=UTF-8&oe=UTF-8&safe=off&selm=83cjap%244qr%241%40nnrp1.deja.com&rnum=2
	http://groups.google.com/groups?q=dvd+challenge+key+authentication&hl=en&lr=&ie=UTF-8&oe=UTF-8&safe=off&selm=6u6jtr%24vss%241%40nnrp1.dejanews.com&rnum=5
 */

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "css-auth.h"

/* From ntddcdvd.h */

#define FILE_READ_ACCESS                  0x00000001
#define FILE_WRITE_ACCESS                 0x00000002
#define METHOD_BUFFERED                   0

#define FILE_DEVICE_DVD                   0x00000033
#define IOCTL_DVD_BASE                 FILE_DEVICE_DVD

#define CTL_CODE(DeviceType,Function,Method,Access) (((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method))

#define IOCTL_DVD_START_SESSION     CTL_CODE(IOCTL_DVD_BASE, 0x0400, METHOD_BUFFERED, FILE_READ_ACCESS)
#define IOCTL_DVD_READ_KEY          CTL_CODE(IOCTL_DVD_BASE, 0x0401, METHOD_BUFFERED, FILE_READ_ACCESS)
#define IOCTL_DVD_SEND_KEY          CTL_CODE(IOCTL_DVD_BASE, 0x0402, METHOD_BUFFERED, FILE_READ_ACCESS)
#define IOCTL_DVD_END_SESSION       CTL_CODE(IOCTL_DVD_BASE, 0x0403, METHOD_BUFFERED, FILE_READ_ACCESS)
#define IOCTL_DVD_SET_READ_AHEAD    CTL_CODE(IOCTL_DVD_BASE, 0x0404, METHOD_BUFFERED, FILE_READ_ACCESS)
#define IOCTL_DVD_GET_REGION        CTL_CODE(IOCTL_DVD_BASE, 0x0405, METHOD_BUFFERED, FILE_READ_ACCESS)
#define IOCTL_DVD_SEND_KEY2         CTL_CODE(IOCTL_DVD_BASE, 0x0406, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)

#define IOCTL_DVD_READ_STRUCTURE    CTL_CODE(IOCTL_DVD_BASE, 0x0450, METHOD_BUFFERED, FILE_READ_ACCESS)
#define IOCTL_STORAGE_SET_READ_AHEAD        CTL_CODE(IOCTL_STORAGE_BASE, 0x0100, METHOD_BUFFERED, FILE_READ_ACCESS)

/*
	 Used with IOCTL_DVD_END_SESSION to end all DVD sessions at once
 */
#define DVD_END_ALL_SESSIONS ((long) 0xffffffff)

struct DVD_REGION
{
	unsigned char copy_system;
	unsigned char region_data;		/* current media region (not playable when set) */
	unsigned char system_region;	/* current drive region (playable when set) */
	unsigned char reset_count;		/* number of resets available */
};

struct DVD_READ_STRUCTURE {
	/* Contains an offset to the logical block address of the descriptor to be retrieved. */
	LARGE_INTEGER block_byte_offset;

	/* 0:Physical descriptor, 1:Copyright descriptor, 2:Disk key descriptor
	   3:BCA descriptor, 4:Manufacturer descriptor, 5:Max descriptor
	 */
	long format;

	/* Session ID, that is obtained by IOCTL_DVD_START_SESSION */
	long session;

	/* From 0 to 4 */
	unsigned char layer_no;
};

struct DVD_LAYER_DESCRIPTOR
{
    unsigned short length;

	unsigned char book_version : 4;

	/* 0:DVD-ROM, 1:DVD-RAM, 2:DVD-R, 3:DVD-RW, 9:DVD-RW */
	unsigned char book_type : 4;

	unsigned char minimum_rate : 4;

	/* The physical size of the media. 0:120 mm, 1:80 mm. */
    unsigned char disk_size : 4;

    /* 1:Read-only layer, 2:Recordable layer, 4:Rewritable layer */
	unsigned char layer_type : 4;

	/* 0:parallel track path, 1:opposite track path */
    unsigned char track_path : 1;

	/* 0:one layers, 1:two layers, and so on */
    unsigned char num_of_layers : 2;

    unsigned char reserved1 : 1;

	/* 0:0.74 μm/track, 1:0.80 μm/track, 2:0.615 μm/track */
    unsigned char track_density : 4;

	/* 0:0.267 μm/bit, 1:0.293 μm/bit, 2:0.409 to 0.435 μm/bit, 4:0.280 to 0.291 μm/bit, 8:0.353 μm/bit */
    unsigned char linear_density : 4;

	/* Must be either 0x30000:DVD-ROM or DVD-R/-RW or 0x31000:DVD-RAM or DVD+RW */
    unsigned long starting_data_sector;

    unsigned long end_data_sector;
    unsigned long end_layer_zero_sector;
    unsigned char reserved5 : 7;

	/* 0 indicates no BCA data */
	unsigned char BCA_flag : 1;

	unsigned char reserved6;
};

struct DVD_COPYRIGHT_DESCRIPTOR
{
	unsigned char protection;
    unsigned char region;
    unsigned short reserved;
};

struct DVD_MANUFACTURER_DESCRIPTOR
{
	unsigned char manufacturing[2048];
};

enum
{
	DvdChallengeKey = 0x01,
	DvdBusKey1,
	DvdBusKey2,
	DvdTitleKey,
	DvdAsf,
	DvdSetRpcKey = 0x6,
	DvdGetRpcKey = 0x8,
	DvdDiskKey = 0x80,
	DvdInvalidateAGID = 0x3f
};

struct DVD_COPY_PROTECT_KEY
{
    unsigned long length;
    long session_id;

	long type;
    unsigned long flags;

	union
	{
        struct
		{
            ULONG FileHandle;
            ULONG Reserved;   // used for NT alignment
        };
		LARGE_INTEGER TitleOffset;
    } parameters;

	union
	{
		unsigned char key_data[4];
		unsigned char challenge_key[12];
	};
};

static struct
{
	int info;
	int debug;
	int verbose;
} opt;

static void
strerror_win32 (int errnum, char buf[], int size)
{
	FormatMessage (
	 FORMAT_MESSAGE_FROM_SYSTEM |
	 FORMAT_MESSAGE_IGNORE_INSERTS,
	 NULL,
	 errnum,
	 0,
	 buf,
	 size,
	 NULL);
	buf[strlen (buf) - 2] = '\0';
	sprintf (buf + strlen (buf), " (%d)", errnum);
}

static void
perror_win32 (const char *who, const char *filename, int lineno)
{
	char msg[1024];
	strerror_win32 (GetLastError (), msg, sizeof (msg));
	fprintf (stderr, "%s: %s at %s:%d\n", who, msg, filename, lineno);
}

static void
dvd_end_session (HANDLE file, long session)
{
	DWORD bytes_read;
	if (!DeviceIoControl (file, IOCTL_DVD_END_SESSION, &session, sizeof (session),
	  0, 0, &bytes_read, 0))
	{
		perror_win32 ("dodo", __FILE__, __LINE__);
		exit (1);
	}

	if (opt.debug)
		fprintf (stderr, "IOCTL_DVD_END_SESSION\n");
}

static void
show_drive_info (HANDLE file, int session, struct DVD_REGION region)
{
	struct DVD_READ_STRUCTURE read_struct;
	struct DVD_COPYRIGHT_DESCRIPTOR copyright;
	struct DVD_LAYER_DESCRIPTOR layer;
	struct DVD_MANUFACTURER_DESCRIPTOR manufacturer;
	DWORD bytes_read;

	((long*) &read_struct.block_byte_offset)[0] = 0;
	((long*) &read_struct.block_byte_offset)[1] = 0;
	read_struct.session = session;
	read_struct.layer_no = 0;

	read_struct.format = 0;
	if (!DeviceIoControl (file, IOCTL_DVD_READ_STRUCTURE, &read_struct, sizeof (read_struct),
	  &layer, sizeof (layer), &bytes_read, 0))
	{
		perror_win32 ("dodo", __FILE__, __LINE__);
		dvd_end_session (file, session);
		exit (1);
	}

	read_struct.format = 1;
	if (!DeviceIoControl (file, IOCTL_DVD_READ_STRUCTURE, &read_struct, sizeof (read_struct),
	  &copyright, sizeof (copyright), &bytes_read, 0))
	{
		perror_win32 ("dodo", __FILE__, __LINE__);
		dvd_end_session (file, session);
		exit (1);
	}

	read_struct.format = 4;
	if (!DeviceIoControl (file, IOCTL_DVD_READ_STRUCTURE, &read_struct, sizeof (read_struct),
	  &manufacturer, sizeof (manufacturer), &bytes_read, 0))
	{
		perror_win32 ("dodo", __FILE__, __LINE__);
		dvd_end_session (file, session);
		exit (1);
	}

	fprintf (stderr,
	  "\n"
	  "Region code\n"
	  "\n"
	  "    Copy system . . . . : %d\n"
	  "    Region data . . . . : %d\n"
	  "    System region . . . : %d\n"
	  "    Reset count . . . . : %d\n"
	  "\n"
	  "Layer info %d[%d]\n"
	  "\n"
	  "    Book version  . . . : %d\n"
	  "    Book type . . . . . : %d\n"
	  "    Minimum rate  . . . : %d\n"
	  "    Disk size . . . . . : %d\n"
	  "    Layer type  . . . . : %d\n"
	  "    Track path  . . . . : %d\n"
	  "    # of layers . . . . : %d\n"
	  "    Track density . . . : %d\n"
	  "    Liner density . . . : %d\n"
	  "    Start sector  . . . : %#x\n"
	  "    End sector  . . . . : %#x\n"
	  "    End sector L0 . . . : %#x\n"
	  "    BCA Flag  . . . . . : %d\n"
	  "    Copy protection . . : %d\n"
	  "    Play region . . . . : %d\n"
	  "    Manufacturing . . . : %s\n"
	  "\n",
	  region.copy_system,
	  region.region_data,
	  region.system_region,
	  region.reset_count,
	  0, layer.num_of_layers + 1,
	  layer.book_version,
	  layer.book_type,
	  layer.minimum_rate,
	  layer.disk_size,
	  layer.layer_type,
	  layer.track_path,
	  layer.num_of_layers + 1,
	  layer.track_density,
	  layer.linear_density,
	  layer.starting_data_sector,
	  layer.end_data_sector,
	  layer.end_layer_zero_sector,
	  layer.BCA_flag,
	  copyright.protection,
	  copyright.region,
	  manufacturer.manufacturing);

	return;
}

static int
is_dvd_drive (char letter)
{
	char path[] = "c:";
	path[0] = letter;
	/* FIXME: Differentiate the drive is DVD or CD-ROM. */
	return GetDriveType (path) == DRIVE_CDROM;
}

static void
show_key_data (const char *name, unsigned char key_data[], int size)
{
	int i;
	fprintf (stderr, "%s:", name);
	for (i = 0; i < size; i++)
		fprintf (stderr, "%2x ", key_data[i]);
	fprintf (stderr, "\n");
}

static void
reverse_key_data (char key_data[], int size)
{
	int i;
	for (i = 0; i < size / 2; i++)
	{
		int temp;
		temp = key_data[i];
		key_data[i] = key_data[(size - 1) - i];
		key_data[(size - 1) - i] = temp;
	}
}

static long
dvd_start_session (HANDLE file)
{
	DWORD bytes_read;
	long session;

	if (DeviceIoControl (file, IOCTL_DVD_START_SESSION, 0, 0, &session,
	  sizeof (session), &bytes_read, 0))
	{
		return session;
	}

	if (GetLastError () == ERROR_INVALID_FUNCTION)
	{
		long session = DVD_END_ALL_SESSIONS;
		if (DeviceIoControl (file, IOCTL_DVD_END_SESSION, &session, sizeof (session), 0, 0, &bytes_read, 0))
			return dvd_start_session (file);

		for (session = 0; session < 10; session++)
		{
			if (DeviceIoControl (file, IOCTL_DVD_END_SESSION, &session, sizeof (session), 0, 0, &bytes_read, 0))
				return dvd_start_session (file);
		}
	}

	perror_win32 ("dodo", __FILE__, __LINE__);
	exit (1);
	return 0;
}

static void
dvd_get_disk_key (HANDLE file, long session, char disk_key[2048])
{
	struct DVD_READ_STRUCTURE read_struct;
	DWORD bytes_read;

	((long*) &read_struct.block_byte_offset)[0] = 0;
	((long*) &read_struct.block_byte_offset)[1] = 0;
	read_struct.session = session;
	read_struct.layer_no = 0;

	read_struct.format = 2;
	if (!DeviceIoControl (file, IOCTL_DVD_READ_STRUCTURE, &read_struct, sizeof (read_struct),
	  &disk_key, sizeof (disk_key), &bytes_read, 0))
	{
		perror_win32 ("dodo", __FILE__, __LINE__);
		dvd_end_session (file, session);
		exit (1);
	}
}

static void
authenticate (char letter)
{
	HANDLE file;
	char path[] = "\\\\.\\x:";
	DWORD bytes_read;
	long session;
	struct DVD_REGION region;
	struct DVD_COPY_PROTECT_KEY key = {0};

	if (!is_dvd_drive (letter))
	{
		fprintf (stderr, "dodo: '%c' drive is not a DVD drive", letter);
		exit (1);
	}

	path[4] = letter;
	file = CreateFile (path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
      NULL, OPEN_EXISTING, 0, NULL);
	if (file == INVALID_HANDLE_VALUE)
	{
		perror_win32 ("dodo", __FILE__, __LINE__);
		exit (1);
	}

	if (!DeviceIoControl (file, IOCTL_DVD_GET_REGION, 0, 0,
	  &region, sizeof (region), &bytes_read, 0))
	{
		perror_win32 ("dodo", __FILE__, __LINE__);
		exit (1);
	}

	session = dvd_start_session (file);

	if (opt.info)
		show_drive_info (file, session, region);

	/*
		Send our challenge key
	 */

	key.length = 12 + 24;
	key.session_id = session;
	key.type = DvdChallengeKey;
	key.flags = 0;
	{
		struct ChallengeKey { char a[12]; } c_key = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0 };
		*(struct ChallengeKey*) &key.challenge_key = c_key;
	}
	if (!DeviceIoControl (file, IOCTL_DVD_SEND_KEY, &key, key.length, 0, 0, &bytes_read, 0))
	{
		perror_win32 ("dodo", __FILE__, __LINE__);
		dvd_end_session (file, session);
		exit (1);
	}

	/*
		Receive the bus-key1 (or authentication key)
	 */

	key.length = 8 + 24;
	key.type = DvdBusKey1;
	if (!DeviceIoControl (file, IOCTL_DVD_READ_KEY, &key, key.length,
	  &key, key.length, &bytes_read, 0))
	{
		perror_win32 ("dodo", __FILE__, __LINE__);
		dvd_end_session (file, session);
		exit (1);
	}

	if (opt.verbose)
		show_key_data ("Bus-key1", key.key_data, 8);

	/*
		Receive the challenge key
	 */

	key.length = 12 + 24;
	key.type = DvdChallengeKey;
	if (!DeviceIoControl (file, IOCTL_DVD_READ_KEY, &key, key.length,
	  &key, key.length, &bytes_read, 0))
	{
		perror_win32 ("dodo", __FILE__, __LINE__);
		dvd_end_session (file, session);
		exit (1);
	}

	reverse_key_data (key.key_data, 10);
	if (opt.verbose)
		show_key_data ("Challenge-key", key.key_data, 12);

	{
		char key2[5];
		CryptKey2 (0, (void*) &key.key_data, (void*) key2);
		reverse_key_data (key2, 5);
		if (opt.verbose)
			show_key_data ("Bus-key2", key2, 8);
		memcpy (key.key_data, key2, 8);
	}

	/*
		Send the bus key2
	 */

	key.length = 8 + 24;
	key.type = DvdBusKey2;
	if (!DeviceIoControl (file, IOCTL_DVD_SEND_KEY, &key, key.length, 0, 0, &bytes_read, 0))
	{
		perror_win32 ("dodo", __FILE__, __LINE__);
		dvd_end_session (file, session);
		exit (1);
	}

	fprintf (stderr, "dodo: drive '%c' authorized\n", letter);

	/*
	 * It is necessary to retrive the disk key to complete authentication.
	 */
	{
		char disk_key[2048];
		dvd_get_disk_key (file, session, disk_key);
	}

	dvd_end_session (file, session);
}

static void
usage (void)
{
	fprintf (stderr,
	 "Usage: Dodo drive letter\n"
	 "\n"
	 "      --info        display information about a dvd disc\n"
	 "  -v, --verbose     output lots of information\n"
	 "  -h, --help        print this help and exit\n"
	 "      --version     output version information and exit\n"
	 "\n"
	 "Report bugs to http://www.blogan.com/\n");
}

static void
do_test (void)
{
	struct DVD_COPY_PROTECT_KEY key;
	struct ChallengeKey { char a[12]; } c_key = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, };
	int i;
	char enc_key[5];

	for (i = 0; i < 31; i++)
	{
		CryptKey1 (i, (void*) &c_key, (void*) enc_key);
		reverse_key_data (enc_key, 5);
		show_key_data ("Encrypted key", enc_key, 5);
	}

	fprintf (stderr, "sizeof (struct DVD_COPY_PROTECT_KEY) = %d\n", (char*) &key.key_data - (char*) &key);
}

int
main (int argc, char *argv[])
{
	if (argc <= 1)
	{
		usage ();
		return 1;
	}

	argc--;
	argv++;
	while (argc > 0)
	{
		if (strcmp (*argv, "--help") == 0 || strcmp (*argv, "--h") == 0)
		{
			usage ();
			return 0;
		}
		else if (strcmp (*argv, "--version") == 0)
		{
			fprintf (stderr, "Dodo, a dvd player. Version 0.1 #9 " __DATE__ "\n");
			return 0;
		}
		else if (strcmp (*argv, "--info") == 0)
			opt.info = 1;
		else if (strcmp (*argv, "--debug") == 0)
		{
			opt.debug = 1;
			opt.verbose = 1;
			do_test ();
		}
		else if (strcmp (*argv, "--verbose") == 0 || strcmp (*argv, "-v") == 0)
			opt.verbose = 1;
		else if ((*argv)[0] == '-')
		{
			fprintf (stderr,
			 "dodo: unrecognized option '%s'\n"
			 "Try 'dodo --help' for more information.\n",
			 *argv);
		}
		else
		{
			const char *a = *argv;
			if (isalpha (a[0]) && (a[1] == '\0' || a[1] == ':'))
				authenticate (a[0]);
			else
			{
				fprintf (stderr, "dodo: invalid drive letter, %s", a);
				return 1;
			}
		}

		argc--;
		argv++;
	}

	return 0;
}

blogan | RecentChanges | Preferences
Edit text of this page | View other revisions
Last edited January 30, 2003 4:27 am (diff)
Search: 