Source code of Windows XP (NT5)
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

  1. /*--
  2. Copyright (C) Microsoft Corporation, 1999
  3. --*/
  4. // @@BEGIN_DDKSPLIT
  5. /*--
  6. Module Name:
  7. sec.c
  8. Abstract:
  9. !!! THIS IS SENSITIVE INFORMATION !!!
  10. THIS CODE MUST NEVER BE INCLUDED IN ANY EXTERNAL DOCUMENTATION
  11. These functions MUST be static to prevent symbols when we ship
  12. Environment:
  13. kernel mode only
  14. Revision History:
  15. --*/
  16. /*
  17. KEY1 == \Registry\Machine\Software\Microsoft\
  18. DPID == \Registry\Machine\Software\Microsoft\
  19. Windows NT\CurrentVersion\DigitalProductId [REG_BINARY]
  20. KEY2 == KEY1 + DPID Hash
  21. PHSH == DPID Hash
  22. DHSH == Drive Hash based off vendor, product, revision, and serial no.
  23. UVAL == obfuscated value containing both current region and reset count
  24. Overview:
  25. KEY1 and DPID must both exist. furthermore, it is a given that the
  26. DPID is unique to a machine and changing it is catastrophic. Based
  27. upon these presumptions, we use the DPID to create a semi-unique key
  28. under KEY1 that is based off the DPID (KEY2). KEY2 will store values
  29. for each DVD RPC Phase 1 drive.
  30. It is also a given that both the region AND reset count will change
  31. each time the key is written. This allows the obfuscation method to
  32. rely on either the reset or the region, but does not require both.
  33. Each byte should rely on one of the above, in order to prevent any
  34. large sequences of bytes from staying the same between changes.
  35. Each value under KEY2 will have a one-to-one correlation to a
  36. specific TYPE of drive (UVAL). Identical drives will share regions
  37. and region reset counts. This is a "better" solution than sharing
  38. region and reset counts for all devices, which was the only other
  39. choice. OEMs must be made aware of this. This is a good reason to
  40. install RPC Phase 2 drives into machines.
  41. The UVAL is read by CdromGetRpc0Settings(). If the read results in
  42. invalid data, we will mark the device as VIOLATING the license
  43. agreements.
  44. The UVAL name is based upon the DHSH as follows:
  45. Take the DHSH and copy it as legal characters for the registry
  46. by OR'ing with the value 0x20 (all characters higher than 20
  47. are legal? -- verify with JVERT). This also has the benefit of
  48. being a LOSSY method, but with a FIXED string length.
  49. The data within UVAL is REG_QWORD. The data breakdown can be found
  50. in the functions SecureDvdEncodeSettings() and SecureDvdDecodeSettings()
  51. NOTE: One main difficulty still exists in determining the difference
  52. between the above key not existing due to user deletion vs. the above
  53. key not existing due to first install of this drive.
  54. OPTIONAL: It is highly preferred to have KEY3 seeded in the system
  55. hive. This will prevent the casual deletion of the KEY3 tree to reset
  56. all the region counts to max. It is unknown if this is simple at
  57. this time, but allows option 2 (below) to change to deletion, which
  58. may be a better option.
  59. OPTIONAL: save another key (UKEY), which, if it exists, means this
  60. machine should NEVER be allowed to work again. this will force a
  61. reinstall, and reduce the effectiveness of a brute-force attack
  62. to an unmodified driver unless they realize this key is being set.
  63. this will also allow a method to determine if a user deleted KEY2.
  64. PSS can know about this magic key. cdrom should log an EVENTLOG
  65. saying that the CSS license agreement has been breached. this key
  66. should never be set for any error conditions when reading the key.
  67. FunctionalFlow:
  68. ReadDvdRegionAndResetCount()
  69. [O] if (SecureDvdLicenseBreachDetected()) {
  70. [O] LogLicenseError();
  71. [O] return;
  72. [O] }
  73. if (!NT_SUCCESS(SecureDvdGetRegKeyHandle(h)) &&
  74. reason was DNE) {
  75. LogLicenseError();
  76. return;
  77. }
  78. PHSH = SecureDvdGetProductHash();
  79. if (PHSH == INVALID_HASH) {
  80. return;
  81. }
  82. DHSH = SecureDvdGetDriveHash();
  83. if (DHSH == INVALID_HASH) {
  84. return;
  85. }
  86. if (!ReadValue( DriveKey, Data )) {
  87. INITIALIZE_DRIVE_DATA( Data );
  88. }
  89. //
  90. // data exists, if it's incorrect, LogLicenseError()
  91. //
  92. if (!DecodeSettings( QWORD, DHSH, PHSH )) {
  93. LogLicenseError();
  94. return;
  95. }
  96. // set region & count
  97. return;
  98. WriteDvdRegionAndResetCount()
  99. [O] if (SecureDvdLicenseBreachDetected()) {
  100. [O] return FALSE;
  101. [O] }
  102. if (!NT_SUCCESS(SecureDvdGetRegKeyHandle(h)) &&
  103. reason was DNE) {
  104. return FALSE;
  105. }
  106. PHSH = SecureDvdGetProductHash();
  107. if (PHSH == INVALID_HASH) {
  108. return FALSE;
  109. }
  110. DHSH = SecureDvdGetDriveHash();
  111. if (DHSH == INVALID_HASH) {
  112. return FALSE;
  113. }
  114. QWORD = EncodeSettings( DHSH, PHSH, Region, Resets );
  115. if (QWORD == INVALID_HASH) {
  116. return FALSE;
  117. }
  118. if (!WriteValue( DriveKey, Data )) {
  119. return FALSE;
  120. }
  121. return TRUE;
  122. */
  123. // @@END_DDKSPLIT
  124. #include "sec.h"
  125. #include "sec.tmh"
  126. // @@BEGIN_DDKSPLIT
  127. //
  128. // the digital product id structure is defined
  129. // in \nt\private\windows\setup\pidgen\inc\pidgen.h
  130. // (this was as of 10/06/1999)
  131. //
  132. typedef struct {
  133. ULONG dwLength;
  134. SHORT wVersionMajor;
  135. SHORT wVersionMinor;
  136. UCHAR szPid2[24];
  137. ULONG dwKeyIdx;
  138. UCHAR szSku[16];
  139. UCHAR abCdKey[16];
  140. ULONG dwCloneStatus;
  141. ULONG dwTime;
  142. ULONG dwRandom;
  143. ULONG dwLicenseType;
  144. ULONG adwLicenseData[2];
  145. UCHAR szOemId[8];
  146. ULONG dwBundleId;
  147. UCHAR aszHardwareIdStatic[8];
  148. ULONG dwHardwareIdTypeStatic;
  149. ULONG dwBiosChecksumStatic;
  150. ULONG dwVolSerStatic;
  151. ULONG dwTotalRamStatic;
  152. ULONG dwVideoBiosChecksumStatic;
  153. UCHAR aszHardwareIdDynamic[8];
  154. ULONG dwHardwareIdTypeDynamic;
  155. ULONG dwBiosChecksumDynamic;
  156. ULONG dwVolSerDynamic;
  157. ULONG dwTotalRamDynamic;
  158. ULONG dwVideoBiosChecksumDynamic;
  159. ULONG dwCrc32;
  160. } DIGITALPID, *PDIGITALPID;
  161. ////////////////////////////////////////////////////////////////////////////////
  162. //
  163. // These functions are not called externally. Make them static to make
  164. // debugging more difficult in the shipping versions.
  165. //
  166. ////////////////////////////////////////////////////////////////////////////////
  167. STATIC
  168. ULONG
  169. RotateULong(
  170. IN ULONG N,
  171. IN LONG BitsToRotate
  172. )
  173. // validated for -64 through +64
  174. {
  175. if (BitsToRotate < 0) {
  176. BitsToRotate = - BitsToRotate; // negate
  177. BitsToRotate %= 8*sizeof(ULONG); // less than bits
  178. BitsToRotate = 8*sizeof(ULONG) - BitsToRotate; // equivalent positive
  179. } else {
  180. BitsToRotate %= 8*sizeof(ULONG); // less than bits
  181. }
  182. return ((N << BitsToRotate) |
  183. (N >> ((8*sizeof(ULONG)) - BitsToRotate)));
  184. }
  185. STATIC
  186. ULONGLONG
  187. RotateULongLong(
  188. IN ULONGLONG N,
  189. IN LONG BitsToRotate
  190. )
  191. // validated for -128 through +128
  192. {
  193. if (BitsToRotate < 0) {
  194. BitsToRotate = - BitsToRotate;
  195. BitsToRotate %= 8*sizeof(ULONGLONG);
  196. BitsToRotate = 8*sizeof(ULONGLONG) - BitsToRotate;
  197. } else {
  198. BitsToRotate %= 8*sizeof(ULONGLONG);
  199. }
  200. return ((N << BitsToRotate) |
  201. (N >> ((8*sizeof(ULONGLONG)) - BitsToRotate)));
  202. }
  203. STATIC
  204. BOOLEAN
  205. SecureDvdRegionInvalid(
  206. IN UCHAR NegativeRegionMask
  207. )
  208. // validated for all inputs
  209. {
  210. UCHAR positiveMask = ~NegativeRegionMask;
  211. if (positiveMask == 0) {
  212. ASSERT(!"This routine should never be called with the value 0xff");
  213. return TRUE;
  214. }
  215. //
  216. // region non-zero, drop the lowest bit
  217. // (this is a cool hack, learned when implementing a fast
  218. // way to count the number of set bits in a variable.)
  219. //
  220. positiveMask = positiveMask & (positiveMask-1);
  221. //
  222. // if still non-zero, had more than one bit set
  223. //
  224. if (positiveMask) {
  225. TraceLog((CdromSecInfo, "DvdInvalidRegion: TRUE for many bits\n"));
  226. return TRUE;
  227. }
  228. return FALSE;
  229. }
  230. STATIC
  231. ULONGLONG
  232. SecureDvdGetDriveHash(
  233. IN PSTORAGE_DEVICE_DESCRIPTOR Descriptor
  234. )
  235. // validated for all fields filled
  236. // validated for some fields NULL
  237. // validated for all fields NULL
  238. // validated for some fields invalid (too large?)
  239. // validated for all fields invalid (too large?)
  240. /*
  241. ** returns a ULONGLONG which is the HASH for a given DVD device.
  242. ** NOTE: because this does not check SCSI IDs, identical drives
  243. ** will share the same region and reset counts.
  244. */
  245. {
  246. ULONGLONG checkSum = 0;
  247. ULONG characters = 0;
  248. LONG i;
  249. if (Descriptor->VendorIdOffset > 0x12345678) {
  250. TraceLog((CdromSecError,
  251. "DvdDriveHash: VendorIdOffset is too large (%x)\n",
  252. Descriptor->VendorIdOffset));
  253. Descriptor->VendorIdOffset = 0;
  254. }
  255. if (Descriptor->ProductIdOffset > 0x12345678) {
  256. TraceLog((CdromSecError,
  257. "DvdDriveHash: ProductIdOffset is too large (%x)\n",
  258. Descriptor->ProductIdOffset));
  259. Descriptor->ProductIdOffset = 0;
  260. }
  261. if (Descriptor->ProductRevisionOffset > 0x12345678) {
  262. TraceLog((CdromSecError,
  263. "DvdDriveHash: ProducetRevisionOffset is too "
  264. " large (%x)\n", Descriptor->ProductRevisionOffset));
  265. Descriptor->ProductRevisionOffset = 0;
  266. }
  267. if (Descriptor->SerialNumberOffset > 0x12345678) {
  268. TraceLog((CdromSecError,
  269. "DvdDriveHash: SerialNumberOffset is too "
  270. "large (%x)\n", Descriptor->SerialNumberOffset));
  271. Descriptor->SerialNumberOffset = 0;
  272. }
  273. if ((!Descriptor->VendorIdOffset ) &&
  274. (!Descriptor->ProductIdOffset ) &&
  275. (!Descriptor->ProductRevisionOffset) ) {
  276. TraceLog((CdromSecError, "DvdDriveHash: Invalid Descriptor at %p!\n",
  277. Descriptor));
  278. return INVALID_HASH;
  279. }
  280. //
  281. // take one byte at a time, XOR together
  282. // should provide a semi-unique hash
  283. //
  284. for (i=0;i<4;i++) {
  285. PUCHAR string = (PUCHAR)Descriptor;
  286. ULONG offset = 0;
  287. switch(i) {
  288. case 0: // vendorId
  289. TraceLog((CdromSecInfo, "DvdDriveHash: Adding Vendor\n"));
  290. offset = Descriptor->VendorIdOffset;
  291. break;
  292. case 1: // productId
  293. TraceLog((CdromSecInfo, "DvdDriveHash: Adding Product\n"));
  294. offset = Descriptor->ProductIdOffset;
  295. break;
  296. case 2: // revision
  297. TraceLog((CdromSecInfo, "DvdDriveHash: Adding Revision\n"));
  298. offset = Descriptor->ProductRevisionOffset;
  299. break;
  300. case 3: // serialNumber
  301. TraceLog((CdromSecInfo, "DvdDriveHash: Adding SerialNumber\n"));
  302. offset = Descriptor->SerialNumberOffset;
  303. break;
  304. default:
  305. TraceLog((CdromSecError, "DvdDriveHash: TOO MANY LOOPS!!!\n"));
  306. offset = 0;
  307. break;
  308. }
  309. //
  310. // add the string to our checksum
  311. //
  312. if (offset != 0) {
  313. for (string += offset; *string; string++) {
  314. //
  315. // take each character, multiply it by a "random"
  316. // value. rotate the value.
  317. //
  318. ULONGLONG temp;
  319. if (*string == ' ') {
  320. // don't include spaces in the character count
  321. // nor in the hash
  322. continue;
  323. }
  324. //
  325. // dereference the value first!
  326. //
  327. temp = (ULONGLONG)(*string);
  328. //
  329. // guaranteed no overflow in UCHAR * ULONG in ULONGLONG
  330. //
  331. temp *= DVD_RANDOMIZER[ characters%DVD_RANDOMIZER_SIZE ];
  332. //
  333. // this rotation is just to spread the values around
  334. // the 64 bits more evenly
  335. //
  336. temp = RotateULongLong(temp, 8*characters);
  337. //
  338. // increment number of characters used in checksum
  339. // (used to verify we have enough characters)
  340. //
  341. characters++;
  342. //
  343. // XOR it into the checksum
  344. //
  345. checkSum ^= temp;
  346. } // end of string
  347. if (checkSum == 0) {
  348. TraceLog((CdromSecInfo, "DvdDriveHash: zero checksum -- using "
  349. "random value\n"));
  350. checkSum ^= DVD_RANDOMIZER[ characters%DVD_RANDOMIZER_SIZE ];
  351. characters++;
  352. }
  353. } // end of non-zero offset
  354. } // end of four strings (vendor, product, revision, serialNo)
  355. //
  356. // we have to use more than four characters
  357. // for this to be useful
  358. //
  359. if (characters <= 4) {
  360. TraceLog((CdromSecError, "DvdDriveHash: Too few useful characters (%x) "
  361. "for unique disk hash\n", characters));
  362. return INVALID_HASH;
  363. }
  364. return checkSum;
  365. }
  366. //
  367. // static, not called externally
  368. //
  369. STATIC
  370. NTSTATUS
  371. SecureDvdEncodeSettings(
  372. IN ULONGLONG DpidHash,
  373. IN ULONGLONG DriveHash,
  374. OUT PULONGLONG Obfuscated,
  375. IN UCHAR RegionMask,
  376. IN UCHAR ResetCount
  377. )
  378. // validated for all valid inputs.
  379. // validated for invalid inputs.
  380. {
  381. LARGE_INTEGER largeInteger;
  382. ULONGLONG set;
  383. LONG i;
  384. LONG rotate;
  385. UCHAR temp = 0;
  386. UCHAR random1;
  387. UCHAR random2;
  388. //
  389. // using the return from KeQueryTickCount() should give
  390. // semi-random data
  391. //
  392. KeQueryTickCount(&largeInteger);
  393. random2 = 0;
  394. for (i=0; i < sizeof(ULONGLONG); i++) {
  395. random2 ^= ((largeInteger.QuadPart >> (8*i)) & 0xff);
  396. }
  397. // set temp == sum of all 4-bit values
  398. // 16 in ULONGLONG, times max value of
  399. // 15 each is less than MAX_UCHAR
  400. for (i=0; i < 2*sizeof(ULONGLONG); i++) {
  401. temp += (UCHAR)( (DpidHash >> (4*i)) & 0xf );
  402. }
  403. //
  404. // validate these settings here
  405. //
  406. if (DpidHash == INVALID_HASH) {
  407. TraceLog((CdromSecError, "DvdEncode: Invalid DigitalProductId Hash\n"));
  408. goto UserFailure;
  409. }
  410. if (DriveHash == INVALID_HASH) {
  411. TraceLog((CdromSecError, "DvdEncode: Invalid Drive Hash\n"));
  412. goto UserFailure;
  413. }
  414. if (RegionMask == 0xff) {
  415. TraceLog((CdromSecError, "DvdEncode: Shouldn't attempt to write "
  416. "mask of 0xff\n"));
  417. goto UserFailure;
  418. }
  419. if (SecureDvdRegionInvalid(RegionMask)) {
  420. TraceLog((CdromSecError, "DvdEncode: Invalid region\n"));
  421. goto LicenseViolation;
  422. }
  423. if (ResetCount >= 2) {
  424. TraceLog((CdromSecError, "DvdEncode: Too many reset counts\n"));
  425. goto LicenseViolation;
  426. }
  427. //
  428. // using the return from KeQueryTickCount() should give
  429. // semi-random data
  430. //
  431. KeQueryTickCount(&largeInteger);
  432. random1 = 0;
  433. for (i=0; i < sizeof(ULONGLONG); i++) {
  434. random1 ^= ((largeInteger.QuadPart >> (8*i)) & 0xff);
  435. }
  436. TraceLog((CdromSecInfo,
  437. "DvdEncode: Random1 = %x Random2 = %x\n",
  438. random1, random2));
  439. //
  440. // they must all fit into UCHAR! they should, since each one is
  441. // individually a UCHAR, and only bitwise operations are being
  442. // performed on them.
  443. //
  444. //
  445. // the first cast to UCHAR prevents signed extension.
  446. // the second cast to ULONGLONG allows high bits preserved by '|'
  447. //
  448. set = (ULONGLONG)0;
  449. for (i=0; i < sizeof(ULONGLONG); i++) {
  450. set ^= (ULONGLONG)random2 << (8*i);
  451. }
  452. set ^= (ULONGLONG)
  453. ((ULONGLONG)((UCHAR)(random1 ^ temp)) << 8*7) |
  454. ((ULONGLONG)((UCHAR)(RegionMask ^ temp)) << 8*6) |
  455. ((ULONGLONG)((UCHAR)(ResetCount ^ RegionMask ^ random1)) << 8*5) |
  456. ((ULONGLONG)((UCHAR)(0)) << 8*4) |
  457. ((ULONGLONG)((UCHAR)(ResetCount ^ temp)) << 8*3) |
  458. ((ULONGLONG)((UCHAR)(ResetCount ^ ((DriveHash >> 13) & 0xff))) << 8*2) |
  459. ((ULONGLONG)((UCHAR)(random1)) << 8*1) |
  460. ((ULONGLONG)((UCHAR)(RegionMask ^ ((DriveHash >> 23) & 0xff))) << 8*0) ;
  461. TraceLog((CdromSecInfo,
  462. "DvdEncode: Pre-rotate: %016I64x temp = %x\n",
  463. set, temp));
  464. //
  465. // rotate it a semi-random, non-multiple-of-eight bits
  466. //
  467. rotate = (LONG)((DpidHash & 0xb) + 1); // {15,14,10,9,7,5,2,1}
  468. TraceLog((CdromSecInfo,
  469. "DvdEncode: Rotating %x bits\n", rotate));
  470. *Obfuscated = RotateULongLong(set, rotate);
  471. return STATUS_SUCCESS;
  472. UserFailure:
  473. *Obfuscated = INVALID_HASH;
  474. return STATUS_UNSUCCESSFUL;
  475. LicenseViolation:
  476. *Obfuscated = INVALID_HASH;
  477. return STATUS_LICENSE_VIOLATION;
  478. }
  479. STATIC
  480. NTSTATUS
  481. SecureDvdDecodeSettings(
  482. IN ULONGLONG DpidHash,
  483. IN ULONGLONG DriveHash,
  484. IN ULONGLONG Set,
  485. OUT PUCHAR RegionMask,
  486. OUT PUCHAR ResetCount
  487. )
  488. // validated for many correct inputs, of all region/reset combinations
  489. // validated for many incorrect inputs.
  490. {
  491. UCHAR random;
  492. UCHAR region;
  493. UCHAR resets;
  494. UCHAR temp = 0;
  495. LONG i, rotate;
  496. // set temp == sum of all 4-bit values
  497. // 16 in ULONGLONG, times max value of
  498. // 15 each is less than MAX_UCHAR
  499. for (i=0; i < 2*sizeof(ULONGLONG); i++) {
  500. temp += (UCHAR)( (DpidHash >> (4*i)) & 0xf );
  501. }
  502. rotate = (LONG)((DpidHash & 0xb) + 1); // {15,14,10,9,7,5,2,1}
  503. Set = RotateULongLong(Set, -rotate);
  504. TraceLog((CdromSecInfo, "DvdDecode: Post-rotate: %016I64x\n", Set));
  505. random = (UCHAR)(Set >> 8*4); // random2
  506. TraceLog((CdromSecInfo, "DvdDecode: Random2 = %x\n", random));
  507. for (i = 0; i < sizeof(ULONGLONG); i++) {
  508. Set ^= (ULONGLONG)random << (8*i);
  509. }
  510. //
  511. // bytes 6,4,3,1 are taken 'as-is'
  512. // bytes 7,5,2,0 are verified
  513. //
  514. region = ((UCHAR)(Set >> 8*6)) ^ temp;
  515. resets = ((UCHAR)(Set >> 8*3)) ^ temp;
  516. random = ((UCHAR)(Set >> 8*1)); // make it random1
  517. TraceLog((CdromSecInfo, "DvdDecode: Random1 = %x Region = %x Resets = %x\n",
  518. random, region, resets));
  519. // verify the bits
  520. if (((UCHAR)(Set >> 8*7)) != (random ^ temp)) {
  521. TraceLog((CdromSecError, "DvdDecode: Invalid Byte 7\n"));
  522. goto ViolatedLicense;
  523. }
  524. random ^= (UCHAR)(Set >> 8*5);
  525. if (random != (resets ^ region)) {
  526. TraceLog((CdromSecError, "DvdDecode: Invalid Byte 5\n"));
  527. goto ViolatedLicense;
  528. }
  529. random = (UCHAR)(DriveHash >> 13);
  530. random ^= (UCHAR)(Set >> 8*2);
  531. if (random != resets) {
  532. TraceLog((CdromSecError, "DvdDecode: Invalid Byte 2\n"));
  533. goto ViolatedLicense;
  534. }
  535. random = (UCHAR)(DriveHash >> 23);
  536. random ^= (UCHAR)(Set >> 8*0);
  537. if (random != region) {
  538. TraceLog((CdromSecError, "DvdDecode: Invalid Byte 0\n"));
  539. goto ViolatedLicense;
  540. }
  541. if (SecureDvdRegionInvalid(region)) {
  542. TraceLog((CdromSecError, "DvdDecode: Region was invalid\n"));
  543. goto ViolatedLicense;
  544. }
  545. if (resets >= 2) {
  546. TraceLog((CdromSecError, "DvdDecode: Reset count was invalid\n"));
  547. goto ViolatedLicense;
  548. }
  549. TraceLog((CdromSecInfo, "DvdDecode: Successfully validated stored data\n"));
  550. *RegionMask = region;
  551. *ResetCount = resets;
  552. return STATUS_SUCCESS;
  553. ViolatedLicense:
  554. *RegionMask = 0x00;
  555. *ResetCount = 0x00;
  556. return STATUS_LICENSE_VIOLATION;
  557. }
  558. STATIC
  559. NTSTATUS
  560. SecureDvdGetSettingsCallBack(
  561. IN PWSTR ValueName,
  562. IN ULONG ValueType,
  563. IN PVOID ValueData,
  564. IN ULONG ValueLength,
  565. IN PVOID UnusedContext,
  566. IN PDVD_REGISTRY_CONTEXT Context
  567. )
  568. {
  569. ULONGLONG hash = 0;
  570. NTSTATUS status;
  571. if (ValueType != REG_QWORD) {
  572. TraceLog((CdromSecError, "DvdGetSettingsCallback: Not REG_BINARY\n"));
  573. goto ViolatedLicense;
  574. }
  575. if (ValueLength != sizeof(ULONGLONG)) {
  576. TraceLog((CdromSecError, "DvdGetSettingsCallback: DVD Settings data too "
  577. "small (%x bytes)\n", ValueLength));
  578. goto ViolatedLicense;
  579. }
  580. hash = *((PULONGLONG)ValueData);
  581. if (hash == INVALID_HASH) {
  582. TraceLog((CdromSecError, "DvdGetSettingsCallback: Invalid hash stored?\n"));
  583. goto ViolatedLicense;
  584. }
  585. //
  586. // validate the data
  587. // this also sets the values in the context upon success.
  588. //
  589. status = SecureDvdDecodeSettings(Context->DpidHash,
  590. Context->DriveHash,
  591. hash,
  592. &Context->RegionMask,
  593. &Context->ResetCount);
  594. if (status == STATUS_LICENSE_VIOLATION) {
  595. TraceLog((CdromSecError, "DvdGetSettingsCallback: data was violated!\n"));
  596. goto ViolatedLicense;
  597. }
  598. //
  599. // the above call to SecureDvdDecodeSettings can only return
  600. // success or a license violation
  601. //
  602. ASSERT(NT_SUCCESS(status));
  603. return STATUS_SUCCESS;
  604. ViolatedLicense:
  605. Context->DriveHash = INVALID_HASH;
  606. Context->DpidHash = INVALID_HASH;
  607. Context->RegionMask = 0;
  608. Context->ResetCount = 0;
  609. return STATUS_LICENSE_VIOLATION;
  610. }
  611. STATIC
  612. NTSTATUS
  613. SecureDvdGetDigitalProductIdCallBack(
  614. IN PWSTR ValueName,
  615. IN ULONG ValueType,
  616. IN PDIGITALPID DigitalPid, // ValueData
  617. IN ULONG ValueLength,
  618. IN PVOID UnusedVariable,
  619. IN PULONGLONG DpidHash
  620. )
  621. // validated for non-REG_BINARY
  622. // validated for good data
  623. // validated for short data
  624. {
  625. NTSTATUS status = STATUS_LICENSE_VIOLATION;
  626. ULONGLONG hash = 0;
  627. if (ValueType != REG_BINARY) {
  628. TraceLog((CdromSecError, "DvdDPIDCallback: Not REG_BINARY\n"));
  629. *DpidHash = INVALID_HASH;
  630. return STATUS_LICENSE_VIOLATION;
  631. }
  632. if (ValueLength < 4*sizeof(ULONGLONG)) {
  633. TraceLog((CdromSecError,
  634. "DvdDPIDCallback: DPID data too small (%x bytes)\n",
  635. ValueLength));
  636. *DpidHash = INVALID_HASH;
  637. return STATUS_LICENSE_VIOLATION;
  638. }
  639. //
  640. // apparently, only 13 bytes of the DigitalPID are
  641. // going to stay static across upgrades. even these
  642. // will change if the boot hard drive, video card, or
  643. // bios signature changes. nonetheless, this is only
  644. // supposed to keep the honest people honest. :)
  645. //
  646. //
  647. // 8 bytes to fill == 64 bytes (need to rotate at least 48 bits)
  648. //
  649. TraceLog((CdromSecInfo,
  650. "Bios %08x Video %08x VolSer %08x\n",
  651. DigitalPid->dwBiosChecksumStatic,
  652. DigitalPid->dwVideoBiosChecksumStatic,
  653. DigitalPid->dwVolSerStatic));
  654. hash ^= DigitalPid->dwBiosChecksumStatic; // 4 bytes // bios signature
  655. hash = RotateULongLong(hash, 13); // prime number
  656. hash ^= DigitalPid->dwVideoBiosChecksumStatic; // 4 bytes // video card
  657. hash = RotateULongLong(hash, 13); // prime number
  658. hash ^= DigitalPid->dwVolSerStatic; // 4 bytes // hard drive
  659. hash = RotateULongLong(hash, 13); // prime number
  660. *DpidHash = hash;
  661. return STATUS_SUCCESS;
  662. }
  663. STATIC
  664. NTSTATUS
  665. SecureDvdReturnDPIDHash(
  666. PULONGLONG DpidHash
  667. )
  668. {
  669. RTL_QUERY_REGISTRY_TABLE queryTable[2];
  670. NTSTATUS status;
  671. // cannot be PAGED_CODE() because queryTable cannot be swapped out!
  672. //
  673. // query the value
  674. //
  675. RtlZeroMemory(&(queryTable[0]), 2 * sizeof(RTL_QUERY_REGISTRY_TABLE));
  676. queryTable[0].Name = L"DigitalProductId";
  677. queryTable[0].EntryContext = DpidHash;
  678. queryTable[0].DefaultType = 0;
  679. queryTable[0].DefaultData = NULL;
  680. queryTable[0].DefaultLength = 0;
  681. queryTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED;
  682. queryTable[0].QueryRoutine = SecureDvdGetDigitalProductIdCallBack;
  683. *DpidHash = INVALID_HASH;
  684. status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
  685. L"\\Registry\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion",
  686. &(queryTable[0]),
  687. NULL,
  688. NULL);
  689. if (status == STATUS_LICENSE_VIOLATION) {
  690. TraceLog((CdromSecError,
  691. "DvdReturnDPIDHash: Invalid DPID!\n"));
  692. } else if (!NT_SUCCESS(status)) {
  693. TraceLog((CdromSecError,
  694. "DvdReturnDPIDHash: Cannot get DPID (%x)\n", status));
  695. } else {
  696. TraceLog((CdromSecInfo,
  697. "DvdReturnDPIDHash: Hash is now %I64x\n",
  698. *DpidHash));
  699. }
  700. return status;
  701. }
  702. ////////////////////////////////////////////////////////////////////////////////
  703. //// Everything past here has not been component tested
  704. ////////////////////////////////////////////////////////////////////////////////
  705. #define SECURE_DVD_SET_SECURITY_ON_HANDLE 0
  706. STATIC
  707. NTSTATUS
  708. SecureDvdSetHandleSecurity(
  709. IN HANDLE Handle
  710. )
  711. {
  712. #if SECURE_DVD_SET_SECURITY_ON_HANDLE
  713. PACL newAcl = NULL;
  714. ULONG newAclSize;
  715. SECURITY_DESCRIPTOR securityDescriptor;
  716. NTSTATUS status;
  717. //
  718. // from \nt\private\ntos\io\pnpinit.c
  719. //
  720. //SeEnableAccessToExports();
  721. TRY {
  722. newAclSize = sizeof(ACL);
  723. newAclSize += sizeof(ACCESS_ALLOWED_ACE);
  724. newAclSize -= sizeof(ULONG);
  725. newAclSize += RtlLengthSid(SeExports->SeLocalSystemSid);
  726. newAcl = ExAllocatePoolWithTag(PagedPool, newAclSize, DVD_TAG_SECURITY);
  727. if (newAcl == NULL) {
  728. status = STATUS_INSUFFICIENT_RESOURCES;
  729. LEAVE;
  730. }
  731. status = RtlCreateSecurityDescriptor(&securityDescriptor,
  732. SECURITY_DESCRIPTOR_REVISION);
  733. if (!NT_SUCCESS(status)) {
  734. ASSERT(!"failed to create a security descriptor?");
  735. LEAVE;
  736. }
  737. status = RtlCreateAcl(newAcl, newAclSize, ACL_REVISION);
  738. if (!NT_SUCCESS(status)) {
  739. ASSERT(!"failed to create a new ACL?");
  740. LEAVE;
  741. }
  742. status = RtlAddAccessAllowedAce(newAcl,
  743. ACL_REVISION,
  744. KEY_ALL_ACCESS,
  745. SeExports->SeLocalSystemSid);
  746. if (!NT_SUCCESS(status)) {
  747. ASSERT(!"failed to add LocalSystem to ACL");
  748. LEAVE;
  749. }
  750. status = RtlSetDaclSecurityDescriptor(&securityDescriptor,
  751. TRUE,
  752. newAcl,
  753. FALSE);
  754. if (!NT_SUCCESS(status)) {
  755. ASSERT(!"failed to set acl in security descriptor?");
  756. LEAVE;
  757. }
  758. status = RtlValidSecurityDescriptor(&securityDescriptor);
  759. if (!NT_SUCCESS(status)) {
  760. ASSERT(!"failed to validate security descriptor?");
  761. LEAVE;
  762. }
  763. status = ZwSetSecurityObject(Handle,
  764. // PROTECTED_DACL_SECURITY_INFORMATION,
  765. DACL_SECURITY_INFORMATION,
  766. &securityDescriptor);
  767. if (!NT_SUCCESS(status)) {
  768. ASSERT(!"Failed to set security on handle\n");
  769. LEAVE;
  770. }
  771. status = STATUS_SUCCESS;
  772. } FINALLY {
  773. if (newAcl != NULL) {
  774. ExFreePool(newAcl);
  775. newAcl = NULL;
  776. }
  777. }
  778. #endif
  779. return STATUS_SUCCESS;
  780. }
  781. STATIC
  782. NTSTATUS
  783. SecureDvdGetRegistryHandle(
  784. IN ULONGLONG DpidHash,
  785. OUT PHANDLE Handle
  786. )
  787. {
  788. OBJECT_ATTRIBUTES objectAttributes;
  789. UNICODE_STRING hashString;
  790. NTSTATUS status;
  791. LONG i;
  792. //
  793. // using char[] instead of char* allows modification of the
  794. // string in this routine (a way of obfuscating the string)
  795. // 0 ....+.... 1....+.. ..2....+. ...3....+. ...4
  796. WCHAR string[] = L"\\Registry\\Machine\\Software\\Microsoft\\Windows NT";
  797. WCHAR *hash = &(string[37]);
  798. PULONGLONG hashAsUlonglong = (PULONGLONG)hash;
  799. for (i = 0; i < sizeof(ULONGLONG); i++) {
  800. UCHAR temp;
  801. temp = (UCHAR)(DpidHash >> (8*i));
  802. SET_FLAG(temp, 0x20); // more than 32
  803. CLEAR_FLAG(temp, 0x80); // less than 128
  804. hash[i] = (WCHAR)temp; // make it a wide char
  805. }
  806. hash[i] = UNICODE_NULL;
  807. RtlInitUnicodeString(&hashString, string);
  808. RtlZeroMemory(&objectAttributes, sizeof(OBJECT_ATTRIBUTES));
  809. InitializeObjectAttributes(&objectAttributes,
  810. &hashString,
  811. OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
  812. RTL_REGISTRY_ABSOLUTE, // NULL?
  813. NULL // no security descriptor
  814. );
  815. status = ZwCreateKey(Handle,
  816. KEY_ALL_ACCESS,
  817. &objectAttributes,
  818. 0,
  819. NULL, // can be a unicode string....
  820. REG_OPTION_NON_VOLATILE,
  821. NULL);
  822. if (!NT_SUCCESS(status)) {
  823. TraceLog((CdromSecError,
  824. "DvdGetRegistryHandle: Failed to create key (%x)\n",
  825. status));
  826. return status;
  827. }
  828. status = SecureDvdSetHandleSecurity(*Handle);
  829. if (!NT_SUCCESS(status)) {
  830. TraceLog((CdromSecError,
  831. "DvdGetRegistryHandle: Failed to set key security (%x)\n",
  832. status));
  833. ZwClose(*Handle);
  834. *Handle = INVALID_HANDLE_VALUE;
  835. }
  836. return status;
  837. }
  838. STATIC
  839. VOID
  840. SecureDvdCreateValueNameFromHash(
  841. IN ULONGLONG DriveHash,
  842. OUT PWCHAR HashString
  843. )
  844. {
  845. PUCHAR buffer = (PUCHAR)HashString;
  846. LONG i;
  847. RtlZeroMemory(HashString, 17*sizeof(WCHAR));
  848. sprintf(buffer, "%016I64x", DriveHash);
  849. // now massage the data to be unicode
  850. for (i = 15; i >= 0; i--) {
  851. HashString[i] = buffer[i];
  852. }
  853. }
  854. STATIC
  855. NTSTATUS
  856. SecureDvdReadOrWriteRegionAndResetCount(
  857. IN PDEVICE_OBJECT Fdo,
  858. IN UCHAR NewRegion,
  859. IN BOOLEAN ReadingTheValues
  860. )
  861. //
  862. // NewRegion is ignored if ReadingTheValues is TRUE
  863. //
  864. {
  865. PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
  866. PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension;
  867. PCDROM_DATA cddata;
  868. NTSTATUS status;
  869. ULONG keyDisposition;
  870. DVD_REGISTRY_CONTEXT registryContext;
  871. HANDLE semiSecureHandle = INVALID_HANDLE_VALUE;
  872. PAGED_CODE();
  873. ASSERT(commonExtension->IsFdo);
  874. cddata = (PCDROM_DATA)(commonExtension->DriverData);
  875. if (cddata->DvdRpc0LicenseFailure) {
  876. TraceLog((CdromSecError,
  877. "Dvd%sSettings: Already violated licensing\n",
  878. (ReadingTheValues ? "Read" : "Write")
  879. ));
  880. goto ViolatedLicense;
  881. }
  882. RtlZeroMemory(&registryContext, sizeof(DVD_REGISTRY_CONTEXT));
  883. //
  884. // first ensure they didn't violate the CSS agreement
  885. // by checking for the existance of Mr. Enigma
  886. //
  887. {
  888. HANDLE regHandle;
  889. OBJECT_ATTRIBUTES objectAttributes;
  890. UNICODE_STRING mrEnigmaString;
  891. RtlInitUnicodeString(&mrEnigmaString,
  892. L"\\Registry\\Machine\\Software\\Microsoft\\Mr. Enigma");
  893. RtlZeroMemory(&objectAttributes, sizeof(OBJECT_ATTRIBUTES));
  894. InitializeObjectAttributes(&objectAttributes,
  895. &mrEnigmaString,
  896. OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
  897. RTL_REGISTRY_ABSOLUTE, // NULL?
  898. NULL); // no security descriptor
  899. status = ZwOpenKey(&regHandle, KEY_ALL_ACCESS, &objectAttributes);
  900. if (!NT_SUCCESS(status)) {
  901. TraceLog((CdromSecError,
  902. "Dvd%sSettings: Mr. Enigma doesn't exist. This will "
  903. "fail DVD video playback\n",
  904. (ReadingTheValues ? "Read" : "Write")
  905. ));
  906. // red herring :)
  907. } else {
  908. ZwClose(regHandle);
  909. }
  910. }
  911. //
  912. // then, get the DigitalProductIdHash and this DriveHash
  913. //
  914. {
  915. status = SecureDvdReturnDPIDHash(&registryContext.DpidHash);
  916. // if this fails, we are in serious trouble!
  917. if (status == STATUS_LICENSE_VIOLATION) {
  918. TraceLog((CdromSecError,
  919. "Dvd%sSettings: License error getting DPIDHash?\n",
  920. (ReadingTheValues ? "Read" : "Write")));
  921. goto ViolatedLicense;
  922. } else if (!NT_SUCCESS(status)) {
  923. TraceLog((CdromSecError,
  924. "Dvd%sSettings: Couldn't get DPID Hash! (%x)\n",
  925. (ReadingTheValues ? "Read" : "Write"), status));
  926. goto RetryExit;
  927. }
  928. if (registryContext.DpidHash == INVALID_HASH) {
  929. goto ErrorExit;
  930. }
  931. registryContext.DriveHash =
  932. SecureDvdGetDriveHash(fdoExtension->DeviceDescriptor);
  933. if (registryContext.DriveHash == INVALID_HASH) {
  934. TraceLog((CdromSecError,
  935. "Dvd%sSettings: Couldn't create drive hash(!)\n",
  936. (ReadingTheValues ? "Read" : "Write")));
  937. goto ErrorExit;
  938. }
  939. }
  940. //
  941. // finally get a handle based upon the DigitalProductIdHash
  942. // to our "semi-secure" registry key, creating it if neccessary.
  943. //
  944. status= SecureDvdGetRegistryHandle(registryContext.DpidHash,
  945. &semiSecureHandle);
  946. if (!NT_SUCCESS(status)) {
  947. TraceLog((CdromSecError,
  948. "Dvd%sSettings: Could not get semi-secure handle %x\n",
  949. (ReadingTheValues ? "Read" : "Write"), status));
  950. goto ErrorExit;
  951. }
  952. //
  953. // if reading the values, use the semi-secure handle to open a subkey,
  954. // read its data, close the handle, it.
  955. //
  956. //
  957. if (ReadingTheValues) {
  958. WCHAR hashString[17]; // 16 + NULL
  959. RTL_QUERY_REGISTRY_TABLE queryTable[2];
  960. SecureDvdCreateValueNameFromHash(registryContext.DriveHash, hashString);
  961. RtlZeroMemory(&queryTable[0], 2*sizeof(RTL_QUERY_REGISTRY_TABLE));
  962. queryTable[0].DefaultData = NULL;
  963. queryTable[0].DefaultLength = 0;
  964. queryTable[0].DefaultType = 0;
  965. queryTable[0].EntryContext = &registryContext;
  966. queryTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED;
  967. queryTable[0].Name = hashString;
  968. queryTable[0].QueryRoutine = SecureDvdGetSettingsCallBack;
  969. status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
  970. semiSecureHandle,
  971. &queryTable[0],
  972. &registryContext,
  973. NULL);
  974. if (status == STATUS_LICENSE_VIOLATION) {
  975. TraceLog((CdromSecError,
  976. "Dvd%sSettings: Invalid value in registry!\n",
  977. (ReadingTheValues ? "Read" : "Write")));
  978. goto ViolatedLicense;
  979. } else if (!NT_SUCCESS(status)) {
  980. TraceLog((CdromSecError,
  981. "Dvd%sSettings: Other non-license error (%x)\n",
  982. (ReadingTheValues ? "Read" : "Write"), status));
  983. goto ErrorExit;
  984. }
  985. //
  986. // set the real values....
  987. //
  988. cddata->Rpc0SystemRegion = registryContext.RegionMask;
  989. cddata->Rpc0SystemRegionResetCount = registryContext.ResetCount;
  990. //
  991. // everything is kosher!
  992. //
  993. TraceLog((CdromSecInfo,
  994. "Dvd%sSettings: Region %x Reset %x\n",
  995. (ReadingTheValues ? "Read" : "Write"),
  996. cddata->Rpc0SystemRegion,
  997. cddata->Rpc0SystemRegionResetCount));
  998. } else { // !ReadingTheValues, iow, writing them....
  999. //
  1000. // if writing the values, obfuscate them first (which also validates),
  1001. // then use the semi-secure handle to write the subkey
  1002. //
  1003. WCHAR hashString[17]; // 16 + NULL
  1004. ULONGLONG obfuscated;
  1005. //
  1006. // don't munge the device extension until we modify the registry
  1007. // (see below for modification of device extension data)
  1008. //
  1009. registryContext.RegionMask = NewRegion;
  1010. registryContext.ResetCount = cddata->Rpc0SystemRegionResetCount-1;
  1011. //
  1012. // this also validates the settings
  1013. //
  1014. SecureDvdCreateValueNameFromHash(registryContext.DriveHash, hashString);
  1015. status = SecureDvdEncodeSettings(registryContext.DpidHash,
  1016. registryContext.DriveHash,
  1017. &obfuscated,
  1018. registryContext.RegionMask,
  1019. registryContext.ResetCount);
  1020. if (status == STATUS_LICENSE_VIOLATION) {
  1021. TraceLog((CdromSecError,
  1022. "Dvd%sSettings: User may have modified memory! "
  1023. "%x %x\n", (ReadingTheValues ? "Read" : "Write"),
  1024. registryContext.RegionMask,
  1025. registryContext.ResetCount));
  1026. goto ViolatedLicense;
  1027. } else if (!NT_SUCCESS(status)) {
  1028. TraceLog((CdromSecError,
  1029. "Dvd%sSettings: Couldn't obfuscate data %x %x\n",
  1030. (ReadingTheValues ? "Read" : "Write"),
  1031. registryContext.RegionMask,
  1032. registryContext.ResetCount));
  1033. goto ErrorExit;
  1034. }
  1035. //
  1036. // save them for posterity
  1037. //
  1038. TraceLog((CdromSecInfo,
  1039. "Dvd%sSettings: Data is %016I64x\n",
  1040. (ReadingTheValues ? "Read" : "Write"),
  1041. obfuscated));
  1042. status = RtlWriteRegistryValue(RTL_REGISTRY_HANDLE,
  1043. semiSecureHandle,
  1044. hashString,
  1045. REG_QWORD,
  1046. &obfuscated,
  1047. (ULONG)(sizeof(ULONGLONG))
  1048. );
  1049. if (!NT_SUCCESS(status)) {
  1050. TraceLog((CdromSecError,
  1051. "Dvd%sSettings: Couldn't save %x\n",
  1052. (ReadingTheValues ? "Read" : "Write"), status));
  1053. goto ErrorExit;
  1054. }
  1055. //
  1056. // make the change in the device extension data also
  1057. //
  1058. cddata->Rpc0SystemRegion = NewRegion;
  1059. cddata->Rpc0SystemRegionResetCount--;
  1060. TraceLog((CdromSecInfo,
  1061. "Dvd%sSettings: Region %x Reset %x\n",
  1062. (ReadingTheValues ? "Read" : "Write"),
  1063. cddata->Rpc0SystemRegion,
  1064. cddata->Rpc0SystemRegionResetCount));
  1065. }
  1066. if (semiSecureHandle != INVALID_HANDLE_VALUE) {
  1067. ZwClose(semiSecureHandle);
  1068. }
  1069. return STATUS_SUCCESS;
  1070. ViolatedLicense: {
  1071. PIO_ERROR_LOG_PACKET errorLogEntry;
  1072. if (semiSecureHandle != INVALID_HANDLE_VALUE) {
  1073. ZwClose(semiSecureHandle);
  1074. }
  1075. /*
  1076. errorLogEntry = (PIO_ERROR_LOG_ENTRY)
  1077. IoAllocateErrorLogEntry(Fdo,
  1078. (UCHAR)(sizeof(IO_ERROR_LOG_PACKET)));
  1079. if (errorLogEntry != NULL) {
  1080. errorLogEntry->FinalStatus = STATUS_LICENSE_VIOLATION;
  1081. errorLogEntry->ErrorCode = STATUS_LICENSE_VIOLATION;
  1082. errorLogEntry->MajorFunctionCode = IRP_MJ_START_DEVICE;
  1083. IoWriteErrorLogEntry(errorLogEntry);
  1084. }
  1085. */
  1086. TraceLog((CdromSecError,
  1087. "Dvd%sSettings: License Violation Detected\n",
  1088. (ReadingTheValues ? "Read" : "Write")));
  1089. cddata->DvdRpc0LicenseFailure = TRUE; // no playback
  1090. cddata->Rpc0SystemRegion = 0xff; // no regions
  1091. cddata->Rpc0SystemRegionResetCount = 0; // no resets
  1092. return STATUS_LICENSE_VIOLATION;
  1093. }
  1094. RetryExit:
  1095. if (ReadingTheValues) {
  1096. cddata->Rpc0RetryRegistryCallback = 1;
  1097. }
  1098. //
  1099. // fall-through to Error Exit...
  1100. //
  1101. ErrorExit:
  1102. TraceLog((CdromSecError,
  1103. "Dvd%sSettings: Non-License Error Detected\n",
  1104. (ReadingTheValues ? "Read" : "Write")));
  1105. //
  1106. // don't modify the device extension on non-license-violation errors
  1107. //
  1108. if (semiSecureHandle != INVALID_HANDLE_VALUE) {
  1109. ZwClose(semiSecureHandle);
  1110. }
  1111. return STATUS_UNSUCCESSFUL;
  1112. }
  1113. ////////////////////////////////////////////////////////////////////////////////
  1114. //
  1115. // The following functions are externally accessible. They therefore cannot
  1116. // be either STATIC nor INLINE
  1117. // static to make debugging more difficult in the shipping versions.
  1118. //
  1119. // These exports return one of only three NTSTATUS values:
  1120. // STATUS_SUCCESS
  1121. // STATUS_UNSUCCESSFUL
  1122. // STATUS_LICENSE_VIOLATION
  1123. //
  1124. ////////////////////////////////////////////////////////////////////////////////
  1125. NTSTATUS
  1126. CdRomGetRpc0Settings(
  1127. IN PDEVICE_OBJECT Fdo
  1128. )
  1129. {
  1130. PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension;
  1131. PCDROM_DATA cddata = (PCDROM_DATA)(commonExtension->DriverData);
  1132. NTSTATUS status;
  1133. KeWaitForMutexObject(&cddata->Rpc0RegionMutex, UserRequest, KernelMode,
  1134. FALSE, NULL);
  1135. status = SecureDvdReadOrWriteRegionAndResetCount(Fdo, 0, TRUE);
  1136. KeReleaseMutex(&cddata->Rpc0RegionMutex, FALSE);
  1137. return status;
  1138. }
  1139. NTSTATUS
  1140. CdRomSetRpc0Settings(
  1141. IN PDEVICE_OBJECT Fdo,
  1142. IN UCHAR NewRegion
  1143. )
  1144. {
  1145. PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension;
  1146. PCDROM_DATA cddata = (PCDROM_DATA)(commonExtension->DriverData);
  1147. NTSTATUS status;
  1148. KeWaitForMutexObject(&cddata->Rpc0RegionMutex, UserRequest, KernelMode,
  1149. FALSE, NULL);
  1150. status = SecureDvdReadOrWriteRegionAndResetCount(Fdo, NewRegion, FALSE);
  1151. KeReleaseMutex(&cddata->Rpc0RegionMutex, FALSE);
  1152. return status;
  1153. }
  1154. #if 0
  1155. // @@END_DDKSPLIT
  1156. NTSTATUS
  1157. CdRomGetRpc0Settings(
  1158. IN PDEVICE_OBJECT Fdo
  1159. )
  1160. {
  1161. PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension;
  1162. PCDROM_DATA cddata = (PCDROM_DATA)(commonExtension->DriverData);
  1163. cddata->Rpc0SystemRegion = (UCHAR)(~1); // region one
  1164. cddata->Rpc0SystemRegionResetCount = 0; // no resets
  1165. return STATUS_SUCCESS;
  1166. }
  1167. NTSTATUS
  1168. CdRomSetRpc0Settings(
  1169. IN PDEVICE_OBJECT Fdo,
  1170. IN UCHAR NewRegion
  1171. )
  1172. {
  1173. return STATUS_SUCCESS;
  1174. }
  1175. // @@BEGIN_DDKSPLIT
  1176. #endif // 0 -- DDK stub for all the stuff we do...
  1177. // @@END_DDKSPLIT