mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1521 lines
42 KiB
1521 lines
42 KiB
/*--
|
|
|
|
Copyright (C) Microsoft Corporation, 1999
|
|
|
|
--*/
|
|
|
|
// @@BEGIN_DDKSPLIT
|
|
/*--
|
|
|
|
Module Name:
|
|
|
|
sec.c
|
|
|
|
Abstract:
|
|
|
|
!!! THIS IS SENSITIVE INFORMATION !!!
|
|
THIS CODE MUST NEVER BE INCLUDED IN ANY EXTERNAL DOCUMENTATION
|
|
|
|
These functions MUST be static to prevent symbols when we ship
|
|
|
|
Environment:
|
|
|
|
kernel mode only
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
/*
|
|
|
|
KEY1 == \Registry\Machine\Software\Microsoft\
|
|
DPID == \Registry\Machine\Software\Microsoft\
|
|
Windows NT\CurrentVersion\DigitalProductId [REG_BINARY]
|
|
|
|
KEY2 == KEY1 + DPID Hash
|
|
PHSH == DPID Hash
|
|
DHSH == Drive Hash based off vendor, product, revision, and serial no.
|
|
UVAL == obfuscated value containing both current region and reset count
|
|
|
|
|
|
|
|
Overview:
|
|
KEY1 and DPID must both exist. furthermore, it is a given that the
|
|
DPID is unique to a machine and changing it is catastrophic. Based
|
|
upon these presumptions, we use the DPID to create a semi-unique key
|
|
under KEY1 that is based off the DPID (KEY2). KEY2 will store values
|
|
for each DVD RPC Phase 1 drive.
|
|
|
|
It is also a given that both the region AND reset count will change
|
|
each time the key is written. This allows the obfuscation method to
|
|
rely on either the reset or the region, but does not require both.
|
|
Each byte should rely on one of the above, in order to prevent any
|
|
large sequences of bytes from staying the same between changes.
|
|
|
|
Each value under KEY2 will have a one-to-one correlation to a
|
|
specific TYPE of drive (UVAL). Identical drives will share regions
|
|
and region reset counts. This is a "better" solution than sharing
|
|
region and reset counts for all devices, which was the only other
|
|
choice. OEMs must be made aware of this. This is a good reason to
|
|
install RPC Phase 2 drives into machines.
|
|
|
|
The UVAL is read by CdromGetRpc0Settings(). If the read results in
|
|
invalid data, we will mark the device as VIOLATING the license
|
|
agreements.
|
|
|
|
The UVAL name is based upon the DHSH as follows:
|
|
Take the DHSH and copy it as legal characters for the registry
|
|
by OR'ing with the value 0x20 (all characters higher than 20
|
|
are legal? -- verify with JVERT). This also has the benefit of
|
|
being a LOSSY method, but with a FIXED string length.
|
|
|
|
|
|
The data within UVAL is REG_QWORD. The data breakdown can be found
|
|
in the functions SecureDvdEncodeSettings() and SecureDvdDecodeSettings()
|
|
|
|
NOTE: One main difficulty still exists in determining the difference
|
|
between the above key not existing due to user deletion vs. the above
|
|
key not existing due to first install of this drive.
|
|
|
|
OPTIONAL: It is highly preferred to have KEY3 seeded in the system
|
|
hive. This will prevent the casual deletion of the KEY3 tree to reset
|
|
all the region counts to max. It is unknown if this is simple at
|
|
this time, but allows option 2 (below) to change to deletion, which
|
|
may be a better option.
|
|
|
|
OPTIONAL: save another key (UKEY), which, if it exists, means this
|
|
machine should NEVER be allowed to work again. this will force a
|
|
reinstall, and reduce the effectiveness of a brute-force attack
|
|
to an unmodified driver unless they realize this key is being set.
|
|
this will also allow a method to determine if a user deleted KEY2.
|
|
PSS can know about this magic key. cdrom should log an EVENTLOG
|
|
saying that the CSS license agreement has been breached. this key
|
|
should never be set for any error conditions when reading the key.
|
|
|
|
|
|
FunctionalFlow:
|
|
|
|
ReadDvdRegionAndResetCount()
|
|
|
|
[O] if (SecureDvdLicenseBreachDetected()) {
|
|
[O] LogLicenseError();
|
|
[O] return;
|
|
[O] }
|
|
if (!NT_SUCCESS(SecureDvdGetRegKeyHandle(h)) &&
|
|
reason was DNE) {
|
|
LogLicenseError();
|
|
return;
|
|
}
|
|
PHSH = SecureDvdGetProductHash();
|
|
if (PHSH == INVALID_HASH) {
|
|
return;
|
|
}
|
|
DHSH = SecureDvdGetDriveHash();
|
|
if (DHSH == INVALID_HASH) {
|
|
return;
|
|
}
|
|
|
|
if (!ReadValue( DriveKey, Data )) {
|
|
INITIALIZE_DRIVE_DATA( Data );
|
|
}
|
|
|
|
//
|
|
// data exists, if it's incorrect, LogLicenseError()
|
|
//
|
|
if (!DecodeSettings( QWORD, DHSH, PHSH )) {
|
|
LogLicenseError();
|
|
return;
|
|
}
|
|
|
|
// set region & count
|
|
|
|
return;
|
|
|
|
|
|
WriteDvdRegionAndResetCount()
|
|
|
|
[O] if (SecureDvdLicenseBreachDetected()) {
|
|
[O] return FALSE;
|
|
[O] }
|
|
if (!NT_SUCCESS(SecureDvdGetRegKeyHandle(h)) &&
|
|
reason was DNE) {
|
|
return FALSE;
|
|
}
|
|
PHSH = SecureDvdGetProductHash();
|
|
if (PHSH == INVALID_HASH) {
|
|
return FALSE;
|
|
}
|
|
DHSH = SecureDvdGetDriveHash();
|
|
if (DHSH == INVALID_HASH) {
|
|
return FALSE;
|
|
}
|
|
|
|
QWORD = EncodeSettings( DHSH, PHSH, Region, Resets );
|
|
if (QWORD == INVALID_HASH) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (!WriteValue( DriveKey, Data )) {
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
|
|
*/
|
|
// @@END_DDKSPLIT
|
|
|
|
#include "sec.h"
|
|
#include "sec.tmh"
|
|
|
|
|
|
// @@BEGIN_DDKSPLIT
|
|
|
|
//
|
|
// the digital product id structure is defined
|
|
// in \nt\private\windows\setup\pidgen\inc\pidgen.h
|
|
// (this was as of 10/06/1999)
|
|
//
|
|
|
|
typedef struct {
|
|
ULONG dwLength;
|
|
SHORT wVersionMajor;
|
|
SHORT wVersionMinor;
|
|
|
|
UCHAR szPid2[24];
|
|
|
|
ULONG dwKeyIdx;
|
|
|
|
UCHAR szSku[16];
|
|
UCHAR abCdKey[16];
|
|
|
|
ULONG dwCloneStatus;
|
|
ULONG dwTime;
|
|
ULONG dwRandom;
|
|
ULONG dwLicenseType;
|
|
ULONG adwLicenseData[2];
|
|
|
|
UCHAR szOemId[8];
|
|
|
|
ULONG dwBundleId;
|
|
|
|
UCHAR aszHardwareIdStatic[8];
|
|
|
|
ULONG dwHardwareIdTypeStatic;
|
|
ULONG dwBiosChecksumStatic;
|
|
ULONG dwVolSerStatic;
|
|
ULONG dwTotalRamStatic;
|
|
ULONG dwVideoBiosChecksumStatic;
|
|
|
|
UCHAR aszHardwareIdDynamic[8];
|
|
|
|
ULONG dwHardwareIdTypeDynamic;
|
|
ULONG dwBiosChecksumDynamic;
|
|
ULONG dwVolSerDynamic;
|
|
ULONG dwTotalRamDynamic;
|
|
ULONG dwVideoBiosChecksumDynamic;
|
|
ULONG dwCrc32;
|
|
|
|
} DIGITALPID, *PDIGITALPID;
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// These functions are not called externally. Make them static to make
|
|
// debugging more difficult in the shipping versions.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
STATIC
|
|
ULONG
|
|
RotateULong(
|
|
IN ULONG N,
|
|
IN LONG BitsToRotate
|
|
)
|
|
// validated for -64 through +64
|
|
{
|
|
if (BitsToRotate < 0) {
|
|
BitsToRotate = - BitsToRotate; // negate
|
|
BitsToRotate %= 8*sizeof(ULONG); // less than bits
|
|
BitsToRotate = 8*sizeof(ULONG) - BitsToRotate; // equivalent positive
|
|
} else {
|
|
BitsToRotate %= 8*sizeof(ULONG); // less than bits
|
|
}
|
|
|
|
return ((N << BitsToRotate) |
|
|
(N >> ((8*sizeof(ULONG)) - BitsToRotate)));
|
|
}
|
|
|
|
|
|
STATIC
|
|
ULONGLONG
|
|
RotateULongLong(
|
|
IN ULONGLONG N,
|
|
IN LONG BitsToRotate
|
|
)
|
|
// validated for -128 through +128
|
|
{
|
|
if (BitsToRotate < 0) {
|
|
BitsToRotate = - BitsToRotate;
|
|
BitsToRotate %= 8*sizeof(ULONGLONG);
|
|
BitsToRotate = 8*sizeof(ULONGLONG) - BitsToRotate;
|
|
} else {
|
|
BitsToRotate %= 8*sizeof(ULONGLONG);
|
|
}
|
|
|
|
return ((N << BitsToRotate) |
|
|
(N >> ((8*sizeof(ULONGLONG)) - BitsToRotate)));
|
|
}
|
|
|
|
|
|
STATIC
|
|
BOOLEAN
|
|
SecureDvdRegionInvalid(
|
|
IN UCHAR NegativeRegionMask
|
|
)
|
|
// validated for all inputs
|
|
{
|
|
UCHAR positiveMask = ~NegativeRegionMask;
|
|
|
|
if (positiveMask == 0) {
|
|
ASSERT(!"This routine should never be called with the value 0xff");
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// region non-zero, drop the lowest bit
|
|
// (this is a cool hack, learned when implementing a fast
|
|
// way to count the number of set bits in a variable.)
|
|
//
|
|
|
|
positiveMask = positiveMask & (positiveMask-1);
|
|
|
|
//
|
|
// if still non-zero, had more than one bit set
|
|
//
|
|
|
|
if (positiveMask) {
|
|
TraceLog((CdromSecInfo, "DvdInvalidRegion: TRUE for many bits\n"));
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
STATIC
|
|
ULONGLONG
|
|
SecureDvdGetDriveHash(
|
|
IN PSTORAGE_DEVICE_DESCRIPTOR Descriptor
|
|
)
|
|
// validated for all fields filled
|
|
// validated for some fields NULL
|
|
// validated for all fields NULL
|
|
// validated for some fields invalid (too large?)
|
|
// validated for all fields invalid (too large?)
|
|
/*
|
|
** returns a ULONGLONG which is the HASH for a given DVD device.
|
|
** NOTE: because this does not check SCSI IDs, identical drives
|
|
** will share the same region and reset counts.
|
|
*/
|
|
{
|
|
ULONGLONG checkSum = 0;
|
|
ULONG characters = 0;
|
|
LONG i;
|
|
|
|
if (Descriptor->VendorIdOffset > 0x12345678) {
|
|
TraceLog((CdromSecError,
|
|
"DvdDriveHash: VendorIdOffset is too large (%x)\n",
|
|
Descriptor->VendorIdOffset));
|
|
Descriptor->VendorIdOffset = 0;
|
|
}
|
|
|
|
if (Descriptor->ProductIdOffset > 0x12345678) {
|
|
TraceLog((CdromSecError,
|
|
"DvdDriveHash: ProductIdOffset is too large (%x)\n",
|
|
Descriptor->ProductIdOffset));
|
|
Descriptor->ProductIdOffset = 0;
|
|
}
|
|
|
|
if (Descriptor->ProductRevisionOffset > 0x12345678) {
|
|
TraceLog((CdromSecError,
|
|
"DvdDriveHash: ProducetRevisionOffset is too "
|
|
" large (%x)\n", Descriptor->ProductRevisionOffset));
|
|
Descriptor->ProductRevisionOffset = 0;
|
|
}
|
|
|
|
if (Descriptor->SerialNumberOffset > 0x12345678) {
|
|
TraceLog((CdromSecError,
|
|
"DvdDriveHash: SerialNumberOffset is too "
|
|
"large (%x)\n", Descriptor->SerialNumberOffset));
|
|
Descriptor->SerialNumberOffset = 0;
|
|
}
|
|
|
|
if ((!Descriptor->VendorIdOffset ) &&
|
|
(!Descriptor->ProductIdOffset ) &&
|
|
(!Descriptor->ProductRevisionOffset) ) {
|
|
|
|
TraceLog((CdromSecError, "DvdDriveHash: Invalid Descriptor at %p!\n",
|
|
Descriptor));
|
|
return INVALID_HASH;
|
|
|
|
}
|
|
|
|
//
|
|
// take one byte at a time, XOR together
|
|
// should provide a semi-unique hash
|
|
//
|
|
for (i=0;i<4;i++) {
|
|
|
|
PUCHAR string = (PUCHAR)Descriptor;
|
|
ULONG offset = 0;
|
|
|
|
switch(i) {
|
|
case 0: // vendorId
|
|
TraceLog((CdromSecInfo, "DvdDriveHash: Adding Vendor\n"));
|
|
offset = Descriptor->VendorIdOffset;
|
|
break;
|
|
case 1: // productId
|
|
TraceLog((CdromSecInfo, "DvdDriveHash: Adding Product\n"));
|
|
offset = Descriptor->ProductIdOffset;
|
|
break;
|
|
case 2: // revision
|
|
TraceLog((CdromSecInfo, "DvdDriveHash: Adding Revision\n"));
|
|
offset = Descriptor->ProductRevisionOffset;
|
|
break;
|
|
case 3: // serialNumber
|
|
TraceLog((CdromSecInfo, "DvdDriveHash: Adding SerialNumber\n"));
|
|
offset = Descriptor->SerialNumberOffset;
|
|
break;
|
|
default:
|
|
TraceLog((CdromSecError, "DvdDriveHash: TOO MANY LOOPS!!!\n"));
|
|
offset = 0;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// add the string to our checksum
|
|
//
|
|
|
|
if (offset != 0) {
|
|
|
|
|
|
for (string += offset; *string; string++) {
|
|
|
|
//
|
|
// take each character, multiply it by a "random"
|
|
// value. rotate the value.
|
|
//
|
|
|
|
ULONGLONG temp;
|
|
|
|
if (*string == ' ') {
|
|
// don't include spaces in the character count
|
|
// nor in the hash
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// dereference the value first!
|
|
//
|
|
|
|
temp = (ULONGLONG)(*string);
|
|
|
|
//
|
|
// guaranteed no overflow in UCHAR * ULONG in ULONGLONG
|
|
//
|
|
|
|
temp *= DVD_RANDOMIZER[ characters%DVD_RANDOMIZER_SIZE ];
|
|
|
|
//
|
|
// this rotation is just to spread the values around
|
|
// the 64 bits more evenly
|
|
//
|
|
|
|
temp = RotateULongLong(temp, 8*characters);
|
|
|
|
//
|
|
// increment number of characters used in checksum
|
|
// (used to verify we have enough characters)
|
|
//
|
|
|
|
characters++;
|
|
|
|
//
|
|
// XOR it into the checksum
|
|
//
|
|
|
|
checkSum ^= temp;
|
|
|
|
} // end of string
|
|
|
|
if (checkSum == 0) {
|
|
|
|
TraceLog((CdromSecInfo, "DvdDriveHash: zero checksum -- using "
|
|
"random value\n"));
|
|
checkSum ^= DVD_RANDOMIZER[ characters%DVD_RANDOMIZER_SIZE ];
|
|
characters++;
|
|
|
|
}
|
|
|
|
} // end of non-zero offset
|
|
|
|
} // end of four strings (vendor, product, revision, serialNo)
|
|
|
|
|
|
//
|
|
// we have to use more than four characters
|
|
// for this to be useful
|
|
//
|
|
if (characters <= 4) {
|
|
TraceLog((CdromSecError, "DvdDriveHash: Too few useful characters (%x) "
|
|
"for unique disk hash\n", characters));
|
|
return INVALID_HASH;
|
|
}
|
|
|
|
return checkSum;
|
|
}
|
|
|
|
|
|
//
|
|
// static, not called externally
|
|
//
|
|
STATIC
|
|
NTSTATUS
|
|
SecureDvdEncodeSettings(
|
|
IN ULONGLONG DpidHash,
|
|
IN ULONGLONG DriveHash,
|
|
OUT PULONGLONG Obfuscated,
|
|
IN UCHAR RegionMask,
|
|
IN UCHAR ResetCount
|
|
)
|
|
// validated for all valid inputs.
|
|
// validated for invalid inputs.
|
|
{
|
|
LARGE_INTEGER largeInteger;
|
|
ULONGLONG set;
|
|
LONG i;
|
|
LONG rotate;
|
|
UCHAR temp = 0;
|
|
|
|
UCHAR random1;
|
|
UCHAR random2;
|
|
|
|
//
|
|
// using the return from KeQueryTickCount() should give
|
|
// semi-random data
|
|
//
|
|
|
|
KeQueryTickCount(&largeInteger);
|
|
random2 = 0;
|
|
for (i=0; i < sizeof(ULONGLONG); i++) {
|
|
random2 ^= ((largeInteger.QuadPart >> (8*i)) & 0xff);
|
|
}
|
|
|
|
// set temp == sum of all 4-bit values
|
|
// 16 in ULONGLONG, times max value of
|
|
// 15 each is less than MAX_UCHAR
|
|
for (i=0; i < 2*sizeof(ULONGLONG); i++) {
|
|
|
|
temp += (UCHAR)( (DpidHash >> (4*i)) & 0xf );
|
|
|
|
}
|
|
|
|
//
|
|
// validate these settings here
|
|
//
|
|
|
|
if (DpidHash == INVALID_HASH) {
|
|
TraceLog((CdromSecError, "DvdEncode: Invalid DigitalProductId Hash\n"));
|
|
goto UserFailure;
|
|
}
|
|
if (DriveHash == INVALID_HASH) {
|
|
TraceLog((CdromSecError, "DvdEncode: Invalid Drive Hash\n"));
|
|
goto UserFailure;
|
|
}
|
|
|
|
if (RegionMask == 0xff) {
|
|
TraceLog((CdromSecError, "DvdEncode: Shouldn't attempt to write "
|
|
"mask of 0xff\n"));
|
|
goto UserFailure;
|
|
}
|
|
if (SecureDvdRegionInvalid(RegionMask)) {
|
|
TraceLog((CdromSecError, "DvdEncode: Invalid region\n"));
|
|
goto LicenseViolation;
|
|
}
|
|
if (ResetCount >= 2) {
|
|
TraceLog((CdromSecError, "DvdEncode: Too many reset counts\n"));
|
|
goto LicenseViolation;
|
|
}
|
|
|
|
//
|
|
// using the return from KeQueryTickCount() should give
|
|
// semi-random data
|
|
//
|
|
|
|
KeQueryTickCount(&largeInteger);
|
|
random1 = 0;
|
|
for (i=0; i < sizeof(ULONGLONG); i++) {
|
|
random1 ^= ((largeInteger.QuadPart >> (8*i)) & 0xff);
|
|
}
|
|
|
|
TraceLog((CdromSecInfo,
|
|
"DvdEncode: Random1 = %x Random2 = %x\n",
|
|
random1, random2));
|
|
|
|
//
|
|
// they must all fit into UCHAR! they should, since each one is
|
|
// individually a UCHAR, and only bitwise operations are being
|
|
// performed on them.
|
|
//
|
|
|
|
//
|
|
// the first cast to UCHAR prevents signed extension.
|
|
// the second cast to ULONGLONG allows high bits preserved by '|'
|
|
//
|
|
|
|
set = (ULONGLONG)0;
|
|
for (i=0; i < sizeof(ULONGLONG); i++) {
|
|
set ^= (ULONGLONG)random2 << (8*i);
|
|
}
|
|
|
|
set ^= (ULONGLONG)
|
|
((ULONGLONG)((UCHAR)(random1 ^ temp)) << 8*7) |
|
|
((ULONGLONG)((UCHAR)(RegionMask ^ temp)) << 8*6) |
|
|
((ULONGLONG)((UCHAR)(ResetCount ^ RegionMask ^ random1)) << 8*5) |
|
|
((ULONGLONG)((UCHAR)(0)) << 8*4) |
|
|
((ULONGLONG)((UCHAR)(ResetCount ^ temp)) << 8*3) |
|
|
((ULONGLONG)((UCHAR)(ResetCount ^ ((DriveHash >> 13) & 0xff))) << 8*2) |
|
|
((ULONGLONG)((UCHAR)(random1)) << 8*1) |
|
|
((ULONGLONG)((UCHAR)(RegionMask ^ ((DriveHash >> 23) & 0xff))) << 8*0) ;
|
|
|
|
TraceLog((CdromSecInfo,
|
|
"DvdEncode: Pre-rotate: %016I64x temp = %x\n",
|
|
set, temp));
|
|
|
|
//
|
|
// rotate it a semi-random, non-multiple-of-eight bits
|
|
//
|
|
rotate = (LONG)((DpidHash & 0xb) + 1); // {15,14,10,9,7,5,2,1}
|
|
|
|
TraceLog((CdromSecInfo,
|
|
"DvdEncode: Rotating %x bits\n", rotate));
|
|
*Obfuscated = RotateULongLong(set, rotate);
|
|
return STATUS_SUCCESS;
|
|
|
|
|
|
UserFailure:
|
|
*Obfuscated = INVALID_HASH;
|
|
return STATUS_UNSUCCESSFUL;
|
|
|
|
LicenseViolation:
|
|
*Obfuscated = INVALID_HASH;
|
|
return STATUS_LICENSE_VIOLATION;
|
|
|
|
|
|
}
|
|
|
|
|
|
STATIC
|
|
NTSTATUS
|
|
SecureDvdDecodeSettings(
|
|
IN ULONGLONG DpidHash,
|
|
IN ULONGLONG DriveHash,
|
|
IN ULONGLONG Set,
|
|
OUT PUCHAR RegionMask,
|
|
OUT PUCHAR ResetCount
|
|
)
|
|
// validated for many correct inputs, of all region/reset combinations
|
|
// validated for many incorrect inputs.
|
|
{
|
|
UCHAR random;
|
|
UCHAR region;
|
|
UCHAR resets;
|
|
UCHAR temp = 0;
|
|
|
|
LONG i, rotate;
|
|
|
|
// set temp == sum of all 4-bit values
|
|
// 16 in ULONGLONG, times max value of
|
|
// 15 each is less than MAX_UCHAR
|
|
|
|
for (i=0; i < 2*sizeof(ULONGLONG); i++) {
|
|
|
|
temp += (UCHAR)( (DpidHash >> (4*i)) & 0xf );
|
|
|
|
}
|
|
rotate = (LONG)((DpidHash & 0xb) + 1); // {15,14,10,9,7,5,2,1}
|
|
|
|
Set = RotateULongLong(Set, -rotate);
|
|
TraceLog((CdromSecInfo, "DvdDecode: Post-rotate: %016I64x\n", Set));
|
|
|
|
random = (UCHAR)(Set >> 8*4); // random2
|
|
|
|
TraceLog((CdromSecInfo, "DvdDecode: Random2 = %x\n", random));
|
|
|
|
for (i = 0; i < sizeof(ULONGLONG); i++) {
|
|
Set ^= (ULONGLONG)random << (8*i);
|
|
}
|
|
|
|
//
|
|
// bytes 6,4,3,1 are taken 'as-is'
|
|
// bytes 7,5,2,0 are verified
|
|
//
|
|
|
|
region = ((UCHAR)(Set >> 8*6)) ^ temp;
|
|
resets = ((UCHAR)(Set >> 8*3)) ^ temp;
|
|
random = ((UCHAR)(Set >> 8*1)); // make it random1
|
|
|
|
TraceLog((CdromSecInfo, "DvdDecode: Random1 = %x Region = %x Resets = %x\n",
|
|
random, region, resets));
|
|
|
|
// verify the bits
|
|
|
|
if (((UCHAR)(Set >> 8*7)) != (random ^ temp)) {
|
|
TraceLog((CdromSecError, "DvdDecode: Invalid Byte 7\n"));
|
|
goto ViolatedLicense;
|
|
}
|
|
|
|
random ^= (UCHAR)(Set >> 8*5);
|
|
if (random != (resets ^ region)) {
|
|
TraceLog((CdromSecError, "DvdDecode: Invalid Byte 5\n"));
|
|
goto ViolatedLicense;
|
|
}
|
|
|
|
random = (UCHAR)(DriveHash >> 13);
|
|
random ^= (UCHAR)(Set >> 8*2);
|
|
if (random != resets) {
|
|
TraceLog((CdromSecError, "DvdDecode: Invalid Byte 2\n"));
|
|
goto ViolatedLicense;
|
|
}
|
|
|
|
random = (UCHAR)(DriveHash >> 23);
|
|
random ^= (UCHAR)(Set >> 8*0);
|
|
if (random != region) {
|
|
TraceLog((CdromSecError, "DvdDecode: Invalid Byte 0\n"));
|
|
goto ViolatedLicense;
|
|
}
|
|
|
|
if (SecureDvdRegionInvalid(region)) {
|
|
TraceLog((CdromSecError, "DvdDecode: Region was invalid\n"));
|
|
goto ViolatedLicense;
|
|
}
|
|
if (resets >= 2) {
|
|
TraceLog((CdromSecError, "DvdDecode: Reset count was invalid\n"));
|
|
goto ViolatedLicense;
|
|
}
|
|
|
|
TraceLog((CdromSecInfo, "DvdDecode: Successfully validated stored data\n"));
|
|
|
|
*RegionMask = region;
|
|
*ResetCount = resets;
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
ViolatedLicense:
|
|
|
|
*RegionMask = 0x00;
|
|
*ResetCount = 0x00;
|
|
return STATUS_LICENSE_VIOLATION;
|
|
}
|
|
|
|
|
|
STATIC
|
|
NTSTATUS
|
|
SecureDvdGetSettingsCallBack(
|
|
IN PWSTR ValueName,
|
|
IN ULONG ValueType,
|
|
IN PVOID ValueData,
|
|
IN ULONG ValueLength,
|
|
IN PVOID UnusedContext,
|
|
IN PDVD_REGISTRY_CONTEXT Context
|
|
)
|
|
{
|
|
ULONGLONG hash = 0;
|
|
NTSTATUS status;
|
|
|
|
if (ValueType != REG_QWORD) {
|
|
TraceLog((CdromSecError, "DvdGetSettingsCallback: Not REG_BINARY\n"));
|
|
goto ViolatedLicense;
|
|
}
|
|
|
|
if (ValueLength != sizeof(ULONGLONG)) {
|
|
TraceLog((CdromSecError, "DvdGetSettingsCallback: DVD Settings data too "
|
|
"small (%x bytes)\n", ValueLength));
|
|
goto ViolatedLicense;
|
|
}
|
|
|
|
hash = *((PULONGLONG)ValueData);
|
|
|
|
if (hash == INVALID_HASH) {
|
|
TraceLog((CdromSecError, "DvdGetSettingsCallback: Invalid hash stored?\n"));
|
|
goto ViolatedLicense;
|
|
}
|
|
|
|
//
|
|
// validate the data
|
|
// this also sets the values in the context upon success.
|
|
//
|
|
|
|
status = SecureDvdDecodeSettings(Context->DpidHash,
|
|
Context->DriveHash,
|
|
hash,
|
|
&Context->RegionMask,
|
|
&Context->ResetCount);
|
|
|
|
if (status == STATUS_LICENSE_VIOLATION) {
|
|
|
|
TraceLog((CdromSecError, "DvdGetSettingsCallback: data was violated!\n"));
|
|
goto ViolatedLicense;
|
|
|
|
}
|
|
|
|
//
|
|
// the above call to SecureDvdDecodeSettings can only return
|
|
// success or a license violation
|
|
//
|
|
|
|
ASSERT(NT_SUCCESS(status));
|
|
return STATUS_SUCCESS;
|
|
|
|
|
|
|
|
ViolatedLicense:
|
|
Context->DriveHash = INVALID_HASH;
|
|
Context->DpidHash = INVALID_HASH;
|
|
Context->RegionMask = 0;
|
|
Context->ResetCount = 0;
|
|
return STATUS_LICENSE_VIOLATION;
|
|
}
|
|
|
|
|
|
STATIC
|
|
NTSTATUS
|
|
SecureDvdGetDigitalProductIdCallBack(
|
|
IN PWSTR ValueName,
|
|
IN ULONG ValueType,
|
|
IN PDIGITALPID DigitalPid, // ValueData
|
|
IN ULONG ValueLength,
|
|
IN PVOID UnusedVariable,
|
|
IN PULONGLONG DpidHash
|
|
)
|
|
// validated for non-REG_BINARY
|
|
// validated for good data
|
|
// validated for short data
|
|
{
|
|
NTSTATUS status = STATUS_LICENSE_VIOLATION;
|
|
ULONGLONG hash = 0;
|
|
|
|
if (ValueType != REG_BINARY) {
|
|
TraceLog((CdromSecError, "DvdDPIDCallback: Not REG_BINARY\n"));
|
|
*DpidHash = INVALID_HASH;
|
|
return STATUS_LICENSE_VIOLATION;
|
|
}
|
|
|
|
if (ValueLength < 4*sizeof(ULONGLONG)) {
|
|
TraceLog((CdromSecError,
|
|
"DvdDPIDCallback: DPID data too small (%x bytes)\n",
|
|
ValueLength));
|
|
*DpidHash = INVALID_HASH;
|
|
return STATUS_LICENSE_VIOLATION;
|
|
}
|
|
|
|
//
|
|
// apparently, only 13 bytes of the DigitalPID are
|
|
// going to stay static across upgrades. even these
|
|
// will change if the boot hard drive, video card, or
|
|
// bios signature changes. nonetheless, this is only
|
|
// supposed to keep the honest people honest. :)
|
|
//
|
|
|
|
//
|
|
// 8 bytes to fill == 64 bytes (need to rotate at least 48 bits)
|
|
//
|
|
|
|
TraceLog((CdromSecInfo,
|
|
"Bios %08x Video %08x VolSer %08x\n",
|
|
DigitalPid->dwBiosChecksumStatic,
|
|
DigitalPid->dwVideoBiosChecksumStatic,
|
|
DigitalPid->dwVolSerStatic));
|
|
|
|
hash ^= DigitalPid->dwBiosChecksumStatic; // 4 bytes // bios signature
|
|
hash = RotateULongLong(hash, 13); // prime number
|
|
hash ^= DigitalPid->dwVideoBiosChecksumStatic; // 4 bytes // video card
|
|
hash = RotateULongLong(hash, 13); // prime number
|
|
hash ^= DigitalPid->dwVolSerStatic; // 4 bytes // hard drive
|
|
hash = RotateULongLong(hash, 13); // prime number
|
|
|
|
*DpidHash = hash;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
STATIC
|
|
NTSTATUS
|
|
SecureDvdReturnDPIDHash(
|
|
PULONGLONG DpidHash
|
|
)
|
|
{
|
|
RTL_QUERY_REGISTRY_TABLE queryTable[2];
|
|
NTSTATUS status;
|
|
|
|
// cannot be PAGED_CODE() because queryTable cannot be swapped out!
|
|
|
|
//
|
|
// query the value
|
|
//
|
|
|
|
RtlZeroMemory(&(queryTable[0]), 2 * sizeof(RTL_QUERY_REGISTRY_TABLE));
|
|
queryTable[0].Name = L"DigitalProductId";
|
|
queryTable[0].EntryContext = DpidHash;
|
|
queryTable[0].DefaultType = 0;
|
|
queryTable[0].DefaultData = NULL;
|
|
queryTable[0].DefaultLength = 0;
|
|
queryTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED;
|
|
queryTable[0].QueryRoutine = SecureDvdGetDigitalProductIdCallBack;
|
|
|
|
*DpidHash = INVALID_HASH;
|
|
|
|
status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
|
|
L"\\Registry\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion",
|
|
&(queryTable[0]),
|
|
NULL,
|
|
NULL);
|
|
|
|
if (status == STATUS_LICENSE_VIOLATION) {
|
|
|
|
TraceLog((CdromSecError,
|
|
"DvdReturnDPIDHash: Invalid DPID!\n"));
|
|
|
|
} else if (!NT_SUCCESS(status)) {
|
|
|
|
TraceLog((CdromSecError,
|
|
"DvdReturnDPIDHash: Cannot get DPID (%x)\n", status));
|
|
|
|
} else {
|
|
|
|
TraceLog((CdromSecInfo,
|
|
"DvdReturnDPIDHash: Hash is now %I64x\n",
|
|
*DpidHash));
|
|
|
|
}
|
|
return status;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//// Everything past here has not been component tested
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#define SECURE_DVD_SET_SECURITY_ON_HANDLE 0
|
|
|
|
STATIC
|
|
NTSTATUS
|
|
SecureDvdSetHandleSecurity(
|
|
IN HANDLE Handle
|
|
)
|
|
{
|
|
|
|
#if SECURE_DVD_SET_SECURITY_ON_HANDLE
|
|
|
|
PACL newAcl = NULL;
|
|
ULONG newAclSize;
|
|
SECURITY_DESCRIPTOR securityDescriptor;
|
|
NTSTATUS status;
|
|
//
|
|
// from \nt\private\ntos\io\pnpinit.c
|
|
//
|
|
|
|
//SeEnableAccessToExports();
|
|
|
|
TRY {
|
|
newAclSize = sizeof(ACL);
|
|
newAclSize += sizeof(ACCESS_ALLOWED_ACE);
|
|
newAclSize -= sizeof(ULONG);
|
|
newAclSize += RtlLengthSid(SeExports->SeLocalSystemSid);
|
|
|
|
newAcl = ExAllocatePoolWithTag(PagedPool, newAclSize, DVD_TAG_SECURITY);
|
|
if (newAcl == NULL) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
LEAVE;
|
|
}
|
|
|
|
|
|
status = RtlCreateSecurityDescriptor(&securityDescriptor,
|
|
SECURITY_DESCRIPTOR_REVISION);
|
|
if (!NT_SUCCESS(status)) {
|
|
ASSERT(!"failed to create a security descriptor?");
|
|
LEAVE;
|
|
}
|
|
|
|
|
|
status = RtlCreateAcl(newAcl, newAclSize, ACL_REVISION);
|
|
if (!NT_SUCCESS(status)) {
|
|
ASSERT(!"failed to create a new ACL?");
|
|
LEAVE;
|
|
}
|
|
|
|
|
|
status = RtlAddAccessAllowedAce(newAcl,
|
|
ACL_REVISION,
|
|
KEY_ALL_ACCESS,
|
|
SeExports->SeLocalSystemSid);
|
|
if (!NT_SUCCESS(status)) {
|
|
ASSERT(!"failed to add LocalSystem to ACL");
|
|
LEAVE;
|
|
}
|
|
|
|
|
|
status = RtlSetDaclSecurityDescriptor(&securityDescriptor,
|
|
TRUE,
|
|
newAcl,
|
|
FALSE);
|
|
if (!NT_SUCCESS(status)) {
|
|
ASSERT(!"failed to set acl in security descriptor?");
|
|
LEAVE;
|
|
}
|
|
|
|
|
|
status = RtlValidSecurityDescriptor(&securityDescriptor);
|
|
if (!NT_SUCCESS(status)) {
|
|
ASSERT(!"failed to validate security descriptor?");
|
|
LEAVE;
|
|
}
|
|
|
|
|
|
status = ZwSetSecurityObject(Handle,
|
|
// PROTECTED_DACL_SECURITY_INFORMATION,
|
|
DACL_SECURITY_INFORMATION,
|
|
&securityDescriptor);
|
|
if (!NT_SUCCESS(status)) {
|
|
ASSERT(!"Failed to set security on handle\n");
|
|
LEAVE;
|
|
}
|
|
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
} FINALLY {
|
|
|
|
if (newAcl != NULL) {
|
|
ExFreePool(newAcl);
|
|
newAcl = NULL;
|
|
}
|
|
|
|
}
|
|
#endif
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
STATIC
|
|
NTSTATUS
|
|
SecureDvdGetRegistryHandle(
|
|
IN ULONGLONG DpidHash,
|
|
OUT PHANDLE Handle
|
|
)
|
|
{
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
UNICODE_STRING hashString;
|
|
NTSTATUS status;
|
|
LONG i;
|
|
//
|
|
// using char[] instead of char* allows modification of the
|
|
// string in this routine (a way of obfuscating the string)
|
|
// 0 ....+.... 1....+.. ..2....+. ...3....+. ...4
|
|
WCHAR string[] = L"\\Registry\\Machine\\Software\\Microsoft\\Windows NT";
|
|
WCHAR *hash = &(string[37]);
|
|
PULONGLONG hashAsUlonglong = (PULONGLONG)hash;
|
|
|
|
for (i = 0; i < sizeof(ULONGLONG); i++) {
|
|
|
|
UCHAR temp;
|
|
temp = (UCHAR)(DpidHash >> (8*i));
|
|
SET_FLAG(temp, 0x20); // more than 32
|
|
CLEAR_FLAG(temp, 0x80); // less than 128
|
|
hash[i] = (WCHAR)temp; // make it a wide char
|
|
|
|
}
|
|
hash[i] = UNICODE_NULL;
|
|
|
|
|
|
RtlInitUnicodeString(&hashString, string);
|
|
|
|
RtlZeroMemory(&objectAttributes, sizeof(OBJECT_ATTRIBUTES));
|
|
|
|
|
|
InitializeObjectAttributes(&objectAttributes,
|
|
&hashString,
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
|
RTL_REGISTRY_ABSOLUTE, // NULL?
|
|
NULL // no security descriptor
|
|
);
|
|
|
|
status = ZwCreateKey(Handle,
|
|
KEY_ALL_ACCESS,
|
|
&objectAttributes,
|
|
0,
|
|
NULL, // can be a unicode string....
|
|
REG_OPTION_NON_VOLATILE,
|
|
NULL);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
TraceLog((CdromSecError,
|
|
"DvdGetRegistryHandle: Failed to create key (%x)\n",
|
|
status));
|
|
return status;
|
|
}
|
|
|
|
status = SecureDvdSetHandleSecurity(*Handle);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
TraceLog((CdromSecError,
|
|
"DvdGetRegistryHandle: Failed to set key security (%x)\n",
|
|
status));
|
|
ZwClose(*Handle);
|
|
*Handle = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
STATIC
|
|
VOID
|
|
SecureDvdCreateValueNameFromHash(
|
|
IN ULONGLONG DriveHash,
|
|
OUT PWCHAR HashString
|
|
)
|
|
{
|
|
PUCHAR buffer = (PUCHAR)HashString;
|
|
LONG i;
|
|
|
|
RtlZeroMemory(HashString, 17*sizeof(WCHAR));
|
|
|
|
sprintf(buffer, "%016I64x", DriveHash);
|
|
|
|
// now massage the data to be unicode
|
|
for (i = 15; i >= 0; i--) {
|
|
HashString[i] = buffer[i];
|
|
}
|
|
}
|
|
|
|
|
|
STATIC
|
|
NTSTATUS
|
|
SecureDvdReadOrWriteRegionAndResetCount(
|
|
IN PDEVICE_OBJECT Fdo,
|
|
IN UCHAR NewRegion,
|
|
IN BOOLEAN ReadingTheValues
|
|
)
|
|
//
|
|
// NewRegion is ignored if ReadingTheValues is TRUE
|
|
//
|
|
{
|
|
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
|
|
PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension;
|
|
PCDROM_DATA cddata;
|
|
NTSTATUS status;
|
|
|
|
ULONG keyDisposition;
|
|
DVD_REGISTRY_CONTEXT registryContext;
|
|
HANDLE semiSecureHandle = INVALID_HANDLE_VALUE;
|
|
|
|
PAGED_CODE();
|
|
ASSERT(commonExtension->IsFdo);
|
|
|
|
cddata = (PCDROM_DATA)(commonExtension->DriverData);
|
|
|
|
if (cddata->DvdRpc0LicenseFailure) {
|
|
TraceLog((CdromSecError,
|
|
"Dvd%sSettings: Already violated licensing\n",
|
|
(ReadingTheValues ? "Read" : "Write")
|
|
));
|
|
goto ViolatedLicense;
|
|
}
|
|
|
|
RtlZeroMemory(®istryContext, sizeof(DVD_REGISTRY_CONTEXT));
|
|
|
|
//
|
|
// first ensure they didn't violate the CSS agreement
|
|
// by checking for the existance of Mr. Enigma
|
|
//
|
|
{
|
|
HANDLE regHandle;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
UNICODE_STRING mrEnigmaString;
|
|
|
|
RtlInitUnicodeString(&mrEnigmaString,
|
|
L"\\Registry\\Machine\\Software\\Microsoft\\Mr. Enigma");
|
|
|
|
RtlZeroMemory(&objectAttributes, sizeof(OBJECT_ATTRIBUTES));
|
|
InitializeObjectAttributes(&objectAttributes,
|
|
&mrEnigmaString,
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
|
RTL_REGISTRY_ABSOLUTE, // NULL?
|
|
NULL); // no security descriptor
|
|
|
|
status = ZwOpenKey(®Handle, KEY_ALL_ACCESS, &objectAttributes);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
TraceLog((CdromSecError,
|
|
"Dvd%sSettings: Mr. Enigma doesn't exist. This will "
|
|
"fail DVD video playback\n",
|
|
(ReadingTheValues ? "Read" : "Write")
|
|
));
|
|
// red herring :)
|
|
|
|
} else {
|
|
|
|
ZwClose(regHandle);
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// then, get the DigitalProductIdHash and this DriveHash
|
|
//
|
|
|
|
{
|
|
status = SecureDvdReturnDPIDHash(®istryContext.DpidHash);
|
|
|
|
// if this fails, we are in serious trouble!
|
|
if (status == STATUS_LICENSE_VIOLATION) {
|
|
|
|
TraceLog((CdromSecError,
|
|
"Dvd%sSettings: License error getting DPIDHash?\n",
|
|
(ReadingTheValues ? "Read" : "Write")));
|
|
goto ViolatedLicense;
|
|
|
|
} else if (!NT_SUCCESS(status)) {
|
|
|
|
TraceLog((CdromSecError,
|
|
"Dvd%sSettings: Couldn't get DPID Hash! (%x)\n",
|
|
(ReadingTheValues ? "Read" : "Write"), status));
|
|
goto RetryExit;
|
|
|
|
}
|
|
|
|
if (registryContext.DpidHash == INVALID_HASH) {
|
|
|
|
goto ErrorExit;
|
|
}
|
|
|
|
registryContext.DriveHash =
|
|
SecureDvdGetDriveHash(fdoExtension->DeviceDescriptor);
|
|
if (registryContext.DriveHash == INVALID_HASH) {
|
|
TraceLog((CdromSecError,
|
|
"Dvd%sSettings: Couldn't create drive hash(!)\n",
|
|
(ReadingTheValues ? "Read" : "Write")));
|
|
goto ErrorExit;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// finally get a handle based upon the DigitalProductIdHash
|
|
// to our "semi-secure" registry key, creating it if neccessary.
|
|
//
|
|
status= SecureDvdGetRegistryHandle(registryContext.DpidHash,
|
|
&semiSecureHandle);
|
|
if (!NT_SUCCESS(status)) {
|
|
TraceLog((CdromSecError,
|
|
"Dvd%sSettings: Could not get semi-secure handle %x\n",
|
|
(ReadingTheValues ? "Read" : "Write"), status));
|
|
goto ErrorExit;
|
|
}
|
|
|
|
//
|
|
// if reading the values, use the semi-secure handle to open a subkey,
|
|
// read its data, close the handle, it.
|
|
//
|
|
//
|
|
if (ReadingTheValues) {
|
|
|
|
WCHAR hashString[17]; // 16 + NULL
|
|
RTL_QUERY_REGISTRY_TABLE queryTable[2];
|
|
|
|
SecureDvdCreateValueNameFromHash(registryContext.DriveHash, hashString);
|
|
|
|
RtlZeroMemory(&queryTable[0], 2*sizeof(RTL_QUERY_REGISTRY_TABLE));
|
|
|
|
queryTable[0].DefaultData = NULL;
|
|
queryTable[0].DefaultLength = 0;
|
|
queryTable[0].DefaultType = 0;
|
|
queryTable[0].EntryContext = ®istryContext;
|
|
queryTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED;
|
|
queryTable[0].Name = hashString;
|
|
queryTable[0].QueryRoutine = SecureDvdGetSettingsCallBack;
|
|
|
|
status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
|
|
semiSecureHandle,
|
|
&queryTable[0],
|
|
®istryContext,
|
|
NULL);
|
|
|
|
if (status == STATUS_LICENSE_VIOLATION) {
|
|
TraceLog((CdromSecError,
|
|
"Dvd%sSettings: Invalid value in registry!\n",
|
|
(ReadingTheValues ? "Read" : "Write")));
|
|
goto ViolatedLicense;
|
|
} else if (!NT_SUCCESS(status)) {
|
|
TraceLog((CdromSecError,
|
|
"Dvd%sSettings: Other non-license error (%x)\n",
|
|
(ReadingTheValues ? "Read" : "Write"), status));
|
|
goto ErrorExit;
|
|
}
|
|
|
|
//
|
|
// set the real values....
|
|
//
|
|
|
|
cddata->Rpc0SystemRegion = registryContext.RegionMask;
|
|
cddata->Rpc0SystemRegionResetCount = registryContext.ResetCount;
|
|
|
|
//
|
|
// everything is kosher!
|
|
//
|
|
|
|
TraceLog((CdromSecInfo,
|
|
"Dvd%sSettings: Region %x Reset %x\n",
|
|
(ReadingTheValues ? "Read" : "Write"),
|
|
cddata->Rpc0SystemRegion,
|
|
cddata->Rpc0SystemRegionResetCount));
|
|
|
|
|
|
|
|
} else { // !ReadingTheValues, iow, writing them....
|
|
|
|
//
|
|
// if writing the values, obfuscate them first (which also validates),
|
|
// then use the semi-secure handle to write the subkey
|
|
//
|
|
|
|
WCHAR hashString[17]; // 16 + NULL
|
|
ULONGLONG obfuscated;
|
|
|
|
//
|
|
// don't munge the device extension until we modify the registry
|
|
// (see below for modification of device extension data)
|
|
//
|
|
|
|
registryContext.RegionMask = NewRegion;
|
|
registryContext.ResetCount = cddata->Rpc0SystemRegionResetCount-1;
|
|
|
|
//
|
|
// this also validates the settings
|
|
//
|
|
|
|
SecureDvdCreateValueNameFromHash(registryContext.DriveHash, hashString);
|
|
|
|
status = SecureDvdEncodeSettings(registryContext.DpidHash,
|
|
registryContext.DriveHash,
|
|
&obfuscated,
|
|
registryContext.RegionMask,
|
|
registryContext.ResetCount);
|
|
|
|
|
|
|
|
if (status == STATUS_LICENSE_VIOLATION) {
|
|
|
|
TraceLog((CdromSecError,
|
|
"Dvd%sSettings: User may have modified memory! "
|
|
"%x %x\n", (ReadingTheValues ? "Read" : "Write"),
|
|
registryContext.RegionMask,
|
|
registryContext.ResetCount));
|
|
goto ViolatedLicense;
|
|
|
|
} else if (!NT_SUCCESS(status)) {
|
|
|
|
TraceLog((CdromSecError,
|
|
"Dvd%sSettings: Couldn't obfuscate data %x %x\n",
|
|
(ReadingTheValues ? "Read" : "Write"),
|
|
registryContext.RegionMask,
|
|
registryContext.ResetCount));
|
|
goto ErrorExit;
|
|
|
|
}
|
|
|
|
//
|
|
// save them for posterity
|
|
//
|
|
|
|
TraceLog((CdromSecInfo,
|
|
"Dvd%sSettings: Data is %016I64x\n",
|
|
(ReadingTheValues ? "Read" : "Write"),
|
|
obfuscated));
|
|
|
|
status = RtlWriteRegistryValue(RTL_REGISTRY_HANDLE,
|
|
semiSecureHandle,
|
|
hashString,
|
|
REG_QWORD,
|
|
&obfuscated,
|
|
(ULONG)(sizeof(ULONGLONG))
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
TraceLog((CdromSecError,
|
|
"Dvd%sSettings: Couldn't save %x\n",
|
|
(ReadingTheValues ? "Read" : "Write"), status));
|
|
goto ErrorExit;
|
|
}
|
|
|
|
//
|
|
// make the change in the device extension data also
|
|
//
|
|
|
|
cddata->Rpc0SystemRegion = NewRegion;
|
|
cddata->Rpc0SystemRegionResetCount--;
|
|
|
|
TraceLog((CdromSecInfo,
|
|
"Dvd%sSettings: Region %x Reset %x\n",
|
|
(ReadingTheValues ? "Read" : "Write"),
|
|
cddata->Rpc0SystemRegion,
|
|
cddata->Rpc0SystemRegionResetCount));
|
|
|
|
|
|
}
|
|
|
|
if (semiSecureHandle != INVALID_HANDLE_VALUE) {
|
|
ZwClose(semiSecureHandle);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
|
|
ViolatedLicense: {
|
|
PIO_ERROR_LOG_PACKET errorLogEntry;
|
|
|
|
if (semiSecureHandle != INVALID_HANDLE_VALUE) {
|
|
ZwClose(semiSecureHandle);
|
|
}
|
|
|
|
/*
|
|
errorLogEntry = (PIO_ERROR_LOG_ENTRY)
|
|
IoAllocateErrorLogEntry(Fdo,
|
|
(UCHAR)(sizeof(IO_ERROR_LOG_PACKET)));
|
|
|
|
if (errorLogEntry != NULL) {
|
|
errorLogEntry->FinalStatus = STATUS_LICENSE_VIOLATION;
|
|
errorLogEntry->ErrorCode = STATUS_LICENSE_VIOLATION;
|
|
errorLogEntry->MajorFunctionCode = IRP_MJ_START_DEVICE;
|
|
IoWriteErrorLogEntry(errorLogEntry);
|
|
}
|
|
*/
|
|
|
|
TraceLog((CdromSecError,
|
|
"Dvd%sSettings: License Violation Detected\n",
|
|
(ReadingTheValues ? "Read" : "Write")));
|
|
cddata->DvdRpc0LicenseFailure = TRUE; // no playback
|
|
cddata->Rpc0SystemRegion = 0xff; // no regions
|
|
cddata->Rpc0SystemRegionResetCount = 0; // no resets
|
|
return STATUS_LICENSE_VIOLATION;
|
|
}
|
|
|
|
RetryExit:
|
|
|
|
if (ReadingTheValues) {
|
|
cddata->Rpc0RetryRegistryCallback = 1;
|
|
}
|
|
|
|
//
|
|
// fall-through to Error Exit...
|
|
//
|
|
|
|
ErrorExit:
|
|
TraceLog((CdromSecError,
|
|
"Dvd%sSettings: Non-License Error Detected\n",
|
|
(ReadingTheValues ? "Read" : "Write")));
|
|
//
|
|
// don't modify the device extension on non-license-violation errors
|
|
//
|
|
if (semiSecureHandle != INVALID_HANDLE_VALUE) {
|
|
ZwClose(semiSecureHandle);
|
|
}
|
|
|
|
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// The following functions are externally accessible. They therefore cannot
|
|
// be either STATIC nor INLINE
|
|
// static to make debugging more difficult in the shipping versions.
|
|
//
|
|
// These exports return one of only three NTSTATUS values:
|
|
// STATUS_SUCCESS
|
|
// STATUS_UNSUCCESSFUL
|
|
// STATUS_LICENSE_VIOLATION
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
NTSTATUS
|
|
CdRomGetRpc0Settings(
|
|
IN PDEVICE_OBJECT Fdo
|
|
)
|
|
{
|
|
PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension;
|
|
PCDROM_DATA cddata = (PCDROM_DATA)(commonExtension->DriverData);
|
|
NTSTATUS status;
|
|
|
|
KeWaitForMutexObject(&cddata->Rpc0RegionMutex, UserRequest, KernelMode,
|
|
FALSE, NULL);
|
|
status = SecureDvdReadOrWriteRegionAndResetCount(Fdo, 0, TRUE);
|
|
KeReleaseMutex(&cddata->Rpc0RegionMutex, FALSE);
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
CdRomSetRpc0Settings(
|
|
IN PDEVICE_OBJECT Fdo,
|
|
IN UCHAR NewRegion
|
|
)
|
|
{
|
|
PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension;
|
|
PCDROM_DATA cddata = (PCDROM_DATA)(commonExtension->DriverData);
|
|
NTSTATUS status;
|
|
|
|
KeWaitForMutexObject(&cddata->Rpc0RegionMutex, UserRequest, KernelMode,
|
|
FALSE, NULL);
|
|
status = SecureDvdReadOrWriteRegionAndResetCount(Fdo, NewRegion, FALSE);
|
|
KeReleaseMutex(&cddata->Rpc0RegionMutex, FALSE);
|
|
return status;
|
|
}
|
|
|
|
|
|
#if 0
|
|
// @@END_DDKSPLIT
|
|
|
|
NTSTATUS
|
|
CdRomGetRpc0Settings(
|
|
IN PDEVICE_OBJECT Fdo
|
|
)
|
|
{
|
|
PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension;
|
|
PCDROM_DATA cddata = (PCDROM_DATA)(commonExtension->DriverData);
|
|
|
|
cddata->Rpc0SystemRegion = (UCHAR)(~1); // region one
|
|
cddata->Rpc0SystemRegionResetCount = 0; // no resets
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
CdRomSetRpc0Settings(
|
|
IN PDEVICE_OBJECT Fdo,
|
|
IN UCHAR NewRegion
|
|
)
|
|
{
|
|
return STATUS_SUCCESS;
|
|
}
|
|
// @@BEGIN_DDKSPLIT
|
|
#endif // 0 -- DDK stub for all the stuff we do...
|
|
// @@END_DDKSPLIT
|
|
|
|
|
|
|
|
|