DRAFT: Take more than your usual care.

Bug Check 0x9A

The SYSTEM_LICENSE_VIOLATION bug check reports a fatal error in validating the license to use Windows.

Bug Check Code: SYSTEM_LICENSE_VIOLATION
1st Argument: case
2nd Argument: status code or number of bytes or 0, depending on case
3rd Argument: sub-case or 0
4th Argument: 0

Some two dozen different causes are indicated by the first argument. For most, the second argument is a status code. Some have sub-cases indicated by the third argument. The fourth argument is always zero.

To understand all this bug check’s cases, it helps to know the kernel’s algorithm for license validation. Windows Vista has two distinct approaches. One is inherited from earlier versions and is here referred to as license protection. The other involves the new scheme of named license values and is here referred to as tamper detection.

License Protection

For license protection, relevant registry values are monitored for changes, most such changes are signalled as license violations, and all changes are undone by rewriting the value.

Whatever may be suggested by this bug check’s symbolic name or stated explicitly in Microsoft’s documentation, an occurrence of bug check 0x9A for license protection does not mean that the license for using Windows has been found to be violated. The bug check means only that the kernel ran into a problem while trying to detect a violation and protect the license. Though the most likely reason is very plausibly that a user has damaged relevant registry keys or values while attempting to violate the license, the violation is not of itself what bug check 0x9A reports.

In particular, when the detection proceeds without error and actually does find that the relevant registry values have changed in a way that violates the license, the kernel does not respond with any bug check, let alone with this one. What it does instead is to raise a so-called hard error. The visible outcome is not a stopped system, just an entry in the System event log, with the stern text:

Application popup: Windows - License Violation : The system has detected tampering with your 
registered product type. This is a violation of your software license. Tampering with product type 
is not permitted. 

Incidentally, lawyers may want to consider that the presence of such an entry in the System log is not immediately good evidence that the system detected a license violation. It can be produced by any kernel-mode component, such as a driver, simply by calling the (admittedly undocumented) function ExRaiseHardError and giving STATUS_LICENSE_VIOLATION (0xC000026A) as the status code.

Initialisation

License protection is prepared at the beginning of phase 2 of system initialisation. Ordinarily, the failure of any operation for initialising license protection causes bug check 0x9A. In Setup mode, most failures are not fatal but just cause the kernel to proceed no further with protecting the license.

The Setup Key

Though license protection is much reduced in Setup mode, the kernel must be certain that it is in Setup mode. The determination is from the registry values SetupType and SystemSetupInProgress in the HKEY_LOCAL_MACHINE\System\Setup key. The kernel assumes that each value has at least 4 bytes of data. The kernel interprets 1 and 4 for SetupType as meaning that Windows is in Setup mode, and 1 for SystemSetupInProgress as meaning that system setup is in progress.

The kernel must not only open the Setup key but also reference it as an object. The only purpose to this is to protect the key from being deleted. (The kernel’s code for deleting a registry key explictly checks that the object to be deleted is not this one that the kernel has kept referenced for license protection.) If Setup mode is detected from the subsequent reading of SetupType, the kernel dereferences the object.

In WinPE mode, checking these values in the Setup key is as far as the kernel proceeds with license protection. (WinPE mode is known from phase 1, if MININT is in the boot options.) That it proceeds even this far seems useless and is presumably vestigial: earlier versions will have done something non-trivial, namely to protect the SystemPrefix value in the Setup key.

The ProductOptions Key

The essential registry values for differentiating the various editions of a Windows version are ProductType and ProductSuite in the registry key HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\ProductOptions.

In Setup mode, failure to open the ProductOptions key causes the kernel to abandon license protection.

Except in Setup mode, the kernel must not only open the ProductOptions key but also reference it as an object. The only purpose to this is to protect the key from being deleted. (The kernel’s code for deleting a registry key explictly checks that the object to be deleted is not this one that the kernel has kept referenced for license validation.)

In Windows Vista, the product type and product suite are determined only in part from the ProductType and ProductSuite values. A value named ProductPolicy (also in the ProductOptions key) provides for an essentially arbitrary number of named license values. One of these, Kernel-ProductInfo, is checked in phase 0 of initialisation and may override what was read from ProductType and ProductSuite. If the product type has been re-determined from Kernel-ProductInfo, then in phase 2 unless in Setup mode, the kernel rewrites both values, whether or not they are changed. It then proceeds as if for earlier versions, which involves re-reading what it has just written.

The general idea to protecting the ProductType and ProductSuite values is to read them now, save their original data, and then watch the ProductOptions key for changes, so that any attempt to change these values can be undone by rewriting the original data. It seems that the ProductType is mandatory for the scheme but the ProductSuite is merely desirable. However, there are quirks. If the kernel cannot obtain memory for the ProductType value’s original data, it abandons license protection. In Setup mode, failure to read the ProductType also causes the kernel to abandon license protection. The ProductSuite value gets protected only if it has more than four bytes of data. For some reason, obtaining memory in which to hold this data is vital, but failure to read the data is not. In Setup mode, failure to read the ProductSuite causes the kernel to abandon license protection.

Having saved the original data for these two values, the kernel registers a work queue item that is to execute as a change-notify callback for the ProductOptions key. The action taken should this work queue item ever execute is discussed below. In Setup mode, failure to register the callback causes the kernel to abandon license protection.

The LicenseInfoSuites Key

More registry values for licensing are supported through the HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\LicenseInfoSuites key. Even the presence of this key is merely optional. If the key cannot be opened, the initialisation of license protection is complete.

The LicenseInfoSuites key may have subkeys, each treated here as representing some sort of suite. Each of these subkeys may have yet more subkeys, each treated here as representing some sort of suite product. (These terms are from Microsoft’s documentation of the bug check. The LicenseInfoSuites key and its subkeys seem otherwise to be undocumented.)

The kernel’s only interest in the keys for the suites and suite products is the ConcurrentLimit value.

TO BE DONE

Changes in the ProductOptions Key

During initialisation, the kernel registers a work queue item which is to execute if changes are detected in the ProductOptions key. Of course, not all changes in that key suggest a license violation, just changes to the ProductType value and only then if not in Setup mode. A change of ProductType to LanmanNT or ServerNT from anything other than WinNT is not treated as a license violation. Any other change, including if implied by failure to read the value, is.

TO BE DONE

Changes in a LicenseInfoSuites Subkey

TO BE DONE

Tamper Detection

TO BE DONE

Approximately every hour, the kernel resynchronises system time with the real-time clock. (This resynchronisation is performed at other times, too, particularly after events that may have stopped the handling of timer interrupts, as when execution breaks to a kernel-mode debugger.) This hourly update is also an opportunity for license validation. Unless system setup is in progress, the kernel checks that the license data has not been tampered with and that the license has not expired its evaluation period (as specified by the Kernel-ExpirationDate license value).

Bug Check Parameters

In the table that follows, the several cases in which the 3rd argument takes values that Microsoft does not document for this bug check are highlighted yellow.

1st Argument 2nd Argument 3rd Argument Remarks
0x02 status code 0 error opening Setup key (during initialisation)
0x03 status code 0 error reading SetupType or SystemSetupInProgress value (during initialisation)
0x06 status code 0 error opening ProductOptions key (during initialisation)
0x07 status code 0 error reading ProductType value (during initialisation)
0x08 status code 0 error registering change-notify callback for ProductOptions key (during initialisation)
0x0B status code 0 error referencing Setup key as object (during initialisation)
0x0C status code 0 error referencing ProductOptions key as object (during initialisation)
0x0D status code 0 error re-opening ProductOptions key (during ProductOptions callback)
0x11 status code 1 error setting ProductType (during initialisation or in ProductOptions callback);
error setting ProductPolicy (in ProductOptions callback)
2 error setting ProductSuite (during initialisation or in ProductOptions callback)
4 error re-registering change-notify callback for ProductOptions key
0x12 status code 0 error re-opening key for suite
0x13 status code 0 error reading a suite key
1 error reading the LicenseInfoSuites key
2 error reading ConcurrentLimit for suite
0x14 size wanted 0 for information about key for suite product;
1 for composing registry paths for suite products;
2 to save data for ProductSuite value;
3 for composing registry paths for suites;
4 for information about key for suite;
5 for reading ConcurrentLimit for suite;
6 for array of structures describing suites;
7 to save registry path for particular suite
error obtaining memory for intended operation
0x15 status code 0 error setting ConcurrentLimit for suite
0x16 status code 0 error opening key for suite product
1 error opening key for suite
0x17 status code 0 error setting ConcurrentLimit for suite product
0x18 status code 0 during change-notify callback;
1 during initialisation
error registering change-notify callback for suite key
0x1A status code 0 error enumerating LicenseInfoSuites key
0x1B 0 0 tampering with license data detected